From 2b94f095616d43419af33fbe2af4d746813eae8f Mon Sep 17 00:00:00 2001 From: meanmail Date: Thu, 25 Jan 2024 17:30:02 +0100 Subject: [PATCH 01/57] Update Python version and refactor CI/CD pipeline Upgraded Python version to 3.10.13. Refactored GitHub Actions workflows, renamed 'main.yml' to 'ci.yml', introduced new workflows for auto-formatter, and author assignment. Moreover, the 'requirements.txt' and 'setup.py' files have been removed as dependencies are managed by Poetry. (cherry picked from commit 5300951d152915c835d8e3ff541210bd08e323d9) --- .flake8 | 23 - .github/dependabot.yml | 17 + .github/workflows/actions/prepare/action.yml | 18 + .github/workflows/auto-author-assign.yml | 16 + .github/workflows/auto-format.yml | 35 + .github/workflows/ci.yml | 60 ++ .github/workflows/main.yml | 59 -- .poetry-version | 1 + .python-version | 1 + hstest/__init__.py | 34 +- hstest/check_result.py | 2 + hstest/common/file_utils.py | 24 +- hstest/common/os_utils.py | 8 +- hstest/common/process_utils.py | 41 +- hstest/common/reflection_utils.py | 91 +- hstest/common/utils.py | 33 +- hstest/dynamic/dynamic_test.py | 34 +- hstest/dynamic/input/dynamic_input_func.py | 14 +- hstest/dynamic/input/dynamic_input_handler.py | 20 +- hstest/dynamic/input/dynamic_testing.py | 118 +-- hstest/dynamic/input/input_handler.py | 15 +- hstest/dynamic/input/input_mock.py | 46 +- hstest/dynamic/output/colored_output.py | 2 + .../dynamic/output/infinite_loop_detector.py | 59 +- hstest/dynamic/output/output_handler.py | 35 +- hstest/dynamic/output/output_mock.py | 60 +- hstest/dynamic/security/exit_exception.py | 9 +- hstest/dynamic/security/exit_handler.py | 22 +- hstest/dynamic/security/thread_group.py | 20 +- hstest/dynamic/security/thread_handler.py | 24 +- hstest/dynamic/system_handler.py | 26 +- hstest/exception/failure_handler.py | 28 +- hstest/exception/outcomes.py | 18 +- hstest/exception/testing.py | 6 +- hstest/exceptions.py | 2 + hstest/outcomes/compilation_error_outcome.py | 12 +- hstest/outcomes/error_outcome.py | 32 +- hstest/outcomes/exception_outcome.py | 20 +- hstest/outcomes/outcome.py | 66 +- hstest/outcomes/unexpected_error_outcome.py | 11 +- hstest/outcomes/wrong_answer_outcome.py | 14 +- hstest/stage/__init__.py | 12 +- hstest/stage/django_test.py | 16 +- hstest/stage/flask_test.py | 23 +- hstest/stage/plotting_test.py | 19 +- hstest/stage/sql_test.py | 9 +- hstest/stage/stage_test.py | 112 +-- hstest/stage_test.py | 4 +- hstest/test_case/__init__.py | 12 +- hstest/test_case/attach/django_settings.py | 6 +- hstest/test_case/attach/flask_settings.py | 6 +- hstest/test_case/check_result.py | 13 +- hstest/test_case/test_case.py | 106 +-- hstest/testing/__init__.py | 0 .../execution/filtering/file_filter.py | 38 +- .../execution/filtering/main_filter.py | 31 +- .../testing/execution/main_module_executor.py | 41 +- .../testing/execution/process/cpp_executor.py | 23 +- .../testing/execution/process/go_executor.py | 20 +- .../execution/process/javascript_executor.py | 6 +- .../execution/process/python_executor.py | 6 +- .../execution/process/shell_executor.py | 6 +- hstest/testing/execution/process_executor.py | 96 +-- hstest/testing/execution/program_executor.py | 69 +- .../runnable/python_runnable_file.py | 4 +- .../execution/runnable/runnable_file.py | 13 +- .../execution/searcher/base_searcher.py | 149 ++-- .../execution/searcher/cpp_searcher.py | 21 +- .../testing/execution/searcher/go_searcher.py | 26 +- .../execution/searcher/javascript_searcher.py | 15 +- .../execution/searcher/python_searcher.py | 41 +- .../execution/searcher/shell_searcher.py | 15 +- .../execution/searcher/sql_searcher.py | 13 +- hstest/testing/execution_options.py | 12 +- hstest/testing/plotting/drawing/drawing.py | 20 +- .../plotting/drawing/drawing_builder.py | 2 + .../testing/plotting/drawing/drawing_data.py | 14 +- .../plotting/drawing/drawing_library.py | 3 + .../testing/plotting/drawing/drawing_type.py | 3 + .../plotting/drawing_data_normalizer.py | 18 +- hstest/testing/plotting/matplotlib_handler.py | 150 ++-- hstest/testing/plotting/pandas_handler.py | 299 ++----- hstest/testing/plotting/seaborn_handler.py | 145 ++-- hstest/testing/process_wrapper.py | 155 ++-- hstest/testing/runner/__init__.py | 0 .../runner/async_dynamic_testing_runner.py | 26 +- .../runner/django_application_runner.py | 87 +- .../runner/flask_application_runner.py | 73 +- hstest/testing/runner/plot_testing_runner.py | 34 +- hstest/testing/runner/sql_runner.py | 63 +- hstest/testing/runner/test_runner.py | 15 +- hstest/testing/settings.py | 8 +- hstest/testing/state_machine.py | 38 +- hstest/testing/test_run.py | 50 +- hstest/testing/tested_program.py | 43 +- hstest/testing/unittest/__init__.py | 0 hstest/testing/unittest/expected_fail_test.py | 25 +- .../unittest/unexepected_error_test.py | 4 +- hstest/testing/unittest/user_error_test.py | 4 +- poetry.lock | 778 ++++++++++++++++++ pyproject.toml | 163 +++- requirements-dev.txt | 6 - requirements.txt | 2 - setup.py | 26 - tests/__init__.py | 0 .../test.py | 4 +- tests/test_check_result.py | 12 +- tests/test_testcase.py | 70 +- tests/testing.py | 90 +- 109 files changed, 2837 insertions(+), 1752 deletions(-) delete mode 100644 .flake8 create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/actions/prepare/action.yml create mode 100644 .github/workflows/auto-author-assign.yml create mode 100644 .github/workflows/auto-format.yml create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/main.yml create mode 100644 .poetry-version create mode 100644 .python-version create mode 100644 hstest/testing/__init__.py create mode 100644 hstest/testing/runner/__init__.py create mode 100644 hstest/testing/unittest/__init__.py create mode 100644 poetry.lock delete mode 100644 requirements-dev.txt delete mode 100644 requirements.txt delete mode 100644 setup.py create mode 100644 tests/__init__.py diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 3e135126..00000000 --- a/.flake8 +++ /dev/null @@ -1,23 +0,0 @@ -[flake8] -count = True -max-line-length = 100 -max-doc-length = 100 -exclude = - .git, - __pycache__, - build, - dist, - node_modules, - venv, - .mypy_cache, - tests/projects, - tests/outcomes/**/main*.py, - tests/outcomes/**/cleaning.py, - tests/outcomes/**/pandas_*.py, - tests/outcomes/**/matplotlib_*.py, - tests/outcomes/**/seaborn_*.py -ignore = - # visually indented line with same indent as next logical line - E129, - # W504 line break after binary operator line break after binary operator - W504 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..bb611915 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "00:00" + groups: + all-actions: + patterns: [ "*" ] + + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "daily" + time: "00:00" diff --git a/.github/workflows/actions/prepare/action.yml b/.github/workflows/actions/prepare/action.yml new file mode 100644 index 00000000..cc3133d8 --- /dev/null +++ b/.github/workflows/actions/prepare/action.yml @@ -0,0 +1,18 @@ +name: 'Prepare environment' +description: 'Prepare environment' + +runs: + using: "composite" + steps: + - name: Install Poetry + run: pipx install poetry==$(head -n 1 .poetry-version) + shell: bash + + - uses: actions/setup-python@v5 + with: + python-version-file: '.python-version' + cache: 'poetry' + + - name: Install dependencies + run: poetry install --only main,dev --no-interaction --no-ansi + shell: bash diff --git a/.github/workflows/auto-author-assign.yml b/.github/workflows/auto-author-assign.yml new file mode 100644 index 00000000..62d531bb --- /dev/null +++ b/.github/workflows/auto-author-assign.yml @@ -0,0 +1,16 @@ +name: Auto Author Assign + +on: + pull_request_target: + types: [ opened, reopened ] + +permissions: + pull-requests: write + +jobs: + assign-author: + runs-on: [self-hosted, small] + timeout-minutes: 30 + if: ${{ !github.event.pull_request.assignee }} + steps: + - uses: toshimaru/auto-author-assign@v2.1.0 diff --git a/.github/workflows/auto-format.yml b/.github/workflows/auto-format.yml new file mode 100644 index 00000000..5e492ce7 --- /dev/null +++ b/.github/workflows/auto-format.yml @@ -0,0 +1,35 @@ +name: Format code +on: + pull_request: + push: + branches: + - 'master' + - 'release' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true + +jobs: + format_backend: + name: Format with ruff + runs-on: [ self-hosted, small ] + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + + - uses: ./.github/workflows/actions/prepare + + - run: poetry run ruff --fix --unsafe-fixes --preview --exit-zero . + - run: poetry run ruff format . + + - name: Commit changes + uses: EndBug/add-and-commit@v9 + with: + fetch: false + default_author: github_actions + message: 'Backend: Auto format' + add: '.' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..b802a710 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: CI + +on: + push: + branches: + - 'master' + - 'release' + pull_request: + +jobs: + lint: + name: Lint with ruff + runs-on: [ self-hosted, small ] + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/workflows/actions/prepare + + - name: Check files using the ruff formatter + run: poetry run ruff --fix --unsafe-fixes --preview . + + mypy: + name: Static Type Checking + runs-on: [ self-hosted, small ] + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/workflows/actions/prepare + + - name: Mypy + run: poetry run mypy . + + test-ubuntu: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - [ self-hosted, small ] + - windows-latest + - macos-latest + python-version: [ "3.10", "3.11", "3.12" ] + steps: + - uses: actions/checkout@v3 + + - name: Install Poetry + run: pipx install poetry==$(head -n 1 .poetry-version) + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: 'poetry' + + - name: Install dependencies + run: poetry install --only main,dev --no-interaction --no-ansi + + - name: Run unittests + run: python tests/testing.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 8b94c925..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Python package - -on: [ push ] - -jobs: - Lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.7 - uses: actions/setup-python@v4 - with: - python-version: "3.7" - - name: Python version - run: python --version - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install -r requirements-dev.txt - npm install - - name: python version - run: python --version - - name: Lint - run: flake8 - test-ubuntu: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: ["ubuntu-20.04", "windows-latest", "macos-latest"] - python-version: [ "3.6", "3.7", "3.8", "3.9", "3.10", "3.11.0-rc.2" ] - steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Python version - run: python --version - - name: Get pip cache dir - id: pip-cache - run: | - echo "::set-output name=dir::$(pip cache dir)" - - name: pip cache - uses: actions/cache@v3 - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt', '**/requirements-dev.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install -r requirements-dev.txt - npm install - - name: Run unittests - run: python tests/testing.py diff --git a/.poetry-version b/.poetry-version new file mode 100644 index 00000000..943f9cbc --- /dev/null +++ b/.poetry-version @@ -0,0 +1 @@ +1.7.1 diff --git a/.python-version b/.python-version new file mode 100644 index 00000000..9919bf8c --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.10.13 diff --git a/hstest/__init__.py b/hstest/__init__.py index 86b7709e..772deb31 100644 --- a/hstest/__init__.py +++ b/hstest/__init__.py @@ -1,22 +1,20 @@ -__all__ = [ - 'StageTest', - 'DjangoTest', - 'FlaskTest', - 'PlottingTest', - 'SQLTest', - - 'TestCase', - 'SimpleTestCase', - - 'CheckResult', - 'correct', - 'wrong', +from __future__ import annotations - 'WrongAnswer', - 'TestPassed', - - 'dynamic_test', - 'TestedProgram', +__all__ = [ + "CheckResult", + "DjangoTest", + "FlaskTest", + "PlottingTest", + "SQLTest", + "SimpleTestCase", + "StageTest", + "TestCase", + "TestPassed", + "TestedProgram", + "WrongAnswer", + "correct", + "dynamic_test", + "wrong", ] from hstest.dynamic.dynamic_test import dynamic_test diff --git a/hstest/check_result.py b/hstest/check_result.py index 017fa1fd..5720ba3b 100644 --- a/hstest/check_result.py +++ b/hstest/check_result.py @@ -1,3 +1,5 @@ # deprecated, but old tests use "from hstest.check_result import CheckResult" # new way to import is "from hstest import CheckResult" +from __future__ import annotations + from hstest.test_case import CheckResult, correct, wrong # noqa: F401 diff --git a/hstest/common/file_utils.py b/hstest/common/file_utils.py index 099c5aee..293898ea 100644 --- a/hstest/common/file_utils.py +++ b/hstest/common/file_utils.py @@ -1,42 +1,42 @@ +from __future__ import annotations + +import contextlib import os -from typing import Dict from hstest.exception.testing import FileDeletionError -def create_files(files: Dict[str, str]): +def create_files(files: dict[str, str]) -> None: for file, content in files.items(): - with open(file, 'w') as f: + with open(file, "w", encoding="locale") as f: f.write(content) -def delete_files(files: Dict[str, str]): - for file in files.keys(): +def delete_files(files: dict[str, str]) -> None: + for file in files: if os.path.isfile(file): try: os.remove(file) except PermissionError: - raise FileDeletionError() + raise FileDeletionError -def safe_delete(filename): +def safe_delete(filename) -> None: if os.path.exists(filename): - try: + with contextlib.suppress(BaseException): os.remove(filename) - except BaseException: - pass def walk_user_files(folder): curr_folder = os.path.abspath(folder) - test_folder = os.path.join(curr_folder, 'test') + test_folder = os.path.join(curr_folder, "test") for folder, dirs, files in os.walk(curr_folder): if folder.startswith(test_folder): continue if folder == curr_folder: - for file in 'test.py', 'tests.py': + for file in "test.py", "tests.py": if file in files: files.remove(file) diff --git a/hstest/common/os_utils.py b/hstest/common/os_utils.py index b3364de5..4c346919 100644 --- a/hstest/common/os_utils.py +++ b/hstest/common/os_utils.py @@ -1,13 +1,15 @@ +from __future__ import annotations + import platform def is_windows(): - return platform.system() == 'Windows' + return platform.system() == "Windows" def is_mac(): - return platform.system() == 'Darwin' + return platform.system() == "Darwin" def is_linux(): - return platform.system() == 'Linux' + return platform.system() == "Linux" diff --git a/hstest/common/process_utils.py b/hstest/common/process_utils.py index c8457c02..0c29949f 100644 --- a/hstest/common/process_utils.py +++ b/hstest/common/process_utils.py @@ -1,42 +1,40 @@ -import sys +from __future__ import annotations + import threading import weakref from concurrent.futures import ThreadPoolExecutor from concurrent.futures.thread import _worker +from typing import TYPE_CHECKING -from hstest.dynamic.security.thread_group import ThreadGroup +if TYPE_CHECKING: + from hstest.dynamic.security.thread_group import ThreadGroup class DaemonThreadPoolExecutor(ThreadPoolExecutor): - def __init__(self, max_workers: int = 1, name: str = '', group: ThreadGroup = None): + def __init__(self, max_workers: int = 1, name: str = "", group: ThreadGroup = None) -> None: super().__init__(max_workers=max_workers, thread_name_prefix=name) self.group = group # Adjusted method from the ThreadPoolExecutor class just to create threads as daemons - def _adjust_thread_count(self): - if sys.version_info >= (3, 8): - # if idle threads are available, don't spin new threads - if self._idle_semaphore.acquire(timeout=0): - return + def _adjust_thread_count(self) -> None: + if self._idle_semaphore.acquire(timeout=0): + return # When the executor gets lost, the weakref callback will wake up # the worker threads. - def weakref_cb(_, q=self._work_queue): + def weakref_cb(_, q=self._work_queue) -> None: q.put(None) num_threads = len(self._threads) if num_threads < self._max_workers: - thread_name = '%s_%d' % (self._thread_name_prefix or self, - num_threads) - - if sys.version_info >= (3, 7): - args = (weakref.ref(self, weakref_cb), - self._work_queue, - self._initializer, - self._initargs) - else: - args = (weakref.ref(self, weakref_cb), - self._work_queue) + thread_name = "%s_%d" % (self._thread_name_prefix or self, num_threads) + + args = ( + weakref.ref(self, weakref_cb), + self._work_queue, + self._initializer, + self._initargs, + ) t = threading.Thread(name=thread_name, target=_worker, args=args, group=self.group) t.daemon = True @@ -46,5 +44,6 @@ def weakref_cb(_, q=self._work_queue): def is_port_in_use(port): import socket + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - return s.connect_ex(('localhost', port)) == 0 + return s.connect_ex(("localhost", port)) == 0 diff --git a/hstest/common/reflection_utils.py b/hstest/common/reflection_utils.py index dc0f04ea..4e45517e 100644 --- a/hstest/common/reflection_utils.py +++ b/hstest/common/reflection_utils.py @@ -1,6 +1,7 @@ +from __future__ import annotations + import inspect import os -from typing import List from hstest.exception.failure_handler import get_traceback_stack @@ -9,21 +10,21 @@ def is_tests(stage): package = inspect.getmodule(stage).__package__ file = inspect.getmodule(stage).__file__ return ( - package and package.startswith('tests.outcomes.') or - package and package.startswith('tests.projects.') or - file and f'{os.sep}hs-test-python{os.sep}tests{os.sep}outcomes{os.sep}' in file or - file and f'{os.sep}hs-test-python{os.sep}tests{os.sep}projects{os.sep}' in file or - file and f'{os.sep}hs-test-python{os.sep}tests{os.sep}sql{os.sep}' in file + (package and package.startswith("tests.outcomes.")) + or (package and package.startswith("tests.projects.")) + or (file and f"{os.sep}hs-test-python{os.sep}tests{os.sep}outcomes{os.sep}" in file) + or (file and f"{os.sep}hs-test-python{os.sep}tests{os.sep}projects{os.sep}" in file) + or (file and f"{os.sep}hs-test-python{os.sep}tests{os.sep}sql{os.sep}" in file) ) -def setup_cwd(stage): +def setup_cwd(stage) -> None: if stage.is_tests: test_file = inspect.getmodule(stage).__file__ test_folder = os.path.dirname(test_file) os.chdir(test_folder) - if os.path.basename(os.getcwd()) == 'test': + if os.path.basename(os.getcwd()) == "test": os.chdir(os.path.dirname(os.getcwd())) @@ -31,15 +32,15 @@ def get_stacktrace(ex: BaseException, hide_internals=False) -> str: traceback_stack = get_traceback_stack(ex) if not hide_internals: - return ''.join(traceback_stack) + return "".join(traceback_stack) if isinstance(ex, SyntaxError): - if ex.filename.startswith('<'): # "", or "" + if ex.filename.startswith("<"): # "", or "" user_dir = ex.filename else: user_dir = os.path.dirname(ex.filename) + os.sep else: - user_dir = '' + user_dir = "" user_traceback = [] for tr in traceback_stack[::-1][1:-1]: @@ -47,23 +48,21 @@ def get_stacktrace(ex: BaseException, hide_internals=False) -> str: break user_traceback += [tr] - user_traceback = [tr for tr in user_traceback - if f'{os.sep}hstest{os.sep}' not in tr] + user_traceback = [tr for tr in user_traceback if f"{os.sep}hstest{os.sep}" not in tr] return clean_stacktrace(traceback_stack, user_traceback[::-1], user_dir) def _fix_python_syntax_error(str_trace: str) -> str: - python_traceback_initial_phrase = 'Traceback (most recent call last):' + python_traceback_initial_phrase = "Traceback (most recent call last):" python_traceback_start = ' File "' - is_python_syntax_error = 'SyntaxError' in str_trace and ( - f'\n{python_traceback_start}' in str_trace or - str_trace.startswith(python_traceback_start) + is_python_syntax_error = "SyntaxError" in str_trace and ( + f"\n{python_traceback_start}" in str_trace or str_trace.startswith(python_traceback_start) ) if is_python_syntax_error and python_traceback_initial_phrase not in str_trace: - str_trace = python_traceback_initial_phrase + '\n' + str_trace + str_trace = python_traceback_initial_phrase + "\n" + str_trace return str_trace @@ -72,23 +71,23 @@ def str_to_stacktrace(str_trace: str) -> str: str_trace = _fix_python_syntax_error(str_trace) lines = str_trace.splitlines() - traceback_lines = [i for i, line in enumerate(lines) if line.startswith(' File ')] + traceback_lines = [i for i, line in enumerate(lines) if line.startswith(" File ")] if len(traceback_lines) < 1: return str_trace traceback_stack = [] - for line_from, line_to in zip(traceback_lines, traceback_lines[1:]): - actual_lines = lines[line_from: line_to] + for line_from, line_to in zip(traceback_lines, traceback_lines[1:], strict=False): + actual_lines = lines[line_from:line_to] needed_lines = [line for line in actual_lines if line.startswith(" ")] - traceback_stack += ['\n'.join(needed_lines) + '\n'] + traceback_stack += ["\n".join(needed_lines) + "\n"] - last_traceback = '' - before = '\n'.join(lines[:traceback_lines[0]]) + '\n' - after = '' + last_traceback = "" + before = "\n".join(lines[: traceback_lines[0]]) + "\n" + after = "" - for line in lines[traceback_lines[-1]:]: + for line in lines[traceback_lines[-1] :]: if not after and line.startswith(" "): last_traceback += line + "\n" else: @@ -98,7 +97,7 @@ def str_to_stacktrace(str_trace: str) -> str: user_traceback = [] for trace in traceback_stack: - r''' + r""" Avoid traceback elements such as: File "C:\Users\**\JetBrains\**\plugins\python\helpers\pydev\pydevd.py", line 1477, in _exec @@ -107,26 +106,27 @@ def str_to_stacktrace(str_trace: str) -> str: exec(compile(contents+"\n", file, 'exec'), glob, loc) Which will appear when testing locally inside PyCharm. - ''' # noqa: W291, W505, E501 - if f'{os.sep}JetBrains{os.sep}' in trace: + """ # noqa: W291, E501 + if f"{os.sep}JetBrains{os.sep}" in trace: continue - r''' + r""" Avoid traceback elements such as: File "C:\\Python39\\lib\\importlib\\__init__.py", line 127, in import_module return _bootstrap._gcd_import(name[level:], package, level) - ''' - if f'{os.sep}importlib{os.sep}' in trace: + """ + if f"{os.sep}importlib{os.sep}" in trace: continue user_traceback += [trace] - return clean_stacktrace([before] + user_traceback + [after], user_traceback) + return clean_stacktrace([before, *user_traceback, after], user_traceback) -def clean_stacktrace(full_traceback: List[str], - user_traceback: List[str], user_dir: str = '') -> str: +def clean_stacktrace( + full_traceback: list[str], user_traceback: list[str], user_dir: str = "" +) -> str: dir_names = [] for tr in user_traceback: try: @@ -135,17 +135,18 @@ def clean_stacktrace(full_traceback: List[str], except ValueError: continue - user_file = tr[start_index: end_index] + user_file = tr[start_index:end_index] - if user_file.startswith('<'): + if user_file.startswith("<"): continue - dir_name = os.path.dirname(tr[start_index: end_index]) + dir_name = os.path.dirname(tr[start_index:end_index]) if os.path.isdir(dir_name): dir_names += [os.path.abspath(dir_name)] if dir_names: from hstest.common.os_utils import is_windows + if is_windows(): drives = {} for dir_name in dir_names: @@ -154,28 +155,28 @@ def clean_stacktrace(full_traceback: List[str], if len(drives) > 1: max_drive = max(drives.values()) - drive_to_leave = [d for d in drives if drives[d] == max_drive][0] + drive_to_leave = next(d for d in drives if drives[d] == max_drive) dir_names = [d for d in dir_names if d.startswith(drive_to_leave)] user_dir = os.path.commonpath(dir_names) + os.sep cleaned_traceback = [] for trace in full_traceback[1:-1]: - if trace.startswith(' ' * 4): + if trace.startswith(" " * 4): # Trace line that starts with 4 is a line with SyntaxError cleaned_traceback += [trace] - elif user_dir in trace or ('<' in trace and '>' in trace and '" in trace and " lines that are always in the stacktrace # but include , because it's definitely user's code - if not user_dir.startswith('<'): + if not user_dir.startswith("<"): if user_dir in trace: - trace = trace.replace(user_dir, '') + trace = trace.replace(user_dir, "") else: folder_name = os.path.basename(user_dir[:-1]) if folder_name in trace: index = trace.index(folder_name) - trace = ' File "' + trace[index + len(folder_name + os.sep):] + trace = ' File "' + trace[index + len(folder_name + os.sep) :] cleaned_traceback += [trace] - return full_traceback[0] + ''.join(cleaned_traceback) + full_traceback[-1] + return full_traceback[0] + "".join(cleaned_traceback) + full_traceback[-1] diff --git a/hstest/common/utils.py b/hstest/common/utils.py index 2f85acdc..c950d318 100644 --- a/hstest/common/utils.py +++ b/hstest/common/utils.py @@ -1,37 +1,38 @@ +from __future__ import annotations + from time import sleep -from typing import Callable +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Callable -failed_msg_start = '#educational_plugin FAILED + ' -failed_msg_continue = '#educational_plugin ' -success_msg = '#educational_plugin test OK' +failed_msg_start = "#educational_plugin FAILED + " +failed_msg_continue = "#educational_plugin " +success_msg = "#educational_plugin test OK" def failed(message: str, is_unittest: bool): - """ Reports failure """ + """Reports failure.""" if not is_unittest: lines = message.splitlines() - print('\n' + failed_msg_start + lines[0]) + print("\n" + failed_msg_start + lines[0]) # noqa: T201 for line in lines[1:]: - print(failed_msg_continue + line) + print(failed_msg_continue + line) # noqa: T201 return -1, message def passed(is_unittest: bool): - """ Reports success """ + """Reports success.""" if not is_unittest: - print('\n' + success_msg) - return 0, 'test OK' + print("\n" + success_msg) # noqa: T201 + return 0, "test OK" def clean_text(text: str) -> str: - return ( - text.replace('\r\n', '\n') - .replace('\r', '\n') - .replace('\u00a0', '\u0020') - ) + return text.replace("\r\n", "\n").replace("\r", "\n").replace("\u00a0", "\u0020") -def try_many_times(times_to_try: int, sleep_time_ms: int, exit_func: Callable[[], bool]): +def try_many_times(times_to_try: int, sleep_time_ms: int, exit_func: Callable[[], bool]) -> bool: while times_to_try > 0: times_to_try -= 1 if exit_func(): diff --git a/hstest/dynamic/dynamic_test.py b/hstest/dynamic/dynamic_test.py index c0952a4b..344b69d4 100644 --- a/hstest/dynamic/dynamic_test.py +++ b/hstest/dynamic/dynamic_test.py @@ -1,23 +1,26 @@ +from __future__ import annotations + import inspect -from typing import Any, Dict, List +from typing import Any from hstest.stage_test import StageTest from hstest.test_case.test_case import DEFAULT_TIME_LIMIT -def dynamic_test(func=None, *, - order: int = 0, - time_limit: int = DEFAULT_TIME_LIMIT, - data: List[Any] = None, - feedback: str = "", - repeat: int = 1, - files: Dict[str, str] = None): - """ - Decorator for creating dynamic tests - """ +def dynamic_test( + func=None, + *, + order: int = 0, + time_limit: int = DEFAULT_TIME_LIMIT, + data: list[Any] | None = None, + feedback: str = "", + repeat: int = 1, + files: dict[str, str] | None = None, +): + """Decorator for creating dynamic tests.""" class DynamicTestingMethod: - def __init__(self, fn): + def __init__(self, fn) -> None: self.fn = fn def __set_name__(self, owner, name): @@ -32,17 +35,18 @@ def __set_name__(self, owner, name): return from hstest.dynamic.input.dynamic_testing import DynamicTestElement - methods: List[DynamicTestElement] = owner.dynamic_methods() + + methods: list[DynamicTestElement] = owner.dynamic_methods() methods += [ DynamicTestElement( - test=lambda *a, **k: self.fn(*a, **k), + test=self.fn, name=self.fn.__name__, order=(order, len(methods)), repeat=repeat, time_limit=time_limit, feedback=feedback, data=data, - files=files + files=files, ) ] diff --git a/hstest/dynamic/input/dynamic_input_func.py b/hstest/dynamic/input/dynamic_input_func.py index 25e33d8a..9a730366 100644 --- a/hstest/dynamic/input/dynamic_input_func.py +++ b/hstest/dynamic/input/dynamic_input_func.py @@ -1,17 +1,21 @@ -from typing import Callable, Optional, TYPE_CHECKING, Union +from __future__ import annotations + +from typing import TYPE_CHECKING if TYPE_CHECKING: + from collections.abc import Callable + from hstest import CheckResult - InputFunction = Callable[[str], Union[str, CheckResult]] - DynamicTestFunction = Callable[[], Optional[str]] + InputFunction = Callable[[str], str | CheckResult] + DynamicTestFunction = Callable[[], str | None] class DynamicInputFunction: - def __init__(self, trigger_count: int, func: 'InputFunction'): + def __init__(self, trigger_count: int, func: InputFunction) -> None: self.trigger_count = trigger_count self.input_function = func - def trigger(self): + def trigger(self) -> None: if self.trigger_count > 0: self.trigger_count -= 1 diff --git a/hstest/dynamic/input/dynamic_input_handler.py b/hstest/dynamic/input/dynamic_input_handler.py index c02edc7e..5226a317 100644 --- a/hstest/dynamic/input/dynamic_input_handler.py +++ b/hstest/dynamic/input/dynamic_input_handler.py @@ -1,4 +1,6 @@ -from typing import List, Optional, TYPE_CHECKING +from __future__ import annotations + +from typing import TYPE_CHECKING from hstest.common.utils import clean_text from hstest.dynamic.output.infinite_loop_detector import loop_detector @@ -9,22 +11,22 @@ class DynamicInputHandler: - def __init__(self, func: 'DynamicTestFunction'): + def __init__(self, func: DynamicTestFunction) -> None: self._dynamic_input_function: DynamicTestFunction = func - self._input_lines: List[str] = [] + self._input_lines: list[str] = [] - def eject_next_line(self) -> Optional[str]: + def eject_next_line(self) -> str | None: if len(self._input_lines) == 0: self._eject_next_input() if len(self._input_lines) == 0: return None - next_line = self._input_lines.pop(0) + '\n' - OutputHandler.inject_input('> ' + next_line) + next_line = self._input_lines.pop(0) + "\n" + OutputHandler.inject_input("> " + next_line) loop_detector.input_requested() return next_line - def _eject_next_input(self): + def _eject_next_input(self) -> None: new_input = self._dynamic_input_function() if new_input is None: @@ -32,7 +34,7 @@ def _eject_next_input(self): new_input = clean_text(new_input) - if new_input.endswith('\n'): + if new_input.endswith("\n"): new_input = new_input[:-1] - self._input_lines += new_input.split('\n') + self._input_lines += new_input.split("\n") diff --git a/hstest/dynamic/input/dynamic_testing.py b/hstest/dynamic/input/dynamic_testing.py index 7cbd1f8b..94df423e 100644 --- a/hstest/dynamic/input/dynamic_testing.py +++ b/hstest/dynamic/input/dynamic_testing.py @@ -1,53 +1,60 @@ +from __future__ import annotations + import typing -from typing import Any, Callable, Dict, List, Optional, Tuple +from typing import Any from hstest.common.utils import clean_text from hstest.exception.outcomes import TestPassed, UnexpectedError, WrongAnswer from hstest.testing.tested_program import TestedProgram if typing.TYPE_CHECKING: + from collections.abc import Callable + from hstest import CheckResult, StageTest, TestCase from hstest.dynamic.input.dynamic_input_func import DynamicInputFunction - DynamicTesting = Callable[[], Optional[CheckResult]] - DynamicTestingWithoutParams = Callable[[StageTest, Any], Optional[CheckResult]] + DynamicTesting = Callable[[], CheckResult | None] + DynamicTestingWithoutParams = Callable[[StageTest, Any], CheckResult | None] class DynamicTestElement: - def __init__(self, - test: 'DynamicTestingWithoutParams', - name: str, - order: Tuple[int, int], - repeat: int, - time_limit: int, - feedback: str, - data: List[Any], - files: Dict[str, str]): + def __init__( + self, + test: DynamicTestingWithoutParams, + name: str, + order: tuple[int, int], + repeat: int, + time_limit: int, + feedback: str, + data: list[Any], + files: dict[str, str], + ) -> None: self.test: DynamicTestingWithoutParams = test - self.name: str = f"Data passed to dynamic method \"{name}\"" + self.name: str = f'Data passed to dynamic method "{name}"' self.method_name = name - self.order: Tuple[int, int] = order + self.order: tuple[int, int] = order self.repeat: int = repeat self.time_limit: int = time_limit self.feedback: str = feedback - self.data: Optional[List[Any]] = data - self.files: Optional[Dict[str, str]] = files - self.args_list: Optional[List[List[Any]]] = None + self.data: list[Any] | None = data + self.files: dict[str, str] | None = files + self.args_list: list[list[Any]] | None = None - def extract_parametrized_data(self): + def extract_parametrized_data(self) -> None: if self.data is None: self.data = [[]] - if type(self.data) not in [list, tuple]: - raise UnexpectedError(f"{self.name} should be of type " - f"\"list\" or \"tuple\", found {type(self.data)}.") + if type(self.data) not in {list, tuple}: + msg = f"{self.name} should be of type " f'"list" or "tuple", found {type(self.data)}.' + raise UnexpectedError(msg) if len(self.data) == 0: - raise UnexpectedError(f"{self.name} should not be empty.") + msg = f"{self.name} should not be empty." + raise UnexpectedError(msg) found_lists_inside = True for obj in self.data: - if type(obj) not in [list, tuple]: + if type(obj) not in {list, tuple}: found_lists_inside = False break @@ -56,49 +63,57 @@ def extract_parametrized_data(self): else: self.args_list = [[obj] for obj in self.data] - def check_errors(self): + def check_errors(self) -> None: if self.repeat < 0: - raise UnexpectedError(f'Dynamic test "{self.method_name}" ' - f'should not be repeated < 0 times, found {self.repeat}') + msg = ( + f'Dynamic test "{self.method_name}" ' + f"should not be repeated < 0 times, found {self.repeat}" + ) + raise UnexpectedError(msg) if self.files is not None: if type(self.files) != dict: - raise UnexpectedError(f"'Files' parameter in dynamic test should be of type " - f"\"dict\", found {type(self.files)}.") + msg = ( + f"'Files' parameter in dynamic test should be of type " + f'"dict", found {type(self.files)}.' + ) + raise UnexpectedError(msg) for k, v in self.files.items(): if type(k) != str: - raise UnexpectedError( + msg = ( f"All keys in 'files' parameter in dynamic test should be " - f"of type \"str\", found {type(k)}." + f'of type "str", found {type(k)}.' ) + raise UnexpectedError(msg) if type(v) != str: - raise UnexpectedError( + msg = ( f"All values in 'files' parameter in dynamic test should be " - f"of type \"str\", found {type(v)}." + f'of type "str", found {type(v)}.' ) + raise UnexpectedError(msg) - def get_tests(self, obj) -> List['DynamicTesting']: + def get_tests(self, obj) -> list[DynamicTesting]: tests = [] - for i in range(self.repeat): + for _i in range(self.repeat): for args in self.args_list: tests += [lambda o=obj, a=args: self.test(o, *a)] return tests -def to_dynamic_testing(source: str, args: List[str], - input_funcs: List['DynamicInputFunction']) -> 'DynamicTesting': +def to_dynamic_testing( + source: str, args: list[str], input_funcs: list[DynamicInputFunction] +) -> DynamicTesting: from hstest.dynamic.input.dynamic_input_func import DynamicInputFunction from hstest.test_case.check_result import CheckResult class InputFunctionHandler: - def __init__(self, funcs: List[DynamicInputFunction]): - self.input_funcs: List[DynamicInputFunction] = [] + def __init__(self, funcs: list[DynamicInputFunction]) -> None: + self.input_funcs: list[DynamicInputFunction] = [] for func in funcs: - self.input_funcs += [ - DynamicInputFunction(func.trigger_count, func.input_function)] + self.input_funcs += [DynamicInputFunction(func.trigger_count, func.input_function)] - def eject_next_input(self, curr_output: str) -> Optional[str]: + def eject_next_input(self, curr_output: str) -> str | None: if len(self.input_funcs) == 0: return None @@ -109,22 +124,24 @@ def eject_next_input(self, curr_output: str) -> Optional[str]: next_func = input_function.input_function - new_input: Optional[str] + new_input: str | None try: obj = next_func(curr_output) if isinstance(obj, str) or obj is None: new_input = obj elif isinstance(obj, CheckResult): if obj.is_correct: - raise TestPassed() + raise TestPassed else: raise WrongAnswer(obj.feedback) else: raise UnexpectedError( - 'Dynamic input should return ' + - f'str or CheckResult objects only. Found: {type(obj)}') + "Dynamic input should return " + + f"str or CheckResult objects only. Found: {type(obj)}" + ) except BaseException as ex: from hstest.stage_test import StageTest + StageTest.curr_test_run.set_error_in_test(ex) return None @@ -136,7 +153,7 @@ def eject_next_input(self, curr_output: str) -> Optional[str]: return new_input - def dynamic_testing_function() -> Optional[CheckResult]: + def dynamic_testing_function() -> CheckResult | None: program = TestedProgram(source) output: str = program.start(*args) @@ -154,15 +171,16 @@ def dynamic_testing_function() -> Optional[CheckResult]: return dynamic_testing_function -def search_dynamic_tests(obj: 'StageTest') -> List['TestCase']: +def search_dynamic_tests(obj: StageTest) -> list[TestCase]: from hstest.test_case.test_case import TestCase - methods: List[DynamicTestElement] = obj.dynamic_methods() + + methods: list[DynamicTestElement] = obj.dynamic_methods() for m in methods: m.extract_parametrized_data() m.check_errors() - tests: List[TestCase] = [] + tests: list[TestCase] = [] for dte in sorted(methods, key=lambda x: x.order): for test in dte.get_tests(obj): @@ -171,7 +189,7 @@ def search_dynamic_tests(obj: 'StageTest') -> List['TestCase']: dynamic_testing=test, time_limit=dte.time_limit, feedback=dte.feedback, - files=dte.files + files=dte.files, ) ] diff --git a/hstest/dynamic/input/input_handler.py b/hstest/dynamic/input/input_handler.py index dd6c3421..42c702e2 100644 --- a/hstest/dynamic/input/input_handler.py +++ b/hstest/dynamic/input/input_handler.py @@ -1,10 +1,13 @@ -import io +from __future__ import annotations + import sys from typing import Any, TYPE_CHECKING from hstest.dynamic.input.input_mock import InputMock if TYPE_CHECKING: + import io + from hstest.dynamic.input.dynamic_input_func import DynamicTestFunction from hstest.dynamic.input.input_mock import Condition @@ -14,17 +17,19 @@ class InputHandler: mock_in: InputMock = InputMock() @staticmethod - def replace_input(): + def replace_input() -> None: sys.stdin = InputHandler.mock_in @staticmethod - def revert_input(): + def revert_input() -> None: sys.stdin = InputHandler.real_in @staticmethod - def install_input_handler(obj: Any, condition: 'Condition', input_func: 'DynamicTestFunction'): + def install_input_handler( + obj: Any, condition: Condition, input_func: DynamicTestFunction + ) -> None: InputHandler.mock_in.install_input_handler(obj, condition, input_func) @staticmethod - def uninstall_input_handler(obj: Any): + def uninstall_input_handler(obj: Any) -> None: InputHandler.mock_in.uninstall_input_handler(obj) diff --git a/hstest/dynamic/input/input_mock.py b/hstest/dynamic/input/input_mock.py index 44533385..b70a3a04 100644 --- a/hstest/dynamic/input/input_mock.py +++ b/hstest/dynamic/input/input_mock.py @@ -1,39 +1,43 @@ -from typing import Any, Callable, Dict, TYPE_CHECKING +from __future__ import annotations + +from typing import Any, TYPE_CHECKING from hstest.dynamic.input.dynamic_input_handler import DynamicInputHandler from hstest.dynamic.security.exit_exception import ExitException -from hstest.dynamic.security.thread_group import ThreadGroup from hstest.exception.outcomes import OutOfInputError, UnexpectedError from hstest.testing.settings import Settings if TYPE_CHECKING: + from collections.abc import Callable + from hstest.dynamic.input.dynamic_input_func import DynamicTestFunction + from hstest.dynamic.security.thread_group import ThreadGroup Condition = Callable[[], bool] class ConditionalInputHandler: - def __init__(self, condition: 'Condition', handler: DynamicInputHandler): + def __init__(self, condition: Condition, handler: DynamicInputHandler) -> None: self.condition = condition self.handler = handler class InputMock: - def __init__(self): - self.handlers: Dict[ThreadGroup, ConditionalInputHandler] = {} + def __init__(self) -> None: + self.handlers: dict[ThreadGroup, ConditionalInputHandler] = {} - def install_input_handler(self, obj: Any, condition: 'Condition', - input_func: 'DynamicTestFunction'): + def install_input_handler( + self, obj: Any, condition: Condition, input_func: DynamicTestFunction + ) -> None: if obj in self.handlers: - raise UnexpectedError("Cannot install input handler from the same program twice") - self.handlers[obj] = ConditionalInputHandler( - condition, - DynamicInputHandler(input_func) - ) + msg = "Cannot install input handler from the same program twice" + raise UnexpectedError(msg) + self.handlers[obj] = ConditionalInputHandler(condition, DynamicInputHandler(input_func)) - def uninstall_input_handler(self, obj: Any): + def uninstall_input_handler(self, obj: Any) -> None: if obj not in self.handlers: - raise UnexpectedError("Cannot uninstall input handler that doesn't exist") + msg = "Cannot uninstall input handler that doesn't exist" + raise UnexpectedError(msg) del self.handlers[obj] def __get_input_handler(self) -> DynamicInputHandler: @@ -42,17 +46,21 @@ def __get_input_handler(self) -> DynamicInputHandler: return handler.handler from hstest import StageTest - StageTest.curr_test_run.set_error_in_test(UnexpectedError( - "Cannot find input handler to read data")) - raise ExitException() + + StageTest.curr_test_run.set_error_in_test( + UnexpectedError("Cannot find input handler to read data") + ) + raise ExitException def readline(self) -> str: line = self.__get_input_handler().eject_next_line() if line is None: if not Settings.allow_out_of_input: from hstest import StageTest + StageTest.curr_test_run.set_error_in_test(OutOfInputError()) - raise ExitException() + raise ExitException else: - raise EOFError('EOF when reading a line') + msg = "EOF when reading a line" + raise EOFError(msg) return line diff --git a/hstest/dynamic/output/colored_output.py b/hstest/dynamic/output/colored_output.py index 2266888f..c9476d9d 100644 --- a/hstest/dynamic/output/colored_output.py +++ b/hstest/dynamic/output/colored_output.py @@ -1,6 +1,8 @@ # Thanks to https://stackoverflow.com/a/45444716 # reset +from __future__ import annotations + RESET = "\033[0m" # Regular Colors diff --git a/hstest/dynamic/output/infinite_loop_detector.py b/hstest/dynamic/output/infinite_loop_detector.py index e41606b3..305f97c0 100644 --- a/hstest/dynamic/output/infinite_loop_detector.py +++ b/hstest/dynamic/output/infinite_loop_detector.py @@ -1,20 +1,21 @@ -from typing import List +from __future__ import annotations + +from typing import NoReturn from hstest.dynamic.security.exit_exception import ExitException from hstest.exception.testing import InfiniteLoopException class InfiniteLoopDetector: - - def __init__(self): + def __init__(self) -> None: self.working: bool = True self.check_same_input_between_requests = True self.check_no_input_requests_for_long = False self.check_repeatable_output = True - self._curr_line: List[str] = [] - self._since_last_input: List[str] = [] + self._curr_line: list[str] = [] + self._since_last_input: list[str] = [] self._between_input_requests = [] self._BETWEEN_INPUT_SAVED_SIZE = 20 @@ -33,7 +34,7 @@ def __init__(self): self._chars_since_last_check = 0 self._CHARS_SINCE_LAST_CHECK_MAX = 100 - def write(self, output: str): + def write(self, output: str) -> None: if not self.working: return @@ -48,11 +49,11 @@ def write(self, output: str): self._chars_since_last_input += len(output) self._chars_since_last_check += len(output) - new_lines = output.count('\n') + new_lines = output.count("\n") if new_lines: self._lines_since_last_input += new_lines - self._every_line += [''.join(self._curr_line)] + self._every_line += ["".join(self._curr_line)] self._curr_line = [] if len(self._every_line) > self._EVERY_LINE_SAVED_SIZE: self._every_line.pop(0) @@ -63,7 +64,7 @@ def write(self, output: str): self._check_inf_loop_chars() self._chars_since_last_check = 0 - def reset(self): + def reset(self) -> None: self._curr_line = [] self._chars_since_last_input = 0 self._lines_since_last_input = 0 @@ -72,11 +73,11 @@ def reset(self): self._between_input_requests = [] self._every_line = [] - def input_requested(self): + def input_requested(self) -> None: if not self.working: return - self._between_input_requests += [''.join(self._since_last_input)] + self._between_input_requests += ["".join(self._since_last_input)] if len(self._between_input_requests) > self._BETWEEN_INPUT_SAVED_SIZE: self._between_input_requests.pop(0) self._check_inf_loop_input_requests() @@ -86,17 +87,21 @@ def input_requested(self): self._chars_since_last_input = 0 self._lines_since_last_input = 0 - def _check_inf_loop_chars(self): - if (self.check_no_input_requests_for_long and - self._chars_since_last_input >= self._CHARS_SINCE_LAST_INPUT_MAX): + def _check_inf_loop_chars(self) -> None: + if ( + self.check_no_input_requests_for_long + and self._chars_since_last_input >= self._CHARS_SINCE_LAST_INPUT_MAX + ): self._fail( - f"No input request for the last {str(self._chars_since_last_input)} " + f"No input request for the last {self._chars_since_last_input!s} " f"characters being printed." ) - def _check_inf_loop_lines(self): - if (self.check_no_input_requests_for_long and - self._lines_since_last_input >= self._LINES_SINCE_LAST_INPUT_MAX): + def _check_inf_loop_lines(self) -> None: + if ( + self.check_no_input_requests_for_long + and self._lines_since_last_input >= self._LINES_SINCE_LAST_INPUT_MAX + ): self._fail( f"No input request for the last {self._lines_since_last_input} " f"lines being printed." @@ -111,7 +116,7 @@ def _check_inf_loop_lines(self): for lines_repeated in range(1, self._REPEATABLE_LINES_MAX + 1): self._check_repetition_size(lines_repeated) - def _check_repetition_size(self, lines_repeated: int): + def _check_repetition_size(self, lines_repeated: int) -> None: how_many_repetitions: int = len(self._every_line) // lines_repeated lines_to_check: int = lines_repeated * how_many_repetitions starting_from_index: int = len(self._every_line) - lines_to_check @@ -127,8 +132,7 @@ def _check_repetition_size(self, lines_repeated: int): return if lines_repeated == 1: - self._fail( - f"Last {lines_to_check} lines your program printed are the same.") + self._fail(f"Last {lines_to_check} lines your program printed are the same.") else: self._fail( f"Last {lines_to_check} lines your program printed have " @@ -136,7 +140,7 @@ def _check_repetition_size(self, lines_repeated: int): f"lines of the same text." ) - def _check_inf_loop_input_requests(self): + def _check_inf_loop_input_requests(self) -> None: if not self.check_no_input_requests_for_long: return @@ -146,18 +150,19 @@ def _check_inf_loop_input_requests(self): return self._fail( - f"Between the last {str(self._BETWEEN_INPUT_SAVED_SIZE)} " + f"Between the last {self._BETWEEN_INPUT_SAVED_SIZE!s} " f"input requests the texts being printed are identical." ) - def _fail(self, reason: str): + def _fail(self, reason: str) -> NoReturn: from hstest.stage_test import StageTest - StageTest.curr_test_run.set_error_in_test( - InfiniteLoopException(reason)) + + StageTest.curr_test_run.set_error_in_test(InfiniteLoopException(reason)) from hstest.dynamic.output.output_handler import OutputHandler + OutputHandler.print("INFINITE LOOP DETECTED") - raise ExitException() + raise ExitException loop_detector = InfiniteLoopDetector() diff --git a/hstest/dynamic/output/output_handler.py b/hstest/dynamic/output/output_handler.py index 66580ade..776afcc9 100644 --- a/hstest/dynamic/output/output_handler.py +++ b/hstest/dynamic/output/output_handler.py @@ -1,4 +1,5 @@ -import io +from __future__ import annotations + import sys from typing import Any, TYPE_CHECKING @@ -8,6 +9,8 @@ from hstest.dynamic.security.thread_group import ThreadGroup if TYPE_CHECKING: + import io + from hstest.dynamic.input.input_mock import Condition @@ -19,29 +22,26 @@ class OutputHandler: _mock_err: OutputMock = None @staticmethod - def print(obj): + def print(obj) -> None: if True: return - lines = obj.strip().split('\n') + lines = obj.strip().split("\n") group = ThreadGroup.curr_group() - if group: - name = group.name - else: - name = "Root" + name = group.name if group else "Root" - prepend = f'[{name}] ' + prepend = f"[{name}] " - output = prepend + ('\n' + prepend).join(lines) - full = BLUE + output + '\n' + RESET + output = prepend + ("\n" + prepend).join(lines) + full = BLUE + output + "\n" + RESET if group: OutputHandler.get_real_out().write(full) OutputHandler.get_real_out().flush() else: - print(full, end='') + print(full, end="") # noqa: T201 @staticmethod def get_real_out() -> io.TextIOWrapper: @@ -52,7 +52,7 @@ def get_real_err() -> io.TextIOWrapper: return OutputHandler._mock_err.original @staticmethod - def replace_stdout(): + def replace_stdout() -> None: OutputHandler._real_out = sys.stdout OutputHandler._real_err = sys.stderr @@ -63,13 +63,13 @@ def replace_stdout(): sys.stderr = OutputHandler._mock_err @staticmethod - def revert_stdout(): + def revert_stdout() -> None: OutputHandler.reset_output() sys.stdout = OutputHandler._real_out sys.stderr = OutputHandler._real_err @staticmethod - def reset_output(): + def reset_output() -> None: OutputHandler._mock_out.reset() OutputHandler._mock_err.reset() @@ -90,18 +90,19 @@ def get_partial_output(obj: Any) -> str: return clean_text(OutputHandler._mock_out.partial(obj)) @staticmethod - def inject_input(user_input: str): + def inject_input(user_input: str) -> None: from hstest.stage_test import StageTest + if StageTest.curr_test_run is not None: StageTest.curr_test_run.set_input_used() OutputHandler._mock_out.inject_input(user_input) @staticmethod - def install_output_handler(obj: Any, condition: 'Condition'): + def install_output_handler(obj: Any, condition: Condition) -> None: OutputHandler._mock_out.install_output_handler(obj, condition) OutputHandler._mock_err.install_output_handler(obj, condition) @staticmethod - def uninstall_output_handler(obj: Any): + def uninstall_output_handler(obj: Any) -> None: OutputHandler._mock_out.uninstall_output_handler(obj) OutputHandler._mock_err.uninstall_output_handler(obj) diff --git a/hstest/dynamic/output/output_mock.py b/hstest/dynamic/output/output_mock.py index e21263e8..38303c9a 100644 --- a/hstest/dynamic/output/output_mock.py +++ b/hstest/dynamic/output/output_mock.py @@ -1,5 +1,6 @@ -import io -from typing import Any, Dict, List, TYPE_CHECKING +from __future__ import annotations + +from typing import Any, TYPE_CHECKING from hstest.dynamic.output.colored_output import BLUE, RESET from hstest.dynamic.output.infinite_loop_detector import loop_detector @@ -8,19 +9,20 @@ from hstest.testing.settings import Settings if TYPE_CHECKING: + import io + from hstest.dynamic.input.input_mock import Condition class ConditionalOutput: - def __init__(self, condition: 'Condition'): + def __init__(self, condition: Condition) -> None: self.condition = condition - self.output: List[str] = [] + self.output: list[str] = [] class OutputMock: - """ - original stream is used to actually see - the test in the console and nothing else + """original stream is used to actually see + the test in the console and nothing else. cloned stream is used to collect all output from the test and redirect to check function @@ -32,25 +34,25 @@ class OutputMock: but also injected input from the test """ - def __init__(self, real_out: io.TextIOWrapper, is_stderr: bool = False): + def __init__(self, real_out: io.TextIOWrapper, is_stderr: bool = False) -> None: class RealOutputMock: - def __init__(self, out: io.TextIOWrapper): + def __init__(self, out: io.TextIOWrapper) -> None: self.out = out - def write(self, text): + def write(self, text) -> None: if not ignore_stdout: self.out.write(text) - def flush(self): + def flush(self) -> None: self.out.flush() - def close(self): + def close(self) -> None: self.out.close() self._original: RealOutputMock = RealOutputMock(real_out) - self._cloned: List[str] = [] # used in check function - self._dynamic: List[str] = [] # used to append inputs - self._partial: Dict[Any, ConditionalOutput] = {} # separated outputs for each program + self._cloned: list[str] = [] # used in check function + self._dynamic: list[str] = [] # used to append inputs + self._partial: dict[Any, ConditionalOutput] = {} # separated outputs for each program self._is_stderr = is_stderr @property @@ -59,19 +61,19 @@ def original(self): @property def cloned(self) -> str: - return ''.join(self._cloned) + return "".join(self._cloned) @property def dynamic(self) -> str: - return ''.join(self._dynamic) + return "".join(self._dynamic) def partial(self, obj: Any) -> str: output = self._partial[obj].output - result = ''.join(output) + result = "".join(output) output.clear() return result - def write(self, text): + def write(self, text) -> None: partial_handler = self.__get_partial_handler() if partial_handler is None: @@ -86,34 +88,36 @@ def write(self, text): loop_detector.write(text) - def getvalue(self): + def getvalue(self) -> None: pass - def flush(self): + def flush(self) -> None: self._original.flush() - def close(self): + def close(self) -> None: self._original.close() - def inject_input(self, user_input: str): + def inject_input(self, user_input: str) -> None: self._original.write(user_input) self._dynamic.append(user_input) - def reset(self): + def reset(self) -> None: self._cloned.clear() self._dynamic.clear() for value in self._partial.values(): value.output.clear() loop_detector.reset() - def install_output_handler(self, obj: Any, condition: 'Condition'): + def install_output_handler(self, obj: Any, condition: Condition) -> None: if obj in self._partial: - raise UnexpectedError("Cannot install output handler from the same program twice") + msg = "Cannot install output handler from the same program twice" + raise UnexpectedError(msg) self._partial[obj] = ConditionalOutput(condition) - def uninstall_output_handler(self, obj: Any): + def uninstall_output_handler(self, obj: Any) -> None: if obj not in self._partial: - raise UnexpectedError("Cannot uninstall output handler that doesn't exist") + msg = "Cannot uninstall output handler that doesn't exist" + raise UnexpectedError(msg) del self._partial[obj] def __get_partial_handler(self): diff --git a/hstest/dynamic/security/exit_exception.py b/hstest/dynamic/security/exit_exception.py index f1c99b5b..8997147b 100644 --- a/hstest/dynamic/security/exit_exception.py +++ b/hstest/dynamic/security/exit_exception.py @@ -1,4 +1,9 @@ +from __future__ import annotations + +from typing import NoReturn + + class ExitException(BaseException): @staticmethod - def throw(): - raise ExitException() + def throw() -> NoReturn: + raise ExitException diff --git a/hstest/dynamic/security/exit_handler.py b/hstest/dynamic/security/exit_handler.py index 2aeef134..c48f82e9 100644 --- a/hstest/dynamic/security/exit_handler.py +++ b/hstest/dynamic/security/exit_handler.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import builtins import os import signal @@ -28,20 +30,20 @@ def is_replaced(): return ExitHandler._replaced @staticmethod - def replace_exit(): + def replace_exit() -> None: if not ExitHandler._saved: ExitHandler._saved = True - ExitHandler._builtins_quit = builtins.quit if hasattr(builtins, 'quit') else None - ExitHandler._builtins_exit = builtins.exit if hasattr(builtins, 'exit') else None - ExitHandler._os_kill = os.kill if hasattr(os, 'kill') else None - ExitHandler._os__exit = os._exit if hasattr(os, '_exit') else None - ExitHandler._os_killpg = os.killpg if hasattr(os, 'killpg') else None - ExitHandler._sys_exit = sys.exit if hasattr(sys, 'exit') else None + ExitHandler._builtins_quit = builtins.quit if hasattr(builtins, "quit") else None + ExitHandler._builtins_exit = builtins.exit if hasattr(builtins, "exit") else None + ExitHandler._os_kill = os.kill if hasattr(os, "kill") else None + ExitHandler._os__exit = os._exit if hasattr(os, "_exit") else None + ExitHandler._os_killpg = os.killpg if hasattr(os, "killpg") else None + ExitHandler._sys_exit = sys.exit if hasattr(sys, "exit") else None ExitHandler._signal_pthread_kill = ( - signal.pthread_kill if hasattr(signal, 'pthread_kill') else None + signal.pthread_kill if hasattr(signal, "pthread_kill") else None ) ExitHandler._signal_siginterrupt = ( - signal.siginterrupt if hasattr(signal, 'siginterrupt') else None + signal.siginterrupt if hasattr(signal, "siginterrupt") else None ) builtins.quit = _throw_exit_exception @@ -56,7 +58,7 @@ def replace_exit(): ExitHandler._replaced = True @staticmethod - def revert_exit(): + def revert_exit() -> None: if ExitHandler._replaced: builtins.quit = ExitHandler._builtins_quit builtins.exit = ExitHandler._builtins_exit diff --git a/hstest/dynamic/security/thread_group.py b/hstest/dynamic/security/thread_group.py index 1365e2e5..dccbd3b2 100644 --- a/hstest/dynamic/security/thread_group.py +++ b/hstest/dynamic/security/thread_group.py @@ -1,23 +1,25 @@ +from __future__ import annotations + from threading import current_thread, Thread -from typing import List, Optional class ThreadGroup: - def __init__(self, name: str = None): + def __init__(self, name: str | None = None) -> None: if name: self._name: str = name else: from hstest import StageTest + test_num = StageTest.curr_test_global - self._name = f'Test {test_num}' + self._name = f"Test {test_num}" - self.threads: List[Thread] = [] + self.threads: list[Thread] = [] curr = current_thread() if hasattr(curr, "_group"): - self._parent: Optional[ThreadGroup] = curr._group + self._parent: ThreadGroup | None = curr._group else: - self._parent: Optional[ThreadGroup] = None + self._parent: ThreadGroup | None = None @property def name(self): @@ -27,9 +29,9 @@ def name(self): def parent(self): return self._parent - def add(self, thread: Thread): + def add(self, thread: Thread) -> None: self.threads.append(thread) @staticmethod - def curr_group() -> 'ThreadGroup': - return getattr(current_thread(), '_group', None) + def curr_group() -> ThreadGroup: + return getattr(current_thread(), "_group", None) diff --git a/hstest/dynamic/security/thread_handler.py b/hstest/dynamic/security/thread_handler.py index 469b8ddb..5c2d701e 100644 --- a/hstest/dynamic/security/thread_handler.py +++ b/hstest/dynamic/security/thread_handler.py @@ -1,24 +1,28 @@ +from __future__ import annotations + from threading import current_thread, Thread -from typing import Callable, Optional +from typing import TYPE_CHECKING from hstest.dynamic.security.thread_group import ThreadGroup +if TYPE_CHECKING: + from collections.abc import Callable -class ThreadHandler: +class ThreadHandler: _group = None - _old_init: Optional[Callable[[], Thread]] = None + _old_init: Callable[[], Thread] | None = None @classmethod - def install_thread_group(cls): + def install_thread_group(cls) -> None: if cls._old_init is None: cls._old_init = Thread.__init__ Thread.__init__ = ThreadHandler.init - cls._group = ThreadGroup('Main') + cls._group = ThreadGroup("Main") current_thread()._group = cls._group @classmethod - def uninstall_thread_group(cls): + def uninstall_thread_group(cls) -> None: if cls._old_init is not None: Thread.__init__ = cls._old_init cls._old_init = None @@ -26,9 +30,9 @@ def uninstall_thread_group(cls): cls._group = None @staticmethod - def init(self, group=None, target=None, name=None, - args=(), kwargs=None, *, daemon=None): - + def init( + self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None + ) -> None: ThreadHandler._old_init(self, None, target, name, args, kwargs, daemon=daemon) # Custom addition to Thread class (implement thread groups) @@ -37,7 +41,7 @@ def init(self, group=None, target=None, name=None, else: curr = current_thread() - if hasattr(curr, '_group'): + if hasattr(curr, "_group"): self._group = curr._group else: self._group = ThreadGroup(self._name) diff --git a/hstest/dynamic/system_handler.py b/hstest/dynamic/system_handler.py index 4b40fc9d..09f50cc6 100644 --- a/hstest/dynamic/system_handler.py +++ b/hstest/dynamic/system_handler.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from threading import current_thread, Lock from typing import Any, TYPE_CHECKING @@ -18,7 +20,7 @@ class SystemHandler: __locker_thread = None @staticmethod - def set_up(): + def set_up() -> None: SystemHandler._lock_system_for_testing() OutputHandler.replace_stdout() @@ -27,7 +29,7 @@ def set_up(): ThreadHandler.install_thread_group() @staticmethod - def tear_down(): + def tear_down() -> None: SystemHandler._unlock_system_for_testing() OutputHandler.revert_stdout() @@ -36,33 +38,33 @@ def tear_down(): ThreadHandler.uninstall_thread_group() @staticmethod - def _lock_system_for_testing(): + def _lock_system_for_testing() -> None: with SystemHandler.__lock: if SystemHandler.__locked: - raise ErrorWithFeedback( - "Cannot start the testing process more than once") + msg = "Cannot start the testing process more than once" + raise ErrorWithFeedback(msg) SystemHandler.__locked = True SystemHandler.__locker_thread = current_thread() @staticmethod - def _unlock_system_for_testing(): + def _unlock_system_for_testing() -> None: if current_thread() != SystemHandler.__locker_thread: - raise ErrorWithFeedback( - "Cannot tear down the testing process from the other thread") + msg = "Cannot tear down the testing process from the other thread" + raise ErrorWithFeedback(msg) with SystemHandler.__lock: if not SystemHandler.__locked: - raise ErrorWithFeedback( - "Cannot tear down the testing process more than once") + msg = "Cannot tear down the testing process more than once" + raise ErrorWithFeedback(msg) SystemHandler.__locked = False SystemHandler.__locker_thread = None @staticmethod - def install_handler(obj: Any, condition: 'Condition', input_func: 'DynamicTestFunction'): + def install_handler(obj: Any, condition: Condition, input_func: DynamicTestFunction) -> None: InputHandler.install_input_handler(obj, condition, input_func) OutputHandler.install_output_handler(obj, condition) @staticmethod - def uninstall_handler(obj: Any): + def uninstall_handler(obj: Any) -> None: InputHandler.uninstall_input_handler(obj) OutputHandler.uninstall_output_handler(obj) diff --git a/hstest/exception/failure_handler.py b/hstest/exception/failure_handler.py index 21ba43f8..ab10456d 100644 --- a/hstest/exception/failure_handler.py +++ b/hstest/exception/failure_handler.py @@ -1,34 +1,30 @@ +from __future__ import annotations + import platform -import sys import traceback -from typing import List from hstest.testing.execution_options import inside_docker -def get_report(): +def get_report() -> str: if not inside_docker: name_os = platform.system() + " " + platform.release() python = platform.python_version() implementation = platform.python_implementation() return ( - 'Submitted via IDE\n' - '\n' - f'OS {name_os}\n' - f'{implementation} {python}\n' - f'Testing library version 8' + "Submitted via IDE\n" + "\n" + f"OS {name_os}\n" + f"{implementation} {python}\n" + f"Testing library version 8" ) else: - return 'Submitted via web' + return "Submitted via web" -def get_traceback_stack(ex: BaseException) -> List[str]: - if sys.version_info >= (3, 10): - return traceback.format_exception(ex) - else: - exc_tb = ex.__traceback__ - return traceback.format_exception(etype=type(ex), value=ex, tb=exc_tb) +def get_traceback_stack(ex: BaseException) -> list[str]: + return traceback.format_exception(ex) def get_exception_text(ex: BaseException) -> str: - return ''.join(get_traceback_stack(ex)) + return "".join(get_traceback_stack(ex)) diff --git a/hstest/exception/outcomes.py b/hstest/exception/outcomes.py index 74912d9c..7000582d 100644 --- a/hstest/exception/outcomes.py +++ b/hstest/exception/outcomes.py @@ -1,4 +1,4 @@ -from typing import Optional +from __future__ import annotations class OutcomeError(BaseException): @@ -6,35 +6,35 @@ class OutcomeError(BaseException): class SyntaxException(OutcomeError): - def __init__(self, exception: SyntaxError, file: str): + def __init__(self, exception: SyntaxError, file: str) -> None: self.file: str = file self.exception: SyntaxError = exception class ExceptionWithFeedback(OutcomeError): - def __init__(self, error_text: str, real_exception: Optional[BaseException]): + def __init__(self, error_text: str, real_exception: BaseException | None) -> None: self.error_text: str = error_text self.real_exception: BaseException = real_exception class ErrorWithFeedback(OutcomeError): - def __init__(self, error_text: str): + def __init__(self, error_text: str) -> None: self.error_text = error_text class OutOfInputError(ErrorWithFeedback): - def __init__(self): - super().__init__('Program ran out of input. You tried to read more than expected.') + def __init__(self) -> None: + super().__init__("Program ran out of input. You tried to read more than expected.") class UnexpectedError(OutcomeError): - def __init__(self, error_text: str, ex: Optional[BaseException] = None): + def __init__(self, error_text: str, ex: BaseException | None = None) -> None: self.error_text = error_text self.exception = ex class CompilationError(OutcomeError): - def __init__(self, error_text: str): + def __init__(self, error_text: str) -> None: self.error_text = error_text @@ -43,5 +43,5 @@ class TestPassed(OutcomeError): class WrongAnswer(OutcomeError): - def __init__(self, feedback: str): + def __init__(self, feedback: str) -> None: self.feedback = feedback diff --git a/hstest/exception/testing.py b/hstest/exception/testing.py index 19fb0ed9..dd9dba3f 100644 --- a/hstest/exception/testing.py +++ b/hstest/exception/testing.py @@ -1,6 +1,8 @@ +from __future__ import annotations + class TimeLimitException(BaseException): - def __init__(self, time_limit_ms: int): + def __init__(self, time_limit_ms: int) -> None: self.time_limit_ms: int = time_limit_ms @@ -13,7 +15,7 @@ class TestedProgramFinishedEarly(BaseException): class InfiniteLoopException(BaseException): - def __init__(self, message: str): + def __init__(self, message: str) -> None: self.message = message diff --git a/hstest/exceptions.py b/hstest/exceptions.py index 2c54a85c..1f64f026 100644 --- a/hstest/exceptions.py +++ b/hstest/exceptions.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from hstest.exception.outcomes import TestPassed, WrongAnswer # deprecated, but have to be sure old tests work as expected diff --git a/hstest/outcomes/compilation_error_outcome.py b/hstest/outcomes/compilation_error_outcome.py index e88484af..615787cd 100644 --- a/hstest/outcomes/compilation_error_outcome.py +++ b/hstest/outcomes/compilation_error_outcome.py @@ -1,12 +1,18 @@ -from hstest.exception.outcomes import CompilationError +from __future__ import annotations + +from typing import TYPE_CHECKING + from hstest.outcomes.outcome import Outcome +if TYPE_CHECKING: + from hstest.exception.outcomes import CompilationError + class CompilationErrorOutcome(Outcome): - def __init__(self, ex: CompilationError): + def __init__(self, ex: CompilationError) -> None: super().__init__() self.test_number = -1 self.error_text = ex.error_text def get_type(self) -> str: - return 'Compilation error' + return "Compilation error" diff --git a/hstest/outcomes/error_outcome.py b/hstest/outcomes/error_outcome.py index 1f5d3e1d..fdf3d78e 100644 --- a/hstest/outcomes/error_outcome.py +++ b/hstest/outcomes/error_outcome.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from hstest.common.reflection_utils import get_stacktrace from hstest.exception.outcomes import ErrorWithFeedback from hstest.exception.testing import FileDeletionError, InfiniteLoopException, TimeLimitException @@ -5,7 +7,7 @@ class ErrorOutcome(Outcome): - def __init__(self, test_num: int, cause: BaseException): + def __init__(self, test_num: int, cause: BaseException) -> None: super().__init__() self.test_number = test_num @@ -22,28 +24,30 @@ def __init__(self, test_num: int, cause: BaseException): self.error_text = "Infinite loop detected.\n" + cause.message elif isinstance(cause, KeyboardInterrupt): - self.error_text = "It seems like you've stopped the testing process forcibly.\n" \ - "If this is not the case, please report this issue to support" + self.error_text = ( + "It seems like you've stopped the testing process forcibly.\n" + "If this is not the case, please report this issue to support" + ) self.stack_trace = get_stacktrace(cause, hide_internals=False) - def _init_permission_error(self, _: FileDeletionError): + def _init_permission_error(self, _: FileDeletionError) -> None: self.error_text = ( - "The file you opened " + - "can't be deleted after the end of the test. " + - "Probably you didn't close it." + "The file you opened " + + "can't be deleted after the end of the test. " + + "Probably you didn't close it." ) - def _init_time_limit_exception(self, ex: TimeLimitException): + def _init_time_limit_exception(self, ex: TimeLimitException) -> None: time_limit: int = ex.time_limit_ms - time_unit: str = 'milliseconds' + time_unit: str = "milliseconds" if time_limit > 1999: time_limit //= 1000 - time_unit = 'seconds' + time_unit = "seconds" self.error_text = ( - 'In this test, the program is running for a long time, ' + - f'more than {time_limit} {time_unit}. Most likely, ' + - 'the program has gone into an infinite loop.' + "In this test, the program is running for a long time, " + + f"more than {time_limit} {time_unit}. Most likely, " + + "the program has gone into an infinite loop." ) def get_type(self) -> str: - return 'Error' + return "Error" diff --git a/hstest/outcomes/exception_outcome.py b/hstest/outcomes/exception_outcome.py index cda56014..2216402e 100644 --- a/hstest/outcomes/exception_outcome.py +++ b/hstest/outcomes/exception_outcome.py @@ -1,10 +1,16 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + from hstest.common.reflection_utils import get_stacktrace, str_to_stacktrace -from hstest.exception.outcomes import ExceptionWithFeedback from hstest.outcomes.outcome import Outcome +if TYPE_CHECKING: + from hstest.exception.outcomes import ExceptionWithFeedback + class ExceptionOutcome(Outcome): - def __init__(self, test_num: int, ex: ExceptionWithFeedback): + def __init__(self, test_num: int, ex: ExceptionWithFeedback) -> None: super().__init__() cause = ex.real_exception feedback = ex.error_text @@ -17,13 +23,13 @@ def __init__(self, test_num: int, ex: ExceptionWithFeedback): else: self.stack_trace = str_to_stacktrace(feedback) - self.error_text = '' + self.error_text = "" - eof = 'EOFError: EOF when reading a line' - eof_feedback = 'Probably your program run out of input (tried to read more than expected)' + eof = "EOFError: EOF when reading a line" + eof_feedback = "Probably your program run out of input (tried to read more than expected)" if self.stack_trace.strip().endswith(eof): - self.error_text += '\n\n' + eof_feedback + self.error_text += "\n\n" + eof_feedback def get_type(self) -> str: - return 'Exception' + return "Exception" diff --git a/hstest/outcomes/outcome.py b/hstest/outcomes/outcome.py index c67591c6..5a3a9a16 100644 --- a/hstest/outcomes/outcome.py +++ b/hstest/outcomes/outcome.py @@ -1,36 +1,41 @@ +from __future__ import annotations + from hstest.common.reflection_utils import str_to_stacktrace from hstest.common.utils import clean_text from hstest.dynamic.output.output_handler import OutputHandler from hstest.exception.outcomes import ( - CompilationError, ErrorWithFeedback, ExceptionWithFeedback, WrongAnswer + CompilationError, + ErrorWithFeedback, + ExceptionWithFeedback, + WrongAnswer, ) from hstest.exception.testing import FileDeletionError, InfiniteLoopException, TimeLimitException class Outcome: - def __init__(self, test_number: int = 0, error_text: str = '', stack_trace: str = ''): + def __init__(self, test_number: int = 0, error_text: str = "", stack_trace: str = "") -> None: self.test_number: int = test_number self.error_text: str = error_text self.stack_trace: str = stack_trace def get_type(self) -> str: - raise NotImplementedError() + raise NotImplementedError - def __str__(self): + def __str__(self) -> str: if self.test_number == 0: - when_error_happened = ' during testing' + when_error_happened = " during testing" elif self.test_number > 0: - when_error_happened = f' in test #{self.test_number}' + when_error_happened = f" in test #{self.test_number}" else: - when_error_happened = '' + when_error_happened = "" result = self.get_type() + when_error_happened if self.error_text: - result += '\n\n' + clean_text(self.error_text.strip()) + result += "\n\n" + clean_text(self.error_text.strip()) if self.stack_trace: - result += '\n\n' + clean_text(self.stack_trace.strip()) + result += "\n\n" + clean_text(self.stack_trace.strip()) full_out = OutputHandler.get_dynamic_output() full_err = str_to_stacktrace(OutputHandler.get_err()) @@ -44,10 +49,11 @@ def __str__(self): worth_showing_args = len(arguments.strip()) != 0 from hstest.stage_test import StageTest + test_run = StageTest.curr_test_run if worth_showing_out or worth_showing_err or worth_showing_args: - result += '\n\n' + result += "\n\n" if worth_showing_out or worth_showing_err: result += "Please find below the output of your program during this failed test.\n" if test_run and test_run.input_used: @@ -57,12 +63,12 @@ def __str__(self): result += "\n---\n\n" if worth_showing_args: - result += arguments + '\n\n' + result += arguments + "\n\n" if worth_showing_out: if worth_showing_err: - result += 'stdout:\n' - result += trimmed_out + '\n\n' + result += "stdout:\n" + result += trimmed_out + "\n\n" if worth_showing_err: result += "stderr:\n" + trimmed_err @@ -71,9 +77,10 @@ def __str__(self): @staticmethod def __get_args(): - arguments = '' + arguments = "" from hstest.stage_test import StageTest + test_run = StageTest.curr_test_run if test_run is not None: @@ -81,10 +88,10 @@ def __get_args(): programs_with_args = [p for p in tested_programs if len(p.run_args)] for pr in programs_with_args: - arguments += 'Arguments' + arguments += "Arguments" if len(tested_programs) > 1: - arguments += f' for {pr}' - pr_args = [f'"{arg}"' if ' ' in arg else arg for arg in pr.run_args] + arguments += f" for {pr}" + pr_args = [f'"{arg}"' if " " in arg else arg for arg in pr.run_args] arguments += f': {" ".join(pr_args)}\n' arguments = arguments.strip() @@ -93,17 +100,19 @@ def __get_args(): @staticmethod def __trim_lines(full_out): - result = '' + result = "" max_lines_in_output = 250 lines = full_out.splitlines() is_output_too_long = len(lines) > max_lines_in_output if is_output_too_long: - result += f'[last {max_lines_in_output} lines of output are shown, ' \ - f'{len(lines) - max_lines_in_output} skipped]\n' + result += ( + f"[last {max_lines_in_output} lines of output are shown, " + f"{len(lines) - max_lines_in_output} skipped]\n" + ) last_lines = lines[-max_lines_in_output:] - result += '\n'.join(last_lines) + result += "\n".join(last_lines) else: result += full_out @@ -126,13 +135,14 @@ def get_outcome(ex: BaseException, curr_test: int): elif isinstance(ex, CompilationError): return CompilationErrorOutcome(ex) - elif isinstance(ex, ( - ErrorWithFeedback, - FileDeletionError, - TimeLimitException, - InfiniteLoopException, - KeyboardInterrupt - )): + elif isinstance( + ex, + ErrorWithFeedback + | FileDeletionError + | TimeLimitException + | InfiniteLoopException + | KeyboardInterrupt, + ): return ErrorOutcome(curr_test, ex) else: diff --git a/hstest/outcomes/unexpected_error_outcome.py b/hstest/outcomes/unexpected_error_outcome.py index 01103fb5..6bf84364 100644 --- a/hstest/outcomes/unexpected_error_outcome.py +++ b/hstest/outcomes/unexpected_error_outcome.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from hstest.common.reflection_utils import get_stacktrace from hstest.exception.failure_handler import get_report from hstest.exception.outcomes import UnexpectedError @@ -5,14 +7,13 @@ class UnexpectedErrorOutcome(Outcome): - def __init__(self, test_num: int, cause: BaseException): + def __init__(self, test_num: int, cause: BaseException) -> None: super().__init__() self.test_number = test_num - self.error_text = 'We have recorded this bug ' \ - 'and will fix it soon.\n\n' + get_report() + self.error_text = "We have recorded this bug " "and will fix it soon.\n\n" + get_report() self.stack_trace = get_stacktrace(cause, hide_internals=False) if isinstance(cause, UnexpectedError) and cause.exception is not None: - self.stack_trace += '\n' + get_stacktrace(cause.exception, hide_internals=False) + self.stack_trace += "\n" + get_stacktrace(cause.exception, hide_internals=False) def get_type(self) -> str: - return 'Unexpected error' + return "Unexpected error" diff --git a/hstest/outcomes/wrong_answer_outcome.py b/hstest/outcomes/wrong_answer_outcome.py index 427a49e1..01df0163 100644 --- a/hstest/outcomes/wrong_answer_outcome.py +++ b/hstest/outcomes/wrong_answer_outcome.py @@ -1,10 +1,16 @@ -from hstest.exception.outcomes import WrongAnswer +from __future__ import annotations + +from typing import TYPE_CHECKING + from hstest.outcomes.outcome import Outcome +if TYPE_CHECKING: + from hstest.exception.outcomes import WrongAnswer + class WrongAnswerOutcome(Outcome): - def __init__(self, test_num: int, ex: WrongAnswer): - super().__init__(test_num, ex.feedback, '') + def __init__(self, test_num: int, ex: WrongAnswer) -> None: + super().__init__(test_num, ex.feedback, "") def get_type(self) -> str: - return 'Wrong answer' + return "Wrong answer" diff --git a/hstest/stage/__init__.py b/hstest/stage/__init__.py index f76d54f1..6cc14d38 100644 --- a/hstest/stage/__init__.py +++ b/hstest/stage/__init__.py @@ -1,9 +1,11 @@ +from __future__ import annotations + __all__ = [ - 'StageTest', - 'DjangoTest', - 'FlaskTest', - 'PlottingTest', - 'SQLTest', + "DjangoTest", + "FlaskTest", + "PlottingTest", + "SQLTest", + "StageTest", ] from hstest.stage.django_test import DjangoTest diff --git a/hstest/stage/django_test.py b/hstest/stage/django_test.py index 04a5a044..82f460a0 100644 --- a/hstest/stage/django_test.py +++ b/hstest/stage/django_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from urllib.request import urlopen from hstest.common.utils import clean_text @@ -15,24 +17,22 @@ class DjangoTest(StageTest): test_database: str = attach.test_database use_database: bool = attach.use_database - def __init__(self, args='', *, source: str = ''): + def __init__(self, args="", *, source: str = "") -> None: super().__init__(args, source=source) self.attach.use_database = self.use_database loop_detector.working = False Settings.do_reset_output = False def read_page(self, link: str) -> str: - """ - Deprecated, use get(...) instead - """ + """Deprecated, use get(...) instead.""" return clean_text(urlopen(link).read().decode()) - def get_url(self, link: str = ''): - if link.startswith('/'): + def get_url(self, link: str = "") -> str: + if link.startswith("/"): link = link[1:] - return f'http://localhost:{self.attach.port}/{link}' + return f"http://localhost:{self.attach.port}/{link}" def get(self, link: str) -> str: - if not link.startswith('http://'): + if not link.startswith("http://"): link = self.get_url(link) return clean_text(urlopen(link).read().decode()) diff --git a/hstest/stage/flask_test.py b/hstest/stage/flask_test.py index f0efdafe..6684d25d 100644 --- a/hstest/stage/flask_test.py +++ b/hstest/stage/flask_test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from urllib.request import urlopen from hstest.common.utils import clean_text @@ -13,7 +15,7 @@ class FlaskTest(StageTest): runner = FlaskApplicationRunner() attach: FlaskSettings = FlaskSettings() - def __init__(self, args='', *, source: str = ''): + def __init__(self, args="", *, source: str = "") -> None: super().__init__(args, source=source) loop_detector.working = False Settings.do_reset_output = False @@ -33,28 +35,31 @@ def __init__(self, args='', *, source: str = ''): else: self.attach.sources += [item] - def get_url(self, link: str = '', *, source: str = None): - if link.startswith('/'): + def get_url(self, link: str = "", *, source: str | None = None): + if link.startswith("/"): link = link[1:] def create_url(port: int) -> str: - return f'http://localhost:{port}/{link}' + return f"http://localhost:{port}/{link}" if len(self.attach.sources) == 1: return create_url(self.attach.sources[0][1]) elif len(self.attach.sources) == 0: - raise UnexpectedError('Cannot find sources') + msg = "Cannot find sources" + raise UnexpectedError(msg) sources_fits = [i for i in self.attach.sources if i[0] == source] if len(sources_fits) == 0: - raise UnexpectedError(f'Bad source: {source}') + msg = f"Bad source: {source}" + raise UnexpectedError(msg) elif len(sources_fits) > 1: - raise UnexpectedError(f'Multiple sources ({len(sources_fits)}) found: {source}') + msg = f"Multiple sources ({len(sources_fits)}) found: {source}" + raise UnexpectedError(msg) return create_url(sources_fits[0][1]) - def get(self, link: str, *, source: str = None) -> str: - if not link.startswith('http://'): + def get(self, link: str, *, source: str | None = None) -> str: + if not link.startswith("http://"): link = self.get_url(link, source=source) return clean_text(urlopen(link).read().decode()) diff --git a/hstest/stage/plotting_test.py b/hstest/stage/plotting_test.py index 2be941e4..fcc1e2f2 100644 --- a/hstest/stage/plotting_test.py +++ b/hstest/stage/plotting_test.py @@ -1,22 +1,25 @@ -from typing import List +from __future__ import annotations + +from typing import TYPE_CHECKING from hstest.stage.stage_test import StageTest -from hstest.testing.plotting.drawing.drawing import Drawing from hstest.testing.runner.plot_testing_runner import PlottingTestingRunner +if TYPE_CHECKING: + from hstest.testing.plotting.drawing.drawing import Drawing -class PlottingTest(StageTest): - def __init__(self, args='', *, source: str = ''): +class PlottingTest(StageTest): + def __init__(self, args="", *, source: str = "") -> None: super().__init__(args, source=source) - self._all_drawings: List[Drawing] = [] - self._new_drawings: List[Drawing] = [] + self._all_drawings: list[Drawing] = [] + self._new_drawings: list[Drawing] = [] self.runner = PlottingTestingRunner(self._all_drawings, self._new_drawings) - def all_figures(self) -> List[Drawing]: + def all_figures(self) -> list[Drawing]: return self._all_drawings - def new_figures(self) -> List[Drawing]: + def new_figures(self) -> list[Drawing]: result = self._new_drawings[:] self._new_drawings.clear() return result diff --git a/hstest/stage/sql_test.py b/hstest/stage/sql_test.py index fa328fd9..615f486e 100644 --- a/hstest/stage/sql_test.py +++ b/hstest/stage/sql_test.py @@ -1,4 +1,4 @@ -from typing import Dict +from __future__ import annotations from hstest.exception.outcomes import WrongAnswer from hstest.stage.stage_test import StageTest @@ -6,10 +6,10 @@ class SQLTest(StageTest): - queries: Dict[str, str] = dict() + queries: dict[str, str] = {} db: any = None - def __init__(self, source: str = ''): + def __init__(self, source: str = "") -> None: super().__init__(source) self.runner = SQLRunner(self) @@ -22,7 +22,8 @@ def execute(self, query_name: str): try: return cursor.execute(self.queries[query_name]) except Exception as ex: - raise WrongAnswer(f"Error while running '{query_name}': \n\n{ex}") + msg = f"Error while running '{query_name}': \n\n{ex}" + raise WrongAnswer(msg) def execute_and_fetch_all(self, query_name: str): return self.execute(query_name).fetchall() diff --git a/hstest/stage/stage_test.py b/hstest/stage/stage_test.py index b2f1b7be..a9e07e1b 100644 --- a/hstest/stage/stage_test.py +++ b/hstest/stage/stage_test.py @@ -1,6 +1,9 @@ +from __future__ import annotations + +import contextlib import os import unittest -from typing import Any, Dict, List, Optional, Tuple, Type +from typing import Any, TYPE_CHECKING from hstest.common.file_utils import walk_user_files from hstest.common.reflection_utils import is_tests, setup_cwd @@ -12,8 +15,6 @@ from hstest.exception.failure_handler import get_exception_text, get_report from hstest.exception.outcomes import OutcomeError, UnexpectedError, WrongAnswer from hstest.outcomes.outcome import Outcome -from hstest.test_case.check_result import CheckResult -from hstest.test_case.test_case import TestCase from hstest.testing.execution.main_module_executor import MainModuleExecutor from hstest.testing.execution.process.cpp_executor import CppExecutor from hstest.testing.execution.process.go_executor import GoExecutor @@ -22,24 +23,34 @@ from hstest.testing.execution.process.shell_executor import ShellExecutor from hstest.testing.execution_options import force_process_testing from hstest.testing.runner.async_dynamic_testing_runner import AsyncDynamicTestingRunner -from hstest.testing.runner.test_runner import TestRunner from hstest.testing.test_run import TestRun +if TYPE_CHECKING: + from hstest.test_case.check_result import CheckResult + from hstest.test_case.test_case import TestCase + from hstest.testing.runner.test_runner import TestRunner + class DirMeta(type): def __dir__(self): from hstest.testing.unittest.expected_fail_test import ExpectedFailTest from hstest.testing.unittest.unexepected_error_test import UnexpectedErrorTest from hstest.testing.unittest.user_error_test import UserErrorTest - if (not issubclass(self, StageTest) or self == StageTest or - self in {ExpectedFailTest, UserErrorTest, UnexpectedErrorTest}): + + if ( + not issubclass(self, StageTest) + or self == StageTest + or self in {ExpectedFailTest, UserErrorTest, UnexpectedErrorTest} + ): return [] - init_dir = dir(super(DirMeta, self)) + list(self.__dict__.keys()) - filtered_dir = list(filter(lambda x: not str(x).startswith('test'), init_dir)) - filtered_dir.append('test_run_unittest') - if (not self.dynamic_methods() and - 'generate' not in init_dir and - not issubclass(self, ExpectedFailTest)): + init_dir = dir(super()) + list(self.__dict__.keys()) + filtered_dir = list(filter(lambda x: not str(x).startswith("test"), init_dir)) + filtered_dir.append("test_run_unittest") + if ( + not self.dynamic_methods() + and "generate" not in init_dir + and not issubclass(self, ExpectedFailTest) + ): return [] return set(filtered_dir) @@ -48,11 +59,11 @@ class StageTest(unittest.TestCase, metaclass=DirMeta): runner: TestRunner = None attach: Any = None source: str = None - curr_test_run: Optional[TestRun] = None + curr_test_run: TestRun | None = None curr_test_global: int = 0 - def __init__(self, args='', *, source: str = ''): - super(StageTest, self).__init__('test_run_unittest') + def __init__(self, args="", *, source: str = "") -> None: + super().__init__("test_run_unittest") self.is_tests = False if self.source: @@ -60,29 +71,29 @@ def __init__(self, args='', *, source: str = ''): else: self.source_name: str = source - def test_run_unittest(self): + def test_run_unittest(self) -> None: result, feedback = self.run_tests(is_unittest=True) if result != 0: self.fail(feedback) - def after_all_tests(self): + def after_all_tests(self) -> None: pass def _init_runner(self) -> TestRunner: - for folder, dirs, files in walk_user_files(os.getcwd()): + for _folder, _dirs, files in walk_user_files(os.getcwd()): for f in files: - if f.endswith('.cpp'): + if f.endswith(".cpp"): return AsyncDynamicTestingRunner(CppExecutor) - if f.endswith('.go'): + if f.endswith(".go"): return AsyncDynamicTestingRunner(GoExecutor) - if f.endswith('.js'): + if f.endswith(".js"): return AsyncDynamicTestingRunner(JavascriptExecutor) - if f.endswith('.sh'): + if f.endswith(".sh"): return AsyncDynamicTestingRunner(ShellExecutor) - if f.endswith('.py'): + if f.endswith(".py"): if force_process_testing: return AsyncDynamicTestingRunner(PythonExecutor) else: @@ -90,16 +101,17 @@ def _init_runner(self) -> TestRunner: return AsyncDynamicTestingRunner(MainModuleExecutor) - def _init_tests(self) -> List[TestRun]: + def _init_tests(self) -> list[TestRun]: if self.runner is None: self.runner = self._init_runner() - test_runs: List[TestRun] = [] - test_cases: List[TestCase] = list(self.generate()) + test_runs: list[TestRun] = [] + test_cases: list[TestCase] = list(self.generate()) test_cases += search_dynamic_tests(self) if len(test_cases) == 0: - raise UnexpectedError("No tests found") + msg = "No tests found" + raise UnexpectedError(msg) curr_test: int = 0 test_count = len(test_cases) @@ -110,19 +122,17 @@ def _init_tests(self) -> List[TestRun]: if test_case.attach is None: test_case.attach = self.attach curr_test += 1 - test_runs += [ - TestRun(curr_test, test_count, test_case, self.runner) - ] + test_runs += [TestRun(curr_test, test_count, test_case, self.runner)] return test_runs - def __print_test_num(self, num: int): - total_tests = '' if num == self.curr_test_global else f' ({self.curr_test_global})' + def __print_test_num(self, num: int) -> None: + total_tests = "" if num == self.curr_test_global else f" ({self.curr_test_global})" OutputHandler.get_real_out().write( - RED_BOLD + f'\nStart test {num}{total_tests}' + RESET + '\n' + RED_BOLD + f"\nStart test {num}{total_tests}" + RESET + "\n" ) - def run_tests(self, *, debug=False, is_unittest: bool = False) -> Tuple[int, str]: + def run_tests(self, *, debug=False, is_unittest: bool = False) -> tuple[int, str]: curr_test: int = 0 need_tear_down: bool = False try: @@ -133,9 +143,10 @@ def run_tests(self, *, debug=False, is_unittest: bool = False) -> Tuple[int, str if self.is_tests or debug: import hstest.common.utils as hs - hs.failed_msg_start = '' - hs.failed_msg_continue = '' - hs.success_msg = '' + + hs.failed_msg_start = "" + hs.failed_msg_continue = "" + hs.success_msg = "" SystemHandler.set_up() test_runs = self._init_tests() @@ -153,7 +164,7 @@ def run_tests(self, *, debug=False, is_unittest: bool = False) -> Tuple[int, str result: CheckResult = test_run.test() if not result.is_correct: - full_feedback = result.feedback + '\n\n' + test_run.test_case.feedback + full_feedback = result.feedback + "\n\n" + test_run.test_case.feedback raise WrongAnswer(full_feedback.strip()) if test_run.is_last_test(): @@ -163,7 +174,7 @@ def run_tests(self, *, debug=False, is_unittest: bool = False) -> Tuple[int, str SystemHandler.tear_down() return passed(is_unittest) - except BaseException as ex: + except BaseException: if need_tear_down: try: StageTest.curr_test_run.tear_down() @@ -171,7 +182,7 @@ def run_tests(self, *, debug=False, is_unittest: bool = False) -> Tuple[int, str if isinstance(new_ex, OutcomeError): ex = new_ex - build = 'hs-test-python' + build = "hs-test-python" try: report = build + "\n\n" + get_report() @@ -194,23 +205,21 @@ def run_tests(self, *, debug=False, is_unittest: bool = False) -> Tuple[int, str text = get_exception_text(e) except Exception: try: - text = f'{type(e)}: {str(e)}' + text = f"{type(e)}: {e!s}" except Exception: - text = 'Broken exception' + text = "Broken exception" if len(text): traceback += text + "\n\n" - fail_text = 'Unexpected error\n\n' + report + "\n\n" + traceback + fail_text = "Unexpected error\n\n" + report + "\n\n" + traceback except BaseException: # no code execution here allowed so not to throw an exception - fail_text = 'Unexpected error\n\nCannot check the submission\n\n' + report + fail_text = "Unexpected error\n\nCannot check the submission\n\n" + report - try: + with contextlib.suppress(BaseException): SystemHandler.tear_down() - except BaseException: - pass return failed(fail_text, is_unittest) @@ -221,18 +230,19 @@ def run_tests(self, *, debug=False, is_unittest: bool = False) -> Tuple[int, str StageTest.source = None self.after_all_tests() - _dynamic_methods: Dict[Type['StageTest'], List[DynamicTestElement]] = {} + _dynamic_methods: dict[type[StageTest], list[DynamicTestElement]] = {} @classmethod - def dynamic_methods(cls) -> List[DynamicTestElement]: + def dynamic_methods(cls) -> list[DynamicTestElement]: if cls in StageTest._dynamic_methods: return StageTest._dynamic_methods[cls] empty = [] StageTest._dynamic_methods[cls] = empty return empty - def generate(self) -> List[TestCase]: + def generate(self) -> list[TestCase]: return [] def check(self, reply: str, attach: Any) -> CheckResult: - raise UnexpectedError('Can\'t check result: override "check" method') + msg = 'Can\'t check result: override "check" method' + raise UnexpectedError(msg) diff --git a/hstest/stage_test.py b/hstest/stage_test.py index fd5cdea0..e3bf4869 100644 --- a/hstest/stage_test.py +++ b/hstest/stage_test.py @@ -1,3 +1,5 @@ # deprecated, but old tests use "from hstest.stage_test import StageTest" # new way to import is "from hstest import StageTest" -from hstest.stage.stage_test import * # noqa: F401, F403 +from __future__ import annotations + +from hstest.stage.stage_test import * # noqa: F403 diff --git a/hstest/test_case/__init__.py b/hstest/test_case/__init__.py index ae866d9d..63b0487e 100644 --- a/hstest/test_case/__init__.py +++ b/hstest/test_case/__init__.py @@ -1,9 +1,11 @@ +from __future__ import annotations + __all__ = [ - 'TestCase', - 'SimpleTestCase', - 'CheckResult', - 'correct', - 'wrong', + "CheckResult", + "SimpleTestCase", + "TestCase", + "correct", + "wrong", ] from hstest.test_case.check_result import CheckResult, correct, wrong diff --git a/hstest/test_case/attach/django_settings.py b/hstest/test_case/attach/django_settings.py index 1e29f000..ad2bf579 100644 --- a/hstest/test_case/attach/django_settings.py +++ b/hstest/test_case/attach/django_settings.py @@ -1,8 +1,8 @@ -from typing import List +from __future__ import annotations class DjangoSettings: port: int = None use_database: bool = False - test_database: str = 'db.test.sqlite3' - tryout_ports: List[int] = [i for i in range(8000, 8101)] + test_database: str = "db.test.sqlite3" + tryout_ports: list[int] = list(range(8000, 8101)) diff --git a/hstest/test_case/attach/flask_settings.py b/hstest/test_case/attach/flask_settings.py index d13cad20..97aa4324 100644 --- a/hstest/test_case/attach/flask_settings.py +++ b/hstest/test_case/attach/flask_settings.py @@ -1,6 +1,6 @@ -from typing import List, Tuple +from __future__ import annotations class FlaskSettings: - sources: List[Tuple[str, int]] = [] - tryout_ports: List[int] = [i for i in range(8000, 8101)] + sources: list[tuple[str, int]] = [] + tryout_ports: list[int] = list(range(8000, 8101)) diff --git a/hstest/test_case/check_result.py b/hstest/test_case/check_result.py index f2e5e1b9..e5da53c5 100644 --- a/hstest/test_case/check_result.py +++ b/hstest/test_case/check_result.py @@ -1,11 +1,10 @@ -from typing import Optional +from __future__ import annotations from hstest.exception.outcomes import TestPassed, WrongAnswer class CheckResult: - - def __init__(self, result: bool, feedback: str): + def __init__(self, result: bool, feedback: str) -> None: self._result: bool = result self._feedback: str = feedback @@ -18,15 +17,15 @@ def feedback(self) -> str: return self._feedback @staticmethod - def correct() -> 'CheckResult': - return CheckResult(True, '') + def correct() -> CheckResult: + return CheckResult(True, "") @staticmethod - def wrong(feedback: str) -> 'CheckResult': + def wrong(feedback: str) -> CheckResult: return CheckResult(False, feedback) @staticmethod - def from_error(error: BaseException) -> Optional['CheckResult']: + def from_error(error: BaseException) -> CheckResult | None: if isinstance(error, TestPassed): return correct() elif isinstance(error, WrongAnswer): diff --git a/hstest/test_case/test_case.py b/hstest/test_case/test_case.py index 749cc9a9..8a10e852 100644 --- a/hstest/test_case/test_case.py +++ b/hstest/test_case/test_case.py @@ -1,4 +1,6 @@ -from typing import Any, Callable, Dict, List, Optional, Tuple, Type, TYPE_CHECKING, Union +from __future__ import annotations + +from typing import Any, TYPE_CHECKING, Union from hstest.dynamic.input.dynamic_input_func import DynamicInputFunction from hstest.dynamic.input.dynamic_testing import to_dynamic_testing @@ -6,49 +8,53 @@ from hstest.test_case.check_result import CheckResult if TYPE_CHECKING: + from collections.abc import Callable + from hstest.dynamic.input.dynamic_input_func import InputFunction from hstest.dynamic.input.dynamic_testing import DynamicTesting SimpleStepikTest = str - AdvancedStepikTest = Tuple[str, Any] + AdvancedStepikTest = tuple[str, Any] StepikTest = Union[SimpleStepikTest, AdvancedStepikTest] CheckFunction = Callable[[str, Any], CheckResult] PredefinedInput = str RuntimeEvaluatedInput = Union[ - PredefinedInput, InputFunction, Tuple[int, InputFunction], DynamicInputFunction] - DynamicInput = Union[PredefinedInput, List[RuntimeEvaluatedInput]] + PredefinedInput, InputFunction, tuple[int, InputFunction], DynamicInputFunction + ] + DynamicInput = Union[PredefinedInput, list[RuntimeEvaluatedInput]] DEFAULT_TIME_LIMIT: int = 15000 class TestCase: def __init__( - self, *, - stdin: 'DynamicInput' = '', - args: List[str] = None, + self, + *, + stdin: DynamicInput = "", + args: list[str] | None = None, attach: Any = None, - feedback: str = '', - files: Dict[str, str] = None, + feedback: str = "", + files: dict[str, str] | None = None, time_limit: int = DEFAULT_TIME_LIMIT, - check_function: 'CheckFunction' = None, - feedback_on_exception: Dict[Type[Exception], str] = None, + check_function: CheckFunction = None, + feedback_on_exception: dict[type[Exception], str] | None = None, copy_to_attach: bool = False, - dynamic_testing: 'DynamicTesting' = None - ): - + dynamic_testing: DynamicTesting = None, + ) -> None: self.source_name = None - self.input: Optional[str] = None - self.args: List[str] = [] if args is None else args + self.input: str | None = None + self.args: list[str] = [] if args is None else args self.attach: Any = attach self.feedback = feedback - self.files: Dict[str, str] = {} if files is None else files + self.files: dict[str, str] = {} if files is None else files self.time_limit: int = time_limit self.check_func: CheckFunction = check_function - self.feedback_on_exception: Dict[Type[Exception], str] = ( - {} if feedback_on_exception is None else feedback_on_exception) + self.feedback_on_exception: dict[type[Exception], str] = ( + {} if feedback_on_exception is None else feedback_on_exception + ) self.input_funcs = [] self._dynamic_testing: DynamicTesting = dynamic_testing @@ -58,13 +64,14 @@ def __init__( if copy_to_attach: if attach is not None: - raise UnexpectedError( - 'Attach is not None ' - 'but copying from stdin is specified') + msg = "Attach is not None " "but copying from stdin is specified" + raise UnexpectedError(msg) if type(stdin) != str: - raise UnexpectedError( - 'To copy stdin to attach stdin should be of type str ' - f'but found type {type(stdin)}') + msg = ( + "To copy stdin to attach stdin should be of type str " + f"but found type {type(stdin)}" + ) + raise UnexpectedError(msg) self.attach = stdin if type(stdin) == str: @@ -72,9 +79,8 @@ def __init__( self.input_funcs = [DynamicInputFunction(1, lambda x: stdin)] else: if type(stdin) != list: - raise UnexpectedError( - 'Stdin should be either of type str or list ' - f'but found type {type(stdin)}') + msg = "Stdin should be either of type str or list " f"but found type {type(stdin)}" + raise UnexpectedError(msg) for elem in stdin: # type: RuntimeEvaluatedInput if type(elem) == DynamicInputFunction: self.input_funcs += [elem] @@ -82,39 +88,45 @@ def __init__( elif type(elem) == str: self.input_funcs += [DynamicInputFunction(1, lambda x, inp=elem: inp)] - elif str(type(elem)) in ["", ""]: + elif str(type(elem)) in {"", ""}: self.input_funcs += [DynamicInputFunction(1, elem)] - elif type(elem) in (tuple, list): + elif type(elem) in {tuple, list}: if len(elem) == 2: trigger_count: int = elem[0] input_function: InputFunction = elem[1] if type(trigger_count) != int: - raise UnexpectedError( - f'Stdin element\'s 1st element should be of type int, ' - f'found {type(trigger_count)}') + msg = ( + f"Stdin element's 1st element should be of type int, " + f"found {type(trigger_count)}" + ) + raise UnexpectedError(msg) if str(type(input_function)) not in { - "", "" + "", + "", }: - raise UnexpectedError( - f'Stdin element\'s 2nd element should be of type function, ' - f'found {type(input_function)}' + msg = ( + f"Stdin element's 2nd element should be of type function, " + f"found {type(input_function)}" ) + raise UnexpectedError(msg) self.input_funcs += [DynamicInputFunction(trigger_count, input_function)] else: - raise UnexpectedError( - f'Stdin element should have size 2, found {len(elem)}') + msg = f"Stdin element should have size 2, found {len(elem)}" + raise UnexpectedError(msg) else: - raise UnexpectedError( - f'Stdin element should have type DynamicInputFunction or ' - f'tuple of size 1 or 2, found element of type {type(elem)}') + msg = ( + f"Stdin element should have type DynamicInputFunction or " + f"tuple of size 1 or 2, found element of type {type(elem)}" + ) + raise UnexpectedError(msg) @property - def dynamic_testing(self) -> 'DynamicTesting': + def dynamic_testing(self) -> DynamicTesting: if self._dynamic_testing is None: self._dynamic_testing = to_dynamic_testing( self.source_name, self.args, self.input_funcs @@ -122,10 +134,10 @@ def dynamic_testing(self) -> 'DynamicTesting': return self._dynamic_testing @staticmethod - def from_stepik(stepik_tests: List['StepikTest']) -> List['TestCase']: + def from_stepik(stepik_tests: list[StepikTest]) -> list[TestCase]: hs_tests = [] for test in stepik_tests: - if type(test) in (list, tuple): + if type(test) in {list, tuple}: hs_test = TestCase(stdin=test[0], attach=test[1]) elif type(test) is str: hs_test = TestCase(stdin=test) @@ -136,10 +148,10 @@ def from_stepik(stepik_tests: List['StepikTest']) -> List['TestCase']: class SimpleTestCase(TestCase): - def __init__(self, *, stdin: str, stdout: str, feedback: str, **kwargs): + def __init__(self, *, stdin: str, stdout: str, feedback: str, **kwargs) -> None: super().__init__(stdin=stdin, attach=stdout, feedback=feedback, **kwargs) self.check_func = self._custom_check def _custom_check(self, reply: str, expected: str): is_correct = reply.strip() == expected.strip() - return CheckResult(is_correct, '') + return CheckResult(is_correct, "") diff --git a/hstest/testing/__init__.py b/hstest/testing/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/testing/execution/filtering/file_filter.py b/hstest/testing/execution/filtering/file_filter.py index c44e5a54..e002b364 100644 --- a/hstest/testing/execution/filtering/file_filter.py +++ b/hstest/testing/execution/filtering/file_filter.py @@ -1,39 +1,47 @@ +from __future__ import annotations + import re -from typing import Callable, Dict, Set +from collections.abc import Callable Folder = str File = str Source = str Module = str -Sources = Dict[File, Source] +Sources = dict[File, Source] Filter = Callable[[Folder, File, Source], bool] -no_filter: Filter = lambda *a, **kw: True + +def no_filter(*a, **kw) -> bool: + return True class FileFilter: - def __init__(self, - init_files: Callable[[Folder, Sources], None] = no_filter, - folder: Callable[[Folder], bool] = no_filter, - file: Callable[[File], bool] = no_filter, - source: Callable[[Source], bool] = no_filter, - generic: Filter = no_filter): + def __init__( + self, + init_files: Callable[[Folder, Sources], None] = no_filter, + folder: Callable[[Folder], bool] = no_filter, + file: Callable[[File], bool] = no_filter, + source: Callable[[Source], bool] = no_filter, + generic: Filter = no_filter, + ) -> None: self.init_files = init_files self.folder = folder self.file = file self.source = source self.generic = generic - self.filtered: Set[File] = set() + self.filtered: set[File] = set() @staticmethod def regex_filter(regex: str): - return lambda s: re.compile(regex, re.M).search(s) is not None + return lambda s: re.compile(regex, re.MULTILINE).search(s) is not None - def init_filter(self, folder: Folder, sources: Sources): + def init_filter(self, folder: Folder, sources: Sources) -> None: self.init_files(folder, sources) def filter(self, folder: Folder, file: File, source: Source) -> bool: - return self.folder(folder) \ - and self.file(file) \ - and self.source(source) \ + return ( + self.folder(folder) + and self.file(file) + and self.source(source) and self.generic(folder, file, source) + ) diff --git a/hstest/testing/execution/filtering/main_filter.py b/hstest/testing/execution/filtering/main_filter.py index 53ff76bb..2a9dbcf3 100644 --- a/hstest/testing/execution/filtering/main_filter.py +++ b/hstest/testing/execution/filtering/main_filter.py @@ -1,17 +1,30 @@ -from typing import Callable +from __future__ import annotations + +from typing import TYPE_CHECKING from hstest.testing.execution.filtering.file_filter import ( - File, FileFilter, Filter, Folder, no_filter, Source, Sources + File, + FileFilter, + Filter, + Folder, + no_filter, + Source, + Sources, ) +if TYPE_CHECKING: + from collections.abc import Callable + class MainFilter(FileFilter): - def __init__(self, - program_should_contain: str, - init_files: Callable[[Folder, Sources], None] = no_filter, - folder: Callable[[Folder], bool] = no_filter, - file: Callable[[File], bool] = no_filter, - source: Callable[[Source], bool] = no_filter, - generic: Filter = no_filter): + def __init__( + self, + program_should_contain: str, + init_files: Callable[[Folder, Sources], None] = no_filter, + folder: Callable[[Folder], bool] = no_filter, + file: Callable[[File], bool] = no_filter, + source: Callable[[Source], bool] = no_filter, + generic: Filter = no_filter, + ) -> None: super().__init__(init_files, folder, file, source, generic) self.program_should_contain = program_should_contain diff --git a/hstest/testing/execution/main_module_executor.py b/hstest/testing/execution/main_module_executor.py index e319317e..363c668f 100644 --- a/hstest/testing/execution/main_module_executor.py +++ b/hstest/testing/execution/main_module_executor.py @@ -1,8 +1,9 @@ +from __future__ import annotations + import os import runpy import sys -from concurrent.futures import Future -from typing import Optional +from typing import TYPE_CHECKING from hstest.common.process_utils import DaemonThreadPoolExecutor from hstest.dynamic.output.output_handler import OutputHandler @@ -13,30 +14,30 @@ from hstest.testing.execution.program_executor import ProgramExecutor, ProgramState from hstest.testing.execution.searcher.python_searcher import PythonSearcher +if TYPE_CHECKING: + from concurrent.futures import Future + class MainModuleExecutor(ProgramExecutor): - def __init__(self, source_name: str = None): + def __init__(self, source_name: str | None = None) -> None: super().__init__() - OutputHandler.print(f'MainModuleExecutor instantiating, source = {source_name}') + OutputHandler.print(f"MainModuleExecutor instantiating, source = {source_name}") self.runnable = PythonSearcher().find(source_name) - self.__executor: Optional[DaemonThreadPoolExecutor] = None - self.__task: Optional[Future] = None + self.__executor: DaemonThreadPoolExecutor | None = None + self.__task: Future | None = None self.__group = None self.working_directory_before = os.path.abspath(os.getcwd()) - def _invoke_method(self, *args: str): + def _invoke_method(self, *args: str) -> None: from hstest.stage_test import StageTest try: self._machine.set_state(ProgramState.RUNNING) - sys.argv = [self.runnable.file] + list(args) + sys.argv = [self.runnable.file, *list(args)] sys.path.insert(0, self.runnable.folder) - runpy.run_module( - self.runnable.module, - run_name="__main__" - ) + runpy.run_module(self.runnable.module, run_name="__main__") self._machine.set_state(ProgramState.FINISHED) @@ -48,30 +49,30 @@ def _invoke_method(self, *args: str): self._machine.set_state(ProgramState.FINISHED) return - StageTest.curr_test_run.set_error_in_test(ExceptionWithFeedback('', ex)) + StageTest.curr_test_run.set_error_in_test(ExceptionWithFeedback("", ex)) self._machine.set_state(ProgramState.EXCEPTION_THROWN) - def _launch(self, *args: str): - self.modules_before = [k for k in sys.modules.keys()] + def _launch(self, *args: str) -> None: + self.modules_before = list(sys.modules.keys()) from hstest.stage_test import StageTest + test_num = StageTest.curr_test_run.test_num self.__group = ThreadGroup() SystemHandler.install_handler( - self, - lambda: ThreadGroup.curr_group() == self.__group, - lambda: self.request_input() + self, lambda: ThreadGroup.curr_group() == self.__group, self.request_input ) self.__executor = DaemonThreadPoolExecutor( - name=f"MainModuleExecutor test #{test_num}", group=self.__group) + name=f"MainModuleExecutor test #{test_num}", group=self.__group + ) self.__task = self.__executor.submit(lambda: self._invoke_method(*args)) - def _terminate(self): + def _terminate(self) -> None: self.__executor.shutdown(wait=False) self.__task.cancel() with self._machine.cv: diff --git a/hstest/testing/execution/process/cpp_executor.py b/hstest/testing/execution/process/cpp_executor.py index 6849fb58..9e4a3ac7 100644 --- a/hstest/testing/execution/process/cpp_executor.py +++ b/hstest/testing/execution/process/cpp_executor.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from hstest.common.os_utils import is_windows @@ -6,27 +8,36 @@ class CppExecutor(ProcessExecutor): - def __init__(self, source_name: str = None): + def __init__(self, source_name: str | None = None) -> None: super().__init__(CppSearcher().find(source_name)) self.without_extension = os.path.splitext(self.runnable.file)[0] if is_windows(): self.executable = self.without_extension - self.file_name = self.executable + '.exe' + self.file_name = self.executable + ".exe" else: - self.executable = f'./{self.without_extension}' + self.executable = f"./{self.without_extension}" self.file_name = self.without_extension def _compilation_command(self): - return ['g++', '-std=c++20', '-pipe', '-O2', '-static', '-o', self.file_name, self.runnable.file] + return [ + "g++", + "-std=c++20", + "-pipe", + "-O2", + "-static", + "-o", + self.file_name, + self.runnable.file, + ] def _filter_compilation_error(self, error: str) -> str: return error def _execution_command(self, *args: str): - return [self.executable] + list(args) + return [self.executable, *list(args)] - def _cleanup(self): + def _cleanup(self) -> None: if os.path.exists(self.file_name): os.remove(self.file_name) diff --git a/hstest/testing/execution/process/go_executor.py b/hstest/testing/execution/process/go_executor.py index 057ca243..e5d202dc 100644 --- a/hstest/testing/execution/process/go_executor.py +++ b/hstest/testing/execution/process/go_executor.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from hstest.common.os_utils import is_windows @@ -6,28 +8,28 @@ class GoExecutor(ProcessExecutor): - def __init__(self, source_name: str = None): + def __init__(self, source_name: str | None = None) -> None: super().__init__(GoSearcher().find(source_name)) - self.without_go = self.runnable.file[:-len(GoSearcher().extension)] + self.without_go = self.runnable.file[: -len(GoSearcher().extension)] if is_windows(): self.executable = self.without_go - self.file_name = self.executable + '.exe' + self.file_name = self.executable + ".exe" else: - self.executable = f'./{self.without_go}' + self.executable = f"./{self.without_go}" self.file_name = self.without_go def _compilation_command(self): - return ['go', 'build', self.runnable.file] + return ["go", "build", self.runnable.file] def _filter_compilation_error(self, error: str) -> str: - error_lines = [line for line in error.splitlines() if not line.startswith('#')] - return '\n'.join(error_lines) + error_lines = [line for line in error.splitlines() if not line.startswith("#")] + return "\n".join(error_lines) def _execution_command(self, *args: str): - return [self.executable] + list(args) + return [self.executable, *list(args)] - def _cleanup(self): + def _cleanup(self) -> None: if os.path.exists(self.file_name): os.remove(self.file_name) diff --git a/hstest/testing/execution/process/javascript_executor.py b/hstest/testing/execution/process/javascript_executor.py index 8ec4dbaf..c90d3258 100644 --- a/hstest/testing/execution/process/javascript_executor.py +++ b/hstest/testing/execution/process/javascript_executor.py @@ -1,10 +1,12 @@ +from __future__ import annotations + from hstest.testing.execution.process_executor import ProcessExecutor from hstest.testing.execution.searcher.javascript_searcher import JavascriptSearcher class JavascriptExecutor(ProcessExecutor): - def __init__(self, source_name: str = None): + def __init__(self, source_name: str | None = None) -> None: super().__init__(JavascriptSearcher().find(source_name)) def _execution_command(self, *args: str): - return ['node', self.runnable.file] + list(args) + return ["node", self.runnable.file, *list(args)] diff --git a/hstest/testing/execution/process/python_executor.py b/hstest/testing/execution/process/python_executor.py index 23d032e9..b121b145 100644 --- a/hstest/testing/execution/process/python_executor.py +++ b/hstest/testing/execution/process/python_executor.py @@ -1,10 +1,12 @@ +from __future__ import annotations + from hstest.testing.execution.process_executor import ProcessExecutor from hstest.testing.execution.searcher.python_searcher import PythonSearcher class PythonExecutor(ProcessExecutor): - def __init__(self, source_name: str = None): + def __init__(self, source_name: str | None = None) -> None: super().__init__(PythonSearcher().find(source_name)) def _execution_command(self, *args: str): - return ['python', '-u', self.runnable.file] + list(args) + return ["python", "-u", self.runnable.file, *list(args)] diff --git a/hstest/testing/execution/process/shell_executor.py b/hstest/testing/execution/process/shell_executor.py index 71387ea5..2868866b 100644 --- a/hstest/testing/execution/process/shell_executor.py +++ b/hstest/testing/execution/process/shell_executor.py @@ -1,10 +1,12 @@ +from __future__ import annotations + from hstest.testing.execution.process_executor import ProcessExecutor from hstest.testing.execution.searcher.shell_searcher import ShellSearcher class ShellExecutor(ProcessExecutor): - def __init__(self, source_name: str = None): + def __init__(self, source_name: str | None = None) -> None: super().__init__(ShellSearcher().find(source_name)) def _execution_command(self, *args: str): - return ['bash', self.runnable.file] + list(args) + return ["bash", self.runnable.file, *list(args)] diff --git a/hstest/testing/execution/process_executor.py b/hstest/testing/execution/process_executor.py index 7e0bf46e..62391259 100644 --- a/hstest/testing/execution/process_executor.py +++ b/hstest/testing/execution/process_executor.py @@ -1,7 +1,10 @@ +from __future__ import annotations + +import contextlib import os from threading import Thread from time import sleep -from typing import List, Optional +from typing import TYPE_CHECKING from hstest.common.utils import try_many_times from hstest.dynamic.input.input_handler import InputHandler @@ -11,32 +14,35 @@ from hstest.dynamic.system_handler import SystemHandler from hstest.exception.outcomes import CompilationError, ExceptionWithFeedback, OutOfInputError from hstest.testing.execution.program_executor import ProgramExecutor, ProgramState -from hstest.testing.execution.runnable.runnable_file import RunnableFile from hstest.testing.process_wrapper import ProcessWrapper +if TYPE_CHECKING: + from hstest.testing.execution.runnable.runnable_file import RunnableFile + class ProcessExecutor(ProgramExecutor): compiled = False - def __init__(self, runnable: RunnableFile): + def __init__(self, runnable: RunnableFile) -> None: super().__init__() - self.process: Optional[ProcessWrapper] = None + self.process: ProcessWrapper | None = None self.thread = None self.continue_executing = True self.runnable: RunnableFile = runnable - self.__group: Optional[ThreadGroup] = None + self.__group: ThreadGroup | None = None self.working_directory_before = os.path.abspath(os.getcwd()) - def _compilation_command(self, *args: str) -> List[str]: + def _compilation_command(self, *args: str) -> list[str]: return [] def _filter_compilation_error(self, error: str) -> str: return error - def _execution_command(self, *args: str) -> List[str]: - raise NotImplementedError('Method "_execution_command" isn\'t implemented') + def _execution_command(self, *args: str) -> list[str]: + msg = 'Method "_execution_command" isn\'t implemented' + raise NotImplementedError(msg) - def _cleanup(self): + def _cleanup(self) -> None: pass def __compile_program(self) -> bool: @@ -55,13 +61,14 @@ def __compile_program(self) -> bool: error_text = self._filter_compilation_error(process.stderr) from hstest import StageTest + StageTest.curr_test_run.set_error_in_test(CompilationError(error_text)) self._machine.set_state(ProgramState.COMPILATION_ERROR) return False return True - def __handle_process(self, *args: str): + def __handle_process(self, *args: str) -> None: from hstest import StageTest os.chdir(self.runnable.folder) @@ -77,109 +84,102 @@ def __handle_process(self, *args: str): self.process = ProcessWrapper(*command).start() while self.continue_executing: - OutputHandler.print('Handle process - one iteration') + OutputHandler.print("Handle process - one iteration") sleep(0.001) if self.process.is_finished(): - OutputHandler.print('Handle process - finished, breaking') + OutputHandler.print("Handle process - finished, breaking") break is_input_allowed = self.is_input_allowed() is_waiting_input = self.process.is_waiting_input() - OutputHandler.print(f'Handle process - ' - f'input allowed {is_input_allowed}, ' - f'waiting input {is_waiting_input}') + OutputHandler.print( + f"Handle process - " + f"input allowed {is_input_allowed}, " + f"waiting input {is_waiting_input}" + ) if is_input_allowed and is_waiting_input: - OutputHandler.print('Handle process - registering input request') + OutputHandler.print("Handle process - registering input request") self.process.register_input_request() try: - OutputHandler.print('Handle process - try readline') + OutputHandler.print("Handle process - try readline") next_input = InputHandler.mock_in.readline() - OutputHandler.print( - f'Handle process - requested input: {repr(next_input)}' - ) + OutputHandler.print(f"Handle process - requested input: {next_input!r}") self.process.provide_input(next_input) - OutputHandler.print( - f'Handle process - written to stdin: {repr(next_input)}' - ) + OutputHandler.print(f"Handle process - written to stdin: {next_input!r}") except ExitException: - OutputHandler.print('Handle process - EXIT EXCEPTION, stop input') + OutputHandler.print("Handle process - EXIT EXCEPTION, stop input") if self._wait_if_terminated(): if type(StageTest.curr_test_run.error_in_test) == OutOfInputError: StageTest.curr_test_run.set_error_in_test(None) OutputHandler.print( - 'Handle process - Abort stopping input, everything is OK' + "Handle process - Abort stopping input, everything is OK" ) break self.stop_input() except BaseException as ex: - OutputHandler.print(f'Handle process - SOME EXCEPTION {ex}') + OutputHandler.print(f"Handle process - SOME EXCEPTION {ex}") - OutputHandler.print('Handle process - TERMINATE') + OutputHandler.print("Handle process - TERMINATE") self.process.terminate() is_error_happened = self.process.is_error_happened() - OutputHandler.print('Handle process - after termination') - OutputHandler.print(f'Handle process - is error happened {is_error_happened}') + OutputHandler.print("Handle process - after termination") + OutputHandler.print(f"Handle process - is error happened {is_error_happened}") if StageTest.curr_test_run.error_in_test is not None: - OutputHandler.print('Handle process - set state EXCEPTION THROWN (ERROR IN TEST)') + OutputHandler.print("Handle process - set state EXCEPTION THROWN (ERROR IN TEST)") self._machine.set_state(ProgramState.EXCEPTION_THROWN) elif is_error_happened: - OutputHandler.print( - 'Handle process - set state EXCEPTION THROWN (REALLY EXCEPTION)' - ) + OutputHandler.print("Handle process - set state EXCEPTION THROWN (REALLY EXCEPTION)") StageTest.curr_test_run.set_error_in_test( ExceptionWithFeedback(self.process.stderr, None) ) self._machine.set_state(ProgramState.EXCEPTION_THROWN) else: - OutputHandler.print('Handle process - set state FINISHED') + OutputHandler.print("Handle process - set state FINISHED") self._machine.set_state(ProgramState.FINISHED) - OutputHandler.print('Handle process - finishing execution') + OutputHandler.print("Handle process - finishing execution") def _wait_if_terminated(self): return try_many_times(100, 10, lambda: self.process.is_finished(False)) - def _launch(self, *args: str): + def _launch(self, *args: str) -> None: self.__group = ThreadGroup() SystemHandler.install_handler( - self, - lambda: ThreadGroup.curr_group() == self.__group, - lambda: self.request_input() + self, lambda: ThreadGroup.curr_group() == self.__group, self.request_input ) - self.thread = Thread(target=lambda: self.__handle_process(*args), daemon=True, - group=self.__group) + self.thread = Thread( + target=lambda: self.__handle_process(*args), daemon=True, group=self.__group + ) self.thread.start() - def _terminate(self): + def _terminate(self) -> None: self.continue_executing = False self.process.terminate() - OutputHandler.print(f'TERMINATE {self.is_finished()}') + OutputHandler.print(f"TERMINATE {self.is_finished()}") os.chdir(self.working_directory_before) while not self.is_finished(): if self.is_waiting_input(): self._machine.set_state(ProgramState.RUNNING) - OutputHandler.print(f'NOT FINISHED {self._machine.state}') + OutputHandler.print(f"NOT FINISHED {self._machine.state}") sleep(0.001) - def tear_down(self): + def tear_down(self) -> None: working_directory_before = os.path.abspath(os.getcwd()) os.chdir(self.runnable.folder) - try: + with contextlib.suppress(BaseException): self._cleanup() - except BaseException: - pass ProcessExecutor.compiled = False os.chdir(working_directory_before) diff --git a/hstest/testing/execution/program_executor.py b/hstest/testing/execution/program_executor.py index a1b0dc80..2ad71f20 100644 --- a/hstest/testing/execution/program_executor.py +++ b/hstest/testing/execution/program_executor.py @@ -1,5 +1,7 @@ +from __future__ import annotations + from enum import Enum -from typing import Optional +from typing import NoReturn from hstest.dynamic.output.output_handler import OutputHandler from hstest.exception.outcomes import ErrorWithFeedback, UnexpectedError @@ -17,8 +19,8 @@ class ProgramState(Enum): class ProgramExecutor: - def __init__(self, source_name: str = None): - self._input: Optional[str] = None + def __init__(self, source_name: str | None = None) -> None: + self._input: str | None = None self.__in_background: bool = False self.__no_more_input: bool = False @@ -33,18 +35,21 @@ def __init__(self, source_name: str = None): m.add_transition(ProgramState.RUNNING, ProgramState.EXCEPTION_THROWN) m.add_transition(ProgramState.RUNNING, ProgramState.FINISHED) - def _launch(self, *args: str): - raise NotImplementedError('Method "_launch" isn\'t implemented') + def _launch(self, *args: str) -> NoReturn: + msg = 'Method "_launch" isn\'t implemented' + raise NotImplementedError(msg) - def _terminate(self): - raise NotImplementedError('Method "_terminate" isn\'t implemented') + def _terminate(self) -> NoReturn: + msg = 'Method "_terminate" isn\'t implemented' + raise NotImplementedError(msg) def get_output(self) -> str: return OutputHandler.get_partial_output(self) def start(self, *args: str) -> str: if not self._machine.in_state(ProgramState.NOT_STARTED): - raise UnexpectedError(f"Cannot start the program {self} twice") + msg = f"Cannot start the program {self} twice" + raise UnexpectedError(msg) self._launch(*args) @@ -52,23 +57,23 @@ def start(self, *args: str) -> str: self._machine.wait_not_state(ProgramState.NOT_STARTED) return "" - self._machine.wait_not_states( - ProgramState.NOT_STARTED, ProgramState.RUNNING) + self._machine.wait_not_states(ProgramState.NOT_STARTED, ProgramState.RUNNING) - OutputHandler.print('Program executor - after waiting in start() method') + OutputHandler.print("Program executor - after waiting in start() method") return self.__get_execution_output() def execute(self, stdin: str) -> str: if self.is_finished(): from hstest.stage_test import StageTest + StageTest.curr_test_run.set_error_in_test( ErrorWithFeedback( - f"The program {self} has unexpectedly terminated.\n" + - "It finished execution too early, should continue running." + f"The program {self} has unexpectedly terminated.\n" + + "It finished execution too early, should continue running." ) ) - raise TestedProgramFinishedEarly() + raise TestedProgramFinishedEarly if stdin is None: self.stop_input() @@ -76,12 +81,13 @@ def execute(self, stdin: str) -> str: if not self.is_waiting_input(): raise UnexpectedError( - f"Program {self} is not waiting for the input " + - f"(state == \"{self._machine.state}\")") + f"Program {self} is not waiting for the input " + + f'(state == "{self._machine.state}")' + ) if self.__no_more_input: - raise UnexpectedError( - f"Can't pass input to the program {self} - input was prohibited.") + msg = f"Can't pass input to the program {self} - input was prohibited." + raise UnexpectedError(msg) self._input = stdin if self.__in_background: @@ -93,29 +99,29 @@ def execute(self, stdin: str) -> str: self._machine.set_and_wait(ProgramState.RUNNING) return self.__get_execution_output() - def stop(self): + def stop(self) -> None: self.__no_more_input = True self._terminate() def __get_execution_output(self) -> str: - OutputHandler.print('Program executor - __get_execution_output()') + OutputHandler.print("Program executor - __get_execution_output()") if self._machine.in_state(ProgramState.EXCEPTION_THROWN): - raise TestedProgramThrewException() - OutputHandler.print('Program executor - __get_execution_output() NO EXCEPTION') + raise TestedProgramThrewException + OutputHandler.print("Program executor - __get_execution_output() NO EXCEPTION") if self.__return_output_after_execution: return self.get_output() return "" - def request_input(self) -> Optional[str]: + def request_input(self) -> str | None: if self.__no_more_input: return None - OutputHandler.print('Program executor - _request_input() invoked, set state WAITING') + OutputHandler.print("Program executor - _request_input() invoked, set state WAITING") self._machine.set_and_wait(ProgramState.WAITING, ProgramState.RUNNING) input_local = self._input self._input = None return input_local - def set_return_output_after_execution(self, value: bool): + def set_return_output_after_execution(self, value: bool) -> None: self.__return_output_after_execution = value def is_finished(self) -> bool: @@ -123,7 +129,7 @@ def is_finished(self) -> bool: exception = self._machine.in_state(ProgramState.EXCEPTION_THROWN) return finished or exception - def stop_input(self): + def stop_input(self) -> None: self.__in_background = True self.__no_more_input = True if self.is_waiting_input(): @@ -135,22 +141,23 @@ def is_input_allowed(self) -> bool: def is_waiting_input(self) -> bool: return self._machine.in_state(ProgramState.WAITING) - def start_in_background(self, *args: str): + def start_in_background(self, *args: str) -> None: self.__in_background = True self.start(*args) - def go_background(self): + def go_background(self) -> None: self.__in_background = True - def stop_background(self): + def stop_background(self) -> None: self.__in_background = False self._machine.wait_state(ProgramState.WAITING) def is_in_background(self): return self.__in_background - def tear_down(self): + def tear_down(self) -> None: pass def __str__(self) -> str: - raise NotImplementedError('Method "__str__" isn\'t implemented') + msg = 'Method "__str__" isn\'t implemented' + raise NotImplementedError(msg) diff --git a/hstest/testing/execution/runnable/python_runnable_file.py b/hstest/testing/execution/runnable/python_runnable_file.py index 5c8987a4..ebe6d6dd 100644 --- a/hstest/testing/execution/runnable/python_runnable_file.py +++ b/hstest/testing/execution/runnable/python_runnable_file.py @@ -1,7 +1,9 @@ +from __future__ import annotations + from hstest.testing.execution.runnable.runnable_file import RunnableFile class PythonRunnableFile(RunnableFile): - def __init__(self, folder: str, file: str, module: str): + def __init__(self, folder: str, file: str, module: str) -> None: super().__init__(folder, file) self.module = module diff --git a/hstest/testing/execution/runnable/runnable_file.py b/hstest/testing/execution/runnable/runnable_file.py index 506e65aa..7f329645 100644 --- a/hstest/testing/execution/runnable/runnable_file.py +++ b/hstest/testing/execution/runnable/runnable_file.py @@ -1,7 +1,16 @@ -from hstest.testing.execution.filtering.file_filter import File, Folder +from __future__ import annotations + +from pathlib import Path +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from hstest.testing.execution.filtering.file_filter import File, Folder class RunnableFile: - def __init__(self, folder: Folder, file: File): + def __init__(self, folder: Folder, file: File) -> None: self.folder = folder self.file = file + + def path(self) -> Path: + return Path(self.folder) / self.file diff --git a/hstest/testing/execution/searcher/base_searcher.py b/hstest/testing/execution/searcher/base_searcher.py index 9457d3cf..7e78ac30 100644 --- a/hstest/testing/execution/searcher/base_searcher.py +++ b/hstest/testing/execution/searcher/base_searcher.py @@ -1,6 +1,7 @@ +from __future__ import annotations + import os import re -from typing import Dict, List, Optional, Set, Tuple, Union from hstest.common.file_utils import walk_user_files from hstest.exception.outcomes import ErrorWithFeedback, UnexpectedError @@ -15,13 +16,15 @@ class BaseSearcher: @property def extension(self) -> str: - raise NotImplementedError('Property "extension" should be implemented') + msg = 'Property "extension" should be implemented' + raise NotImplementedError(msg) - def search(self, where_to_search: str = None) -> RunnableFile: - raise NotImplementedError('Method "search" should be implemented') + def search(self, where_to_search: str | None = None) -> RunnableFile: + msg = 'Method "search" should be implemented' + raise NotImplementedError(msg) @staticmethod - def _get_contents(folder: Folder, files: List[File]) -> Dict[File, Source]: + def _get_contents(folder: Folder, files: list[File]) -> dict[File, Source]: contents = {} for file in files: @@ -29,7 +32,7 @@ def _get_contents(folder: Folder, files: List[File]) -> Dict[File, Source]: if path in file_contents_cached: contents[file] = file_contents_cached[path] elif os.path.exists(path): - with open(path) as f: + with open(path, encoding="locale") as f: try: file_content = f.read() except UnicodeDecodeError: @@ -48,21 +51,18 @@ def _search_non_cached( pre_main_filter: FileFilter, main_filter: MainFilter, post_main_filter: FileFilter, - force_content_filters: Union[List[MainFilter], None] = None + force_content_filters: list[MainFilter] | None = None, ) -> RunnableFile: - if not force_content_filters: force_content_filters = [] curr_folder = os.path.abspath(where_to_search) - for folder, dirs, files in walk_user_files(curr_folder): - + for folder, _dirs, files in walk_user_files(curr_folder): contents = self._get_contents(folder, files) initial_filter = FileFilter( - file=lambda f: f.endswith(self.extension), - generic=file_filter.filter + file=lambda f: f.endswith(self.extension), generic=file_filter.filter ) candidates = set(files) @@ -70,8 +70,9 @@ def _search_non_cached( for curr_filter in initial_filter, pre_main_filter, main_filter, post_main_filter: curr_filter.init_filter(folder, contents) - filtered_files: Set[File] = { - file for file in files + filtered_files: set[File] = { + file + for file in files if file in contents and curr_filter.filter(folder, file, contents[file]) } @@ -85,10 +86,10 @@ def _search_non_cached( elif curr_filter == initial_filter: for forced_filter in force_content_filters: filtered_files = { - file for file in filtered_files - if file in contents and forced_filter.filter( - folder, file, contents[file] - ) + file + for file in filtered_files + if file in contents + and forced_filter.filter(folder, file, contents[file]) } if len(filtered_files) == 0: should_contain = [ @@ -96,9 +97,8 @@ def _search_non_cached( for forced_filter in force_content_filters if isinstance(forced_filter, MainFilter) ] - raise ErrorWithFeedback( - f'The runnable file should contain all the following lines: {should_contain}' - ) + msg = f"The runnable file should contain all the following lines: {should_contain}" + raise ErrorWithFeedback(msg) if len(filtered_files) == 1: file = filtered_files.pop() @@ -116,46 +116,55 @@ def _search_non_cached( continue if len(candidates) > 1 and len(main_filter.filtered) > 0: - str_files = ', '.join(f'"{f}"' for f in sorted(candidates)) + str_files = ", ".join(f'"{f}"' for f in sorted(candidates)) all_have = [] if main_filter.program_should_contain: all_have.append(main_filter.program_should_contain) - all_have.extend([ - forced_filter.program_should_contain for forced_filter in force_content_filters - if isinstance(forced_filter, MainFilter) - ]) - raise ErrorWithFeedback( - f'Cannot decide which file to run out of the following: {str_files}\n' - f'They all have {all_have}. ' - f'Leave one file with this lines.') + all_have.extend( + [ + forced_filter.program_should_contain + for forced_filter in force_content_filters + if isinstance(forced_filter, MainFilter) + ] + ) + msg = ( + f"Cannot decide which file to run out of the following: {str_files}\n" + f"They all have {all_have}. " + f"Leave one file with this lines." + ) + raise ErrorWithFeedback(msg) if len(candidates) == 0: candidates = initial_filter.filtered - str_files = ', '.join(f'"{f}"' for f in sorted(candidates)) + str_files = ", ".join(f'"{f}"' for f in sorted(candidates)) - raise ErrorWithFeedback( - f'Cannot decide which file to run out of the following: {str_files}\n' + msg = ( + f"Cannot decide which file to run out of the following: {str_files}\n" f'Write "{main_filter.program_should_contain}" ' - f'in one of them to mark it as an entry point.') + f"in one of them to mark it as an entry point." + ) + raise ErrorWithFeedback(msg) - raise ErrorWithFeedback( - 'Cannot find a file to execute your code.\n' - f'Are your project files located at \"{curr_folder}\"?') + msg = ( + "Cannot find a file to execute your code.\n" + f'Are your project files located at "{curr_folder}"?' + ) + raise ErrorWithFeedback(msg) def _search( self, - where_to_search: str = None, + where_to_search: str | None = None, *, file_filter: FileFilter = None, pre_main_filter: FileFilter = None, main_filter: MainFilter = None, post_main_filter: FileFilter = None, - force_content_filters: Union[List[MainFilter], None] = None + force_content_filters: list[MainFilter] | None = None, ) -> RunnableFile: - - if not self.extension.startswith('.'): - raise UnexpectedError(f'File extension "{self.extension}" should start with a dot') + if not self.extension.startswith("."): + msg = f'File extension "{self.extension}" should start with a dot' + raise UnexpectedError(msg) if where_to_search is None: where_to_search = os.getcwd() @@ -193,27 +202,25 @@ def _search( return result - def _simple_search(self, - where_to_search: str, - main_desc: str, - main_regex: str, - force_content_filters: Union[List[MainFilter], None] = None - ) -> RunnableFile: - main_searcher = re.compile(main_regex, re.M) + def _simple_search( + self, + where_to_search: str, + main_desc: str, + main_regex: str, + force_content_filters: list[MainFilter] | None = None, + ) -> RunnableFile: + main_searcher = re.compile(main_regex, re.MULTILINE) return self._search( where_to_search, - main_filter=MainFilter( - main_desc, - source=lambda s: main_searcher.search(s) is not None - ), - force_content_filters=force_content_filters + main_filter=MainFilter(main_desc, source=lambda s: main_searcher.search(s) is not None), + force_content_filters=force_content_filters, ) def _base_search(self, where_to_search: str) -> RunnableFile: - return self._simple_search(where_to_search, main_desc='', main_regex='') + return self._simple_search(where_to_search, main_desc="", main_regex="") - def find(self, source: Optional[str]) -> RunnableFile: - if source in [None, '']: + def find(self, source: str | None) -> RunnableFile: + if source in {None, ""}: return self.search() ext = self.extension @@ -224,37 +231,39 @@ def find(self, source: Optional[str]) -> RunnableFile: return self.search(source_folder) elif source_file is not None and os.path.isfile(source_file): - path, sep, file = source_module.rpartition('.') - folder = os.path.abspath(path.replace('.', os.sep)) + path, _sep, file = source_module.rpartition(".") + folder = os.path.abspath(path.replace(".", os.sep)) return RunnableFile(folder, file + ext) else: - path, _, _ = source_module.rpartition('.') - folder = os.path.abspath(path.replace('.', os.sep)) - raise ErrorWithFeedback( - 'Cannot find a file to execute your code.\n' - f'Are your project files located at \"{folder}\"?') + path, _, _ = source_module.rpartition(".") + folder = os.path.abspath(path.replace(".", os.sep)) + msg = ( + "Cannot find a file to execute your code.\n" + f'Are your project files located at "{folder}"?' + ) + raise ErrorWithFeedback(msg) - def _parse_source(self, source: str) -> Tuple[Folder, File, Module]: + def _parse_source(self, source: str) -> tuple[Folder, File, Module]: ext = self.extension - source = source.replace('/', os.sep).replace('\\', os.sep) + source = source.replace("/", os.sep).replace("\\", os.sep) if source.endswith(ext): source_folder = None source_file = source - source_module = source[:-len(ext)].replace(os.sep, '.') + source_module = source[: -len(ext)].replace(os.sep, ".") elif os.sep in source: if source.endswith(os.sep): - source = source[:-len(os.sep)] + source = source[: -len(os.sep)] source_folder = source source_file = None - source_module = source.replace(os.sep, '.') + source_module = source.replace(os.sep, ".") else: - source_folder = source.replace('.', os.sep) + source_folder = source.replace(".", os.sep) source_file = source_folder + ext source_module = source diff --git a/hstest/testing/execution/searcher/cpp_searcher.py b/hstest/testing/execution/searcher/cpp_searcher.py index b01fac3a..c0173070 100644 --- a/hstest/testing/execution/searcher/cpp_searcher.py +++ b/hstest/testing/execution/searcher/cpp_searcher.py @@ -1,25 +1,26 @@ +from __future__ import annotations + import re +from typing import TYPE_CHECKING from hstest.testing.execution.filtering.main_filter import MainFilter -from hstest.testing.execution.runnable.runnable_file import RunnableFile from hstest.testing.execution.searcher.base_searcher import BaseSearcher +if TYPE_CHECKING: + from hstest.testing.execution.runnable.runnable_file import RunnableFile -class CppSearcher(BaseSearcher): +class CppSearcher(BaseSearcher): @property def extension(self) -> str: - return '.cpp' + return ".cpp" - def search(self, where: str = None) -> RunnableFile: - main_func_searcher = re.compile(r'(^|\n)\s*int\s+main\s*\(.*\)', re.M) + def search(self, where: str | None = None) -> RunnableFile: + main_func_searcher = re.compile(r"(^|\n)\s*int\s+main\s*\(.*\)", re.MULTILINE) return self._search( where, force_content_filters=[ - MainFilter( - 'int main()', - source=lambda s: main_func_searcher.search(s) is not None - ), - ] + MainFilter("int main()", source=lambda s: main_func_searcher.search(s) is not None), + ], ) diff --git a/hstest/testing/execution/searcher/go_searcher.py b/hstest/testing/execution/searcher/go_searcher.py index aa00bbe2..838dfc5d 100644 --- a/hstest/testing/execution/searcher/go_searcher.py +++ b/hstest/testing/execution/searcher/go_searcher.py @@ -1,30 +1,30 @@ +from __future__ import annotations + import re +from typing import TYPE_CHECKING from hstest.testing.execution.filtering.main_filter import MainFilter -from hstest.testing.execution.runnable.runnable_file import RunnableFile from hstest.testing.execution.searcher.base_searcher import BaseSearcher +if TYPE_CHECKING: + from hstest.testing.execution.runnable.runnable_file import RunnableFile -class GoSearcher(BaseSearcher): +class GoSearcher(BaseSearcher): @property def extension(self) -> str: - return '.go' + return ".go" - def search(self, where: str = None) -> RunnableFile: - package_searcher = re.compile(r'^\s*package\s*main', re.M) - main_func_searcher = re.compile(r'(^|\n)\s*func\s+main\s*\(\s*\)', re.M) + def search(self, where: str | None = None) -> RunnableFile: + package_searcher = re.compile(r"^\s*package\s*main", re.MULTILINE) + main_func_searcher = re.compile(r"(^|\n)\s*func\s+main\s*\(\s*\)", re.MULTILINE) return self._search( where, force_content_filters=[ + MainFilter("package main", source=lambda s: package_searcher.search(s) is not None), MainFilter( - 'package main', - source=lambda s: package_searcher.search(s) is not None - ), - MainFilter( - 'func main()', - source=lambda s: main_func_searcher.search(s) is not None + "func main()", source=lambda s: main_func_searcher.search(s) is not None ), - ] + ], ) diff --git a/hstest/testing/execution/searcher/javascript_searcher.py b/hstest/testing/execution/searcher/javascript_searcher.py index 027ea5a9..f1ec811f 100644 --- a/hstest/testing/execution/searcher/javascript_searcher.py +++ b/hstest/testing/execution/searcher/javascript_searcher.py @@ -1,12 +1,17 @@ -from hstest.testing.execution.runnable.runnable_file import RunnableFile +from __future__ import annotations + +from typing import TYPE_CHECKING + from hstest.testing.execution.searcher.base_searcher import BaseSearcher +if TYPE_CHECKING: + from hstest.testing.execution.runnable.runnable_file import RunnableFile -class JavascriptSearcher(BaseSearcher): +class JavascriptSearcher(BaseSearcher): @property def extension(self) -> str: - return '.js' + return ".js" - def search(self, where: str = None) -> RunnableFile: - return self._simple_search(where, "function main()", r'(^|\n) *function +main +\( *\)') + def search(self, where: str | None = None) -> RunnableFile: + return self._simple_search(where, "function main()", r"(^|\n) *function +main +\( *\)") diff --git a/hstest/testing/execution/searcher/python_searcher.py b/hstest/testing/execution/searcher/python_searcher.py index f4f85c11..1afd32a0 100644 --- a/hstest/testing/execution/searcher/python_searcher.py +++ b/hstest/testing/execution/searcher/python_searcher.py @@ -1,32 +1,37 @@ +from __future__ import annotations + import os import re -from typing import Optional +from typing import TYPE_CHECKING from hstest.dynamic.output.output_handler import OutputHandler from hstest.testing.execution.filtering.file_filter import FileFilter, Folder, Sources from hstest.testing.execution.filtering.main_filter import MainFilter from hstest.testing.execution.runnable.python_runnable_file import PythonRunnableFile -from hstest.testing.execution.runnable.runnable_file import RunnableFile from hstest.testing.execution.searcher.base_searcher import BaseSearcher +if TYPE_CHECKING: + from hstest.testing.execution.runnable.runnable_file import RunnableFile -class PythonSearcher(BaseSearcher): +class PythonSearcher(BaseSearcher): @property def extension(self) -> str: - return '.py' + return ".py" - def search(self, where_to_search: str = None, file_filter: FileFilter = None) -> RunnableFile: + def search( + self, where_to_search: str | None = None, file_filter: FileFilter = None + ) -> RunnableFile: is_imported = {} - def init_regexes(_: Folder, sources: Sources): + def init_regexes(_: Folder, sources: Sources) -> None: import_regexes = {} for file, source in sources.items(): is_imported[file] = False import_regexes[file] = [ - re.compile(rf'(^|\n)import +[\w., ]*\b{file[:-3]}\b[\w., ]*', re.M), - re.compile(rf'(^|\n)from +\.? *\b{file[:-3]}\b +import +', re.M) + re.compile(rf"(^|\n)import +[\w., ]*\b{file[:-3]}\b[\w., ]*", re.MULTILINE), + re.compile(rf"(^|\n)from +\.? *\b{file[:-3]}\b +import +", re.MULTILINE), ] for file, source in sources.items(): @@ -37,22 +42,16 @@ def init_regexes(_: Folder, sources: Sources): return self._search( where_to_search, file_filter=file_filter, - - pre_main_filter=FileFilter( - init_files=init_regexes, - file=lambda f: not is_imported[f] - ), - + pre_main_filter=FileFilter(init_files=init_regexes, file=lambda f: not is_imported[f]), main_filter=MainFilter( - "if __name__ == '__main__'", - source=lambda s: '__name__' in s and '__main__' in s - ) + "if __name__ == '__main__'", source=lambda s: "__name__" in s and "__main__" in s + ), ) - def find(self, source: Optional[str]) -> PythonRunnableFile: - OutputHandler.print(f'PythonSearcher source = {source}, cwd = {os.getcwd()}') + def find(self, source: str | None) -> PythonRunnableFile: + OutputHandler.print(f"PythonSearcher source = {source}, cwd = {os.getcwd()}") runnable = super().find(source) - OutputHandler.print(f'PythonSearcher found runnable: {runnable.folder}/{runnable.file}') + OutputHandler.print(f"PythonSearcher found runnable: {runnable.folder}/{runnable.file}") return PythonRunnableFile( - runnable.folder, runnable.file, runnable.file[:-len(self.extension)] + runnable.folder, runnable.file, runnable.file[: -len(self.extension)] ) diff --git a/hstest/testing/execution/searcher/shell_searcher.py b/hstest/testing/execution/searcher/shell_searcher.py index effe194b..63f5a8a4 100644 --- a/hstest/testing/execution/searcher/shell_searcher.py +++ b/hstest/testing/execution/searcher/shell_searcher.py @@ -1,12 +1,17 @@ -from hstest.testing.execution.runnable.runnable_file import RunnableFile +from __future__ import annotations + +from typing import TYPE_CHECKING + from hstest.testing.execution.searcher.base_searcher import BaseSearcher +if TYPE_CHECKING: + from hstest.testing.execution.runnable.runnable_file import RunnableFile -class ShellSearcher(BaseSearcher): +class ShellSearcher(BaseSearcher): @property def extension(self) -> str: - return '.sh' + return ".sh" - def search(self, where: str = None) -> RunnableFile: - return self._simple_search(where, "# main", r'(^|\n)# *main') + def search(self, where: str | None = None) -> RunnableFile: + return self._simple_search(where, "# main", r"(^|\n)# *main") diff --git a/hstest/testing/execution/searcher/sql_searcher.py b/hstest/testing/execution/searcher/sql_searcher.py index 986f2892..1f52c04f 100644 --- a/hstest/testing/execution/searcher/sql_searcher.py +++ b/hstest/testing/execution/searcher/sql_searcher.py @@ -1,12 +1,17 @@ -from hstest.testing.execution.runnable.runnable_file import RunnableFile +from __future__ import annotations + +from typing import TYPE_CHECKING + from hstest.testing.execution.searcher.base_searcher import BaseSearcher +if TYPE_CHECKING: + from hstest.testing.execution.runnable.runnable_file import RunnableFile -class SQLSearcher(BaseSearcher): +class SQLSearcher(BaseSearcher): @property def extension(self) -> str: - return '.sql' + return ".sql" - def search(self, where: str = None) -> RunnableFile: + def search(self, where: str | None = None) -> RunnableFile: return self._base_search(where) diff --git a/hstest/testing/execution_options.py b/hstest/testing/execution_options.py index da688918..d82273d6 100644 --- a/hstest/testing/execution_options.py +++ b/hstest/testing/execution_options.py @@ -1,8 +1,10 @@ +from __future__ import annotations + import os import sys -skip_slow: bool = '--skip_slow' in sys.argv -ignore_stdout: bool = '--ignore_stdout' in sys.argv -inside_docker: bool = '--inside_docker' in sys.argv or os.environ.get('INSIDE_DOCKER', '') == '1' -debug_mode: bool = '--debug_mode' in sys.argv or sys.gettrace() is not None -force_process_testing: bool = '--force_process_testing' in sys.argv +skip_slow: bool = "--skip_slow" in sys.argv +ignore_stdout: bool = "--ignore_stdout" in sys.argv +inside_docker: bool = "--inside_docker" in sys.argv or os.environ.get("INSIDE_DOCKER", "") == "1" +debug_mode: bool = "--debug_mode" in sys.argv or sys.gettrace() is not None +force_process_testing: bool = "--force_process_testing" in sys.argv diff --git a/hstest/testing/plotting/drawing/drawing.py b/hstest/testing/plotting/drawing/drawing.py index 6f81db16..84728d72 100644 --- a/hstest/testing/plotting/drawing/drawing.py +++ b/hstest/testing/plotting/drawing/drawing.py @@ -1,16 +1,16 @@ -from typing import Any, Dict, Optional +from __future__ import annotations -from hstest.testing.plotting.drawing.drawing_data import DrawingData +from typing import Any, TYPE_CHECKING +if TYPE_CHECKING: + from hstest.testing.plotting.drawing.drawing_data import DrawingData -class Drawing: - def __init__(self, - library: str, - plot_type: str, - data: Optional[DrawingData], - kwargs: Dict[str, Any]): +class Drawing: + def __init__( + self, library: str, plot_type: str, data: DrawingData | None, kwargs: dict[str, Any] + ) -> None: self.library: str = library self.type: str = plot_type - self.data: Optional[DrawingData] = data - self.kwargs: Dict[str, Any] = kwargs + self.data: DrawingData | None = data + self.kwargs: dict[str, Any] = kwargs diff --git a/hstest/testing/plotting/drawing/drawing_builder.py b/hstest/testing/plotting/drawing/drawing_builder.py index 301cbac5..07ca4ac5 100644 --- a/hstest/testing/plotting/drawing/drawing_builder.py +++ b/hstest/testing/plotting/drawing/drawing_builder.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from hstest.testing.plotting.drawing.drawing import Drawing from hstest.testing.plotting.drawing.drawing_type import DrawingType from hstest.testing.plotting.drawing_data_normalizer import DrawingDataNormalizer diff --git a/hstest/testing/plotting/drawing/drawing_data.py b/hstest/testing/plotting/drawing/drawing_data.py index 952e4915..6296a0bd 100644 --- a/hstest/testing/plotting/drawing/drawing_data.py +++ b/hstest/testing/plotting/drawing/drawing_data.py @@ -1,25 +1,27 @@ -from typing import Optional +from __future__ import annotations import numpy as np class DrawingData: - def __init__(self, x: np.ndarray, y: np.ndarray): + def __init__(self, x: np.ndarray, y: np.ndarray) -> None: try: if type(x) != list and x is not None: x = list(x) if type(y) != list and y is not None: y = list(y) except Exception: - raise ValueError('The data argument should be an array') + msg = "The data argument should be an array" + raise ValueError(msg) if x is not None and y is not None and len(x) != len(y): - raise ValueError('Arrays should be the same length') + msg = "Arrays should be the same length" + raise ValueError(msg) if x is not None: x = np.array(x, dtype=object) if y is not None: y = np.array(y, dtype=object) - self.x: Optional[np.ndarray] = x - self.y: Optional[np.ndarray] = y + self.x: np.ndarray | None = x + self.y: np.ndarray | None = y diff --git a/hstest/testing/plotting/drawing/drawing_library.py b/hstest/testing/plotting/drawing/drawing_library.py index 6f1d5730..6af09a3a 100644 --- a/hstest/testing/plotting/drawing/drawing_library.py +++ b/hstest/testing/plotting/drawing/drawing_library.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + class DrawingLibrary: matplotlib = "matplotlib" pandas = "pandas" diff --git a/hstest/testing/plotting/drawing/drawing_type.py b/hstest/testing/plotting/drawing/drawing_type.py index 0c95a5d7..f3febb89 100644 --- a/hstest/testing/plotting/drawing/drawing_type.py +++ b/hstest/testing/plotting/drawing/drawing_type.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + class DrawingType: # ---------------------- # common types with data diff --git a/hstest/testing/plotting/drawing_data_normalizer.py b/hstest/testing/plotting/drawing_data_normalizer.py index e30928df..a0e0639c 100644 --- a/hstest/testing/plotting/drawing_data_normalizer.py +++ b/hstest/testing/plotting/drawing_data_normalizer.py @@ -1,8 +1,9 @@ +from __future__ import annotations + import numpy as np class DrawingDataNormalizer: - @staticmethod def normalize_x_y_data(x, y) -> np.ndarray: try: @@ -11,21 +12,22 @@ def normalize_x_y_data(x, y) -> np.ndarray: if type(y) != list: y = list(y) except Exception: - raise ValueError('The data argument should be an array') + msg = "The data argument should be an array" + raise ValueError(msg) if len(x) != len(y): - raise ValueError('Arrays should be the same length') + msg = "Arrays should be the same length" + raise ValueError(msg) - result_data = list() + result_data = [] - for a, b in zip(x, y): + for a, b in zip(x, y, strict=False): result_data.append((a, b)) return np.array(result_data, dtype=object) @staticmethod def normalize_hist_data(data) -> np.ndarray: - if type(data) == str: data = [data] @@ -33,7 +35,8 @@ def normalize_hist_data(data) -> np.ndarray: try: data = list(data) except Exception: - raise ValueError('The data argument should be an array') + msg = "The data argument should be an array" + raise ValueError(msg) return np.array(data, dtype=object) @@ -73,6 +76,7 @@ def normalize_hist_data(data) -> np.ndarray: return np.array(result_data, dtype=object) """ # noqa: W293 + return None @staticmethod def normalize_bar_data(x, y) -> np.ndarray: diff --git a/hstest/testing/plotting/matplotlib_handler.py b/hstest/testing/plotting/matplotlib_handler.py index 32388b3e..a4e32924 100644 --- a/hstest/testing/plotting/matplotlib_handler.py +++ b/hstest/testing/plotting/matplotlib_handler.py @@ -1,13 +1,16 @@ +from __future__ import annotations + +import contextlib from copy import deepcopy from importlib import reload from typing import TYPE_CHECKING from hstest.testing.plotting.drawing.drawing_data import DrawingData -try: +with contextlib.suppress(ImportError): import pandas as pd -except ImportError: - pass + +import contextlib from hstest.testing.plotting.drawing.drawing import Drawing from hstest.testing.plotting.drawing.drawing_builder import DrawingBuilder @@ -37,29 +40,26 @@ class MatplotlibHandler: _matplotlib = None @staticmethod - def replace_plots(drawings: 'DrawingsStorage'): - + def replace_plots(drawings: DrawingsStorage) -> None: try: - import matplotlib + import matplotlib as mpl import numpy as np except ModuleNotFoundError: return - def custom_show_func(*args, **kwargs): + def custom_show_func(*args, **kwargs) -> None: pass def hist(x, *args, data=None, **kw): if data is not None: - try: + with contextlib.suppress(Exception): x = data[x] - except Exception: - pass try: if type(x) == pd.DataFrame: for col in x.columns: hist(x[col], *args, **kw) - return + return None elif type(x) == pd.Series: return hist(x.to_numpy(), *args, **kw) except Exception: @@ -68,39 +68,37 @@ def hist(x, *args, data=None, **kw): if type(x) != np.ndarray: x = np.array(x, dtype=object) if len(x.shape) == 2: - import matplotlib.cbook as cbook - x = np.array(cbook._reshape_2D(x, 'x'), dtype=object) + from matplotlib import cbook + + x = np.array(cbook._reshape_2D(x, "x"), dtype=object) if len(x.shape) == 2: for i in range(x.shape[1]): hist(x[:, i], *args, **kw) - return + return None drawings.append( Drawing( DrawingLibrary.matplotlib, DrawingType.hist, DrawingData(x, np.array([1] * len(x), dtype=object)), - kw + kw, ) ) + return None def bar(x, height, *args, data=None, **kw): if data is not None: - try: + with contextlib.suppress(Exception): x = data[x] - except Exception: - pass - try: + with contextlib.suppress(Exception): height = data[height] - except Exception: - pass try: if type(x) == pd.DataFrame: for col in x.columns: bar(x[col], *args, **kw) - return + return None elif type(x) == pd.Series: return bar(x.to_numpy(), height, *args, **kw) elif type(height) == pd.Series: @@ -108,88 +106,73 @@ def bar(x, height, *args, data=None, **kw): except Exception: pass - if type(height) in [int, float]: + if type(height) in {int, float}: height = np.full((len(x),), height) drawings.append( - Drawing( - DrawingLibrary.matplotlib, - DrawingType.bar, - DrawingData(x, height), - kw - ) + Drawing(DrawingLibrary.matplotlib, DrawingType.bar, DrawingData(x, height), kw) ) + return None def barh(x, width, *args, data=None, **kw): return bar(x, width, *args, data=data, **kw) - def plot(*args, **kwargs): - x = list() - y = list() + def plot(*args, **kwargs) -> None: + x = [] + y = [] - if len(args) > 0: - if type(args[0]) is list: - x = args[0] + if len(args) > 0 and type(args[0]) is list: + x = args[0] if len(args) > 1: if type(args[1]) is list: y = args[1] else: - y = [_ for _ in range(len(x))] + y = list(range(len(x))) drawings.append( DrawingBuilder.get_line_drawing( - x, y, + x, + y, DrawingLibrary.matplotlib, kwargs, ) ) - def scatter(x, y, *a, **kwargs): + def scatter(x, y, *a, **kwargs) -> None: drawings.append( DrawingBuilder.get_scatter_drawing( - x, y, + x, + y, DrawingLibrary.matplotlib, kwargs, ) ) - def pie(x, *a, **kw): + def pie(x, *a, **kw) -> None: # Normalize with other plot libraries y = x - x = [''] * len(y) + x = [""] * len(y) - if 'labels' in kw and kw['labels'] is not None: - x = kw['labels'] + if "labels" in kw and kw["labels"] is not None: + x = kw["labels"] drawings.append( - Drawing( - DrawingLibrary.matplotlib, - DrawingType.pie, - DrawingData(x, y), - kw - ) + Drawing(DrawingLibrary.matplotlib, DrawingType.pie, DrawingData(x, y), kw) ) - def violinplot(dataset, *, data=None, **kwargs): + def violinplot(dataset, *, data=None, **kwargs) -> None: if data is not None: - try: + with contextlib.suppress(Exception): dataset = data[dataset] - except Exception: - pass - drawing = Drawing( - DrawingLibrary.matplotlib, - DrawingType.violin, - dataset, - kwargs - ) + drawing = Drawing(DrawingLibrary.matplotlib, DrawingType.violin, dataset, kwargs) drawings.append(drawing) - def imshow(x, **kwargs): + def imshow(x, **kwargs) -> None: curr_data = { # noqa: F841 - 'x': np.array(x, dtype=object) + "x": np.array(x, dtype=object) } drawing = Drawing( @@ -200,10 +183,10 @@ def imshow(x, **kwargs): ) drawings.append(drawing) - def boxplot(x, **kwargs): + def boxplot(x, **kwargs) -> None: curr_data = { # noqa: F841 - 'x': np.array([None], dtype=object), - 'y': np.array(x, dtype=object) + "x": np.array([None], dtype=object), + "y": np.array(x, dtype=object), } drawing = Drawing( @@ -214,47 +197,47 @@ def boxplot(x, **kwargs): ) drawings.append(drawing) - import matplotlib.axes - - class CustomMatplotlibAxes(matplotlib.axes.Axes): + import matplotlib as mpl - def hist(self, x, *a, **kw): + class CustomMatplotlibAxes(mpl.axes.Axes): + def hist(self, x, *a, **kw) -> None: hist(x, *a, **kw) - def bar(self, x, height, *a, **kw): + def bar(self, x, height, *a, **kw) -> None: bar(x, height, *a, **kw) - def barh(self, y, width, *a, **kw): + def barh(self, y, width, *a, **kw) -> None: barh(y, width, *a, **kw) - def plot(self, *args, **kwargs): + def plot(self, *args, **kwargs) -> None: plot(*args, *kwargs) - def scatter(self, x, y, *a, **kwargs): + def scatter(self, x, y, *a, **kwargs) -> None: scatter(x, y, *a, **kwargs) - def pie(self, x, *a, **kw): + def pie(self, x, *a, **kw) -> None: pie(x, *a, **kw) - def violinplot(self, dataset, **kwargs): + def violinplot(self, dataset, **kwargs) -> None: violinplot(dataset, **kwargs) - def imshow(self, x, **kwargs): + def imshow(self, x, **kwargs) -> None: imshow(x, **kwargs) - def boxplot(self, x, **kwargs): + def boxplot(self, x, **kwargs) -> None: boxplot(x, **kwargs) - import matplotlib + import matplotlib as mpl if not MatplotlibHandler._saved: - MatplotlibHandler._Axes = deepcopy(matplotlib.axes.Axes) + MatplotlibHandler._Axes = deepcopy(mpl.axes.Axes) # should be replaced before import matplotlib.pyplot as plt - matplotlib.axes.Axes = CustomMatplotlibAxes + mpl.axes.Axes = CustomMatplotlibAxes from matplotlib.projections import projection_registry - projection_registry.register(matplotlib.axes.Axes) + + projection_registry.register(mpl.axes.Axes) import matplotlib.pyplot as plt @@ -270,7 +253,7 @@ def boxplot(self, x, **kwargs): MatplotlibHandler._imshow = plt.imshow MatplotlibHandler._boxplot = plt.boxplot MatplotlibHandler._show = plt.show - MatplotlibHandler._backend = matplotlib.get_backend() + MatplotlibHandler._backend = mpl.get_backend() plt.hist = hist plt.plot = plot @@ -283,13 +266,12 @@ def boxplot(self, x, **kwargs): plt.boxplot = boxplot plt.show = custom_show_func - matplotlib.use('Agg') + mpl.use("Agg") MatplotlibHandler._replaced = True @staticmethod - def revert_plots(): - + def revert_plots() -> None: if not MatplotlibHandler._replaced: return diff --git a/hstest/testing/plotting/pandas_handler.py b/hstest/testing/plotting/pandas_handler.py index 98e9100a..05494c50 100644 --- a/hstest/testing/plotting/pandas_handler.py +++ b/hstest/testing/plotting/pandas_handler.py @@ -1,11 +1,12 @@ +from __future__ import annotations + +import contextlib from typing import TYPE_CHECKING from hstest.testing.plotting.drawing.drawing_data import DrawingData -try: +with contextlib.suppress(ImportError): import numpy as np -except ImportError: - pass try: import pandas as pd @@ -13,6 +14,8 @@ except ImportError: pass +import contextlib + from hstest.testing.plotting.drawing.drawing import Drawing from hstest.testing.plotting.drawing.drawing_builder import DrawingBuilder from hstest.testing.plotting.drawing.drawing_library import DrawingLibrary @@ -43,67 +46,49 @@ class PandasHandler: plot_name_to_basic_name = { # 'barh': DrawingType.bar, - 'density': DrawingType.dis, - 'kde': DrawingType.dis, + "density": DrawingType.dis, + "kde": DrawingType.dis, } graph_type_to_normalized_data = { - 'scatter': lambda data, x, y: PandasHandler.get_scatter_drawings_with_normalized_data( - data, x, y - ), - 'line': lambda data, x, y: PandasHandler.get_line_drawings_with_normalized_data(data, x, y), - 'pie': lambda data, x, y: PandasHandler.get_pie_drawings_with_normalized_data(data, x, y), + "scatter": PandasHandler.get_scatter_drawings_with_normalized_data, + "line": PandasHandler.get_line_drawings_with_normalized_data, + "pie": PandasHandler.get_pie_drawings_with_normalized_data, # 'bar': lambda data, x, y: PandasHandler.get_bar_drawings_with_normalized_data(data, x, y), - 'box': lambda data, x, y: PandasHandler.get_box_drawings_with_normalized_data(data, x, y), - 'dis': lambda data, x, y: PandasHandler.get_dis_drawings_with_normalized_data(data, x, y), + "box": PandasHandler.get_box_drawings_with_normalized_data, + "dis": PandasHandler.get_dis_drawings_with_normalized_data, } @staticmethod def get_line_drawings_with_normalized_data(data, x, y): - drawings = list() + drawings = [] if type(data) is pd.Series: drawings.append( - DrawingBuilder.get_line_drawing( - data.index, - data, - DrawingLibrary.pandas, - {} - ) + DrawingBuilder.get_line_drawing(data.index, data, DrawingLibrary.pandas, {}) ) return drawings for column in data.columns: drawings.append( - DrawingBuilder.get_line_drawing( - data.index, - data[column], - DrawingLibrary.pandas, - {} - ) + DrawingBuilder.get_line_drawing(data.index, data[column], DrawingLibrary.pandas, {}) ) return drawings @staticmethod def get_scatter_drawings_with_normalized_data(data, x, y): - return [ - DrawingBuilder.get_scatter_drawing( - data[x], data[y], - DrawingLibrary.pandas, - {} - ) - ] + return [DrawingBuilder.get_scatter_drawing(data[x], data[y], DrawingLibrary.pandas, {})] @staticmethod - def get_pie_drawings_with_normalized_data(data: 'pd.DataFrame', x, y): + def get_pie_drawings_with_normalized_data(data: pd.DataFrame, x, y): if type(data) == pd.Series: return [ Drawing( DrawingLibrary.pandas, DrawingType.pie, DrawingData(data.index.to_numpy(), data.to_numpy()), - {} + {}, ) ] @@ -113,7 +98,7 @@ def get_pie_drawings_with_normalized_data(data: 'pd.DataFrame', x, y): DrawingLibrary.pandas, DrawingType.pie, DrawingData(data.index.to_numpy(), data[y].to_numpy()), - {} + {}, ) ] @@ -127,43 +112,31 @@ def get_pie_drawings_with_normalized_data(data: 'pd.DataFrame', x, y): DrawingLibrary.pandas, DrawingType.pie, DrawingData(data.index.to_numpy(), data[column].to_numpy()), - {} + {}, ) ) return drawings @staticmethod - def get_bar_drawings_with_normalized_data(data: 'pd.DataFrame', x, y): + def get_bar_drawings_with_normalized_data(data: pd.DataFrame, x, y): drawings = [] - if x is not None: - x_arr = data[x].to_numpy() - else: - x_arr = data.index.to_numpy() + x_arr = data[x].to_numpy() if x is not None else data.index.to_numpy() if y is not None: - drawing = DrawingBuilder.get_bar_drawing( - x_arr, data[y], - DrawingLibrary.pandas, - {} - ) + drawing = DrawingBuilder.get_bar_drawing(x_arr, data[y], DrawingLibrary.pandas, {}) drawings.append(drawing) return drawings for column in data.columns: if not is_numeric_dtype(data[column]): continue - drawing = DrawingBuilder.get_bar_drawing( - x_arr, data[column], - DrawingLibrary.pandas, - {} - ) + drawing = DrawingBuilder.get_bar_drawing(x_arr, data[column], DrawingLibrary.pandas, {}) drawings.append(drawing) return drawings @staticmethod - def get_box_drawings_with_normalized_data(data: 'pd.DataFrame', x, y): - + def get_box_drawings_with_normalized_data(data: pd.DataFrame, x, y): drawings = [] # Columns are not specified @@ -172,17 +145,9 @@ def get_box_drawings_with_normalized_data(data: 'pd.DataFrame', x, y): if not is_numeric_dtype(data[column]): continue - curr_data = { # noqa: F841 - 'x': np.array([column], dtype=object), - 'y': data[column].to_numpy() - } + curr_data = {"x": np.array([column], dtype=object), "y": data[column].to_numpy()} - drawing = Drawing( - DrawingLibrary.pandas, - DrawingType.box, - None, - {} - ) + drawing = Drawing(DrawingLibrary.pandas, DrawingType.box, None, {}) drawings.append(drawing) return drawings @@ -191,16 +156,11 @@ def get_box_drawings_with_normalized_data(data: 'pd.DataFrame', x, y): continue curr_data = { # noqa: F841 - 'x': np.array([column], dtype=object), - 'y': data[column].to_numpy() + "x": np.array([column], dtype=object), + "y": data[column].to_numpy(), } - drawing = Drawing( - DrawingLibrary.pandas, - DrawingType.box, - None, - {} - ) + drawing = Drawing(DrawingLibrary.pandas, DrawingType.box, None, {}) drawings.append(drawing) return drawings @@ -209,42 +169,25 @@ def get_dis_drawings_with_normalized_data(data, x, y): drawings = [] if type(data) == pd.Series: - curr_data = { - 'x': data.to_numpy() - } + curr_data = {"x": data.to_numpy()} - drawing = Drawing( - DrawingLibrary.pandas, - DrawingType.dis, - None, - {} - ) + drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) drawings.append(drawing) return drawings if x: curr_data = { - 'x': np.array(data[x], dtype=object), + "x": np.array(data[x], dtype=object), } - drawing = Drawing( - DrawingLibrary.pandas, - DrawingType.dis, - None, - {} - ) + drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) drawings.append(drawing) if y: curr_data = { - 'x': np.array(data[y], dtype=object), + "x": np.array(data[y], dtype=object), } - drawing = Drawing( - DrawingLibrary.pandas, - DrawingType.dis, - None, - {} - ) + drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) drawings.append(drawing) if not x and not y: @@ -253,44 +196,29 @@ def get_dis_drawings_with_normalized_data(data, x, y): continue curr_data = { # noqa: F841 - 'x': data[column].to_numpy() + "x": data[column].to_numpy() } - drawing = Drawing( - DrawingLibrary.pandas, - DrawingType.dis, - None, - {} - ) + drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) drawings.append(drawing) return drawings @staticmethod def get_area_drawings_with_normalized_data(data, x, y): drawings = [] - drawing = Drawing( - DrawingLibrary.pandas, - DrawingType.area, - None, - {} - ) + drawing = Drawing(DrawingLibrary.pandas, DrawingType.area, None, {}) drawings.append(drawing) return drawings @staticmethod def get_hexbin_drawings_with_normalized_data(data, x, y): drawings = [] - drawing = Drawing( - DrawingLibrary.pandas, - DrawingType.hexbin, - None, - {} - ) + drawing = Drawing(DrawingLibrary.pandas, DrawingType.hexbin, None, {}) drawings.append(drawing) return drawings @staticmethod - def replace_plots(drawings: 'DrawingsStorage'): + def replace_plots(drawings: DrawingsStorage) -> None: try: import pandas.plotting from pandas.core.accessor import CachedAccessor @@ -308,22 +236,21 @@ def __call__(self, *args, **kw): ) if kind not in self._all_kinds: - raise ValueError(f"{kind} is not a valid plot kind") + msg = f"{kind} is not a valid plot kind" + raise ValueError(msg) data = self._parent.copy() - plot_name = kind if kind not in PandasHandler.plot_name_to_basic_name \ - else PandasHandler.plot_name_to_basic_name[kind] + plot_name = PandasHandler.plot_name_to_basic_name.get(kind, kind) # For boxplot from plot accessor - if plot_name == DrawingType.box: - if 'columns' in kwargs: - x = kwargs['columns'] + if plot_name == DrawingType.box and "columns" in kwargs: + x = kwargs["columns"] plot_to_func = { - 'hist': hist, - 'bar': bar, - 'barh': barh, + "hist": hist, + "bar": bar, + "barh": barh, } if plot_name in PandasHandler.graph_type_to_normalized_data: @@ -335,64 +262,44 @@ def __call__(self, *args, **kw): plot_to_func[plot_name](data, **kw) else: curr_data = { # noqa: F841 - 'data': data, - 'x': x, - 'y': y, - 'kwargs': kwargs + "data": data, + "x": x, + "y": y, + "kwargs": kwargs, } - drawing = Drawing( - DrawingLibrary.pandas, - plot_name, - None, - {} - ) + drawing = Drawing(DrawingLibrary.pandas, plot_name, None, {}) drawings.append(drawing) import pandas.plotting._core - def boxplot( - self, - column=None, - **kwargs - ): + def boxplot(self, column=None, **kwargs) -> None: all_drawings = PandasHandler.get_box_drawings_with_normalized_data(self, column, None) drawings.extend(all_drawings) - def hist( - data, - column=None, - _process_by=True, - **kw - ): + def hist(data, column=None, _process_by=True, **kw): for k in list(kw.keys()): if kw[k] is None: kw.pop(k) - if _process_by and 'by' in kw and type(kw['by']) == str: - try: - kw['by'] = data[kw['by']] - except Exception: - pass + if _process_by and "by" in kw and type(kw["by"]) == str: + with contextlib.suppress(Exception): + kw["by"] = data[kw["by"]] - if 'y' in kw: - try: - data = data[kw.pop('y')] - except Exception: - pass + if "y" in kw: + with contextlib.suppress(Exception): + data = data[kw.pop("y")] - if 'x' in kw: - try: - data = data[kw.pop('x')] - except Exception: - pass + if "x" in kw: + with contextlib.suppress(Exception): + data = data[kw.pop("x")] if type(data) == pandas.DataFrame: if column is not None: return hist(data[column].to_numpy(), **kw) for col in data.columns: hist(data[col].to_numpy(), **kw) - return + return None elif type(data) == pandas.Series: return hist(data.to_numpy(), **kw) @@ -400,37 +307,34 @@ def hist( elif type(data) != np.ndarray: data = np.array(data, dtype=object) if len(data.shape) == 2: - import matplotlib.cbook as cbook - data = np.array(cbook._reshape_2D(data, 'x'), dtype=object) + from matplotlib import cbook + + data = np.array(cbook._reshape_2D(data, "x"), dtype=object) if len(data.shape) == 2: for i in range(data.shape[1]): hist(data[:, i], **kw) - return + return None - if _process_by and 'by' in kw: - by = kw['by'] + if _process_by and "by" in kw: + by = kw["by"] pictures = sorted(set(by), key=str) for pic in pictures: - subplot = [i for i, j in zip(data, by) if j == pic] + subplot = [i for i, j in zip(data, by, strict=False) if j == pic] hist(np.array(subplot, dtype=object), _process_by=False, **kw) - return + return None drawings.append( Drawing( DrawingLibrary.pandas, DrawingType.hist, DrawingData(data, np.array([1] * len(data), dtype=object)), - kw + kw, ) ) + return None - def bar( - data, - x=None, - y=None, - **kw - ): + def bar(data, x=None, y=None, **kw): for k in list(kw.keys()): if kw[k] is None: kw.pop(k) @@ -440,57 +344,36 @@ def bar( if type(y) == str: y = [y] for col in y: - bar(None, - data[x].array.to_numpy(), - data[col].array.to_numpy(), - **kw) - return + bar(None, data[x].array.to_numpy(), data[col].array.to_numpy(), **kw) + return None elif x is not None: for col in data.columns: if col != x: - bar(None, - data[x].array.to_numpy(), - data[col].array.to_numpy(), - **kw) - return + bar(None, data[x].array.to_numpy(), data[col].array.to_numpy(), **kw) + return None elif y is not None: if type(y) == str: y = [y] for col in y: - bar(None, - data[col].index.to_numpy(), - data[col].array.to_numpy(), - **kw) - return + bar(None, data[col].index.to_numpy(), data[col].array.to_numpy(), **kw) + return None else: for col in data.columns: - bar(None, - data[col].index.to_numpy(), - data[col].array.to_numpy(), - **kw) - return + bar(None, data[col].index.to_numpy(), data[col].array.to_numpy(), **kw) + return None elif type(data) == pandas.Series: - return bar(None, - data.index.to_numpy(), - data.array.to_numpy(), - **kw) + return bar(None, data.index.to_numpy(), data.array.to_numpy(), **kw) - drawings.append( - Drawing( - DrawingLibrary.pandas, - DrawingType.bar, - DrawingData(x, y), - kw - ) - ) + drawings.append(Drawing(DrawingLibrary.pandas, DrawingType.bar, DrawingData(x, y), kw)) + return None def barh( self, - ): + ) -> None: pass if not PandasHandler._saved: @@ -517,7 +400,7 @@ def barh( PandasHandler._replaced = True @staticmethod - def revert_plots(): + def revert_plots() -> None: if not PandasHandler._replaced: return diff --git a/hstest/testing/plotting/seaborn_handler.py b/hstest/testing/plotting/seaborn_handler.py index b5b773b3..3bf1b343 100644 --- a/hstest/testing/plotting/seaborn_handler.py +++ b/hstest/testing/plotting/seaborn_handler.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from importlib import reload from typing import TYPE_CHECKING @@ -9,6 +11,8 @@ except ImportError: pass +import contextlib + from hstest.testing.plotting.drawing.drawing import Drawing from hstest.testing.plotting.drawing.drawing_builder import DrawingBuilder from hstest.testing.plotting.drawing.drawing_library import DrawingLibrary @@ -35,22 +39,19 @@ class SeabornHandler: _boxplot = None @staticmethod - def replace_plots(drawings: 'DrawingsStorage'): + def replace_plots(drawings: DrawingsStorage) -> None: try: import numpy as np import seaborn as sns except ModuleNotFoundError: return - def displot(data=None, **kwargs): - x = None if 'x' not in kwargs else kwargs['x'] - y = None if 'y' not in kwargs else kwargs['y'] + def displot(data=None, **kwargs) -> None: + x = kwargs.get("x", None) + y = kwargs.get("y", None) if data is None: - curr_data = { - 'x': np.array(x, dtype=object), - 'y': np.array(y, dtype=object) - } + curr_data = {"x": np.array(x, dtype=object), "y": np.array(y, dtype=object)} drawing = Drawing( DrawingLibrary.seaborn, @@ -70,8 +71,8 @@ def displot(data=None, **kwargs): continue curr_data = { - 'x': np.array([column], dtype=object), - 'y': data[column].to_numpy() + "x": np.array([column], dtype=object), + "y": data[column].to_numpy(), } drawing = Drawing( @@ -89,8 +90,8 @@ def displot(data=None, **kwargs): y_arr = data[y].to_numpy() curr_data = { # noqa: F841 - 'x': np.array(x_arr, dtype=object), - 'y': np.array(y_arr, dtype=object) + "x": np.array(x_arr, dtype=object), + "y": np.array(y_arr, dtype=object), } drawing = Drawing( @@ -103,66 +104,59 @@ def displot(data=None, **kwargs): def histplot(data=None, _process_hue=True, **kw): if data is None: - return + return None - if _process_hue and 'hue' in kw and type(kw['hue']) == str: - try: - kw['hue'] = data[kw['hue']] - except Exception: - pass + if _process_hue and "hue" in kw and type(kw["hue"]) == str: + with contextlib.suppress(Exception): + kw["hue"] = data[kw["hue"]] - if 'y' in kw: - try: - data = data[kw.pop('y')] - except Exception: - pass + if "y" in kw: + with contextlib.suppress(Exception): + data = data[kw.pop("y")] - if 'x' in kw: - try: - data = data[kw.pop('x')] - except Exception: - pass + if "x" in kw: + with contextlib.suppress(Exception): + data = data[kw.pop("x")] if type(data) == pd.DataFrame: for col in data.columns: histplot(data[col], **kw) - return + return None elif type(data) == pd.Series: return histplot(data.to_numpy(), **kw) elif type(data) != np.ndarray: data = np.array(data, dtype=object) if len(data.shape) == 2: - import matplotlib.cbook as cbook - data = np.array(cbook._reshape_2D(data, 'x'), dtype=object) + from matplotlib import cbook + + data = np.array(cbook._reshape_2D(data, "x"), dtype=object) if len(data.shape) == 2: for i in range(data.shape[1]): histplot(data[:, i], **kw) - return + return None - if _process_hue and 'hue' in kw: - hue = kw['hue'] + if _process_hue and "hue" in kw: + hue = kw["hue"] colored_layers = sorted(set(hue), key=str) for pic in colored_layers: - subplot = [i for i, j in zip(data, hue) if j == pic] + subplot = [i for i, j in zip(data, hue, strict=False) if j == pic] histplot(np.array(subplot, dtype=object), _process_hue=False, **kw) - return + return None drawings.append( Drawing( DrawingLibrary.seaborn, DrawingType.hist, DrawingData(data, np.array([1] * len(data), dtype=object)), - kw + kw, ) ) + return None def lineplot(*, data=None, x=None, y=None, **kwargs): - if x is not None: - x_array = data[x].to_numpy() - else: - x_array = data.index.to_numpy() + x_array = data[x].to_numpy() if x is not None else data.index.to_numpy() if y is not None: y_array = data[y].to_numpy() @@ -189,13 +183,14 @@ def lineplot(*, data=None, x=None, y=None, **kwargs): kwargs, ) ) + return None - def lmplot(x=None, y=None, data=None, **kwargs): + def lmplot(x=None, y=None, data=None, **kwargs) -> None: curr_data = { # noqa: F841 - 'data': data, - 'x': x, - 'y': y, - 'kwargs': kwargs + "data": data, + "x": x, + "y": y, + "kwargs": kwargs, } drawing = Drawing( @@ -206,11 +201,12 @@ def lmplot(x=None, y=None, data=None, **kwargs): ) drawings.append(drawing) - def scatterplot(x=None, y=None, data=None, **kwargs): + def scatterplot(x=None, y=None, data=None, **kwargs) -> None: if x is not None and y is not None: drawings.append( DrawingBuilder.get_scatter_drawing( - data[x], data[y], + data[x], + data[y], DrawingLibrary.seaborn, kwargs, ) @@ -225,18 +221,19 @@ def scatterplot(x=None, y=None, data=None, **kwargs): x = data.index drawings.append( DrawingBuilder.get_scatter_drawing( - x, data[column], + x, + data[column], DrawingLibrary.seaborn, kwargs, ) ) - def catplot(x=None, y=None, data=None, **kwargs): + def catplot(x=None, y=None, data=None, **kwargs) -> None: curr_data = { # noqa: F841 - 'data': data, - 'x': x, - 'y': y, - 'kwargs': kwargs + "data": data, + "x": x, + "y": y, + "kwargs": kwargs, } drawing = Drawing( @@ -247,30 +244,23 @@ def catplot(x=None, y=None, data=None, **kwargs): ) drawings.append(drawing) - def barplot(x=None, y=None, data=None, **kwargs): - + def barplot(x=None, y=None, data=None, **kwargs) -> None: x_arr = np.array([], dtype=object) y_arr = np.array([], dtype=object) if data is not None: if x: x_arr = data[x].to_numpy() - y_arr = np.full((x_arr.size,), '', dtype=str) + y_arr = np.full((x_arr.size,), "", dtype=str) if y: y_arr = data[y].to_numpy() if x_arr.size == 0: - x_arr = np.full((y_arr.size,), '', dtype=str) + x_arr = np.full((y_arr.size,), "", dtype=str) drawings.append( - Drawing( - DrawingLibrary.seaborn, - DrawingType.bar, - DrawingData(x_arr, y_arr), - kwargs - ) + Drawing(DrawingLibrary.seaborn, DrawingType.bar, DrawingData(x_arr, y_arr), kwargs) ) - def violinplot(*, x=None, y=None, data=None, **kwargs): - + def violinplot(*, x=None, y=None, data=None, **kwargs) -> None: if data is not None: if x is None and y is not None: data = data[y] @@ -295,12 +285,12 @@ def violinplot(*, x=None, y=None, data=None, **kwargs): drawings.append(drawing) - def heatmap(data=None, **kwargs): + def heatmap(data=None, **kwargs) -> None: if data is None: return curr_data = { # noqa: F841 - 'x': np.array(data, dtype=object) + "x": np.array(data, dtype=object) } drawing = Drawing( @@ -312,13 +302,9 @@ def heatmap(data=None, **kwargs): drawings.append(drawing) - def boxplot(x=None, y=None, data=None, **kwargs): - + def boxplot(x=None, y=None, data=None, **kwargs) -> None: if data is None: - curr_data = { - 'x': np.array(x, dtype=object), - 'y': np.array(y, dtype=object) - } + curr_data = {"x": np.array(x, dtype=object), "y": np.array(y, dtype=object)} drawing = Drawing( DrawingLibrary.seaborn, @@ -339,8 +325,8 @@ def boxplot(x=None, y=None, data=None, **kwargs): continue curr_data = { - 'x': np.array([column], dtype=object), - 'y': data[column].to_numpy() + "x": np.array([column], dtype=object), + "y": data[column].to_numpy(), } drawing = Drawing( @@ -358,8 +344,8 @@ def boxplot(x=None, y=None, data=None, **kwargs): y_arr = data[y].to_numpy() curr_data = { # noqa: F841 - 'x': np.array(x_arr, dtype=object), - 'y': np.array(y_arr, dtype=object) + "x": np.array(x_arr, dtype=object), + "y": np.array(y_arr, dtype=object), } drawing = Drawing( @@ -397,8 +383,7 @@ def boxplot(x=None, y=None, data=None, **kwargs): SeabornHandler._replaced = True @staticmethod - def revert_plots(): - + def revert_plots() -> None: if not SeabornHandler._replaced: return diff --git a/hstest/testing/process_wrapper.py b/hstest/testing/process_wrapper.py index 3b5866c1..46745003 100644 --- a/hstest/testing/process_wrapper.py +++ b/hstest/testing/process_wrapper.py @@ -1,8 +1,9 @@ +from __future__ import annotations + import subprocess import sys from threading import Lock, Thread from time import sleep -from typing import Optional from psutil import NoSuchProcess, Process @@ -19,17 +20,18 @@ class ProcessWrapper: initial_idle_wait = True initial_idle_wait_time = 150 - def __init__(self, *args, check_early_finish=False, register_output=True, - register_io_handler=False): + def __init__( + self, *args, check_early_finish=False, register_output=True, register_io_handler=False + ) -> None: self.lock = Lock() self.args = args - self.process: Optional[subprocess.Popen] = None - self.ps: Optional[Process] = None + self.process: subprocess.Popen | None = None + self.ps: Process | None = None - self.stdout = '' - self.stderr = '' + self.stdout = "" + self.stderr = "" self._alive = True self._pipes_watching = 0 self.terminated = False @@ -48,22 +50,22 @@ def __init__(self, *args, check_early_finish=False, register_output=True, self._group = None def start(self): - command = ' '.join(map(str, self.args)) + command = " ".join(map(str, self.args)) if self.process is not None: - raise UnexpectedError(f"Cannot start the same process twice\n\"{command}\"") + msg = f'Cannot start the same process twice\n"{command}"' + raise UnexpectedError(msg) try: args = [str(a) for a in self.args] - if is_windows(): - if args[0] == 'bash': - # bash doesn't like Windows' \r\n, - # so we use byte stream instead of text stream - # to communicate between processes - self._use_byte_stream = True + if is_windows() and args[0] == "bash": + # bash doesn't like Windows' \r\n, + # so we use byte stream instead of text stream + # to communicate between processes + self._use_byte_stream = True - args = ['cmd', '/c'] + args + args = ["cmd", "/c", *args] self.process = subprocess.Popen( args, @@ -72,12 +74,14 @@ def start(self): stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, - encoding='utf-8' if not self._use_byte_stream else None, + encoding="utf-8" if not self._use_byte_stream else None, ) except Exception as e: from hstest import StageTest + StageTest.curr_test_run.set_error_in_test( - UnexpectedError(f"Cannot start process\n\"{command}\"", e)) + UnexpectedError(f'Cannot start process\n"{command}"', e) + ) self._alive = False self.terminated = True return self @@ -87,30 +91,29 @@ def start(self): if self.register_io_handler: self._group = ThreadGroup() SystemHandler.install_handler( - self, - lambda: ThreadGroup.curr_group() == self._group, - lambda: None + self, lambda: ThreadGroup.curr_group() == self._group, lambda: None ) - Thread(target=lambda: self.check_cpuload(), daemon=True, group=self._group).start() - Thread(target=lambda: self.check_output(), daemon=True, group=self._group).start() - Thread(target=lambda: self.check_stdout(), daemon=True, group=self._group).start() - Thread(target=lambda: self.check_stderr(), daemon=True, group=self._group).start() + Thread(target=self.check_cpuload, daemon=True, group=self._group).start() + Thread(target=self.check_output, daemon=True, group=self._group).start() + Thread(target=self.check_stdout, daemon=True, group=self._group).start() + Thread(target=self.check_stderr, daemon=True, group=self._group).start() return self - def check_alive(self): + def check_alive(self) -> None: if self._alive and self.process.returncode is not None: self._alive = False - def check_pipe(self, read_pipe, write_pipe, write_stdout=False, write_stderr=False): + def check_pipe(self, read_pipe, write_pipe, write_stdout=False, write_stderr=False) -> None: pipe_name = "stdout" if write_stdout else "stderr" with self.lock: self._pipes_watching += 1 - OutputHandler.print(f'Start watching {pipe_name} ' - f'Pipes watching = {self._pipes_watching}') + OutputHandler.print( + f"Start watching {pipe_name} " f"Pipes watching = {self._pipes_watching}" + ) while True: try: @@ -118,26 +121,25 @@ def check_pipe(self, read_pipe, write_pipe, write_stdout=False, write_stderr=Fal if self._use_byte_stream: new_output = new_output.decode() except ValueError: - OutputHandler.print(f'Value error for {pipe_name}... ') + OutputHandler.print(f"Value error for {pipe_name}... ") if self.is_finished(need_wait_output=False): break continue if write_stderr: - OutputHandler.print(f'STDERR + {len(new_output)} symbols: {new_output}') + OutputHandler.print(f"STDERR + {len(new_output)} symbols: {new_output}") if len(new_output) == 0: with self.lock: self._pipes_watching -= 1 OutputHandler.print( - f'Out of {pipe_name}... ' - f'Maybe program terminated. Pipes watching = {self._pipes_watching}' + f"Out of {pipe_name}... " + f"Maybe program terminated. Pipes watching = {self._pipes_watching}" ) if self._pipes_watching == 0: - OutputHandler.print( - f'Set alive = False for {pipe_name}... ') + OutputHandler.print(f"Set alive = False for {pipe_name}... ") self._alive = False self.terminate() @@ -147,7 +149,7 @@ def check_pipe(self, read_pipe, write_pipe, write_stdout=False, write_stderr=Fal if self.register_output: write_pipe.write(new_output) except ExitException: - OutputHandler.print(f'ExitException for {pipe_name}... ') + OutputHandler.print(f"ExitException for {pipe_name}... ") self._alive = False self.terminate() break @@ -158,17 +160,17 @@ def check_pipe(self, read_pipe, write_pipe, write_stdout=False, write_stderr=Fal if write_stderr: self.stderr += new_output - def check_stdout(self): + def check_stdout(self) -> None: self.check_pipe(self.process.stdout, sys.stdout, write_stdout=True) - def check_stderr(self): + def check_stderr(self) -> None: self.check_pipe(self.process.stderr, sys.stderr, write_stderr=True) - def check_cpuload(self): + def check_cpuload(self) -> None: while self._alive: try: cpu_load = self.ps.cpu_percent() - OutputHandler.print(f'Check cpuload - {cpu_load}') + OutputHandler.print(f"Check cpuload - {cpu_load}") if not self.initial_idle_wait: self.cpu_load_history.append(cpu_load) @@ -177,9 +179,9 @@ def check_cpuload(self): self.cpu_load_history.pop(0) except NoSuchProcess: - OutputHandler.print('Check cpuload finished, waiting output') + OutputHandler.print("Check cpuload finished, waiting output") self.wait_output() - OutputHandler.print('Check cpuload finished, set alive = false') + OutputHandler.print("Check cpuload finished, set alive = false") self._alive = False break @@ -191,7 +193,7 @@ def check_cpuload(self): if self.initial_idle_wait_time == 0: self.initial_idle_wait = False - def check_output(self): + def check_output(self) -> None: output_len_prev = len(self.stdout) while self._alive: @@ -200,7 +202,8 @@ def check_output(self): output_len_prev = output_len OutputHandler.print( - f'Check output diff - {diff}. Curr = {output_len}, prev = {output_len_prev}') + f"Check output diff - {diff}. Curr = {output_len}, prev = {output_len_prev}" + ) if not self.initial_idle_wait: self.output_diff_history.append(diff) @@ -219,24 +222,25 @@ def is_waiting_input(self) -> bool: return False program_not_loading_processor = ( - len(self.cpu_load_history) >= self.cpu_load_history_max and - sum(self.cpu_load_history) < 1 + len(self.cpu_load_history) >= self.cpu_load_history_max + and sum(self.cpu_load_history) < 1 ) program_not_printing_anything = ( - len(self.output_diff_history) >= self.output_diff_history_max and - sum(self.output_diff_history) == 0 + len(self.output_diff_history) >= self.output_diff_history_max + and sum(self.output_diff_history) == 0 ) return program_not_loading_processor and program_not_printing_anything - def register_input_request(self): + def register_input_request(self) -> None: if not self.is_waiting_input(): - raise RuntimeError('Program is not waiting for the input') + msg = "Program is not waiting for the input" + raise RuntimeError(msg) self.cpu_load_history = [] self.output_diff_history = [] - def is_finished(self, need_wait_output=True): + def is_finished(self, need_wait_output=True) -> bool: if not self.check_early_finish: return not self._alive @@ -244,7 +248,7 @@ def is_finished(self, need_wait_output=True): return True try: - is_running = self.ps.status() == 'running' + is_running = self.ps.status() == "running" if not is_running: self._alive = False except NoSuchProcess: @@ -257,54 +261,53 @@ def is_finished(self, need_wait_output=True): return not self._alive - def provide_input(self, stdin: str): + def provide_input(self, stdin: str) -> None: if self._use_byte_stream: stdin = stdin.encode() self.process.stdin.write(stdin) - def terminate(self): - OutputHandler.print('Terminate called') + def terminate(self) -> None: + OutputHandler.print("Terminate called") with self.lock: - OutputHandler.print('Terminate - LOCK ACQUIRED') + OutputHandler.print("Terminate - LOCK ACQUIRED") if self.terminated: - OutputHandler.print('Terminate - finished') + OutputHandler.print("Terminate - finished") return - OutputHandler.print('Terminate - BEFORE WAIT STDERR') + OutputHandler.print("Terminate - BEFORE WAIT STDERR") self.wait_output() if self.register_io_handler: SystemHandler.uninstall_handler(self) - OutputHandler.print('Terminate - AFTER WAIT STDERR') + OutputHandler.print("Terminate - AFTER WAIT STDERR") self._alive = False - OutputHandler.print('Terminate - SELF ALIVE == FALSE') + OutputHandler.print("Terminate - SELF ALIVE == FALSE") is_exit_replaced = ExitHandler.is_replaced() if is_exit_replaced: ExitHandler.revert_exit() - OutputHandler.print('Terminate - EXIT REVERTED') + OutputHandler.print("Terminate - EXIT REVERTED") try: parent = Process(self.process.pid) - OutputHandler.print(f'Terminate - parent == {parent}') + OutputHandler.print(f"Terminate - parent == {parent}") for child in parent.children(recursive=True): - OutputHandler.print(f'Terminate - child kill {child}') + OutputHandler.print(f"Terminate - child kill {child}") child.kill() - OutputHandler.print(f'Terminate - parent kill {parent}') + OutputHandler.print(f"Terminate - parent kill {parent}") parent.kill() except NoSuchProcess: - OutputHandler.print('Terminate - NO SUCH PROCESS') - pass + OutputHandler.print("Terminate - NO SUCH PROCESS") finally: - OutputHandler.print('Terminate - finally before kill') + OutputHandler.print("Terminate - finally before kill") self.process.kill() - OutputHandler.print('Terminate - finally before wait') + OutputHandler.print("Terminate - finally before wait") self.process.wait() self.process.stdout.close() @@ -313,13 +316,13 @@ def terminate(self): if is_exit_replaced: ExitHandler.replace_exit() - OutputHandler.print('Terminate - EXIT REPLACED AGAIN') + OutputHandler.print("Terminate - EXIT REPLACED AGAIN") self.terminated = True - OutputHandler.print('Terminate - TERMINATED') - OutputHandler.print('Terminate - finished') + OutputHandler.print("Terminate - TERMINATED") + OutputHandler.print("Terminate - finished") - def wait_output(self): + def wait_output(self) -> None: iterations = 50 sleep_time = 50 / 1000 @@ -333,14 +336,12 @@ def wait_output(self): curr_stdout = self.stdout iterations -= 1 - def wait(self): + def wait(self) -> None: while not self.is_finished(): sleep(0.01) self.wait_output() def is_error_happened(self) -> bool: return ( - not self._alive and len(self.stderr) > 0 and - self.process.returncode != 0 or - 'Traceback' in self.stderr - ) + not self._alive and len(self.stderr) > 0 and self.process.returncode != 0 + ) or "Traceback" in self.stderr diff --git a/hstest/testing/runner/__init__.py b/hstest/testing/runner/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/testing/runner/async_dynamic_testing_runner.py b/hstest/testing/runner/async_dynamic_testing_runner.py index 69faabda..84ea724e 100644 --- a/hstest/testing/runner/async_dynamic_testing_runner.py +++ b/hstest/testing/runner/async_dynamic_testing_runner.py @@ -1,28 +1,30 @@ +from __future__ import annotations + import typing from concurrent.futures import Future, TimeoutError -from typing import Optional, Type from hstest.common.process_utils import DaemonThreadPoolExecutor from hstest.dynamic.output.output_handler import OutputHandler from hstest.exception.testing import ( - TestedProgramFinishedEarly, TestedProgramThrewException, TimeLimitException + TestedProgramFinishedEarly, + TestedProgramThrewException, + TimeLimitException, ) from hstest.exceptions import TestPassed, WrongAnswer from hstest.test_case.check_result import CheckResult, correct, wrong from hstest.testing.execution.main_module_executor import MainModuleExecutor -from hstest.testing.execution.program_executor import ProgramExecutor from hstest.testing.execution_options import debug_mode from hstest.testing.runner.test_runner import TestRunner -from hstest.testing.test_run import TestRun if typing.TYPE_CHECKING: from hstest import TestCase + from hstest.testing.execution.program_executor import ProgramExecutor + from hstest.testing.test_run import TestRun class AsyncDynamicTestingRunner(TestRunner): - - def __init__(self, executor: Type[ProgramExecutor] = MainModuleExecutor): - self.executor: Type[ProgramExecutor] = executor + def __init__(self, executor: type[ProgramExecutor] = MainModuleExecutor) -> None: + self.executor: type[ProgramExecutor] = executor def _run_dynamic_test(self, test_run: TestRun) -> CheckResult: test_case = test_run.test_case @@ -40,7 +42,7 @@ def _run_dynamic_test(self, test_run: TestRun) -> CheckResult: return result - def _run_file(self, test_run: TestRun) -> Optional[CheckResult]: + def _run_file(self, test_run: TestRun) -> CheckResult | None: test_case = test_run.test_case time_limit = test_case.time_limit @@ -61,7 +63,7 @@ def _run_file(self, test_run: TestRun) -> Optional[CheckResult]: return None - def test(self, test_run: TestRun) -> Optional[CheckResult]: + def test(self, test_run: TestRun) -> CheckResult | None: test_case = test_run.test_case result: CheckResult = self._run_file(test_run) @@ -71,8 +73,7 @@ def test(self, test_run: TestRun) -> Optional[CheckResult]: if error is None: try: - return test_case.check_func( - OutputHandler.get_output(), test_case.attach) + return test_case.check_func(OutputHandler.get_output(), test_case.attach) except BaseException as ex: error = ex test_run.set_error_in_test(error) @@ -81,7 +82,8 @@ def test(self, test_run: TestRun) -> Optional[CheckResult]: return result - def tear_down(self, test_case: 'TestCase'): + def tear_down(self, test_case: TestCase) -> None: from hstest import StageTest + for program in StageTest.curr_test_run.tested_programs: program.executor.tear_down() diff --git a/hstest/testing/runner/django_application_runner.py b/hstest/testing/runner/django_application_runner.py index 6de0259a..c1be0784 100644 --- a/hstest/testing/runner/django_application_runner.py +++ b/hstest/testing/runner/django_application_runner.py @@ -1,44 +1,49 @@ +from __future__ import annotations + import os import sys from time import sleep -from typing import List, Optional +from typing import TYPE_CHECKING from hstest.common.file_utils import safe_delete from hstest.common.process_utils import is_port_in_use from hstest.exception.outcomes import ErrorWithFeedback, ExceptionWithFeedback, UnexpectedError from hstest.test_case.attach.django_settings import DjangoSettings from hstest.test_case.check_result import CheckResult -from hstest.test_case.test_case import TestCase from hstest.testing.execution.filtering.file_filter import FileFilter from hstest.testing.execution.searcher.python_searcher import PythonSearcher from hstest.testing.process_wrapper import ProcessWrapper from hstest.testing.runner.test_runner import TestRunner -from hstest.testing.test_run import TestRun + +if TYPE_CHECKING: + from hstest.test_case.test_case import TestCase + from hstest.testing.test_run import TestRun class DjangoApplicationRunner(TestRunner): process: ProcessWrapper = None - port: Optional[int] = None - full_path: Optional[str] = None + port: int | None = None + full_path: str | None = None - def launch_django_application(self, test_case: TestCase): + def launch_django_application(self, test_case: TestCase) -> None: if not isinstance(test_case.attach, DjangoSettings): - raise UnexpectedError( - f'Django tests should have DjangoSettings class as an attach, ' - f'found {type(test_case.attach)}') + msg = ( + f"Django tests should have DjangoSettings class as an attach, " + f"found {type(test_case.attach)}" + ) + raise UnexpectedError(msg) source = test_case.source_name if source is None or not len(source): - source = 'manage' + source = "manage" - full_source = source.replace('.', os.sep) + '.py' + full_source = source.replace(".", os.sep) + ".py" full_path = os.path.abspath(full_source) if not os.path.exists(full_path): filename = os.path.basename(full_source) - runnable = PythonSearcher().search( - file_filter=FileFilter(file=lambda f: f == filename)) + runnable = PythonSearcher().search(file_filter=FileFilter(file=lambda f: f == filename)) full_path = os.path.abspath(runnable.folder + os.sep + runnable.file) self.full_path = full_path @@ -48,11 +53,16 @@ def launch_django_application(self, test_case: TestCase): self.__prepare_database(test_case.attach.test_database) self.process = ProcessWrapper( - sys.executable, self.full_path, 'runserver', self.port, '--noreload', - register_io_handler=True).start() + sys.executable, + self.full_path, + "runserver", + self.port, + "--noreload", + register_io_handler=True, + ).start() i: int = 100 - search_phrase = 'Starting development server at' + search_phrase = "Starting development server at" while i: if search_phrase in self.process.stdout: test_case.attach.port = self.port @@ -68,30 +78,32 @@ def launch_django_application(self, test_case: TestCase): stderr = self.process.stderr.strip() error_info = ( - f'Cannot start Django server because cannot find ' + f"Cannot start Django server because cannot find " f'"{search_phrase}" in process\' output' ) if len(stdout): - error_info += '\n\nstdout:\n' + stdout + error_info += "\n\nstdout:\n" + stdout if len(stderr): - error_info += '\n\nstderr:\n' + stderr + error_info += "\n\nstderr:\n" + stderr raise ErrorWithFeedback(error_info) - def __find_free_port(self, ports: List[int]) -> int: + def __find_free_port(self, ports: list[int]) -> int: for port in ports: if not is_port_in_use(port): return port - raise ErrorWithFeedback( - 'Cannot find a port to start Django application ' - f'(tried ports form {ports[0]} to {ports[-1]})') - - def __prepare_database(self, test_database: str): - os.environ['HYPERSKILL_TEST_DATABASE'] = test_database - with open(test_database, 'w'): + msg = ( + "Cannot find a port to start Django application " + f"(tried ports form {ports[0]} to {ports[-1]})" + ) + raise ErrorWithFeedback(msg) + + def __prepare_database(self, test_database: str) -> None: + os.environ["HYPERSKILL_TEST_DATABASE"] = test_database + with open(test_database, "w", encoding="locale"): pass - migrate = ProcessWrapper(sys.executable, self.full_path, 'migrate', check_early_finish=True) + migrate = ProcessWrapper(sys.executable, self.full_path, "migrate", check_early_finish=True) migrate.start() while not migrate.is_finished() and len(migrate.stderr) == 0: @@ -100,18 +112,21 @@ def __prepare_database(self, test_database: str): if len(migrate.stderr) != 0: migrate.wait_output() - if ('ModuleNotFoundError' in migrate.stderr or - 'ImportError' in migrate.stderr or - 'SyntaxError' in migrate.stderr): + if ( + "ModuleNotFoundError" in migrate.stderr + or "ImportError" in migrate.stderr + or "SyntaxError" in migrate.stderr + ): raise ExceptionWithFeedback(migrate.stderr, None) # stdout and stderr is collected and will be shown to the user - raise ErrorWithFeedback('Cannot apply migrations to an empty database.') + msg = "Cannot apply migrations to an empty database." + raise ErrorWithFeedback(msg) - def set_up(self, test_case: TestCase): + def set_up(self, test_case: TestCase) -> None: self.launch_django_application(test_case) - def tear_down(self, test_case: TestCase): + def tear_down(self, test_case: TestCase) -> None: self._check_errors() if isinstance(test_case.attach, DjangoSettings): @@ -119,12 +134,12 @@ def tear_down(self, test_case: TestCase): if self.process: self.process.terminate() - def _check_errors(self): + def _check_errors(self) -> None: if self.process.is_error_happened(): self.process.terminate() raise ErrorWithFeedback(self.process.stderr) - def test(self, test_run: TestRun) -> Optional[CheckResult]: + def test(self, test_run: TestRun) -> CheckResult | None: self._check_errors() test_case = test_run.test_case diff --git a/hstest/testing/runner/flask_application_runner.py b/hstest/testing/runner/flask_application_runner.py index 0a3391ff..33deac13 100644 --- a/hstest/testing/runner/flask_application_runner.py +++ b/hstest/testing/runner/flask_application_runner.py @@ -1,56 +1,66 @@ +from __future__ import annotations + import os import sys +from pathlib import Path from time import sleep -from typing import List, Optional, Tuple +from typing import TYPE_CHECKING from hstest.common.process_utils import is_port_in_use from hstest.exception.outcomes import ErrorWithFeedback, UnexpectedError from hstest.test_case.attach.flask_settings import FlaskSettings from hstest.test_case.check_result import CheckResult -from hstest.test_case.test_case import TestCase from hstest.testing.process_wrapper import ProcessWrapper from hstest.testing.runner.test_runner import TestRunner -from hstest.testing.test_run import TestRun + +if TYPE_CHECKING: + from hstest.test_case.test_case import TestCase + from hstest.testing.test_run import TestRun class FlaskApplicationRunner(TestRunner): - processes: List[Tuple[str, ProcessWrapper]] = [] + processes: list[tuple[str, ProcessWrapper]] = [] - def launch_flask_applications(self, test_case: TestCase): + def launch_flask_applications(self, test_case: TestCase) -> None: if not isinstance(test_case.attach, FlaskSettings): - raise UnexpectedError( - f'Flask tests should have FlaskSettings class as an attach, ' - f'found {type(test_case.attach)}') + msg = ( + f"Flask tests should have FlaskSettings class as an attach, " + f"found {type(test_case.attach)}" + ) + raise UnexpectedError(msg) sources = test_case.attach.sources if len(sources) == 0: - raise UnexpectedError( - 'Cannot find Flask applications to run, no sources were defined in tests') + msg = "Cannot find Flask applications to run, no sources were defined in tests" + raise UnexpectedError(msg) new_sources = [] for source in sources: filename, port = source - full_source = filename.replace('.', os.sep) + '.py' - full_path = os.path.abspath(full_source) + full_source = Path(filename.replace(".", os.sep) + ".py") + full_path = full_source.resolve() - if not os.path.exists(full_path): - raise ErrorWithFeedback( + if not full_path.exists(): + msg = ( f'Cannot find file named "{os.path.basename(full_path)}" ' f'in folder "{os.path.dirname(full_path)}". ' - f'Check if you deleted it.') + f"Check if you deleted it." + ) + raise ErrorWithFeedback(msg) if port is None: port = self.__find_free_port(test_case.attach.tryout_ports) process = ProcessWrapper( - sys.executable, full_path, f'localhost:{port}', register_io_handler=True).start() + sys.executable, full_path, f"localhost:{port}", register_io_handler=True + ).start() self.processes += [(full_source, process)] i: int = 100 - search_phrase = 'Press CTRL+C to quit' + search_phrase = "Press CTRL+C to quit" while i: if search_phrase in process.stderr: break @@ -65,14 +75,14 @@ def launch_flask_applications(self, test_case: TestCase): stderr = process.stderr.strip() error_info = ( - f'Cannot start Flask server {full_source} ' + f"Cannot start Flask server {full_source} " f'because cannot find "{search_phrase}" in process\' output' ) if len(stdout): - error_info += '\n\nstdout:\n' + stdout + error_info += "\n\nstdout:\n" + stdout if len(stderr): - error_info += '\n\nstderr:\n' + stderr + error_info += "\n\nstderr:\n" + stderr raise ErrorWithFeedback(error_info) @@ -80,29 +90,32 @@ def launch_flask_applications(self, test_case: TestCase): test_case.attach.sources = new_sources - def __find_free_port(self, ports: List[int]) -> int: + def __find_free_port(self, ports: list[int]) -> int: for port in ports: if not is_port_in_use(port): return port - raise ErrorWithFeedback( - 'Cannot find a port to start Flask application ' - f'(tried ports form {ports[0]} to {ports[-1]})') + msg = ( + "Cannot find a port to start Flask application " + f"(tried ports form {ports[0]} to {ports[-1]})" + ) + raise ErrorWithFeedback(msg) - def set_up(self, test_case: TestCase): + def set_up(self, test_case: TestCase) -> None: self.launch_flask_applications(test_case) - def tear_down(self, test_case: TestCase): + def tear_down(self, test_case: TestCase) -> None: for process_item in self.processes: - filename, process = process_item + _filename, process = process_item process.terminate() - def _check_errors(self): + def _check_errors(self) -> None: for process_item in self.processes: filename, process = process_item if process.is_error_happened(): - raise ErrorWithFeedback(f'Error running "{filename}"\n\n{process.stderr}') + msg = f'Error running "{filename}"\n\n{process.stderr}' + raise ErrorWithFeedback(msg) - def test(self, test_run: TestRun) -> Optional[CheckResult]: + def test(self, test_run: TestRun) -> CheckResult | None: self._check_errors() test_case = test_run.test_case diff --git a/hstest/testing/runner/plot_testing_runner.py b/hstest/testing/runner/plot_testing_runner.py index a4663dc1..b3856be7 100644 --- a/hstest/testing/runner/plot_testing_runner.py +++ b/hstest/testing/runner/plot_testing_runner.py @@ -1,6 +1,7 @@ -from typing import List, TYPE_CHECKING +from __future__ import annotations + +from typing import TYPE_CHECKING -from hstest.testing.plotting.drawing.drawing import Drawing from hstest.testing.plotting.matplotlib_handler import MatplotlibHandler from hstest.testing.plotting.pandas_handler import PandasHandler from hstest.testing.plotting.seaborn_handler import SeabornHandler @@ -8,47 +9,42 @@ if TYPE_CHECKING: from hstest import TestCase + from hstest.testing.plotting.drawing.drawing import Drawing class DrawingsStorage: - def __init__(self, - all_drawings: List[Drawing], - new_drawings: List[Drawing]): - self.all_drawings: List[Drawing] = all_drawings - self.new_drawings: List[Drawing] = new_drawings + def __init__(self, all_drawings: list[Drawing], new_drawings: list[Drawing]) -> None: + self.all_drawings: list[Drawing] = all_drawings + self.new_drawings: list[Drawing] = new_drawings - def append(self, drawing: Drawing): + def append(self, drawing: Drawing) -> None: self.all_drawings.append(drawing) self.new_drawings.append(drawing) - def extend(self, drawings: List[Drawing]): + def extend(self, drawings: list[Drawing]) -> None: self.all_drawings.extend(drawings) self.new_drawings.extend(drawings) class PlottingTestingRunner(AsyncDynamicTestingRunner): - - def __init__(self, - all_drawings: List[Drawing], - new_drawings: List[Drawing]): + def __init__(self, all_drawings: list[Drawing], new_drawings: list[Drawing]) -> None: super().__init__() - self.drawings_storage: DrawingsStorage = DrawingsStorage( - all_drawings, new_drawings) + self.drawings_storage: DrawingsStorage = DrawingsStorage(all_drawings, new_drawings) - def set_up(self, test_case: 'TestCase'): + def set_up(self, test_case: TestCase) -> None: super().set_up(test_case) self.replace_plots() - def tear_down(self, test_case: 'TestCase'): + def tear_down(self, test_case: TestCase) -> None: super().tear_down(test_case) self.revert_plots() - def replace_plots(self): + def replace_plots(self) -> None: MatplotlibHandler.replace_plots(self.drawings_storage) PandasHandler.replace_plots(self.drawings_storage) SeabornHandler.replace_plots(self.drawings_storage) - def revert_plots(self): + def revert_plots(self) -> None: MatplotlibHandler.revert_plots() PandasHandler.revert_plots() SeabornHandler.revert_plots() diff --git a/hstest/testing/runner/sql_runner.py b/hstest/testing/runner/sql_runner.py index 7cce21df..30c8a56f 100644 --- a/hstest/testing/runner/sql_runner.py +++ b/hstest/testing/runner/sql_runner.py @@ -1,4 +1,6 @@ -import os +from __future__ import annotations + +import contextlib import re import sqlite3 import typing @@ -9,57 +11,54 @@ from hstest.testing.runner.test_runner import TestRunner if typing.TYPE_CHECKING: + from hstest import SQLTest from hstest.testing.test_run import TestCase, TestRun class SQLRunner(TestRunner): - - def __init__(self, sql_test_cls): + def __init__(self, sql_test_cls: type[SQLTest]) -> None: self.sql_test_cls = sql_test_cls - super(SQLRunner, self).__init__() + super().__init__() - def test(self, test_run: 'TestRun'): + def test(self, test_run: TestRun) -> CheckResult | None: test_case = test_run.test_case try: - result = test_case.dynamic_testing() - return result + return test_case.dynamic_testing() except BaseException as ex: test_run.set_error_in_test(ex) return CheckResult.from_error(test_run.error_in_test) - def set_up(self, test_case: 'TestCase'): + def set_up(self, test_case: TestCase) -> None: self.parse_sql_file() self.set_up_database() - def set_up_database(self): + def set_up_database(self) -> None: if self.sql_test_cls.db is not None: return - self.sql_test_cls.db = sqlite3.connect(':memory:') + self.sql_test_cls.db = sqlite3.connect(":memory:") def parse_sql_file(self) -> None: - sql_file = SQLSearcher().search() - file_path = os.path.join(sql_file.folder, sql_file.file) - - with open(file_path, 'r') as file: - lines = file.readlines() - sql_content = " ".join(lines).replace("\n", "") - commands = re.findall(r'(\w+)\s+?=\s+?"(.*?)"', sql_content) - - for (name, query) in commands: - if not query: - raise WrongAnswer(f"The '{name}' query shouldn't be empty!") - if name in self.sql_test_cls.queries: - self.sql_test_cls.queries[name] = query - - for name in self.sql_test_cls.queries: - if self.sql_test_cls.queries[name] is None: - raise WrongAnswer(f"Can't find '{name}' query from SQL files!") - - def tear_down(self, test_case: 'TestCase'): - try: + file_path = SQLSearcher().search().path() + lines = file_path.read_text(encoding="locale").splitlines() + + sql_content = " ".join(lines).replace("\n", "") + commands = re.findall(r'(\w+)\s+?=\s+?"(.*?)"', sql_content) + + for name, query in commands: + if not query: + msg = f"The '{name}' query shouldn't be empty!" + raise WrongAnswer(msg) + if name in self.sql_test_cls.queries: + self.sql_test_cls.queries[name] = query + + for name in self.sql_test_cls.queries: + if self.sql_test_cls.queries[name] is None: + msg = f"Can't find '{name}' query from SQL files!" + raise WrongAnswer(msg) + + def tear_down(self, test_case: TestCase) -> None: + with contextlib.suppress(Exception): self.sql_test_cls.db.close() - except Exception: - pass diff --git a/hstest/testing/runner/test_runner.py b/hstest/testing/runner/test_runner.py index b0d70d75..05decb78 100644 --- a/hstest/testing/runner/test_runner.py +++ b/hstest/testing/runner/test_runner.py @@ -1,19 +1,20 @@ -import typing -from typing import Optional +from __future__ import annotations -from hstest.check_result import CheckResult +import typing if typing.TYPE_CHECKING: + from hstest.check_result import CheckResult from hstest.test_case.test_case import TestCase from hstest.testing.test_run import TestRun class TestRunner: - def set_up(self, test_case: 'TestCase'): + def set_up(self, test_case: TestCase) -> None: pass - def tear_down(self, test_case: 'TestCase'): + def tear_down(self, test_case: TestCase) -> None: pass - def test(self, test_run: 'TestRun') -> Optional[CheckResult]: - raise NotImplementedError("Test method is not implemented") + def test(self, test_run: TestRun) -> CheckResult | None: + msg = "Test method is not implemented" + raise NotImplementedError(msg) diff --git a/hstest/testing/settings.py b/hstest/testing/settings.py index d8fac536..5ed56072 100644 --- a/hstest/testing/settings.py +++ b/hstest/testing/settings.py @@ -1,7 +1,11 @@ +from __future__ import annotations + + class Settings: do_reset_output: bool = True allow_out_of_input: bool = False catch_stderr: bool = True - def __init__(self): - raise NotImplementedError('Instances of the class Settings are prohibited') + def __init__(self) -> None: + msg = "Instances of the class Settings are prohibited" + raise NotImplementedError(msg) diff --git a/hstest/testing/state_machine.py b/hstest/testing/state_machine.py index 8c013a8c..1026eb0f 100644 --- a/hstest/testing/state_machine.py +++ b/hstest/testing/state_machine.py @@ -1,16 +1,21 @@ +from __future__ import annotations + from threading import Condition -from typing import Any, Callable, Dict, Set +from typing import Any, TYPE_CHECKING from hstest.exception.outcomes import UnexpectedError +if TYPE_CHECKING: + from collections.abc import Callable + class StateMachine: - def __init__(self, initial_value: Any): + def __init__(self, initial_value: Any) -> None: self._state: Any = initial_value - self._transitions: Dict[Any, Set[Any]] = {} + self._transitions: dict[Any, set[Any]] = {} self.cv = Condition() - def add_transition(self, fr: Any, to: Any): + def add_transition(self, fr: Any, to: Any) -> None: if fr not in self._transitions: self._transitions[fr] = set() self._transitions[fr].add(to) @@ -19,10 +24,10 @@ def add_transition(self, fr: Any, to: Any): def state(self) -> Any: return self._state - def in_state(self, state: Any): + def in_state(self, state: Any) -> bool: return self.state == state - def set_and_wait(self, new_state: Any, waiting_state: Any = None): + def set_and_wait(self, new_state: Any, waiting_state: Any = None) -> None: with self.cv: self.set_state(new_state) @@ -31,32 +36,29 @@ def set_and_wait(self, new_state: Any, waiting_state: Any = None): else: self.wait_state(waiting_state) - def wait_state(self, waiting_state: Any): + def wait_state(self, waiting_state: Any) -> None: with self.cv: self._wait_while(lambda: self.state != waiting_state) - def wait_not_state(self, state_to_avoid: Any): + def wait_not_state(self, state_to_avoid: Any) -> None: with self.cv: self._wait_while(lambda: self.state == state_to_avoid) - def wait_not_states(self, *states_to_avoid: Any): - def wait_func(): - for curr_state in states_to_avoid: - if self.state == curr_state: - return True - return False + def wait_not_states(self, *states_to_avoid: Any) -> None: + def wait_func() -> bool: + return any(self.state == curr_state for curr_state in states_to_avoid) + with self.cv: self._wait_while(wait_func) - def _wait_while(self, check_wait: Callable[[], bool]): + def _wait_while(self, check_wait: Callable[[], bool]) -> None: with self.cv: while check_wait(): self.cv.wait() - def set_state(self, new_state: Any): + def set_state(self, new_state: Any) -> None: with self.cv: if new_state not in self._transitions[self.state]: - raise UnexpectedError( - "Cannot transit from " + self.state + " to " + new_state) + raise UnexpectedError("Cannot transit from " + self.state + " to " + new_state) self._state = new_state self.cv.notify_all() diff --git a/hstest/testing/test_run.py b/hstest/testing/test_run.py index ff6ae089..85b83418 100644 --- a/hstest/testing/test_run.py +++ b/hstest/testing/test_run.py @@ -1,4 +1,6 @@ -from typing import List, Optional +from __future__ import annotations + +from typing import TYPE_CHECKING from hstest.check_result import CheckResult, correct from hstest.common.file_utils import create_files, delete_files @@ -6,23 +8,26 @@ from hstest.dynamic.system_handler import SystemHandler from hstest.exception.outcomes import ExceptionWithFeedback, UnexpectedError from hstest.exceptions import TestPassed -from hstest.test_case.test_case import TestCase -from hstest.testing.runner.test_runner import TestRunner from hstest.testing.settings import Settings -from hstest.testing.tested_program import TestedProgram + +if TYPE_CHECKING: + from hstest.test_case.test_case import TestCase + from hstest.testing.runner.test_runner import TestRunner + from hstest.testing.tested_program import TestedProgram class TestRun: - def __init__(self, test_num: int, test_count: int, - test_case: TestCase, test_rummer: TestRunner): + def __init__( + self, test_num: int, test_count: int, test_case: TestCase, test_rummer: TestRunner + ) -> None: self._test_num: int = test_num self._test_count: int = test_count self._test_case: TestCase = test_case self._test_runner: TestRunner = test_rummer self._input_used: bool = False - self._error_in_test: Optional[BaseException] = None - self._tested_programs: List[TestedProgram] = [] + self._error_in_test: BaseException | None = None + self._tested_programs: list[TestedProgram] = [] def is_first_test(self) -> bool: return self._test_num == 1 @@ -51,40 +56,40 @@ def input_used(self) -> bool: return self._input_used @property - def tested_programs(self) -> List[TestedProgram]: + def tested_programs(self) -> list[TestedProgram]: return self._tested_programs @property - def error_in_test(self) -> Optional[BaseException]: + def error_in_test(self) -> BaseException | None: return self._error_in_test - def set_error_in_test(self, err: Optional[BaseException]): + def set_error_in_test(self, err: BaseException | None) -> None: if self._error_in_test is None or err is None: self._error_in_test = err - def set_input_used(self): + def set_input_used(self) -> None: self._input_used = True - def add_tested_program(self, tested_program: TestedProgram): + def add_tested_program(self, tested_program: TestedProgram) -> None: self._tested_programs += [tested_program] - def stop_tested_programs(self): + def stop_tested_programs(self) -> None: for tested_program in self._tested_programs: tested_program.stop() - def invalidate_handlers(self): + def invalidate_handlers(self) -> None: for tested_program in self._tested_programs: SystemHandler.uninstall_handler(tested_program.executor) - def set_up(self): + def set_up(self) -> None: self._test_runner.set_up(self._test_case) - def tear_down(self): + def tear_down(self) -> None: self._test_runner.tear_down(self._test_case) def test(self) -> CheckResult: create_files(self._test_case.files) - # startThreads(testCase.getProcesses()) + # startThreads(testCase.getProcesses()) # noqa: ERA001 if Settings.do_reset_output: OutputHandler.reset_output() @@ -95,7 +100,7 @@ def test(self) -> CheckResult: except BaseException as ex: self.set_error_in_test(ex) - # stopThreads(testCase.getProcesses(), pool) + # stopThreads(testCase.getProcesses(), pool) # noqa: ERA001 delete_files(self._test_case.files) if result is None: @@ -105,11 +110,12 @@ def test(self) -> CheckResult: result = correct() if result is None: - raise UnexpectedError("Result is None after testing") + msg = "Result is None after testing" + raise UnexpectedError(msg) return result - def _check_errors(self): + def _check_errors(self) -> None: error_in_test = self._error_in_test test_case = self._test_case @@ -134,7 +140,7 @@ def _check_errors(self): if hint_in_feedback: raise ExceptionWithFeedback( - feedback + '\n\n' + error_in_test.error_text, None + feedback + "\n\n" + error_in_test.error_text, None ) raise error_in_test diff --git a/hstest/testing/tested_program.py b/hstest/testing/tested_program.py index c7f33e9a..b8d8716a 100644 --- a/hstest/testing/tested_program.py +++ b/hstest/testing/tested_program.py @@ -1,47 +1,56 @@ -from typing import List, Optional, Type +from __future__ import annotations + +from typing import TYPE_CHECKING from hstest.exception.outcomes import UnexpectedError -from hstest.testing.execution.program_executor import ProgramExecutor + +if TYPE_CHECKING: + from hstest.testing.execution.program_executor import ProgramExecutor class TestedProgram: - def __init__(self, source: str = None): + def __init__(self, source: str | None = None) -> None: from hstest import StageTest + runner = StageTest.curr_test_run.test_runner from hstest.testing.runner.async_dynamic_testing_runner import AsyncDynamicTestingRunner + if not isinstance(runner, AsyncDynamicTestingRunner): raise UnexpectedError( - 'TestedProgram is supported only while using AsyncDynamicTestingRunner runner, ' - 'not ' + str(type(runner)) + "TestedProgram is supported only while using AsyncDynamicTestingRunner runner, " + "not " + str(type(runner)) ) if source is None: from hstest.stage_test import StageTest + source = StageTest.curr_test_run.test_case.source_name self._program_executor: ProgramExecutor = runner.executor(source) - self._run_args: Optional[List[str]] = None + self._run_args: list[str] | None = None @property - def run_args(self): + def run_args(self) -> list[str] | None: return self._run_args @property - def executor(self): + def executor(self) -> ProgramExecutor: return self._program_executor - def _init_program(self, *args: str): + def _init_program(self, *args: str) -> None: self._run_args = args from hstest.stage_test import StageTest + if StageTest.curr_test_run: StageTest.curr_test_run.add_tested_program(self) - def feedback_on_exception(self, ex: Type[Exception], feedback: str): + def feedback_on_exception(self, ex: type[Exception], feedback: str) -> None: from hstest import StageTest + StageTest.curr_test_run.test_case.feedback_on_exception[ex] = feedback - def start_in_background(self, *args: str): + def start_in_background(self, *args: str) -> None: self._init_program(*args) self._program_executor.start_in_background(*args) @@ -49,22 +58,22 @@ def start(self, *args: str) -> str: self._init_program(*args) return self._program_executor.start(*args) - def execute(self, stdin: Optional[str]) -> str: + def execute(self, stdin: str | None) -> str: return self._program_executor.execute(stdin) def get_output(self) -> str: return self._program_executor.get_output() - def stop(self): + def stop(self) -> None: self._program_executor.stop() def is_finished(self) -> bool: return self._program_executor.is_finished() - def set_return_output_after_execution(self, value: bool): + def set_return_output_after_execution(self, *, value: bool) -> None: self._program_executor.set_return_output_after_execution(value) - def stop_input(self): + def stop_input(self) -> None: self._program_executor.stop_input() def is_input_allowed(self) -> bool: @@ -73,10 +82,10 @@ def is_input_allowed(self) -> bool: def is_waiting_input(self) -> bool: return self._program_executor.is_waiting_input() - def go_background(self): + def go_background(self) -> None: self._program_executor.go_background() - def stop_background(self): + def stop_background(self) -> None: self._program_executor.stop_background() def is_in_background(self) -> bool: diff --git a/hstest/testing/unittest/__init__.py b/hstest/testing/unittest/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/testing/unittest/expected_fail_test.py b/hstest/testing/unittest/expected_fail_test.py index 75987c6d..ab1a42a7 100644 --- a/hstest/testing/unittest/expected_fail_test.py +++ b/hstest/testing/unittest/expected_fail_test.py @@ -1,21 +1,22 @@ +from __future__ import annotations + from inspect import cleandoc -from typing import List, Union +from typing import ClassVar from hstest import StageTest class ExpectedFailTest(StageTest): - _base_contain: Union[str, List[str]] = [] - _base_not_contain: Union[str, List[str]] = [] + _base_contain: ClassVar[str | list[str]] = [] + _base_not_contain: ClassVar[str | list[str]] = [] - contain: Union[str, List[str]] = [] - not_contain: Union[str, List[str]] = [] + contain: ClassVar[str | list[str]] = [] + not_contain: ClassVar[str | list[str]] = [] - def __init__(self, args): + def __init__(self, args: str) -> None: super().__init__(args) - def test_run_unittest(self): - + def test_run_unittest(self) -> None: if not self.contain and not self.not_contain: self.fail("'contain' or 'not_contain' should not be empty") @@ -23,13 +24,13 @@ def test_run_unittest(self): self.assertEqual(result, -1) - if type(self._base_contain) != list: + if not isinstance(self._base_contain, list): self._base_contain = [self._base_contain] - if type(self._base_not_contain) != list: + if not isinstance(self._base_not_contain, list): self._base_not_contain = [self._base_not_contain] - if type(self.contain) != list: + if not isinstance(self.contain, list): self.contain = [self.contain] - if type(self.not_contain) != list: + if not isinstance(self.not_contain, list): self.not_contain = [self.not_contain] should_contain = self._base_contain + self.contain diff --git a/hstest/testing/unittest/unexepected_error_test.py b/hstest/testing/unittest/unexepected_error_test.py index 36492d4f..3433da0c 100644 --- a/hstest/testing/unittest/unexepected_error_test.py +++ b/hstest/testing/unittest/unexepected_error_test.py @@ -1,5 +1,7 @@ +from __future__ import annotations + from hstest.testing.unittest.expected_fail_test import ExpectedFailTest class UnexpectedErrorTest(ExpectedFailTest): - _base_contain = 'Unexpected error' + _base_contain = "Unexpected error" diff --git a/hstest/testing/unittest/user_error_test.py b/hstest/testing/unittest/user_error_test.py index 1f7b4d9f..71310f1c 100644 --- a/hstest/testing/unittest/user_error_test.py +++ b/hstest/testing/unittest/user_error_test.py @@ -1,5 +1,7 @@ +from __future__ import annotations + from hstest.testing.unittest.expected_fail_test import ExpectedFailTest class UserErrorTest(ExpectedFailTest): - _base_not_contain = 'Unexpected error' + _base_not_contain = "Unexpected error" diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..4586dc4a --- /dev/null +++ b/poetry.lock @@ -0,0 +1,778 @@ +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. + +[[package]] +name = "contourpy" +version = "1.2.0" +description = "Python library for calculating contours of 2D quadrilateral grids" +optional = false +python-versions = ">=3.9" +files = [ + {file = "contourpy-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8"}, + {file = "contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4"}, + {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f"}, + {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e"}, + {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9"}, + {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa"}, + {file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9"}, + {file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab"}, + {file = "contourpy-1.2.0-cp310-cp310-win32.whl", hash = "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488"}, + {file = "contourpy-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41"}, + {file = "contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727"}, + {file = "contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd"}, + {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a"}, + {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063"}, + {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e"}, + {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686"}, + {file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286"}, + {file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95"}, + {file = "contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6"}, + {file = "contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de"}, + {file = "contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0"}, + {file = "contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4"}, + {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779"}, + {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316"}, + {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399"}, + {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0"}, + {file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0"}, + {file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431"}, + {file = "contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f"}, + {file = "contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9"}, + {file = "contourpy-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5fd1810973a375ca0e097dee059c407913ba35723b111df75671a1976efa04bc"}, + {file = "contourpy-1.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:999c71939aad2780f003979b25ac5b8f2df651dac7b38fb8ce6c46ba5abe6ae9"}, + {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7caf9b241464c404613512d5594a6e2ff0cc9cb5615c9475cc1d9b514218ae8"}, + {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:266270c6f6608340f6c9836a0fb9b367be61dde0c9a9a18d5ece97774105ff3e"}, + {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbd50d0a0539ae2e96e537553aff6d02c10ed165ef40c65b0e27e744a0f10af8"}, + {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11f8d2554e52f459918f7b8e6aa20ec2a3bce35ce95c1f0ef4ba36fbda306df5"}, + {file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ce96dd400486e80ac7d195b2d800b03e3e6a787e2a522bfb83755938465a819e"}, + {file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d3364b999c62f539cd403f8123ae426da946e142312a514162adb2addd8d808"}, + {file = "contourpy-1.2.0-cp39-cp39-win32.whl", hash = "sha256:1c88dfb9e0c77612febebb6ac69d44a8d81e3dc60f993215425b62c1161353f4"}, + {file = "contourpy-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:78e6ad33cf2e2e80c5dfaaa0beec3d61face0fb650557100ee36db808bfa6843"}, + {file = "contourpy-1.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:be16975d94c320432657ad2402f6760990cb640c161ae6da1363051805fa8108"}, + {file = "contourpy-1.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b95a225d4948b26a28c08307a60ac00fb8671b14f2047fc5476613252a129776"}, + {file = "contourpy-1.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0d7e03c0f9a4f90dc18d4e77e9ef4ec7b7bbb437f7f675be8e530d65ae6ef956"}, + {file = "contourpy-1.2.0.tar.gz", hash = "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a"}, +] + +[package.dependencies] +numpy = ">=1.20,<2.0" + +[package.extras] +bokeh = ["bokeh", "selenium"] +docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.6.1)", "types-Pillow"] +test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] +test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] + +[[package]] +name = "cycler" +version = "0.12.1" +description = "Composable style cycles" +optional = false +python-versions = ">=3.8" +files = [ + {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, + {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, +] + +[package.extras] +docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] +tests = ["pytest", "pytest-cov", "pytest-xdist"] + +[[package]] +name = "fonttools" +version = "4.47.2" +description = "Tools to manipulate font files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fonttools-4.47.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b629108351d25512d4ea1a8393a2dba325b7b7d7308116b605ea3f8e1be88df"}, + {file = "fonttools-4.47.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c19044256c44fe299d9a73456aabee4b4d06c6b930287be93b533b4737d70aa1"}, + {file = "fonttools-4.47.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8be28c036b9f186e8c7eaf8a11b42373e7e4949f9e9f370202b9da4c4c3f56c"}, + {file = "fonttools-4.47.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f83a4daef6d2a202acb9bf572958f91cfde5b10c8ee7fb1d09a4c81e5d851fd8"}, + {file = "fonttools-4.47.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5a5318ba5365d992666ac4fe35365f93004109d18858a3e18ae46f67907670"}, + {file = "fonttools-4.47.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8f57ecd742545362a0f7186774b2d1c53423ed9ece67689c93a1055b236f638c"}, + {file = "fonttools-4.47.2-cp310-cp310-win32.whl", hash = "sha256:a1c154bb85dc9a4cf145250c88d112d88eb414bad81d4cb524d06258dea1bdc0"}, + {file = "fonttools-4.47.2-cp310-cp310-win_amd64.whl", hash = "sha256:3e2b95dce2ead58fb12524d0ca7d63a63459dd489e7e5838c3cd53557f8933e1"}, + {file = "fonttools-4.47.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:29495d6d109cdbabe73cfb6f419ce67080c3ef9ea1e08d5750240fd4b0c4763b"}, + {file = "fonttools-4.47.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0a1d313a415eaaba2b35d6cd33536560deeebd2ed758b9bfb89ab5d97dc5deac"}, + {file = "fonttools-4.47.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90f898cdd67f52f18049250a6474185ef6544c91f27a7bee70d87d77a8daf89c"}, + {file = "fonttools-4.47.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3480eeb52770ff75140fe7d9a2ec33fb67b07efea0ab5129c7e0c6a639c40c70"}, + {file = "fonttools-4.47.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0255dbc128fee75fb9be364806b940ed450dd6838672a150d501ee86523ac61e"}, + {file = "fonttools-4.47.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f791446ff297fd5f1e2247c188de53c1bfb9dd7f0549eba55b73a3c2087a2703"}, + {file = "fonttools-4.47.2-cp311-cp311-win32.whl", hash = "sha256:740947906590a878a4bde7dd748e85fefa4d470a268b964748403b3ab2aeed6c"}, + {file = "fonttools-4.47.2-cp311-cp311-win_amd64.whl", hash = "sha256:63fbed184979f09a65aa9c88b395ca539c94287ba3a364517698462e13e457c9"}, + {file = "fonttools-4.47.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4ec558c543609e71b2275c4894e93493f65d2f41c15fe1d089080c1d0bb4d635"}, + {file = "fonttools-4.47.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e040f905d542362e07e72e03612a6270c33d38281fd573160e1003e43718d68d"}, + {file = "fonttools-4.47.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dd58cc03016b281bd2c74c84cdaa6bd3ce54c5a7f47478b7657b930ac3ed8eb"}, + {file = "fonttools-4.47.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32ab2e9702dff0dd4510c7bb958f265a8d3dd5c0e2547e7b5f7a3df4979abb07"}, + {file = "fonttools-4.47.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a808f3c1d1df1f5bf39be869b6e0c263570cdafb5bdb2df66087733f566ea71"}, + {file = "fonttools-4.47.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac71e2e201df041a2891067dc36256755b1229ae167edbdc419b16da78732c2f"}, + {file = "fonttools-4.47.2-cp312-cp312-win32.whl", hash = "sha256:69731e8bea0578b3c28fdb43dbf95b9386e2d49a399e9a4ad736b8e479b08085"}, + {file = "fonttools-4.47.2-cp312-cp312-win_amd64.whl", hash = "sha256:b3e1304e5f19ca861d86a72218ecce68f391646d85c851742d265787f55457a4"}, + {file = "fonttools-4.47.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:254d9a6f7be00212bf0c3159e0a420eb19c63793b2c05e049eb337f3023c5ecc"}, + {file = "fonttools-4.47.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eabae77a07c41ae0b35184894202305c3ad211a93b2eb53837c2a1143c8bc952"}, + {file = "fonttools-4.47.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a86a5ab2873ed2575d0fcdf1828143cfc6b977ac448e3dc616bb1e3d20efbafa"}, + {file = "fonttools-4.47.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13819db8445a0cec8c3ff5f243af6418ab19175072a9a92f6cc8ca7d1452754b"}, + {file = "fonttools-4.47.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4e743935139aa485fe3253fc33fe467eab6ea42583fa681223ea3f1a93dd01e6"}, + {file = "fonttools-4.47.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d49ce3ea7b7173faebc5664872243b40cf88814ca3eb135c4a3cdff66af71946"}, + {file = "fonttools-4.47.2-cp38-cp38-win32.whl", hash = "sha256:94208ea750e3f96e267f394d5588579bb64cc628e321dbb1d4243ffbc291b18b"}, + {file = "fonttools-4.47.2-cp38-cp38-win_amd64.whl", hash = "sha256:0f750037e02beb8b3569fbff701a572e62a685d2a0e840d75816592280e5feae"}, + {file = "fonttools-4.47.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d71606c9321f6701642bd4746f99b6089e53d7e9817fc6b964e90d9c5f0ecc6"}, + {file = "fonttools-4.47.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:86e0427864c6c91cf77f16d1fb9bf1bbf7453e824589e8fb8461b6ee1144f506"}, + {file = "fonttools-4.47.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a00bd0e68e88987dcc047ea31c26d40a3c61185153b03457956a87e39d43c37"}, + {file = "fonttools-4.47.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5d77479fb885ef38a16a253a2f4096bc3d14e63a56d6246bfdb56365a12b20c"}, + {file = "fonttools-4.47.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5465df494f20a7d01712b072ae3ee9ad2887004701b95cb2cc6dcb9c2c97a899"}, + {file = "fonttools-4.47.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4c811d3c73b6abac275babb8aa439206288f56fdb2c6f8835e3d7b70de8937a7"}, + {file = "fonttools-4.47.2-cp39-cp39-win32.whl", hash = "sha256:5b60e3afa9635e3dfd3ace2757039593e3bd3cf128be0ddb7a1ff4ac45fa5a50"}, + {file = "fonttools-4.47.2-cp39-cp39-win_amd64.whl", hash = "sha256:7ee48bd9d6b7e8f66866c9090807e3a4a56cf43ffad48962725a190e0dd774c8"}, + {file = "fonttools-4.47.2-py3-none-any.whl", hash = "sha256:7eb7ad665258fba68fd22228a09f347469d95a97fb88198e133595947a20a184"}, + {file = "fonttools-4.47.2.tar.gz", hash = "sha256:7df26dd3650e98ca45f1e29883c96a0b9f5bb6af8d632a6a108bc744fa0bd9b3"}, +] + +[package.extras] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres", "pycairo", "scipy"] +lxml = ["lxml (>=4.0,<5)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=15.1.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] + +[[package]] +name = "kiwisolver" +version = "1.4.5" +description = "A fast implementation of the Cassowary constraint solver" +optional = false +python-versions = ">=3.7" +files = [ + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, + {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, +] + +[[package]] +name = "matplotlib" +version = "3.8.2" +description = "Python plotting package" +optional = false +python-versions = ">=3.9" +files = [ + {file = "matplotlib-3.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:09796f89fb71a0c0e1e2f4bdaf63fb2cefc84446bb963ecdeb40dfee7dfa98c7"}, + {file = "matplotlib-3.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9c6976748a25e8b9be51ea028df49b8e561eed7809146da7a47dbecebab367"}, + {file = "matplotlib-3.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78e4f2cedf303869b782071b55fdde5987fda3038e9d09e58c91cc261b5ad18"}, + {file = "matplotlib-3.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e208f46cf6576a7624195aa047cb344a7f802e113bb1a06cfd4bee431de5e31"}, + {file = "matplotlib-3.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a569130ff53798ea5f50afce7406e91fdc471ca1e0e26ba976a8c734c9427a"}, + {file = "matplotlib-3.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:830f00640c965c5b7f6bc32f0d4ce0c36dfe0379f7dd65b07a00c801713ec40a"}, + {file = "matplotlib-3.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d86593ccf546223eb75a39b44c32788e6f6440d13cfc4750c1c15d0fcb850b63"}, + {file = "matplotlib-3.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a5430836811b7652991939012f43d2808a2db9b64ee240387e8c43e2e5578c8"}, + {file = "matplotlib-3.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9576723858a78751d5aacd2497b8aef29ffea6d1c95981505877f7ac28215c6"}, + {file = "matplotlib-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba9cbd8ac6cf422f3102622b20f8552d601bf8837e49a3afed188d560152788"}, + {file = "matplotlib-3.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:03f9d160a29e0b65c0790bb07f4f45d6a181b1ac33eb1bb0dd225986450148f0"}, + {file = "matplotlib-3.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:3773002da767f0a9323ba1a9b9b5d00d6257dbd2a93107233167cfb581f64717"}, + {file = "matplotlib-3.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:4c318c1e95e2f5926fba326f68177dee364aa791d6df022ceb91b8221bd0a627"}, + {file = "matplotlib-3.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:091275d18d942cf1ee9609c830a1bc36610607d8223b1b981c37d5c9fc3e46a4"}, + {file = "matplotlib-3.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b0f3b8ea0e99e233a4bcc44590f01604840d833c280ebb8fe5554fd3e6cfe8d"}, + {file = "matplotlib-3.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b1704a530395aaf73912be741c04d181f82ca78084fbd80bc737be04848331"}, + {file = "matplotlib-3.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533b0e3b0c6768eef8cbe4b583731ce25a91ab54a22f830db2b031e83cca9213"}, + {file = "matplotlib-3.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:0f4fc5d72b75e2c18e55eb32292659cf731d9d5b312a6eb036506304f4675630"}, + {file = "matplotlib-3.8.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:deaed9ad4da0b1aea77fe0aa0cebb9ef611c70b3177be936a95e5d01fa05094f"}, + {file = "matplotlib-3.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:172f4d0fbac3383d39164c6caafd3255ce6fa58f08fc392513a0b1d3b89c4f89"}, + {file = "matplotlib-3.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7d36c2209d9136cd8e02fab1c0ddc185ce79bc914c45054a9f514e44c787917"}, + {file = "matplotlib-3.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5864bdd7da445e4e5e011b199bb67168cdad10b501750367c496420f2ad00843"}, + {file = "matplotlib-3.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ef8345b48e95cee45ff25192ed1f4857273117917a4dcd48e3905619bcd9c9b8"}, + {file = "matplotlib-3.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:7c48d9e221b637c017232e3760ed30b4e8d5dfd081daf327e829bf2a72c731b4"}, + {file = "matplotlib-3.8.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa11b3c6928a1e496c1a79917d51d4cd5d04f8a2e75f21df4949eeefdf697f4b"}, + {file = "matplotlib-3.8.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1095fecf99eeb7384dabad4bf44b965f929a5f6079654b681193edf7169ec20"}, + {file = "matplotlib-3.8.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:bddfb1db89bfaa855912261c805bd0e10218923cc262b9159a49c29a7a1c1afa"}, + {file = "matplotlib-3.8.2.tar.gz", hash = "sha256:01a978b871b881ee76017152f1f1a0cbf6bd5f7b8ff8c96df0df1bd57d8755a1"}, +] + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +kiwisolver = ">=1.3.1" +numpy = ">=1.21,<2" +packaging = ">=20.0" +pillow = ">=8" +pyparsing = ">=2.3.1" +python-dateutil = ">=2.7" + +[[package]] +name = "mypy" +version = "1.8.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, + {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, + {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, + {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, + {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, + {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, + {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, + {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, + {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, + {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, + {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, + {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, + {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, + {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, + {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, + {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, + {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, + {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, + {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, + {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, + {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, + {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, + {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, + {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, + {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, + {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, + {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "numpy" +version = "1.26.3" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf"}, + {file = "numpy-1.26.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd"}, + {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d45b3ec2faed4baca41c76617fcdcfa4f684ff7a151ce6fc78ad3b6e85af0a6"}, + {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdd2b45bf079d9ad90377048e2747a0c82351989a2165821f0c96831b4a2a54b"}, + {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:211ddd1e94817ed2d175b60b6374120244a4dd2287f4ece45d49228b4d529178"}, + {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1240f767f69d7c4c8a29adde2310b871153df9b26b5cb2b54a561ac85146485"}, + {file = "numpy-1.26.3-cp310-cp310-win32.whl", hash = "sha256:21a9484e75ad018974a2fdaa216524d64ed4212e418e0a551a2d83403b0531d3"}, + {file = "numpy-1.26.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e1591f6ae98bcfac2a4bbf9221c0b92ab49762228f38287f6eeb5f3f55905ce"}, + {file = "numpy-1.26.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b831295e5472954104ecb46cd98c08b98b49c69fdb7040483aff799a755a7374"}, + {file = "numpy-1.26.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e87562b91f68dd8b1c39149d0323b42e0082db7ddb8e934ab4c292094d575d6"}, + {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c66d6fec467e8c0f975818c1796d25c53521124b7cfb760114be0abad53a0a2"}, + {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f25e2811a9c932e43943a2615e65fc487a0b6b49218899e62e426e7f0a57eeda"}, + {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af36e0aa45e25c9f57bf684b1175e59ea05d9a7d3e8e87b7ae1a1da246f2767e"}, + {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:51c7f1b344f302067b02e0f5b5d2daa9ed4a721cf49f070280ac202738ea7f00"}, + {file = "numpy-1.26.3-cp311-cp311-win32.whl", hash = "sha256:7ca4f24341df071877849eb2034948459ce3a07915c2734f1abb4018d9c49d7b"}, + {file = "numpy-1.26.3-cp311-cp311-win_amd64.whl", hash = "sha256:39763aee6dfdd4878032361b30b2b12593fb445ddb66bbac802e2113eb8a6ac4"}, + {file = "numpy-1.26.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a7081fd19a6d573e1a05e600c82a1c421011db7935ed0d5c483e9dd96b99cf13"}, + {file = "numpy-1.26.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12c70ac274b32bc00c7f61b515126c9205323703abb99cd41836e8125ea0043e"}, + {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f784e13e598e9594750b2ef6729bcd5a47f6cfe4a12cca13def35e06d8163e3"}, + {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f24750ef94d56ce6e33e4019a8a4d68cfdb1ef661a52cdaee628a56d2437419"}, + {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:77810ef29e0fb1d289d225cabb9ee6cf4d11978a00bb99f7f8ec2132a84e0166"}, + {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8ed07a90f5450d99dad60d3799f9c03c6566709bd53b497eb9ccad9a55867f36"}, + {file = "numpy-1.26.3-cp312-cp312-win32.whl", hash = "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511"}, + {file = "numpy-1.26.3-cp312-cp312-win_amd64.whl", hash = "sha256:da4b0c6c699a0ad73c810736303f7fbae483bcb012e38d7eb06a5e3b432c981b"}, + {file = "numpy-1.26.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1666f634cb3c80ccbd77ec97bc17337718f56d6658acf5d3b906ca03e90ce87f"}, + {file = "numpy-1.26.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18c3319a7d39b2c6a9e3bb75aab2304ab79a811ac0168a671a62e6346c29b03f"}, + {file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b7e807d6888da0db6e7e75838444d62495e2b588b99e90dd80c3459594e857b"}, + {file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4d362e17bcb0011738c2d83e0a65ea8ce627057b2fdda37678f4374a382a137"}, + {file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b8c275f0ae90069496068c714387b4a0eba5d531aace269559ff2b43655edd58"}, + {file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc0743f0302b94f397a4a65a660d4cd24267439eb16493fb3caad2e4389bccbb"}, + {file = "numpy-1.26.3-cp39-cp39-win32.whl", hash = "sha256:9bc6d1a7f8cedd519c4b7b1156d98e051b726bf160715b769106661d567b3f03"}, + {file = "numpy-1.26.3-cp39-cp39-win_amd64.whl", hash = "sha256:867e3644e208c8922a3be26fc6bbf112a035f50f0a86497f98f228c50c607bb2"}, + {file = "numpy-1.26.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3c67423b3703f8fbd90f5adaa37f85b5794d3366948efe9a5190a5f3a83fc34e"}, + {file = "numpy-1.26.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46f47ee566d98849323f01b349d58f2557f02167ee301e5e28809a8c0e27a2d0"}, + {file = "numpy-1.26.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a8474703bffc65ca15853d5fd4d06b18138ae90c17c8d12169968e998e448bb5"}, + {file = "numpy-1.26.3.tar.gz", hash = "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4"}, +] + +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "pandas" +version = "2.2.0" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8108ee1712bb4fa2c16981fba7e68b3f6ea330277f5ca34fa8d557e986a11670"}, + {file = "pandas-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:736da9ad4033aeab51d067fc3bd69a0ba36f5a60f66a527b3d72e2030e63280a"}, + {file = "pandas-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38e0b4fc3ddceb56ec8a287313bc22abe17ab0eb184069f08fc6a9352a769b18"}, + {file = "pandas-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20404d2adefe92aed3b38da41d0847a143a09be982a31b85bc7dd565bdba0f4e"}, + {file = "pandas-2.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ea3ee3f125032bfcade3a4cf85131ed064b4f8dd23e5ce6fa16473e48ebcaf5"}, + {file = "pandas-2.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f9670b3ac00a387620489dfc1bca66db47a787f4e55911f1293063a78b108df1"}, + {file = "pandas-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a946f210383c7e6d16312d30b238fd508d80d927014f3b33fb5b15c2f895430"}, + {file = "pandas-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a1b438fa26b208005c997e78672f1aa8138f67002e833312e6230f3e57fa87d5"}, + {file = "pandas-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8ce2fbc8d9bf303ce54a476116165220a1fedf15985b09656b4b4275300e920b"}, + {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2707514a7bec41a4ab81f2ccce8b382961a29fbe9492eab1305bb075b2b1ff4f"}, + {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85793cbdc2d5bc32620dc8ffa715423f0c680dacacf55056ba13454a5be5de88"}, + {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cfd6c2491dc821b10c716ad6776e7ab311f7df5d16038d0b7458bc0b67dc10f3"}, + {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a146b9dcacc3123aa2b399df1a284de5f46287a4ab4fbfc237eac98a92ebcb71"}, + {file = "pandas-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbc1b53c0e1fdf16388c33c3cca160f798d38aea2978004dd3f4d3dec56454c9"}, + {file = "pandas-2.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a41d06f308a024981dcaa6c41f2f2be46a6b186b902c94c2674e8cb5c42985bc"}, + {file = "pandas-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:159205c99d7a5ce89ecfc37cb08ed179de7783737cea403b295b5eda8e9c56d1"}, + {file = "pandas-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1e1f3861ea9132b32f2133788f3b14911b68102d562715d71bd0013bc45440"}, + {file = "pandas-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:761cb99b42a69005dec2b08854fb1d4888fdf7b05db23a8c5a099e4b886a2106"}, + {file = "pandas-2.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a20628faaf444da122b2a64b1e5360cde100ee6283ae8effa0d8745153809a2e"}, + {file = "pandas-2.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f5be5d03ea2073627e7111f61b9f1f0d9625dc3c4d8dda72cc827b0c58a1d042"}, + {file = "pandas-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:a626795722d893ed6aacb64d2401d017ddc8a2341b49e0384ab9bf7112bdec30"}, + {file = "pandas-2.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9f66419d4a41132eb7e9a73dcec9486cf5019f52d90dd35547af11bc58f8637d"}, + {file = "pandas-2.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:57abcaeda83fb80d447f28ab0cc7b32b13978f6f733875ebd1ed14f8fbc0f4ab"}, + {file = "pandas-2.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e60f1f7dba3c2d5ca159e18c46a34e7ca7247a73b5dd1a22b6d59707ed6b899a"}, + {file = "pandas-2.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb61dc8567b798b969bcc1fc964788f5a68214d333cade8319c7ab33e2b5d88a"}, + {file = "pandas-2.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:52826b5f4ed658fa2b729264d63f6732b8b29949c7fd234510d57c61dbeadfcd"}, + {file = "pandas-2.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bde2bc699dbd80d7bc7f9cab1e23a95c4375de615860ca089f34e7c64f4a8de7"}, + {file = "pandas-2.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:3de918a754bbf2da2381e8a3dcc45eede8cd7775b047b923f9006d5f876802ae"}, + {file = "pandas-2.2.0.tar.gz", hash = "sha256:30b83f7c3eb217fb4d1b494a57a2fda5444f17834f5df2de6b2ffff68dc3c8e2"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.22.4,<2", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2,<2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0,<2", markers = "python_version >= \"3.12\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.7" + +[package.extras] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] + +[[package]] +name = "pillow" +version = "10.2.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pillow-10.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e"}, + {file = "pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2"}, + {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c"}, + {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0"}, + {file = "pillow-10.2.0-cp310-cp310-win32.whl", hash = "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023"}, + {file = "pillow-10.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72"}, + {file = "pillow-10.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad"}, + {file = "pillow-10.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5"}, + {file = "pillow-10.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311"}, + {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1"}, + {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757"}, + {file = "pillow-10.2.0-cp311-cp311-win32.whl", hash = "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068"}, + {file = "pillow-10.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56"}, + {file = "pillow-10.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1"}, + {file = "pillow-10.2.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef"}, + {file = "pillow-10.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04"}, + {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f"}, + {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb"}, + {file = "pillow-10.2.0-cp312-cp312-win32.whl", hash = "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f"}, + {file = "pillow-10.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9"}, + {file = "pillow-10.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48"}, + {file = "pillow-10.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9"}, + {file = "pillow-10.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d"}, + {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6"}, + {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe"}, + {file = "pillow-10.2.0-cp38-cp38-win32.whl", hash = "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e"}, + {file = "pillow-10.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39"}, + {file = "pillow-10.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67"}, + {file = "pillow-10.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13"}, + {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7"}, + {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591"}, + {file = "pillow-10.2.0-cp39-cp39-win32.whl", hash = "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516"}, + {file = "pillow-10.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8"}, + {file = "pillow-10.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6"}, + {file = "pillow-10.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"}, + {file = "pillow-10.2.0.tar.gz", hash = "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "psutil-wheels" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "psutil-wheels-5.8.0.tar.gz", hash = "sha256:9fb80725195402a66e5db947f239d032500cde75ca5d8625326d797a65341d6f"}, + {file = "psutil_wheels-5.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2cfbb317f3ee0c8ac9dd5d82e6913b0216222d2b22ea65cbc2f8072dabb167d4"}, + {file = "psutil_wheels-5.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ea1f7f6bcc536669a22c07429dde993bc707f45339137b085394faada25fc813"}, + {file = "psutil_wheels-5.8.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d13d705fb5026d3ae476c7988601430dfaa6143e695058a3182146adc0457b7f"}, + {file = "psutil_wheels-5.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:3f0ca7c4c9031e04b18e52cd4c6f17e196bb7896071dd1eacaeb352948b47517"}, + {file = "psutil_wheels-5.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:902ab2a221529cd7c0d9fa2f865fdd22bc45df87db825437aeee0dcaeed9b787"}, + {file = "psutil_wheels-5.8.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:96838ad949609621e369d327834ade3b3e1b0fa3f450e0a7460855a3cf41a6d6"}, + {file = "psutil_wheels-5.8.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:933c4079c8121f8f0d3d1525671e3b6182d804d54c7819b6a7dddeac5605ba69"}, + {file = "psutil_wheels-5.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:5dd57fb06b081bf2e3cebe89ca92f6ef606ecc5e50ac7ecb2dc7a68262d6cd91"}, + {file = "psutil_wheels-5.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:05a4b136c395273066ecd63d64200868fc57561c65f6dda988b28d08f4a60f69"}, + {file = "psutil_wheels-5.8.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c7c13e8264fa26f0bde4ddc15f2959d04c2a8f7537c41541d1503dd159b01a86"}, + {file = "psutil_wheels-5.8.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b07890d22db82a135b8d5149ba1736e0fde998605cfa73c4d030bbfc77e890b6"}, + {file = "psutil_wheels-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:84bb63b669aa918b4a62226276b1c1f952e57a461debfb7b9eed848c41e7cbda"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[[package]] +name = "pyparsing" +version = "3.1.1" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"}, + {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2023.3.post1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, + {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, +] + +[[package]] +name = "ruff" +version = "0.1.14" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.1.14-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:96f76536df9b26622755c12ed8680f159817be2f725c17ed9305b472a757cdbb"}, + {file = "ruff-0.1.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab3f71f64498c7241123bb5a768544cf42821d2a537f894b22457a543d3ca7a9"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7060156ecc572b8f984fd20fd8b0fcb692dd5d837b7606e968334ab7ff0090ab"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a53d8e35313d7b67eb3db15a66c08434809107659226a90dcd7acb2afa55faea"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bea9be712b8f5b4ebed40e1949379cfb2a7d907f42921cf9ab3aae07e6fba9eb"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2270504d629a0b064247983cbc495bed277f372fb9eaba41e5cf51f7ba705a6a"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80258bb3b8909b1700610dfabef7876423eed1bc930fe177c71c414921898efa"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:653230dd00aaf449eb5ff25d10a6e03bc3006813e2cb99799e568f55482e5cae"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b3acc6c4e6928459ba9eb7459dd4f0c4bf266a053c863d72a44c33246bfdbf"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6b3dadc9522d0eccc060699a9816e8127b27addbb4697fc0c08611e4e6aeb8b5"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1c8eca1a47b4150dc0fbec7fe68fc91c695aed798532a18dbb1424e61e9b721f"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:62ce2ae46303ee896fc6811f63d6dabf8d9c389da0f3e3f2bce8bc7f15ef5488"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b2027dde79d217b211d725fc833e8965dc90a16d0d3213f1298f97465956661b"}, + {file = "ruff-0.1.14-py3-none-win32.whl", hash = "sha256:722bafc299145575a63bbd6b5069cb643eaa62546a5b6398f82b3e4403329cab"}, + {file = "ruff-0.1.14-py3-none-win_amd64.whl", hash = "sha256:e3d241aa61f92b0805a7082bd89a9990826448e4d0398f0e2bc8f05c75c63d99"}, + {file = "ruff-0.1.14-py3-none-win_arm64.whl", hash = "sha256:269302b31ade4cde6cf6f9dd58ea593773a37ed3f7b97e793c8594b262466b67"}, + {file = "ruff-0.1.14.tar.gz", hash = "sha256:ad3f8088b2dfd884820289a06ab718cde7d38b94972212cc4ba90d5fbc9955f3"}, +] + +[[package]] +name = "scipy" +version = "1.12.0" +description = "Fundamental algorithms for scientific computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "scipy-1.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:78e4402e140879387187f7f25d91cc592b3501a2e51dfb320f48dfb73565f10b"}, + {file = "scipy-1.12.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:f5f00ebaf8de24d14b8449981a2842d404152774c1a1d880c901bf454cb8e2a1"}, + {file = "scipy-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e53958531a7c695ff66c2e7bb7b79560ffdc562e2051644c5576c39ff8efb563"}, + {file = "scipy-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e32847e08da8d895ce09d108a494d9eb78974cf6de23063f93306a3e419960c"}, + {file = "scipy-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4c1020cad92772bf44b8e4cdabc1df5d87376cb219742549ef69fc9fd86282dd"}, + {file = "scipy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:75ea2a144096b5e39402e2ff53a36fecfd3b960d786b7efd3c180e29c39e53f2"}, + {file = "scipy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:408c68423f9de16cb9e602528be4ce0d6312b05001f3de61fe9ec8b1263cad08"}, + {file = "scipy-1.12.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5adfad5dbf0163397beb4aca679187d24aec085343755fcdbdeb32b3679f254c"}, + {file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3003652496f6e7c387b1cf63f4bb720951cfa18907e998ea551e6de51a04467"}, + {file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b8066bce124ee5531d12a74b617d9ac0ea59245246410e19bca549656d9a40a"}, + {file = "scipy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8bee4993817e204d761dba10dbab0774ba5a8612e57e81319ea04d84945375ba"}, + {file = "scipy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a24024d45ce9a675c1fb8494e8e5244efea1c7a09c60beb1eeb80373d0fecc70"}, + {file = "scipy-1.12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e7e76cc48638228212c747ada851ef355c2bb5e7f939e10952bc504c11f4e372"}, + {file = "scipy-1.12.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f7ce148dffcd64ade37b2df9315541f9adad6efcaa86866ee7dd5db0c8f041c3"}, + {file = "scipy-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c39f92041f490422924dfdb782527a4abddf4707616e07b021de33467f917bc"}, + {file = "scipy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ebda398f86e56178c2fa94cad15bf457a218a54a35c2a7b4490b9f9cb2676c"}, + {file = "scipy-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:95e5c750d55cf518c398a8240571b0e0782c2d5a703250872f36eaf737751338"}, + {file = "scipy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e646d8571804a304e1da01040d21577685ce8e2db08ac58e543eaca063453e1c"}, + {file = "scipy-1.12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:913d6e7956c3a671de3b05ccb66b11bc293f56bfdef040583a7221d9e22a2e35"}, + {file = "scipy-1.12.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba1b0c7256ad75401c73e4b3cf09d1f176e9bd4248f0d3112170fb2ec4db067"}, + {file = "scipy-1.12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:730badef9b827b368f351eacae2e82da414e13cf8bd5051b4bdfd720271a5371"}, + {file = "scipy-1.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6546dc2c11a9df6926afcbdd8a3edec28566e4e785b915e849348c6dd9f3f490"}, + {file = "scipy-1.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:196ebad3a4882081f62a5bf4aeb7326aa34b110e533aab23e4374fcccb0890dc"}, + {file = "scipy-1.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:b360f1b6b2f742781299514e99ff560d1fe9bd1bff2712894b52abe528d1fd1e"}, + {file = "scipy-1.12.0.tar.gz", hash = "sha256:4bf5abab8a36d20193c698b0f1fc282c1d083c94723902c447e5d2f1780936a3"}, +] + +[package.dependencies] +numpy = ">=1.22.4,<1.29.0" + +[package.extras] +dev = ["click", "cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] +doc = ["jupytext", "matplotlib (>2)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] +test = ["asv", "gmpy2", "hypothesis", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "seaborn" +version = "0.13.2" +description = "Statistical data visualization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987"}, + {file = "seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7"}, +] + +[package.dependencies] +matplotlib = ">=3.4,<3.6.1 || >3.6.1" +numpy = ">=1.20,<1.24.0 || >1.24.0" +pandas = ">=1.2" + +[package.extras] +dev = ["flake8", "flit", "mypy", "pandas-stubs", "pre-commit", "pytest", "pytest-cov", "pytest-xdist"] +docs = ["ipykernel", "nbconvert", "numpydoc", "pydata_sphinx_theme (==0.10.0rc2)", "pyyaml", "sphinx (<6.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-issues"] +stats = ["scipy (>=1.7)", "statsmodels (>=0.12)"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "typing-extensions" +version = "4.9.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, +] + +[[package]] +name = "tzdata" +version = "2023.4" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2023.4-py2.py3-none-any.whl", hash = "sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3"}, + {file = "tzdata-2023.4.tar.gz", hash = "sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "55a6ffb8a4095af0f7aa7c1b212b95a8c2f34f566418d6d2c91deb848e08b9b4" diff --git a/pyproject.toml b/pyproject.toml index adbf17d5..2c9da63f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,20 +1,147 @@ -[tool.isort] -py_version=36 -line_length = 100 -multi_line_output = 6 -honor_noqa = true -order_by_type = false -use_parentheses = true -combine_as_imports = true -only_modified = true -lexicographical = true -group_by_package = true -force_alphabetical_sort_within_sections = true -extend_skip_glob = [ +[tool.poetry] +name = "hs-test-python" +version = "0.1.0" +description = "" +authors = ["Hyperskill Team"] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.10" +psutil-wheels = "^5.8.0" + +[tool.poetry.group.dev.dependencies] +mypy = "1.8.0" +ruff = "0.1.14" +matplotlib = "^3.8.2" +seaborn = "^0.13.2" +pandas = "^2.2.0" +scipy = "^1.12.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.ruff] +select = [ + "ALL", +] +ignore = [ + "ANN002", # Missing type annotation for `*args` + "ANN003", # Missing type annotation for `**kwargs` + "ANN101", # Missing type annotation for `self` in method + "ANN102", # Missing type annotation for `cls` in classmethod + "ANN401", # Dynamically typed expressions (typing.Any) are disallowed in + "ARG001", # Unused function argument + "ARG002", # Unused method argument + "ARG004", # Unused static method argument + "CPY001", # Missing copyright notice at top of file + "D100", # Missing docstring in public module + "D101", # Missing docstring in public class + "D102", # Missing docstring in public method + "D103", # Missing docstring in public function + "D104", # Missing docstring in public package + "D105", # Missing docstring in magic method + "D106", # Missing docstring in public nested class + "D107", # Missing docstring in __init__ + "E203", # Whitespace before ':' + "EXE002", # The file is executable but no shebang is present + "FBT003", # Boolean positional value in function call + "FIX002", # Line contains TODO, consider resolving the issue + "N806", # Variable in function should be lowercase + "PLC0415", # `import` should be at the top-level of a file + "PLC1901", # `record['bio'] == ''` can be simplified to `not record['bio']` as an empty string is falsey + "PLR0904", # Too many public methods + "PLR0916", # Too many Boolean expressions + "PLR1706", # Consider using if-else expression (`self.obj.fullname if self.obj else ''`) + "PLR6301", # Method could be a function, class method, or static method + "PT", # Use a regular `assert` instead of unittest-style `assertEqual` + "S101", # Use of `assert` detected + "TD002", # Missing author in TODO + "TD003", # Missing issue link on the line following this TODO + # Ruff format recommend disable trid rule + "COM812", # Trailing comma missing + "COM819", # Checks for the presence of prohibited trailing commas + "D206", # Docstring should be indented with spaces, not tabs + "D300", # Use """triple double quotes""" + "E111", # Indentation is not a multiple of four + "E114", # Indentation is not a multiple of four (comment) + "E117", # Over-indented + "ISC001", # Conflict with ruff format | Checks for implicitly concatenated strings on a single line. + "ISC002", # Checks for implicitly concatenated strings across multiple lines. + "Q000", # Conflict with ruff format | Remove bad quotes + "Q001", # Checks for multiline strings that use single quotes or double quotes + "Q002", # Checks for docstrings that use single quotes or double quotes + "Q003", # Conflict with ruff format | Change outer quotes to avoid escaping inner quotes + "W191", # Indentation contains tabs +] + +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "tests/outcomes", + "tests/projects", + "tests/sql", + "venv", +] +line-length = 100 +target-version = "py310" + +[tool.ruff.mccabe] +max-complexity = 41 + +[tool.ruff.pydocstyle] +convention = "google" + +[tool.ruff.lint.pylint] +max-args = 5 +max-branches = 15 +max-returns = 6 +max-statements = 72 + +[tool.ruff.isort] +combine-as-imports = true +order-by-type = false +required-imports = ["from __future__ import annotations"] + +[tool.mypy] +python_version = "3.10" +check_untyped_defs = true +disallow_any_generics = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +explicit_package_bases = true +ignore_errors = false +ignore_missing_imports = true +implicit_reexport = true +strict_equality = true +strict_optional = true +warn_no_return = true +warn_redundant_casts = true +warn_unreachable = true +warn_unused_configs = true +warn_unused_ignores = true + +exclude = [ + "tests/outcomes", "tests/projects", - "tests/outcomes/**/main*.py", - "tests/outcomes/**/cleaning.py", - "tests/outcomes/**/pandas_*.py", - "tests/outcomes/**/matplotlib_*.py", - "tests/outcomes/**/seaborn_*.py", + "tests/sql", + "venv", ] diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index db2ad1b2..00000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,6 +0,0 @@ -matplotlib -seaborn -pandas -scipy -flake8 -isort diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 7c515cc5..00000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -psutil-wheels ; python_version >= '3.10' -psutil ; python_version < '3.10' diff --git a/setup.py b/setup.py deleted file mode 100644 index 63f4d875..00000000 --- a/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -from setuptools import find_namespace_packages, setup - -with open("README.md", "r") as readme_file: - readme = readme_file.read() - -setup( - name="hs-test-python", - version="11.0.0", - author="Vladimir Turov", - author_email="vladimir.turov@stepik.org", - description=( - "A framework that simplifies testing educational projects for https://hyperskill.org/." - ), - long_description=readme, - long_description_content_type="text/markdown", - url="https://github.com/hyperskill/hs-test-python", - packages=find_namespace_packages(exclude=['tests', 'package.json', 'requirements-dev.txt']), - python_requires=">=3.6", - install_requires=[ - "psutil-wheels ; python_version >= '3.10'", - "psutil ; python_version < '3.10'", - ], - classifiers=[ - "Programming Language :: Python :: 3.6" - ], -) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/outcomes/dynamic_method/test_dont_return_output_after_execution/test.py b/tests/outcomes/dynamic_method/test_dont_return_output_after_execution/test.py index b260e702..b64c032b 100644 --- a/tests/outcomes/dynamic_method/test_dont_return_output_after_execution/test.py +++ b/tests/outcomes/dynamic_method/test_dont_return_output_after_execution/test.py @@ -8,7 +8,7 @@ class TestDontReturnOutputAfterExecution(StageTest): @dynamic_test def test(self): main = TestedProgram('main') - main.set_return_output_after_execution(False) + main.set_return_output_after_execution(value=False) out = main.start() if len(out) != 0: @@ -26,7 +26,7 @@ def test(self): if out != "1 to 2\n2 to 3\n": return wrong("Output is wrong") - main.set_return_output_after_execution(True) + main.set_return_output_after_execution(value=True) if main.execute("") != "3 to 4\n": return wrong("Output should not be empty") diff --git a/tests/test_check_result.py b/tests/test_check_result.py index 3d3481d2..0f38e3ed 100644 --- a/tests/test_check_result.py +++ b/tests/test_check_result.py @@ -1,15 +1,17 @@ +from __future__ import annotations + import unittest from hstest.check_result import CheckResult class TestCheckResult(unittest.TestCase): - def test_true(self): + def test_true(self) -> None: r = CheckResult.correct() self.assertTrue(r.is_correct) - self.assertEqual(r.feedback, '') + self.assertEqual(r.feedback, "") - def test_false(self): - r = CheckResult.wrong('hello') + def test_false(self) -> None: + r = CheckResult.wrong("hello") self.assertFalse(r.is_correct) - self.assertEqual(r.feedback, 'hello') + self.assertEqual(r.feedback, "hello") diff --git a/tests/test_testcase.py b/tests/test_testcase.py index 45ebdb94..df068572 100644 --- a/tests/test_testcase.py +++ b/tests/test_testcase.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import unittest from hstest.exception.outcomes import UnexpectedError @@ -5,71 +7,65 @@ class TestTestCase(unittest.TestCase): - def test_attach_none_default(self): + def test_attach_none_default(self) -> None: test_case = TestCase() self.assertIsNone(test_case.attach) - def test_attach(self): + def test_attach(self) -> None: attach = (1, "abc") test_case = TestCase(attach=attach) self.assertEqual(attach, test_case.attach) - def test_copy_to_attach(self): - test_case = TestCase(stdin='abc', copy_to_attach=True) - self.assertEqual(test_case.attach, 'abc') + def test_copy_to_attach(self) -> None: + test_case = TestCase(stdin="abc", copy_to_attach=True) + self.assertEqual(test_case.attach, "abc") - def test_copy_to_attach_exception(self): + def test_copy_to_attach_exception(self) -> None: with self.assertRaises(UnexpectedError): - TestCase(stdin='abc', attach=(1, 2, 3), copy_to_attach=True) + TestCase(stdin="abc", attach=(1, 2, 3), copy_to_attach=True) - def test_stdin_empty(self): + def test_stdin_empty(self) -> None: test_case = TestCase() - self.assertEqual(test_case.input, '') + self.assertEqual(test_case.input, "") - def test_stdin_passed(self): - stdin = 'abc' + def test_stdin_passed(self) -> None: + stdin = "abc" test_case = TestCase(stdin=stdin) self.assertEqual(test_case.input, stdin) - def test_from_stepik_length(self): - tests = TestCase.from_stepik(['123', '234', '345']) + def test_from_stepik_length(self) -> None: + tests = TestCase.from_stepik(["123", "234", "345"]) self.assertEqual(len(tests), 3) - def test_from_stepik_simple(self): - tests = TestCase.from_stepik(['123', '234', '345']) - self.assertEqual(tests[0].input, '123') + def test_from_stepik_simple(self) -> None: + tests = TestCase.from_stepik(["123", "234", "345"]) + self.assertEqual(tests[0].input, "123") self.assertEqual(tests[0].attach, None) - self.assertEqual(tests[1].input, '234') + self.assertEqual(tests[1].input, "234") self.assertEqual(tests[1].attach, None) - self.assertEqual(tests[2].input, '345') + self.assertEqual(tests[2].input, "345") self.assertEqual(tests[2].attach, None) - def test_from_stepik_with_attach(self): - tests = TestCase.from_stepik( - [('123', 234), ('234', 345), ('345', 456)] - ) - self.assertEqual(tests[0].input, '123') + def test_from_stepik_with_attach(self) -> None: + tests = TestCase.from_stepik([("123", 234), ("234", 345), ("345", 456)]) + self.assertEqual(tests[0].input, "123") self.assertEqual(tests[0].attach, 234) - self.assertEqual(tests[1].input, '234') + self.assertEqual(tests[1].input, "234") self.assertEqual(tests[1].attach, 345) - self.assertEqual(tests[2].input, '345') + self.assertEqual(tests[2].input, "345") self.assertEqual(tests[2].attach, 456) - def test_from_stepik_mixed(self): - tests = TestCase.from_stepik( - [('mixed1', 234567), 'mixed234', ('mixed345', 456234), '567'] - ) - self.assertEqual(tests[0].input, 'mixed1') + def test_from_stepik_mixed(self) -> None: + tests = TestCase.from_stepik([("mixed1", 234567), "mixed234", ("mixed345", 456234), "567"]) + self.assertEqual(tests[0].input, "mixed1") self.assertEqual(tests[0].attach, 234567) - self.assertEqual(tests[1].input, 'mixed234') + self.assertEqual(tests[1].input, "mixed234") self.assertEqual(tests[1].attach, None) - self.assertEqual(tests[2].input, 'mixed345') + self.assertEqual(tests[2].input, "mixed345") self.assertEqual(tests[2].attach, 456234) - self.assertEqual(tests[3].input, '567') + self.assertEqual(tests[3].input, "567") self.assertEqual(tests[3].attach, None) - def test_from_stepik_bad_data(self): + def test_from_stepik_bad_data(self) -> None: with self.assertRaises(UnexpectedError): - TestCase.from_stepik( - [('mixed1', 234567), 234345, ('mixed345', 456234), '567'] - ) + TestCase.from_stepik([("mixed1", 234567), 234345, ("mixed345", 456234), "567"]) diff --git a/tests/testing.py b/tests/testing.py index 1bfb5920..1c2779e6 100644 --- a/tests/testing.py +++ b/tests/testing.py @@ -1,80 +1,88 @@ -import io +from __future__ import annotations + import re import sys import unittest from importlib import import_module from inspect import getmembers, isclass from os import listdir -from os.path import abspath, dirname, isdir, isfile -from typing import List +from pathlib import Path +from typing import Final, TYPE_CHECKING -content_path = dirname( - dirname(abspath(__file__)) -) -sys.path.insert(0, content_path) +content_path = Path(__file__).resolve().parent.parent +sys.path.insert(0, content_path.name) from hstest.common import utils as hs # noqa: E402 from hstest.dynamic.output.colored_output import GREEN_BOLD, RED_BOLD, RESET # noqa: E402 +if TYPE_CHECKING: + import io + class OutputForTest: - def __init__(self, real_out: io.TextIOWrapper): + def __init__(self, real_out: io.TextIOWrapper) -> None: self.original: io.TextIOWrapper = real_out - def write(self, text): - text = re.sub(r'(? None: + text = re.sub(r"(? None: self.original.flush() - def close(self): + def close(self) -> None: self.original.close() +MAX_REPEATS: Final = 5 class UnitTesting: - @staticmethod def test_all() -> bool: old_run = unittest.TestCase.run - def run(self, result=None, repeats=0): + def run(self: unittest.TestCase, + result: unittest.TestResult | None = None, + repeats: int = 0) -> unittest.TestResult: failures_before = 0 if result is None else len(result.failures) test_result = old_run(self, result=result) - is_project_test = 'tests.projects.' in str(self) - if repeats == 5: # max 5 times + is_project_test = "tests.projects." in str(self) + if repeats == MAX_REPEATS: return test_result if is_project_test and test_result and failures_before < len(test_result.failures): - print('Rerun project test') + print("Rerun project test") # noqa: T201 test_result.failures.pop() return run(self, result=test_result, repeats=repeats + 1) return test_result unittest.TestCase.run = run - hs.failed_msg_start = '' - hs.failed_msg_continue = '' - hs.success_msg = '' + hs.failed_msg_start = "" + hs.failed_msg_continue = "" + hs.success_msg = "" tests_suite = [] loader = unittest.TestLoader() - for module in UnitTesting.find_modules(dirname(__file__)): - if 'outcomes' in module and not module.endswith('.test') or \ - 'projects' in module and not module.endswith('.tests'): + for module in UnitTesting.find_modules(Path(__file__).parent): + if ( + ("outcomes" in module + and not module.endswith(".test")) + or ("projects" in module + and not module.endswith(".tests")) + ): continue try: - imported = import_module(f'tests.{module}') + imported = import_module(f"tests.{module}") except ImportError: continue - for name, obj in getmembers(imported): + for _name, obj in getmembers(imported): if isclass(obj) and issubclass(obj, unittest.TestCase): tests_suite += [loader.loadTestsFromTestCase(obj)] @@ -84,27 +92,27 @@ def run(self, result=None, repeats=0): return result.wasSuccessful() @staticmethod - def find_modules(from_directory: str) -> List[str]: - - catalogs = [from_directory] - curr_dir = from_directory + def find_modules(from_directory: Path) -> list[str]: + catalogs = {from_directory} modules = [] while catalogs: curr_catalog = catalogs.pop() for file in listdir(curr_catalog): - curr_location = curr_catalog + '/' + file - if file.startswith('__'): + curr_location = curr_catalog / file + if file.startswith("__"): continue - if isfile(curr_location): - if file.endswith('.py'): - modules += [curr_location[len(curr_dir) + 1:-3].replace('/', '.')] - elif isdir(curr_location): - catalogs += [curr_location] + if curr_location.is_file(): + if file.endswith(".py"): + modules.append( + ".".join(curr_location.relative_to(from_directory).parts)[:-3] + ) + elif curr_location.is_dir(): + catalogs.add(curr_location) return modules -if __name__ == '__main__': - exit(0 if UnitTesting.test_all() else -1) +if __name__ == "__main__": + sys.exit(0 if UnitTesting.test_all() else -1) From 3122773c48b675ddb16686d582eef26726166219 Mon Sep 17 00:00:00 2001 From: meanmail Date: Mon, 20 May 2024 10:55:46 +0200 Subject: [PATCH 02/57] Refactor codebase to use pathlib and type annotations The codebase has been refactored to use the pathlib module for file handling operations, replacing the os module. Additionally, type annotations have been added throughout the codebase to improve readability and facilitate static type checking. These changes aim to enhance both the maintainability and reliability of the code. (cherry picked from commit e2ade205e1c2324b7b30911122542307dda0906d) --- .github/workflows/auto-format.yml | 2 +- hstest/common/__init__.py | 0 hstest/common/file_utils.py | 36 +- hstest/common/os_utils.py | 6 +- hstest/common/process_utils.py | 11 +- hstest/common/reflection_utils.py | 65 +- hstest/common/utils.py | 4 +- hstest/dynamic/__init__.py | 0 hstest/dynamic/dynamic_test.py | 10 +- hstest/dynamic/input/__init__.py | 0 hstest/dynamic/input/dynamic_testing.py | 20 +- hstest/dynamic/input/input_mock.py | 5 +- hstest/dynamic/output/__init__.py | 0 hstest/dynamic/output/output_handler.py | 2 +- hstest/dynamic/output/output_mock.py | 40 +- hstest/dynamic/security/__init__.py | 0 hstest/dynamic/security/exit_handler.py | 2 +- hstest/dynamic/security/thread_group.py | 6 +- hstest/dynamic/security/thread_handler.py | 17 +- hstest/exception/__init__.py | 0 hstest/exception/failure_handler.py | 3 +- hstest/exception/outcomes.py | 10 +- hstest/outcomes/__init__.py | 0 hstest/outcomes/error_outcome.py | 10 +- hstest/outcomes/outcome.py | 15 +- hstest/stage/django_test.py | 6 +- hstest/stage/flask_test.py | 16 +- hstest/stage/plotting_test.py | 2 +- hstest/stage/sql_test.py | 12 +- hstest/stage/stage_test.py | 55 +- hstest/test_case/attach/__init__.py | 0 hstest/test_case/attach/django_settings.py | 4 +- hstest/test_case/attach/flask_settings.py | 6 +- hstest/test_case/check_result.py | 11 +- hstest/test_case/test_case.py | 32 +- hstest/testing/execution/__init__.py | 0 .../testing/execution/filtering/__init__.py | 0 .../execution/filtering/file_filter.py | 2 +- .../testing/execution/main_module_executor.py | 5 +- hstest/testing/execution/process/__init__.py | 0 .../testing/execution/process/cpp_executor.py | 14 +- .../testing/execution/process/go_executor.py | 13 +- .../execution/process/javascript_executor.py | 2 +- .../execution/process/python_executor.py | 2 +- .../execution/process/shell_executor.py | 2 +- hstest/testing/execution/process_executor.py | 26 +- hstest/testing/execution/program_executor.py | 11 +- hstest/testing/execution/runnable/__init__.py | 0 .../execution/runnable/runnable_file.py | 9 +- hstest/testing/execution/searcher/__init__.py | 0 .../execution/searcher/base_searcher.py | 91 +-- .../execution/searcher/cpp_searcher.py | 4 +- .../testing/execution/searcher/go_searcher.py | 4 +- .../execution/searcher/javascript_searcher.py | 4 +- .../execution/searcher/python_searcher.py | 8 +- .../execution/searcher/shell_searcher.py | 4 +- .../execution/searcher/sql_searcher.py | 4 +- hstest/testing/plotting/__init__.py | 0 hstest/testing/plotting/drawing/__init__.py | 0 .../plotting/drawing/drawing_builder.py | 18 +- .../testing/plotting/drawing/drawing_data.py | 8 +- .../plotting/drawing_data_normalizer.py | 28 +- hstest/testing/plotting/matplotlib_handler.py | 56 +- hstest/testing/plotting/pandas_handler.py | 396 +++++----- hstest/testing/plotting/seaborn_handler.py | 67 +- hstest/testing/process_wrapper.py | 26 +- .../runner/async_dynamic_testing_runner.py | 7 +- .../runner/django_application_runner.py | 20 +- .../runner/flask_application_runner.py | 13 +- hstest/testing/runner/sql_runner.py | 2 +- hstest/testing/test_run.py | 2 +- poetry.lock | 720 +++++++++--------- pyproject.toml | 84 +- tests/testing.py | 15 +- 74 files changed, 1094 insertions(+), 981 deletions(-) create mode 100644 hstest/common/__init__.py create mode 100644 hstest/dynamic/__init__.py create mode 100644 hstest/dynamic/input/__init__.py create mode 100644 hstest/dynamic/output/__init__.py create mode 100644 hstest/dynamic/security/__init__.py create mode 100644 hstest/exception/__init__.py create mode 100644 hstest/outcomes/__init__.py create mode 100644 hstest/test_case/attach/__init__.py create mode 100644 hstest/testing/execution/__init__.py create mode 100644 hstest/testing/execution/filtering/__init__.py create mode 100644 hstest/testing/execution/process/__init__.py create mode 100644 hstest/testing/execution/runnable/__init__.py create mode 100644 hstest/testing/execution/searcher/__init__.py create mode 100644 hstest/testing/plotting/__init__.py create mode 100644 hstest/testing/plotting/drawing/__init__.py diff --git a/.github/workflows/auto-format.yml b/.github/workflows/auto-format.yml index 5e492ce7..f4a2bfc3 100644 --- a/.github/workflows/auto-format.yml +++ b/.github/workflows/auto-format.yml @@ -23,7 +23,7 @@ jobs: - uses: ./.github/workflows/actions/prepare - - run: poetry run ruff --fix --unsafe-fixes --preview --exit-zero . + - run: poetry run ruff check --fix --unsafe-fixes --preview --exit-zero . - run: poetry run ruff format . - name: Commit changes diff --git a/hstest/common/__init__.py b/hstest/common/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/common/file_utils.py b/hstest/common/file_utils.py index 293898ea..9504e514 100644 --- a/hstest/common/file_utils.py +++ b/hstest/common/file_utils.py @@ -2,42 +2,42 @@ import contextlib import os +from pathlib import Path from hstest.exception.testing import FileDeletionError def create_files(files: dict[str, str]) -> None: for file, content in files.items(): - with open(file, "w", encoding="locale") as f: - f.write(content) + Path(file).write_text(content, encoding="locale") def delete_files(files: dict[str, str]) -> None: - for file in files: - if os.path.isfile(file): + for file in map(Path, files): + if file.is_file(): try: - os.remove(file) - except PermissionError: - raise FileDeletionError + file.unlink() + except PermissionError as ex: + raise FileDeletionError from ex -def safe_delete(filename) -> None: - if os.path.exists(filename): - with contextlib.suppress(BaseException): - os.remove(filename) +def safe_delete(filename: str) -> None: + with contextlib.suppress(BaseException): + Path(filename).unlink(missing_ok=True) -def walk_user_files(folder): - curr_folder = os.path.abspath(folder) - test_folder = os.path.join(curr_folder, "test") +def walk_user_files(curr_folder: Path) -> tuple[Path, list[str], list[str]]: + curr_folder = curr_folder.resolve() + test_folder = curr_folder / "test" for folder, dirs, files in os.walk(curr_folder): - if folder.startswith(test_folder): + folder_ = Path(folder) + if folder_.is_relative_to(test_folder): continue - if folder == curr_folder: - for file in "test.py", "tests.py": + if folder_ == curr_folder: + for file in ("test.py", "tests.py"): if file in files: files.remove(file) - yield folder, dirs, files + yield Path(folder_), dirs, files diff --git a/hstest/common/os_utils.py b/hstest/common/os_utils.py index 4c346919..08498421 100644 --- a/hstest/common/os_utils.py +++ b/hstest/common/os_utils.py @@ -3,13 +3,13 @@ import platform -def is_windows(): +def is_windows() -> bool: return platform.system() == "Windows" -def is_mac(): +def is_mac() -> bool: return platform.system() == "Darwin" -def is_linux(): +def is_linux() -> bool: return platform.system() == "Linux" diff --git a/hstest/common/process_utils.py b/hstest/common/process_utils.py index 0c29949f..f2d66e0e 100644 --- a/hstest/common/process_utils.py +++ b/hstest/common/process_utils.py @@ -3,10 +3,13 @@ import threading import weakref from concurrent.futures import ThreadPoolExecutor -from concurrent.futures.thread import _worker -from typing import TYPE_CHECKING +from concurrent.futures.thread import _worker # noqa: PLC2701 +from typing import Any, TYPE_CHECKING if TYPE_CHECKING: + import queue + from concurrent.futures.process import _WorkItem + from hstest.dynamic.security.thread_group import ThreadGroup @@ -22,7 +25,7 @@ def _adjust_thread_count(self) -> None: # When the executor gets lost, the weakref callback will wake up # the worker threads. - def weakref_cb(_, q=self._work_queue) -> None: + def weakref_cb(_: int, q: queue.SimpleQueue[_WorkItem[Any]] = self._work_queue) -> None: q.put(None) num_threads = len(self._threads) @@ -42,7 +45,7 @@ def weakref_cb(_, q=self._work_queue) -> None: self._threads.add(t) -def is_port_in_use(port): +def is_port_in_use(port: int) -> bool: import socket with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: diff --git a/hstest/common/reflection_utils.py b/hstest/common/reflection_utils.py index 4e45517e..f08f2404 100644 --- a/hstest/common/reflection_utils.py +++ b/hstest/common/reflection_utils.py @@ -2,11 +2,17 @@ import inspect import os +from itertools import pairwise +from pathlib import Path +from typing import TYPE_CHECKING from hstest.exception.failure_handler import get_traceback_stack +if TYPE_CHECKING: + from hstest import StageTest -def is_tests(stage): + +def is_tests(stage: object) -> bool: package = inspect.getmodule(stage).__package__ file = inspect.getmodule(stage).__file__ return ( @@ -18,17 +24,18 @@ def is_tests(stage): ) -def setup_cwd(stage) -> None: +def setup_cwd(stage: StageTest) -> None: if stage.is_tests: - test_file = inspect.getmodule(stage).__file__ - test_folder = os.path.dirname(test_file) + test_file = Path(inspect.getmodule(stage).__file__) + test_folder = test_file.parent os.chdir(test_folder) - if os.path.basename(os.getcwd()) == "test": - os.chdir(os.path.dirname(os.getcwd())) + cwd = Path.cwd() + if cwd.name == "test": + os.chdir(cwd.parent) -def get_stacktrace(ex: BaseException, hide_internals=False) -> str: +def get_stacktrace(ex: BaseException, *, hide_internals: bool = False) -> str: traceback_stack = get_traceback_stack(ex) if not hide_internals: @@ -38,7 +45,7 @@ def get_stacktrace(ex: BaseException, hide_internals=False) -> str: if ex.filename.startswith("<"): # "", or "" user_dir = ex.filename else: - user_dir = os.path.dirname(ex.filename) + os.sep + user_dir = Path(ex.filename).parent.name + os.sep else: user_dir = "" @@ -78,7 +85,7 @@ def str_to_stacktrace(str_trace: str) -> str: traceback_stack = [] - for line_from, line_to in zip(traceback_lines, traceback_lines[1:], strict=False): + for line_from, line_to in pairwise(traceback_lines): actual_lines = lines[line_from:line_to] needed_lines = [line for line in actual_lines if line.startswith(" ")] traceback_stack += ["\n".join(needed_lines) + "\n"] @@ -140,9 +147,9 @@ def clean_stacktrace( if user_file.startswith("<"): continue - dir_name = os.path.dirname(tr[start_index:end_index]) - if os.path.isdir(dir_name): - dir_names += [os.path.abspath(dir_name)] + dir_name = Path(tr[start_index:end_index]).parent + if dir_name.is_dir(): + dir_names += [dir_name.resolve()] if dir_names: from hstest.common.os_utils import is_windows @@ -162,21 +169,25 @@ def clean_stacktrace( cleaned_traceback = [] for trace in full_traceback[1:-1]: - if trace.startswith(" " * 4): - # Trace line that starts with 4 is a line with SyntaxError - cleaned_traceback += [trace] - elif user_dir in trace or ("<" in trace and ">" in trace and " lines that are always in the stacktrace - # but include , because it's definitely user's code - if not user_dir.startswith("<"): - if user_dir in trace: - trace = trace.replace(user_dir, "") + # Trace line that starts with 4 is a line with SyntaxError + # avoid including lines that are always in the stacktrace + # but include , because it's definitely user's code + if (not trace.startswith(" " * 4) and user_dir in trace) or ( + ("<" in trace and ">" in trace and " tuple[int, str]: """Reports failure.""" if not is_unittest: lines = message.splitlines() @@ -21,7 +21,7 @@ def failed(message: str, is_unittest: bool): return -1, message -def passed(is_unittest: bool): +def passed(*, is_unittest: bool) -> tuple[int, str]: """Reports success.""" if not is_unittest: print("\n" + success_msg) # noqa: T201 diff --git a/hstest/dynamic/__init__.py b/hstest/dynamic/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/dynamic/dynamic_test.py b/hstest/dynamic/dynamic_test.py index 344b69d4..55ad36fb 100644 --- a/hstest/dynamic/dynamic_test.py +++ b/hstest/dynamic/dynamic_test.py @@ -8,7 +8,7 @@ def dynamic_test( - func=None, + func: Any | None = None, *, order: int = 0, time_limit: int = DEFAULT_TIME_LIMIT, @@ -16,16 +16,16 @@ def dynamic_test( feedback: str = "", repeat: int = 1, files: dict[str, str] | None = None, -): +) -> Any: """Decorator for creating dynamic tests.""" class DynamicTestingMethod: - def __init__(self, fn) -> None: + def __init__(self, fn: Any) -> None: self.fn = fn - def __set_name__(self, owner, name): + def __set_name__(self, owner: StageTest, name: str) -> None: # do something with owner, i.e. - # print(f"Decorating {self.fn} and using {owner}") + # print(f"Decorating {self.fn} and using {owner}") # noqa: ERA001 self.fn.class_name = owner.__name__ # then replace ourself with the original method diff --git a/hstest/dynamic/input/__init__.py b/hstest/dynamic/input/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/dynamic/input/dynamic_testing.py b/hstest/dynamic/input/dynamic_testing.py index 94df423e..52c68da5 100644 --- a/hstest/dynamic/input/dynamic_testing.py +++ b/hstest/dynamic/input/dynamic_testing.py @@ -72,7 +72,7 @@ def check_errors(self) -> None: raise UnexpectedError(msg) if self.files is not None: - if type(self.files) != dict: + if not isinstance(self.files, dict): msg = ( f"'Files' parameter in dynamic test should be of type " f'"dict", found {type(self.files)}.' @@ -80,20 +80,20 @@ def check_errors(self) -> None: raise UnexpectedError(msg) for k, v in self.files.items(): - if type(k) != str: + if not isinstance(k, str): msg = ( f"All keys in 'files' parameter in dynamic test should be " f'of type "str", found {type(k)}.' ) raise UnexpectedError(msg) - if type(v) != str: + if not isinstance(v, str): msg = ( f"All values in 'files' parameter in dynamic test should be " f'of type "str", found {type(v)}.' ) raise UnexpectedError(msg) - def get_tests(self, obj) -> list[DynamicTesting]: + def get_tests(self, obj: StageTest) -> list[DynamicTesting]: tests = [] for _i in range(self.repeat): for args in self.args_list: @@ -131,15 +131,15 @@ def eject_next_input(self, curr_output: str) -> str | None: new_input = obj elif isinstance(obj, CheckResult): if obj.is_correct: - raise TestPassed - else: - raise WrongAnswer(obj.feedback) + raise TestPassed # noqa: TRY301 + raise WrongAnswer(obj.feedback) # noqa: TRY301 else: - raise UnexpectedError( + msg = ( "Dynamic input should return " - + f"str or CheckResult objects only. Found: {type(obj)}" + f"str or CheckResult objects only. Found: {type(obj)}" ) - except BaseException as ex: + raise UnexpectedError(msg) # noqa: TRY301 + except BaseException as ex: # noqa: BLE001 from hstest.stage_test import StageTest StageTest.curr_test_run.set_error_in_test(ex) diff --git a/hstest/dynamic/input/input_mock.py b/hstest/dynamic/input/input_mock.py index b70a3a04..3447c7f2 100644 --- a/hstest/dynamic/input/input_mock.py +++ b/hstest/dynamic/input/input_mock.py @@ -60,7 +60,6 @@ def readline(self) -> str: StageTest.curr_test_run.set_error_in_test(OutOfInputError()) raise ExitException - else: - msg = "EOF when reading a line" - raise EOFError(msg) + msg = "EOF when reading a line" + raise EOFError(msg) return line diff --git a/hstest/dynamic/output/__init__.py b/hstest/dynamic/output/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/dynamic/output/output_handler.py b/hstest/dynamic/output/output_handler.py index 776afcc9..39b49897 100644 --- a/hstest/dynamic/output/output_handler.py +++ b/hstest/dynamic/output/output_handler.py @@ -22,7 +22,7 @@ class OutputHandler: _mock_err: OutputMock = None @staticmethod - def print(obj) -> None: + def print(obj: Any) -> None: if True: return diff --git a/hstest/dynamic/output/output_mock.py b/hstest/dynamic/output/output_mock.py index 38303c9a..656f914a 100644 --- a/hstest/dynamic/output/output_mock.py +++ b/hstest/dynamic/output/output_mock.py @@ -20,9 +20,23 @@ def __init__(self, condition: Condition) -> None: self.output: list[str] = [] +class RealOutputMock: + def __init__(self, out: io.TextIOWrapper) -> None: + self.out = out + + def write(self, text: str) -> None: + if not ignore_stdout: + self.out.write(text) + + def flush(self) -> None: + self.out.flush() + + def close(self) -> None: + self.out.close() + + class OutputMock: - """original stream is used to actually see - the test in the console and nothing else. + """Original stream is used to actually see the test in the console and nothing else. cloned stream is used to collect all output from the test and redirect to check function @@ -34,21 +48,7 @@ class OutputMock: but also injected input from the test """ - def __init__(self, real_out: io.TextIOWrapper, is_stderr: bool = False) -> None: - class RealOutputMock: - def __init__(self, out: io.TextIOWrapper) -> None: - self.out = out - - def write(self, text) -> None: - if not ignore_stdout: - self.out.write(text) - - def flush(self) -> None: - self.out.flush() - - def close(self) -> None: - self.out.close() - + def __init__(self, real_out: io.TextIOWrapper, *, is_stderr: bool = False) -> None: self._original: RealOutputMock = RealOutputMock(real_out) self._cloned: list[str] = [] # used in check function self._dynamic: list[str] = [] # used to append inputs @@ -56,7 +56,7 @@ def close(self) -> None: self._is_stderr = is_stderr @property - def original(self): + def original(self) -> RealOutputMock: return self._original @property @@ -73,7 +73,7 @@ def partial(self, obj: Any) -> str: output.clear() return result - def write(self, text) -> None: + def write(self, text: str) -> None: partial_handler = self.__get_partial_handler() if partial_handler is None: @@ -120,7 +120,7 @@ def uninstall_output_handler(self, obj: Any) -> None: raise UnexpectedError(msg) del self._partial[obj] - def __get_partial_handler(self): + def __get_partial_handler(self) -> list[str] | None: for handler in self._partial.values(): if handler.condition(): return handler.output diff --git a/hstest/dynamic/security/__init__.py b/hstest/dynamic/security/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/dynamic/security/exit_handler.py b/hstest/dynamic/security/exit_handler.py index c48f82e9..5779beb2 100644 --- a/hstest/dynamic/security/exit_handler.py +++ b/hstest/dynamic/security/exit_handler.py @@ -26,7 +26,7 @@ class ExitHandler: _signal_siginterrupt = None @staticmethod - def is_replaced(): + def is_replaced() -> bool: return ExitHandler._replaced @staticmethod diff --git a/hstest/dynamic/security/thread_group.py b/hstest/dynamic/security/thread_group.py index dccbd3b2..32bdec36 100644 --- a/hstest/dynamic/security/thread_group.py +++ b/hstest/dynamic/security/thread_group.py @@ -17,16 +17,16 @@ def __init__(self, name: str | None = None) -> None: curr = current_thread() if hasattr(curr, "_group"): - self._parent: ThreadGroup | None = curr._group + self._parent: ThreadGroup | None = curr._group # noqa: SLF001 else: self._parent: ThreadGroup | None = None @property - def name(self): + def name(self) -> str: return self._name @property - def parent(self): + def parent(self) -> ThreadGroup | None: return self._parent def add(self, thread: Thread) -> None: diff --git a/hstest/dynamic/security/thread_handler.py b/hstest/dynamic/security/thread_handler.py index 5c2d701e..6a0d72e2 100644 --- a/hstest/dynamic/security/thread_handler.py +++ b/hstest/dynamic/security/thread_handler.py @@ -1,7 +1,7 @@ from __future__ import annotations from threading import current_thread, Thread -from typing import TYPE_CHECKING +from typing import Any, TYPE_CHECKING from hstest.dynamic.security.thread_group import ThreadGroup @@ -19,19 +19,26 @@ def install_thread_group(cls) -> None: cls._old_init = Thread.__init__ Thread.__init__ = ThreadHandler.init cls._group = ThreadGroup("Main") - current_thread()._group = cls._group + current_thread()._group = cls._group # noqa: SLF001 @classmethod def uninstall_thread_group(cls) -> None: if cls._old_init is not None: Thread.__init__ = cls._old_init cls._old_init = None - del current_thread()._group + del current_thread()._group # noqa: SLF001 cls._group = None @staticmethod def init( - self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None + self: Thread, # noqa: PLW0211 + group: ThreadGroup | None = None, + target: Callable[..., Any] | None = None, + name: str | None = None, + args: tuple[Any, ...] | None = (), + kwargs: dict[str, Any] | None = None, + *, + daemon: bool | None = None, ) -> None: ThreadHandler._old_init(self, None, target, name, args, kwargs, daemon=daemon) @@ -42,7 +49,7 @@ def init( curr = current_thread() if hasattr(curr, "_group"): - self._group = curr._group + self._group = curr._group # noqa: SLF001 else: self._group = ThreadGroup(self._name) diff --git a/hstest/exception/__init__.py b/hstest/exception/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/exception/failure_handler.py b/hstest/exception/failure_handler.py index ab10456d..f2b3283f 100644 --- a/hstest/exception/failure_handler.py +++ b/hstest/exception/failure_handler.py @@ -18,8 +18,7 @@ def get_report() -> str: f"{implementation} {python}\n" f"Testing library version 8" ) - else: - return "Submitted via web" + return "Submitted via web" def get_traceback_stack(ex: BaseException) -> list[str]: diff --git a/hstest/exception/outcomes.py b/hstest/exception/outcomes.py index 7000582d..71bdace7 100644 --- a/hstest/exception/outcomes.py +++ b/hstest/exception/outcomes.py @@ -5,19 +5,19 @@ class OutcomeError(BaseException): pass -class SyntaxException(OutcomeError): +class SyntaxException(OutcomeError): # noqa: N818 def __init__(self, exception: SyntaxError, file: str) -> None: self.file: str = file self.exception: SyntaxError = exception -class ExceptionWithFeedback(OutcomeError): +class ExceptionWithFeedback(OutcomeError): # noqa: N818 def __init__(self, error_text: str, real_exception: BaseException | None) -> None: self.error_text: str = error_text self.real_exception: BaseException = real_exception -class ErrorWithFeedback(OutcomeError): +class ErrorWithFeedback(OutcomeError): # noqa: N818 def __init__(self, error_text: str) -> None: self.error_text = error_text @@ -38,10 +38,10 @@ def __init__(self, error_text: str) -> None: self.error_text = error_text -class TestPassed(OutcomeError): +class TestPassed(OutcomeError): # noqa: N818 pass -class WrongAnswer(OutcomeError): +class WrongAnswer(OutcomeError): # noqa: N818 def __init__(self, feedback: str) -> None: self.feedback = feedback diff --git a/hstest/outcomes/__init__.py b/hstest/outcomes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/outcomes/error_outcome.py b/hstest/outcomes/error_outcome.py index fdf3d78e..f182e90a 100644 --- a/hstest/outcomes/error_outcome.py +++ b/hstest/outcomes/error_outcome.py @@ -33,20 +33,20 @@ def __init__(self, test_num: int, cause: BaseException) -> None: def _init_permission_error(self, _: FileDeletionError) -> None: self.error_text = ( "The file you opened " - + "can't be deleted after the end of the test. " - + "Probably you didn't close it." + "can't be deleted after the end of the test. " + "Probably you didn't close it." ) def _init_time_limit_exception(self, ex: TimeLimitException) -> None: time_limit: int = ex.time_limit_ms time_unit: str = "milliseconds" - if time_limit > 1999: + if time_limit > 1999: # noqa: PLR2004 time_limit //= 1000 time_unit = "seconds" self.error_text = ( "In this test, the program is running for a long time, " - + f"more than {time_limit} {time_unit}. Most likely, " - + "the program has gone into an infinite loop." + f"more than {time_limit} {time_unit}. Most likely, " + "the program has gone into an infinite loop." ) def get_type(self) -> str: diff --git a/hstest/outcomes/outcome.py b/hstest/outcomes/outcome.py index 5a3a9a16..0460e1ed 100644 --- a/hstest/outcomes/outcome.py +++ b/hstest/outcomes/outcome.py @@ -76,7 +76,7 @@ def __str__(self) -> str: return result.strip() @staticmethod - def __get_args(): + def __get_args() -> str: arguments = "" from hstest.stage_test import StageTest @@ -99,7 +99,7 @@ def __get_args(): return arguments @staticmethod - def __trim_lines(full_out): + def __trim_lines(full_out: str) -> str: result = "" max_lines_in_output = 250 @@ -119,7 +119,7 @@ def __trim_lines(full_out): return result.strip() @staticmethod - def get_outcome(ex: BaseException, curr_test: int): + def get_outcome(ex: BaseException, curr_test: int) -> Outcome: from hstest.outcomes.compilation_error_outcome import CompilationErrorOutcome from hstest.outcomes.error_outcome import ErrorOutcome from hstest.outcomes.exception_outcome import ExceptionOutcome @@ -129,13 +129,13 @@ def get_outcome(ex: BaseException, curr_test: int): if isinstance(ex, WrongAnswer): return WrongAnswerOutcome(curr_test, ex) - elif isinstance(ex, ExceptionWithFeedback): + if isinstance(ex, ExceptionWithFeedback): return ExceptionOutcome(curr_test, ex) - elif isinstance(ex, CompilationError): + if isinstance(ex, CompilationError): return CompilationErrorOutcome(ex) - elif isinstance( + if isinstance( ex, ErrorWithFeedback | FileDeletionError @@ -145,5 +145,4 @@ def get_outcome(ex: BaseException, curr_test: int): ): return ErrorOutcome(curr_test, ex) - else: - return UnexpectedErrorOutcome(curr_test, ex) + return UnexpectedErrorOutcome(curr_test, ex) diff --git a/hstest/stage/django_test.py b/hstest/stage/django_test.py index 82f460a0..445eeefb 100644 --- a/hstest/stage/django_test.py +++ b/hstest/stage/django_test.py @@ -17,7 +17,7 @@ class DjangoTest(StageTest): test_database: str = attach.test_database use_database: bool = attach.use_database - def __init__(self, args="", *, source: str = "") -> None: + def __init__(self, args: str = "", *, source: str = "") -> None: super().__init__(args, source=source) self.attach.use_database = self.use_database loop_detector.working = False @@ -25,7 +25,7 @@ def __init__(self, args="", *, source: str = "") -> None: def read_page(self, link: str) -> str: """Deprecated, use get(...) instead.""" - return clean_text(urlopen(link).read().decode()) + return clean_text(urlopen(link).read().decode()) # noqa: S310 def get_url(self, link: str = "") -> str: if link.startswith("/"): @@ -35,4 +35,4 @@ def get_url(self, link: str = "") -> str: def get(self, link: str) -> str: if not link.startswith("http://"): link = self.get_url(link) - return clean_text(urlopen(link).read().decode()) + return clean_text(urlopen(link).read().decode()) # noqa: S310 diff --git a/hstest/stage/flask_test.py b/hstest/stage/flask_test.py index 6684d25d..12865eef 100644 --- a/hstest/stage/flask_test.py +++ b/hstest/stage/flask_test.py @@ -15,7 +15,7 @@ class FlaskTest(StageTest): runner = FlaskApplicationRunner() attach: FlaskSettings = FlaskSettings() - def __init__(self, args="", *, source: str = "") -> None: + def __init__(self, args: str = "", *, source: str = "") -> None: super().__init__(args, source=source) loop_detector.working = False Settings.do_reset_output = False @@ -23,19 +23,19 @@ def __init__(self, args="", *, source: str = "") -> None: if self.source_name: sources = self.source_name - if type(sources) != list: + if not isinstance(sources, str): sources = [sources] for item in sources: - if type(item) == str: + if isinstance(item, str): self.attach.sources += [(item, None)] - elif type(item) == tuple: + elif isinstance(item, tuple): if len(item) == 1: self.attach.sources += [(item[0], None)] else: self.attach.sources += [item] - def get_url(self, link: str = "", *, source: str | None = None): + def get_url(self, link: str = "", *, source: str | None = None) -> str: if link.startswith("/"): link = link[1:] @@ -44,7 +44,7 @@ def create_url(port: int) -> str: if len(self.attach.sources) == 1: return create_url(self.attach.sources[0][1]) - elif len(self.attach.sources) == 0: + if len(self.attach.sources) == 0: msg = "Cannot find sources" raise UnexpectedError(msg) @@ -52,7 +52,7 @@ def create_url(port: int) -> str: if len(sources_fits) == 0: msg = f"Bad source: {source}" raise UnexpectedError(msg) - elif len(sources_fits) > 1: + if len(sources_fits) > 1: msg = f"Multiple sources ({len(sources_fits)}) found: {source}" raise UnexpectedError(msg) @@ -62,4 +62,4 @@ def get(self, link: str, *, source: str | None = None) -> str: if not link.startswith("http://"): link = self.get_url(link, source=source) - return clean_text(urlopen(link).read().decode()) + return clean_text(urlopen(link).read().decode()) # noqa: S310 diff --git a/hstest/stage/plotting_test.py b/hstest/stage/plotting_test.py index fcc1e2f2..f472f975 100644 --- a/hstest/stage/plotting_test.py +++ b/hstest/stage/plotting_test.py @@ -10,7 +10,7 @@ class PlottingTest(StageTest): - def __init__(self, args="", *, source: str = "") -> None: + def __init__(self, args: str = "", *, source: str = "") -> None: super().__init__(args, source=source) self._all_drawings: list[Drawing] = [] self._new_drawings: list[Drawing] = [] diff --git a/hstest/stage/sql_test.py b/hstest/stage/sql_test.py index 615f486e..c077c4a7 100644 --- a/hstest/stage/sql_test.py +++ b/hstest/stage/sql_test.py @@ -1,19 +1,21 @@ from __future__ import annotations +from typing import Any, ClassVar + from hstest.exception.outcomes import WrongAnswer from hstest.stage.stage_test import StageTest from hstest.testing.runner.sql_runner import SQLRunner class SQLTest(StageTest): - queries: dict[str, str] = {} - db: any = None + queries: ClassVar[dict[str, str]] = {} + db: Any = None def __init__(self, source: str = "") -> None: super().__init__(source) self.runner = SQLRunner(self) - def execute(self, query_name: str): + def execute(self, query_name: str) -> Any: cursor = self.db.cursor() if query_name not in self.queries: @@ -23,7 +25,7 @@ def execute(self, query_name: str): return cursor.execute(self.queries[query_name]) except Exception as ex: msg = f"Error while running '{query_name}': \n\n{ex}" - raise WrongAnswer(msg) + raise WrongAnswer(msg) from ex - def execute_and_fetch_all(self, query_name: str): + def execute_and_fetch_all(self, query_name: str) -> list[tuple]: return self.execute(query_name).fetchall() diff --git a/hstest/stage/stage_test.py b/hstest/stage/stage_test.py index a9e07e1b..7f4fd7d8 100644 --- a/hstest/stage/stage_test.py +++ b/hstest/stage/stage_test.py @@ -1,9 +1,9 @@ from __future__ import annotations import contextlib -import os import unittest -from typing import Any, TYPE_CHECKING +from pathlib import Path +from typing import Any, ClassVar, TYPE_CHECKING from hstest.common.file_utils import walk_user_files from hstest.common.reflection_utils import is_tests, setup_cwd @@ -32,24 +32,24 @@ class DirMeta(type): - def __dir__(self): + def __dir__(cls) -> list[str]: from hstest.testing.unittest.expected_fail_test import ExpectedFailTest from hstest.testing.unittest.unexepected_error_test import UnexpectedErrorTest from hstest.testing.unittest.user_error_test import UserErrorTest if ( - not issubclass(self, StageTest) - or self == StageTest - or self in {ExpectedFailTest, UserErrorTest, UnexpectedErrorTest} + not issubclass(cls, StageTest) + or cls == StageTest + or cls in {ExpectedFailTest, UserErrorTest, UnexpectedErrorTest} ): return [] - init_dir = dir(super()) + list(self.__dict__.keys()) + init_dir = dir(super()) + list(cls.__dict__.keys()) filtered_dir = list(filter(lambda x: not str(x).startswith("test"), init_dir)) filtered_dir.append("test_run_unittest") if ( - not self.dynamic_methods() + not cls.dynamic_methods() and "generate" not in init_dir - and not issubclass(self, ExpectedFailTest) + and not issubclass(cls, ExpectedFailTest) ): return [] return set(filtered_dir) @@ -62,7 +62,7 @@ class StageTest(unittest.TestCase, metaclass=DirMeta): curr_test_run: TestRun | None = None curr_test_global: int = 0 - def __init__(self, args="", *, source: str = "") -> None: + def __init__(self, args: str = "", *, source: str = "") -> None: super().__init__("test_run_unittest") self.is_tests = False @@ -80,7 +80,7 @@ def after_all_tests(self) -> None: pass def _init_runner(self) -> TestRunner: - for _folder, _dirs, files in walk_user_files(os.getcwd()): + for _folder, _dirs, files in walk_user_files(Path.cwd()): for f in files: if f.endswith(".cpp"): return AsyncDynamicTestingRunner(CppExecutor) @@ -96,8 +96,7 @@ def _init_runner(self) -> TestRunner: if f.endswith(".py"): if force_process_testing: return AsyncDynamicTestingRunner(PythonExecutor) - else: - return AsyncDynamicTestingRunner(MainModuleExecutor) + return AsyncDynamicTestingRunner(MainModuleExecutor) return AsyncDynamicTestingRunner(MainModuleExecutor) @@ -113,15 +112,13 @@ def _init_tests(self) -> list[TestRun]: msg = "No tests found" raise UnexpectedError(msg) - curr_test: int = 0 test_count = len(test_cases) - for test_case in test_cases: + for curr_test, test_case in enumerate(test_cases, start=1): test_case.source_name = self.source_name if test_case.check_func is None: test_case.check_func = self.check if test_case.attach is None: test_case.attach = self.attach - curr_test += 1 test_runs += [TestRun(curr_test, test_count, test_case, self.runner)] return test_runs @@ -132,7 +129,7 @@ def __print_test_num(self, num: int) -> None: RED_BOLD + f"\nStart test {num}{total_tests}" + RESET + "\n" ) - def run_tests(self, *, debug=False, is_unittest: bool = False) -> tuple[int, str]: + def run_tests(self, *, debug: bool = False, is_unittest: bool = False) -> tuple[int, str]: curr_test: int = 0 need_tear_down: bool = False try: @@ -165,20 +162,20 @@ def run_tests(self, *, debug=False, is_unittest: bool = False) -> tuple[int, str if not result.is_correct: full_feedback = result.feedback + "\n\n" + test_run.test_case.feedback - raise WrongAnswer(full_feedback.strip()) + raise WrongAnswer(full_feedback.strip()) # noqa: TRY301 if test_run.is_last_test(): need_tear_down = False test_run.tear_down() SystemHandler.tear_down() - return passed(is_unittest) + return passed(is_unittest=is_unittest) - except BaseException: + except BaseException: # noqa: BLE001 if need_tear_down: try: StageTest.curr_test_run.tear_down() - except BaseException as new_ex: + except BaseException as new_ex: # noqa: BLE001 if isinstance(new_ex, OutcomeError): ex = new_ex @@ -186,27 +183,27 @@ def run_tests(self, *, debug=False, is_unittest: bool = False) -> tuple[int, str try: report = build + "\n\n" + get_report() - except Exception: + except Exception: # noqa: BLE001 report = build try: outcome: Outcome = Outcome.get_outcome(ex, curr_test) fail_text = str(outcome) - except BaseException as new_ex: + except BaseException as new_ex: # noqa: BLE001 try: outcome: Outcome = Outcome.get_outcome(new_ex, curr_test) fail_text = str(outcome) - except BaseException as new_ex2: + except BaseException as new_ex2: # noqa: BLE001 try: traceback = "" for e in new_ex2, new_ex, ex: try: text = get_exception_text(e) - except Exception: + except Exception: # noqa: BLE001 try: text = f"{type(e)}: {e!s}" - except Exception: + except Exception: # noqa: BLE001 text = "Broken exception" if len(text): @@ -214,14 +211,14 @@ def run_tests(self, *, debug=False, is_unittest: bool = False) -> tuple[int, str fail_text = "Unexpected error\n\n" + report + "\n\n" + traceback - except BaseException: + except BaseException: # noqa: BLE001 # no code execution here allowed so not to throw an exception fail_text = "Unexpected error\n\nCannot check the submission\n\n" + report with contextlib.suppress(BaseException): SystemHandler.tear_down() - return failed(fail_text, is_unittest) + return failed(fail_text, is_unittest=is_unittest) finally: StageTest.curr_test_run = None @@ -230,7 +227,7 @@ def run_tests(self, *, debug=False, is_unittest: bool = False) -> tuple[int, str StageTest.source = None self.after_all_tests() - _dynamic_methods: dict[type[StageTest], list[DynamicTestElement]] = {} + _dynamic_methods: ClassVar[dict[type[StageTest], list[DynamicTestElement]]] = {} @classmethod def dynamic_methods(cls) -> list[DynamicTestElement]: diff --git a/hstest/test_case/attach/__init__.py b/hstest/test_case/attach/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/test_case/attach/django_settings.py b/hstest/test_case/attach/django_settings.py index ad2bf579..bd8e0c42 100644 --- a/hstest/test_case/attach/django_settings.py +++ b/hstest/test_case/attach/django_settings.py @@ -1,8 +1,10 @@ from __future__ import annotations +from typing import ClassVar + class DjangoSettings: port: int = None use_database: bool = False test_database: str = "db.test.sqlite3" - tryout_ports: list[int] = list(range(8000, 8101)) + tryout_ports: ClassVar[list[int]] = list(range(8000, 8101)) diff --git a/hstest/test_case/attach/flask_settings.py b/hstest/test_case/attach/flask_settings.py index 97aa4324..eb794728 100644 --- a/hstest/test_case/attach/flask_settings.py +++ b/hstest/test_case/attach/flask_settings.py @@ -1,6 +1,8 @@ from __future__ import annotations +from typing import ClassVar + class FlaskSettings: - sources: list[tuple[str, int]] = [] - tryout_ports: list[int] = list(range(8000, 8101)) + sources: ClassVar[list[tuple[str, int]]] = [] + tryout_ports: ClassVar[list[int]] = list(range(8000, 8101)) diff --git a/hstest/test_case/check_result.py b/hstest/test_case/check_result.py index e5da53c5..f3fe0c24 100644 --- a/hstest/test_case/check_result.py +++ b/hstest/test_case/check_result.py @@ -4,7 +4,7 @@ class CheckResult: - def __init__(self, result: bool, feedback: str) -> None: + def __init__(self, result: bool, feedback: str) -> None: # noqa: FBT001 self._result: bool = result self._feedback: str = feedback @@ -18,20 +18,19 @@ def feedback(self) -> str: @staticmethod def correct() -> CheckResult: - return CheckResult(True, "") + return CheckResult(result=True, feedback="") @staticmethod def wrong(feedback: str) -> CheckResult: - return CheckResult(False, feedback) + return CheckResult(result=False, feedback=feedback) @staticmethod def from_error(error: BaseException) -> CheckResult | None: if isinstance(error, TestPassed): return correct() - elif isinstance(error, WrongAnswer): + if isinstance(error, WrongAnswer): return wrong(error.feedback) - else: - return None + return None def correct() -> CheckResult: diff --git a/hstest/test_case/test_case.py b/hstest/test_case/test_case.py index 8a10e852..1451e70b 100644 --- a/hstest/test_case/test_case.py +++ b/hstest/test_case/test_case.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, TYPE_CHECKING, Union +from typing import Any, TYPE_CHECKING from hstest.dynamic.input.dynamic_input_func import DynamicInputFunction from hstest.dynamic.input.dynamic_testing import to_dynamic_testing @@ -15,15 +15,15 @@ SimpleStepikTest = str AdvancedStepikTest = tuple[str, Any] - StepikTest = Union[SimpleStepikTest, AdvancedStepikTest] + StepikTest = SimpleStepikTest | AdvancedStepikTest CheckFunction = Callable[[str, Any], CheckResult] PredefinedInput = str - RuntimeEvaluatedInput = Union[ - PredefinedInput, InputFunction, tuple[int, InputFunction], DynamicInputFunction + RuntimeEvaluatedInput = [ + PredefinedInput | InputFunction | tuple[int, InputFunction] | DynamicInputFunction ] - DynamicInput = Union[PredefinedInput, list[RuntimeEvaluatedInput]] + DynamicInput = PredefinedInput | list[RuntimeEvaluatedInput] DEFAULT_TIME_LIMIT: int = 15000 @@ -66,7 +66,7 @@ def __init__( if attach is not None: msg = "Attach is not None " "but copying from stdin is specified" raise UnexpectedError(msg) - if type(stdin) != str: + if not isinstance(stdin, str): msg = ( "To copy stdin to attach stdin should be of type str " f"but found type {type(stdin)}" @@ -74,29 +74,29 @@ def __init__( raise UnexpectedError(msg) self.attach = stdin - if type(stdin) == str: + if isinstance(stdin, str): self.input = stdin - self.input_funcs = [DynamicInputFunction(1, lambda x: stdin)] + self.input_funcs = [DynamicInputFunction(1, lambda _: stdin)] else: - if type(stdin) != list: + if not isinstance(stdin, list): msg = "Stdin should be either of type str or list " f"but found type {type(stdin)}" raise UnexpectedError(msg) - for elem in stdin: # type: RuntimeEvaluatedInput - if type(elem) == DynamicInputFunction: + for elem in stdin: + if isinstance(elem, DynamicInputFunction): self.input_funcs += [elem] - elif type(elem) == str: - self.input_funcs += [DynamicInputFunction(1, lambda x, inp=elem: inp)] + elif isinstance(elem, str): + self.input_funcs += [DynamicInputFunction(1, lambda _, inp=elem: inp)] elif str(type(elem)) in {"", ""}: self.input_funcs += [DynamicInputFunction(1, elem)] elif type(elem) in {tuple, list}: - if len(elem) == 2: + if len(elem) == 2: # noqa: PLR2004 trigger_count: int = elem[0] input_function: InputFunction = elem[1] - if type(trigger_count) != int: + if not isinstance(trigger_count, int): msg = ( f"Stdin element's 1st element should be of type int, " f"found {type(trigger_count)}" @@ -152,6 +152,6 @@ def __init__(self, *, stdin: str, stdout: str, feedback: str, **kwargs) -> None: super().__init__(stdin=stdin, attach=stdout, feedback=feedback, **kwargs) self.check_func = self._custom_check - def _custom_check(self, reply: str, expected: str): + def _custom_check(self, reply: str, expected: str) -> CheckResult: is_correct = reply.strip() == expected.strip() return CheckResult(is_correct, "") diff --git a/hstest/testing/execution/__init__.py b/hstest/testing/execution/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/testing/execution/filtering/__init__.py b/hstest/testing/execution/filtering/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/testing/execution/filtering/file_filter.py b/hstest/testing/execution/filtering/file_filter.py index e002b364..2d8280e4 100644 --- a/hstest/testing/execution/filtering/file_filter.py +++ b/hstest/testing/execution/filtering/file_filter.py @@ -32,7 +32,7 @@ def __init__( self.filtered: set[File] = set() @staticmethod - def regex_filter(regex: str): + def regex_filter(regex: str) -> Filter: return lambda s: re.compile(regex, re.MULTILINE).search(s) is not None def init_filter(self, folder: Folder, sources: Sources) -> None: diff --git a/hstest/testing/execution/main_module_executor.py b/hstest/testing/execution/main_module_executor.py index 363c668f..f18566ed 100644 --- a/hstest/testing/execution/main_module_executor.py +++ b/hstest/testing/execution/main_module_executor.py @@ -3,6 +3,7 @@ import os import runpy import sys +from pathlib import Path from typing import TYPE_CHECKING from hstest.common.process_utils import DaemonThreadPoolExecutor @@ -26,7 +27,7 @@ def __init__(self, source_name: str | None = None) -> None: self.__executor: DaemonThreadPoolExecutor | None = None self.__task: Future | None = None self.__group = None - self.working_directory_before = os.path.abspath(os.getcwd()) + self.working_directory_before = Path.cwd().resolve() def _invoke_method(self, *args: str) -> None: from hstest.stage_test import StageTest @@ -41,7 +42,7 @@ def _invoke_method(self, *args: str) -> None: self._machine.set_state(ProgramState.FINISHED) - except BaseException as ex: + except BaseException as ex: # noqa: BLE001 if StageTest.curr_test_run.error_in_test is None: # ExitException is thrown in case of exit() or quit() # consider them like normal exit diff --git a/hstest/testing/execution/process/__init__.py b/hstest/testing/execution/process/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/testing/execution/process/cpp_executor.py b/hstest/testing/execution/process/cpp_executor.py index 9e4a3ac7..4b74146a 100644 --- a/hstest/testing/execution/process/cpp_executor.py +++ b/hstest/testing/execution/process/cpp_executor.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +from pathlib import Path from hstest.common.os_utils import is_windows from hstest.testing.execution.process_executor import ProcessExecutor @@ -11,16 +12,16 @@ class CppExecutor(ProcessExecutor): def __init__(self, source_name: str | None = None) -> None: super().__init__(CppSearcher().find(source_name)) - self.without_extension = os.path.splitext(self.runnable.file)[0] + self.without_extension = os.path.splitext(self.runnable.file)[0] # noqa: PTH122 if is_windows(): self.executable = self.without_extension - self.file_name = self.executable + ".exe" + self.file_name = Path(self.executable + ".exe") else: self.executable = f"./{self.without_extension}" - self.file_name = self.without_extension + self.file_name = Path(self.without_extension) - def _compilation_command(self): + def _compilation_command(self) -> list[str]: return [ "g++", "-std=c++20", @@ -35,9 +36,8 @@ def _compilation_command(self): def _filter_compilation_error(self, error: str) -> str: return error - def _execution_command(self, *args: str): + def _execution_command(self, *args: str) -> list[str]: return [self.executable, *list(args)] def _cleanup(self) -> None: - if os.path.exists(self.file_name): - os.remove(self.file_name) + self.file_name.unlink(missing_ok=True) diff --git a/hstest/testing/execution/process/go_executor.py b/hstest/testing/execution/process/go_executor.py index e5d202dc..d1c0befc 100644 --- a/hstest/testing/execution/process/go_executor.py +++ b/hstest/testing/execution/process/go_executor.py @@ -1,6 +1,6 @@ from __future__ import annotations -import os +from pathlib import Path from hstest.common.os_utils import is_windows from hstest.testing.execution.process_executor import ProcessExecutor @@ -15,21 +15,20 @@ def __init__(self, source_name: str | None = None) -> None: if is_windows(): self.executable = self.without_go - self.file_name = self.executable + ".exe" + self.file_name = Path(self.executable + ".exe") else: self.executable = f"./{self.without_go}" - self.file_name = self.without_go + self.file_name = Path(self.without_go) - def _compilation_command(self): + def _compilation_command(self) -> list[str]: return ["go", "build", self.runnable.file] def _filter_compilation_error(self, error: str) -> str: error_lines = [line for line in error.splitlines() if not line.startswith("#")] return "\n".join(error_lines) - def _execution_command(self, *args: str): + def _execution_command(self, *args: str) -> list[str]: return [self.executable, *list(args)] def _cleanup(self) -> None: - if os.path.exists(self.file_name): - os.remove(self.file_name) + self.file_name.unlink(missing_ok=True) diff --git a/hstest/testing/execution/process/javascript_executor.py b/hstest/testing/execution/process/javascript_executor.py index c90d3258..5efa9940 100644 --- a/hstest/testing/execution/process/javascript_executor.py +++ b/hstest/testing/execution/process/javascript_executor.py @@ -8,5 +8,5 @@ class JavascriptExecutor(ProcessExecutor): def __init__(self, source_name: str | None = None) -> None: super().__init__(JavascriptSearcher().find(source_name)) - def _execution_command(self, *args: str): + def _execution_command(self, *args: str) -> list[str]: return ["node", self.runnable.file, *list(args)] diff --git a/hstest/testing/execution/process/python_executor.py b/hstest/testing/execution/process/python_executor.py index b121b145..f504a3cb 100644 --- a/hstest/testing/execution/process/python_executor.py +++ b/hstest/testing/execution/process/python_executor.py @@ -8,5 +8,5 @@ class PythonExecutor(ProcessExecutor): def __init__(self, source_name: str | None = None) -> None: super().__init__(PythonSearcher().find(source_name)) - def _execution_command(self, *args: str): + def _execution_command(self, *args: str) -> list[str]: return ["python", "-u", self.runnable.file, *list(args)] diff --git a/hstest/testing/execution/process/shell_executor.py b/hstest/testing/execution/process/shell_executor.py index 2868866b..724a1062 100644 --- a/hstest/testing/execution/process/shell_executor.py +++ b/hstest/testing/execution/process/shell_executor.py @@ -8,5 +8,5 @@ class ShellExecutor(ProcessExecutor): def __init__(self, source_name: str | None = None) -> None: super().__init__(ShellSearcher().find(source_name)) - def _execution_command(self, *args: str): + def _execution_command(self, *args: str) -> list[str]: return ["bash", self.runnable.file, *list(args)] diff --git a/hstest/testing/execution/process_executor.py b/hstest/testing/execution/process_executor.py index 62391259..834028f6 100644 --- a/hstest/testing/execution/process_executor.py +++ b/hstest/testing/execution/process_executor.py @@ -2,6 +2,7 @@ import contextlib import os +from pathlib import Path from threading import Thread from time import sleep from typing import TYPE_CHECKING @@ -30,7 +31,7 @@ def __init__(self, runnable: RunnableFile) -> None: self.continue_executing = True self.runnable: RunnableFile = runnable self.__group: ThreadGroup | None = None - self.working_directory_before = os.path.abspath(os.getcwd()) + self.working_directory_before = Path.cwd().resolve() def _compilation_command(self, *args: str) -> list[str]: return [] @@ -112,15 +113,16 @@ def __handle_process(self, *args: str) -> None: OutputHandler.print(f"Handle process - written to stdin: {next_input!r}") except ExitException: OutputHandler.print("Handle process - EXIT EXCEPTION, stop input") - if self._wait_if_terminated(): - if type(StageTest.curr_test_run.error_in_test) == OutOfInputError: - StageTest.curr_test_run.set_error_in_test(None) - OutputHandler.print( - "Handle process - Abort stopping input, everything is OK" - ) - break + if self._wait_if_terminated() and isinstance( + StageTest.curr_test_run.error_in_test, OutOfInputError + ): + StageTest.curr_test_run.set_error_in_test(None) + OutputHandler.print( + "Handle process - Abort stopping input, everything is OK" + ) + break self.stop_input() - except BaseException as ex: + except BaseException as ex: # noqa: BLE001 OutputHandler.print(f"Handle process - SOME EXCEPTION {ex}") OutputHandler.print("Handle process - TERMINATE") @@ -147,8 +149,8 @@ def __handle_process(self, *args: str) -> None: OutputHandler.print("Handle process - finishing execution") - def _wait_if_terminated(self): - return try_many_times(100, 10, lambda: self.process.is_finished(False)) + def _wait_if_terminated(self) -> bool: + return try_many_times(100, 10, lambda: self.process.is_finished(need_wait_output=False)) def _launch(self, *args: str) -> None: self.__group = ThreadGroup() @@ -175,7 +177,7 @@ def _terminate(self) -> None: sleep(0.001) def tear_down(self) -> None: - working_directory_before = os.path.abspath(os.getcwd()) + working_directory_before = Path.cwd().resolve() os.chdir(self.runnable.folder) with contextlib.suppress(BaseException): diff --git a/hstest/testing/execution/program_executor.py b/hstest/testing/execution/program_executor.py index 2ad71f20..c127de14 100644 --- a/hstest/testing/execution/program_executor.py +++ b/hstest/testing/execution/program_executor.py @@ -70,7 +70,7 @@ def execute(self, stdin: str) -> str: StageTest.curr_test_run.set_error_in_test( ErrorWithFeedback( f"The program {self} has unexpectedly terminated.\n" - + "It finished execution too early, should continue running." + "It finished execution too early, should continue running." ) ) raise TestedProgramFinishedEarly @@ -80,10 +80,11 @@ def execute(self, stdin: str) -> str: return "" if not self.is_waiting_input(): - raise UnexpectedError( + msg = ( f"Program {self} is not waiting for the input " - + f'(state == "{self._machine.state}")' + f'(state == "{self._machine.state}")' ) + raise UnexpectedError(msg) if self.__no_more_input: msg = f"Can't pass input to the program {self} - input was prohibited." @@ -121,7 +122,7 @@ def request_input(self) -> str | None: self._input = None return input_local - def set_return_output_after_execution(self, value: bool) -> None: + def set_return_output_after_execution(self, *, value: bool) -> None: self.__return_output_after_execution = value def is_finished(self) -> bool: @@ -152,7 +153,7 @@ def stop_background(self) -> None: self.__in_background = False self._machine.wait_state(ProgramState.WAITING) - def is_in_background(self): + def is_in_background(self) -> bool: return self.__in_background def tear_down(self) -> None: diff --git a/hstest/testing/execution/runnable/__init__.py b/hstest/testing/execution/runnable/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/testing/execution/runnable/runnable_file.py b/hstest/testing/execution/runnable/runnable_file.py index 7f329645..a246eaa3 100644 --- a/hstest/testing/execution/runnable/runnable_file.py +++ b/hstest/testing/execution/runnable/runnable_file.py @@ -1,16 +1,17 @@ from __future__ import annotations -from pathlib import Path from typing import TYPE_CHECKING if TYPE_CHECKING: - from hstest.testing.execution.filtering.file_filter import File, Folder + from pathlib import Path + + from hstest.testing.execution.filtering.file_filter import File class RunnableFile: - def __init__(self, folder: Folder, file: File) -> None: + def __init__(self, folder: Path, file: File) -> None: self.folder = folder self.file = file def path(self) -> Path: - return Path(self.folder) / self.file + return self.folder / self.file diff --git a/hstest/testing/execution/searcher/__init__.py b/hstest/testing/execution/searcher/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/testing/execution/searcher/base_searcher.py b/hstest/testing/execution/searcher/base_searcher.py index 7e78ac30..71c44a86 100644 --- a/hstest/testing/execution/searcher/base_searcher.py +++ b/hstest/testing/execution/searcher/base_searcher.py @@ -2,10 +2,11 @@ import os import re +from pathlib import Path from hstest.common.file_utils import walk_user_files from hstest.exception.outcomes import ErrorWithFeedback, UnexpectedError -from hstest.testing.execution.filtering.file_filter import File, FileFilter, Folder, Module, Source +from hstest.testing.execution.filtering.file_filter import File, FileFilter, Module, Source from hstest.testing.execution.filtering.main_filter import MainFilter from hstest.testing.execution.runnable.runnable_file import RunnableFile @@ -19,33 +20,32 @@ def extension(self) -> str: msg = 'Property "extension" should be implemented' raise NotImplementedError(msg) - def search(self, where_to_search: str | None = None) -> RunnableFile: + def search(self, where: Path | None = None) -> RunnableFile: msg = 'Method "search" should be implemented' raise NotImplementedError(msg) @staticmethod - def _get_contents(folder: Folder, files: list[File]) -> dict[File, Source]: + def _get_contents(folder: Path, files: list[Path]) -> dict[Path, Source]: contents = {} for file in files: - path = os.path.abspath(os.path.join(folder, file)) + path = (folder / file).resolve() if path in file_contents_cached: contents[file] = file_contents_cached[path] - elif os.path.exists(path): - with open(path, encoding="locale") as f: - try: - file_content = f.read() - except UnicodeDecodeError: - # binary bile, no need to process - continue - contents[file] = file_content - file_contents_cached[path] = contents[file] + elif path.exists(): + try: + file_content = Path(path).read_text(encoding="locale") + except UnicodeDecodeError: + # binary bile, no need to process + continue + contents[file] = file_content + file_contents_cached[path] = contents[file] return contents def _search_non_cached( self, - where_to_search: str, + where: Path, *, file_filter: FileFilter, pre_main_filter: FileFilter, @@ -56,7 +56,7 @@ def _search_non_cached( if not force_content_filters: force_content_filters = [] - curr_folder = os.path.abspath(where_to_search) + curr_folder = where.resolve() for folder, _dirs, files in walk_user_files(curr_folder): contents = self._get_contents(folder, files) @@ -81,9 +81,8 @@ def _search_non_cached( if len(filtered_files) == 0: if curr_filter == initial_filter: break - else: - continue - elif curr_filter == initial_filter: + continue + if curr_filter == initial_filter: for forced_filter in force_content_filters: filtered_files = { file @@ -97,7 +96,10 @@ def _search_non_cached( for forced_filter in force_content_filters if isinstance(forced_filter, MainFilter) ] - msg = f"The runnable file should contain all the following lines: {should_contain}" + msg = ( + f"The runnable file should contain all the following lines: " + f"{should_contain}" + ) raise ErrorWithFeedback(msg) if len(filtered_files) == 1: @@ -154,7 +156,7 @@ def _search_non_cached( def _search( self, - where_to_search: str | None = None, + where: Path | None = None, *, file_filter: FileFilter = None, pre_main_filter: FileFilter = None, @@ -166,11 +168,11 @@ def _search( msg = f'File extension "{self.extension}" should start with a dot' raise UnexpectedError(msg) - if where_to_search is None: - where_to_search = os.getcwd() + if where is None: + where = Path.cwd() do_caching = False - cache_key = self.extension, where_to_search + cache_key = self.extension, where if file_filter is None: if cache_key in search_cached: @@ -189,7 +191,7 @@ def _search( post_main_filter = FileFilter() result = self._search_non_cached( - where_to_search, + where, file_filter=file_filter, pre_main_filter=pre_main_filter, main_filter=main_filter, @@ -204,20 +206,20 @@ def _search( def _simple_search( self, - where_to_search: str, + where: Path, main_desc: str, main_regex: str, force_content_filters: list[MainFilter] | None = None, ) -> RunnableFile: main_searcher = re.compile(main_regex, re.MULTILINE) return self._search( - where_to_search, + where, main_filter=MainFilter(main_desc, source=lambda s: main_searcher.search(s) is not None), force_content_filters=force_content_filters, ) - def _base_search(self, where_to_search: str) -> RunnableFile: - return self._simple_search(where_to_search, main_desc="", main_regex="") + def _base_search(self, where: Path) -> RunnableFile: + return self._simple_search(where, main_desc="", main_regex="") def find(self, source: str | None) -> RunnableFile: if source in {None, ""}: @@ -227,43 +229,42 @@ def find(self, source: str | None) -> RunnableFile: source_folder, source_file, source_module = self._parse_source(source) - if source_folder is not None and os.path.isdir(source_folder): + if source_folder is not None and source_folder.is_dir(): return self.search(source_folder) - elif source_file is not None and os.path.isfile(source_file): + if source_file is not None and source_file.is_file(): path, _sep, file = source_module.rpartition(".") - folder = os.path.abspath(path.replace(".", os.sep)) + folder = Path(path.replace(".", os.sep)).resolve() return RunnableFile(folder, file + ext) - else: - path, _, _ = source_module.rpartition(".") - folder = os.path.abspath(path.replace(".", os.sep)) - msg = ( - "Cannot find a file to execute your code.\n" - f'Are your project files located at "{folder}"?' - ) - raise ErrorWithFeedback(msg) + path, _, _ = source_module.rpartition(".") + folder = Path(path.replace(".", os.sep)).resolve() + msg = ( + "Cannot find a file to execute your code.\n" + f'Are your project files located at "{folder}"?' + ) + raise ErrorWithFeedback(msg) - def _parse_source(self, source: str) -> tuple[Folder, File, Module]: + def _parse_source(self, source: str) -> tuple[Path, Path, Module]: ext = self.extension - source = source.replace("/", os.sep).replace("\\", os.sep) + source = Path(source.replace("/", os.sep).replace("\\", os.sep)) - if source.endswith(ext): + if source.name.endswith(ext): source_folder = None source_file = source source_module = source[: -len(ext)].replace(os.sep, ".") elif os.sep in source: - if source.endswith(os.sep): + if source.name.endswith(os.sep): source = source[: -len(os.sep)] source_folder = source source_file = None - source_module = source.replace(os.sep, ".") + source_module = source.name.replace(os.sep, ".") else: - source_folder = source.replace(".", os.sep) + source_folder = source.name.replace(".", os.sep) source_file = source_folder + ext source_module = source diff --git a/hstest/testing/execution/searcher/cpp_searcher.py b/hstest/testing/execution/searcher/cpp_searcher.py index c0173070..e730104e 100644 --- a/hstest/testing/execution/searcher/cpp_searcher.py +++ b/hstest/testing/execution/searcher/cpp_searcher.py @@ -7,6 +7,8 @@ from hstest.testing.execution.searcher.base_searcher import BaseSearcher if TYPE_CHECKING: + from pathlib import Path + from hstest.testing.execution.runnable.runnable_file import RunnableFile @@ -15,7 +17,7 @@ class CppSearcher(BaseSearcher): def extension(self) -> str: return ".cpp" - def search(self, where: str | None = None) -> RunnableFile: + def search(self, where: Path | None = None) -> RunnableFile: main_func_searcher = re.compile(r"(^|\n)\s*int\s+main\s*\(.*\)", re.MULTILINE) return self._search( diff --git a/hstest/testing/execution/searcher/go_searcher.py b/hstest/testing/execution/searcher/go_searcher.py index 838dfc5d..ae605fab 100644 --- a/hstest/testing/execution/searcher/go_searcher.py +++ b/hstest/testing/execution/searcher/go_searcher.py @@ -7,6 +7,8 @@ from hstest.testing.execution.searcher.base_searcher import BaseSearcher if TYPE_CHECKING: + from pathlib import Path + from hstest.testing.execution.runnable.runnable_file import RunnableFile @@ -15,7 +17,7 @@ class GoSearcher(BaseSearcher): def extension(self) -> str: return ".go" - def search(self, where: str | None = None) -> RunnableFile: + def search(self, where: Path | None = None) -> RunnableFile: package_searcher = re.compile(r"^\s*package\s*main", re.MULTILINE) main_func_searcher = re.compile(r"(^|\n)\s*func\s+main\s*\(\s*\)", re.MULTILINE) diff --git a/hstest/testing/execution/searcher/javascript_searcher.py b/hstest/testing/execution/searcher/javascript_searcher.py index f1ec811f..b2fcfdfe 100644 --- a/hstest/testing/execution/searcher/javascript_searcher.py +++ b/hstest/testing/execution/searcher/javascript_searcher.py @@ -5,6 +5,8 @@ from hstest.testing.execution.searcher.base_searcher import BaseSearcher if TYPE_CHECKING: + from pathlib import Path + from hstest.testing.execution.runnable.runnable_file import RunnableFile @@ -13,5 +15,5 @@ class JavascriptSearcher(BaseSearcher): def extension(self) -> str: return ".js" - def search(self, where: str | None = None) -> RunnableFile: + def search(self, where: Path | None = None) -> RunnableFile: return self._simple_search(where, "function main()", r"(^|\n) *function +main +\( *\)") diff --git a/hstest/testing/execution/searcher/python_searcher.py b/hstest/testing/execution/searcher/python_searcher.py index 1afd32a0..88aab919 100644 --- a/hstest/testing/execution/searcher/python_searcher.py +++ b/hstest/testing/execution/searcher/python_searcher.py @@ -1,7 +1,7 @@ from __future__ import annotations -import os import re +from pathlib import Path from typing import TYPE_CHECKING from hstest.dynamic.output.output_handler import OutputHandler @@ -27,14 +27,14 @@ def search( def init_regexes(_: Folder, sources: Sources) -> None: import_regexes = {} - for file, source in sources.items(): + for file in sources: is_imported[file] = False import_regexes[file] = [ re.compile(rf"(^|\n)import +[\w., ]*\b{file[:-3]}\b[\w., ]*", re.MULTILINE), re.compile(rf"(^|\n)from +\.? *\b{file[:-3]}\b +import +", re.MULTILINE), ] - for file, source in sources.items(): + for source in sources.values(): for f, (r1, r2) in import_regexes.items(): if r1.search(source) is not None or r2.search(source) is not None: is_imported[f] = True @@ -49,7 +49,7 @@ def init_regexes(_: Folder, sources: Sources) -> None: ) def find(self, source: str | None) -> PythonRunnableFile: - OutputHandler.print(f"PythonSearcher source = {source}, cwd = {os.getcwd()}") + OutputHandler.print(f"PythonSearcher source = {source}, cwd = {Path.cwd()}") runnable = super().find(source) OutputHandler.print(f"PythonSearcher found runnable: {runnable.folder}/{runnable.file}") return PythonRunnableFile( diff --git a/hstest/testing/execution/searcher/shell_searcher.py b/hstest/testing/execution/searcher/shell_searcher.py index 63f5a8a4..b262b170 100644 --- a/hstest/testing/execution/searcher/shell_searcher.py +++ b/hstest/testing/execution/searcher/shell_searcher.py @@ -5,6 +5,8 @@ from hstest.testing.execution.searcher.base_searcher import BaseSearcher if TYPE_CHECKING: + from pathlib import Path + from hstest.testing.execution.runnable.runnable_file import RunnableFile @@ -13,5 +15,5 @@ class ShellSearcher(BaseSearcher): def extension(self) -> str: return ".sh" - def search(self, where: str | None = None) -> RunnableFile: + def search(self, where: Path | None = None) -> RunnableFile: return self._simple_search(where, "# main", r"(^|\n)# *main") diff --git a/hstest/testing/execution/searcher/sql_searcher.py b/hstest/testing/execution/searcher/sql_searcher.py index 1f52c04f..4e98c216 100644 --- a/hstest/testing/execution/searcher/sql_searcher.py +++ b/hstest/testing/execution/searcher/sql_searcher.py @@ -5,6 +5,8 @@ from hstest.testing.execution.searcher.base_searcher import BaseSearcher if TYPE_CHECKING: + from pathlib import Path + from hstest.testing.execution.runnable.runnable_file import RunnableFile @@ -13,5 +15,5 @@ class SQLSearcher(BaseSearcher): def extension(self) -> str: return ".sql" - def search(self, where: str | None = None) -> RunnableFile: + def search(self, where: Path | None = None) -> RunnableFile: return self._base_search(where) diff --git a/hstest/testing/plotting/__init__.py b/hstest/testing/plotting/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/testing/plotting/drawing/__init__.py b/hstest/testing/plotting/drawing/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hstest/testing/plotting/drawing/drawing_builder.py b/hstest/testing/plotting/drawing/drawing_builder.py index 07ca4ac5..ee89c987 100644 --- a/hstest/testing/plotting/drawing/drawing_builder.py +++ b/hstest/testing/plotting/drawing/drawing_builder.py @@ -7,7 +7,7 @@ class DrawingBuilder: @staticmethod - def get_hist_drawing(data, library, kwargs) -> Drawing: + def get_hist_drawing(data: list[float] | str, library: str, kwargs: dict[str, str]) -> Drawing: return Drawing( library, DrawingType.hist, @@ -16,7 +16,9 @@ def get_hist_drawing(data, library, kwargs) -> Drawing: ) @staticmethod - def get_line_drawing(x, y, library, kwargs) -> Drawing: + def get_line_drawing( + x: list[float], y: list[float], library: str, kwargs: dict[str, str] + ) -> Drawing: return Drawing( library, DrawingType.line, @@ -25,7 +27,9 @@ def get_line_drawing(x, y, library, kwargs) -> Drawing: ) @staticmethod - def get_scatter_drawing(x, y, library, kwargs) -> Drawing: + def get_scatter_drawing( + x: list[float], y: list[float], library: str, kwargs: dict[str, str] + ) -> Drawing: return Drawing( library, DrawingType.scatter, @@ -34,7 +38,9 @@ def get_scatter_drawing(x, y, library, kwargs) -> Drawing: ) @staticmethod - def get_pie_drawing(x, y, library, kwargs) -> Drawing: + def get_pie_drawing( + x: list[float], y: list[float], library: str, kwargs: dict[str, str] + ) -> Drawing: return Drawing( library, DrawingType.pie, @@ -43,7 +49,9 @@ def get_pie_drawing(x, y, library, kwargs) -> Drawing: ) @staticmethod - def get_bar_drawing(x, y, library, kwargs) -> Drawing: + def get_bar_drawing( + x: list[float], y: list[float], library: str, kwargs: dict[str, str] + ) -> Drawing: return Drawing( library, DrawingType.bar, diff --git a/hstest/testing/plotting/drawing/drawing_data.py b/hstest/testing/plotting/drawing/drawing_data.py index 6296a0bd..ba4f6805 100644 --- a/hstest/testing/plotting/drawing/drawing_data.py +++ b/hstest/testing/plotting/drawing/drawing_data.py @@ -6,13 +6,13 @@ class DrawingData: def __init__(self, x: np.ndarray, y: np.ndarray) -> None: try: - if type(x) != list and x is not None: + if not isinstance(x, list | None): x = list(x) - if type(y) != list and y is not None: + if not isinstance(y, list | None): y = list(y) - except Exception: + except Exception as e: msg = "The data argument should be an array" - raise ValueError(msg) + raise ValueError(msg) from e if x is not None and y is not None and len(x) != len(y): msg = "Arrays should be the same length" diff --git a/hstest/testing/plotting/drawing_data_normalizer.py b/hstest/testing/plotting/drawing_data_normalizer.py index a0e0639c..f03dddca 100644 --- a/hstest/testing/plotting/drawing_data_normalizer.py +++ b/hstest/testing/plotting/drawing_data_normalizer.py @@ -5,15 +5,15 @@ class DrawingDataNormalizer: @staticmethod - def normalize_x_y_data(x, y) -> np.ndarray: + def normalize_x_y_data(x: list[float], y: list[float]) -> np.ndarray: try: - if type(x) != list: + if not isinstance(x, list): x = list(x) - if type(y) != list: + if not isinstance(y, list): y = list(y) - except Exception: + except Exception as e: msg = "The data argument should be an array" - raise ValueError(msg) + raise ValueError(msg) from e if len(x) != len(y): msg = "Arrays should be the same length" @@ -27,16 +27,16 @@ def normalize_x_y_data(x, y) -> np.ndarray: return np.array(result_data, dtype=object) @staticmethod - def normalize_hist_data(data) -> np.ndarray: - if type(data) == str: + def normalize_hist_data(data: list[float] | str) -> np.ndarray: + if isinstance(data, str): data = [data] - if type(data) != list: + if not isinstance(data, list): try: data = list(data) - except Exception: + except Exception as e: msg = "The data argument should be an array" - raise ValueError(msg) + raise ValueError(msg) from e return np.array(data, dtype=object) @@ -79,17 +79,17 @@ def normalize_hist_data(data) -> np.ndarray: return None @staticmethod - def normalize_bar_data(x, y) -> np.ndarray: + def normalize_bar_data(x: list[float], y: list[float]) -> np.ndarray: return DrawingDataNormalizer.normalize_x_y_data(x, y) @staticmethod - def normalize_line_data(x, y) -> np.ndarray: + def normalize_line_data(x: list[float], y: list[float]) -> np.ndarray: return DrawingDataNormalizer.normalize_x_y_data(x, y) @staticmethod - def normalize_scatter_data(x, y) -> np.ndarray: + def normalize_scatter_data(x: list[float], y: list[float]) -> np.ndarray: return DrawingDataNormalizer.normalize_x_y_data(x, y) @staticmethod - def normalize_pie_data(x, y) -> np.ndarray: + def normalize_pie_data(x: list[float], y: list[float]) -> np.ndarray: return DrawingDataNormalizer.normalize_x_y_data(x, y) diff --git a/hstest/testing/plotting/matplotlib_handler.py b/hstest/testing/plotting/matplotlib_handler.py index a4e32924..7481c2a9 100644 --- a/hstest/testing/plotting/matplotlib_handler.py +++ b/hstest/testing/plotting/matplotlib_handler.py @@ -3,7 +3,7 @@ import contextlib from copy import deepcopy from importlib import reload -from typing import TYPE_CHECKING +from typing import Final, TYPE_CHECKING from hstest.testing.plotting.drawing.drawing_data import DrawingData @@ -20,6 +20,8 @@ if TYPE_CHECKING: from hstest.testing.runner.plot_testing_runner import DrawingsStorage +NUM_SHAPES: Final = 2 + class MatplotlibHandler: _saved = False @@ -50,7 +52,7 @@ def replace_plots(drawings: DrawingsStorage) -> None: def custom_show_func(*args, **kwargs) -> None: pass - def hist(x, *args, data=None, **kw): + def hist(x: list[float], *args, data: list[float] | None = None, **kw) -> None: if data is not None: with contextlib.suppress(Exception): x = data[x] @@ -60,19 +62,19 @@ def hist(x, *args, data=None, **kw): for col in x.columns: hist(x[col], *args, **kw) return None - elif type(x) == pd.Series: + if type(x) == pd.Series: return hist(x.to_numpy(), *args, **kw) - except Exception: + except Exception: # noqa: BLE001, S110 pass if type(x) != np.ndarray: x = np.array(x, dtype=object) - if len(x.shape) == 2: + if len(x.shape) == NUM_SHAPES: from matplotlib import cbook - x = np.array(cbook._reshape_2D(x, "x"), dtype=object) + x = np.array(cbook._reshape_2D(x, "x"), dtype=object) # noqa: SLF001 - if len(x.shape) == 2: + if len(x.shape) == NUM_SHAPES: for i in range(x.shape[1]): hist(x[:, i], *args, **kw) return None @@ -87,7 +89,9 @@ def hist(x, *args, data=None, **kw): ) return None - def bar(x, height, *args, data=None, **kw): + def bar( + x: list[float], height: list[float], *args, data: list[float] | None = None, **kw + ) -> None: if data is not None: with contextlib.suppress(Exception): x = data[x] @@ -99,11 +103,11 @@ def bar(x, height, *args, data=None, **kw): for col in x.columns: bar(x[col], *args, **kw) return None - elif type(x) == pd.Series: + if type(x) == pd.Series: return bar(x.to_numpy(), height, *args, **kw) - elif type(height) == pd.Series: + if type(height) == pd.Series: return bar(x, height.to_numpy(), *args, **kw) - except Exception: + except Exception: # noqa: BLE001, S110 pass if type(height) in {int, float}: @@ -114,7 +118,9 @@ def bar(x, height, *args, data=None, **kw): ) return None - def barh(x, width, *args, data=None, **kw): + def barh( + x: list[float], width: list[float], *args, data: list[float] | None = None, **kw + ) -> None: return bar(x, width, *args, data=data, **kw) def plot(*args, **kwargs) -> None: @@ -138,7 +144,7 @@ def plot(*args, **kwargs) -> None: ) ) - def scatter(x, y, *a, **kwargs) -> None: + def scatter(x: list[float], y: list[float], *a, **kwargs) -> None: drawings.append( DrawingBuilder.get_scatter_drawing( x, @@ -148,7 +154,7 @@ def scatter(x, y, *a, **kwargs) -> None: ) ) - def pie(x, *a, **kw) -> None: + def pie(x: list[float], *a, **kw) -> None: # Normalize with other plot libraries y = x @@ -161,7 +167,7 @@ def pie(x, *a, **kw) -> None: Drawing(DrawingLibrary.matplotlib, DrawingType.pie, DrawingData(x, y), kw) ) - def violinplot(dataset, *, data=None, **kwargs) -> None: + def violinplot(dataset: list[float], *, data: list[float] | None = None, **kwargs) -> None: if data is not None: with contextlib.suppress(Exception): dataset = data[dataset] @@ -170,7 +176,7 @@ def violinplot(dataset, *, data=None, **kwargs) -> None: drawings.append(drawing) - def imshow(x, **kwargs) -> None: + def imshow(x: list[float], **kwargs) -> None: curr_data = { # noqa: F841 "x": np.array(x, dtype=object) } @@ -183,7 +189,7 @@ def imshow(x, **kwargs) -> None: ) drawings.append(drawing) - def boxplot(x, **kwargs) -> None: + def boxplot(x: list[float], **kwargs) -> None: curr_data = { # noqa: F841 "x": np.array([None], dtype=object), "y": np.array(x, dtype=object), @@ -200,31 +206,31 @@ def boxplot(x, **kwargs) -> None: import matplotlib as mpl class CustomMatplotlibAxes(mpl.axes.Axes): - def hist(self, x, *a, **kw) -> None: + def hist(self, x: list[float], *a, **kw) -> None: hist(x, *a, **kw) - def bar(self, x, height, *a, **kw) -> None: + def bar(self, x: list[float], height: list[float], *a, **kw) -> None: bar(x, height, *a, **kw) - def barh(self, y, width, *a, **kw) -> None: + def barh(self, y: list[float], width: list[float], *a, **kw) -> None: barh(y, width, *a, **kw) def plot(self, *args, **kwargs) -> None: plot(*args, *kwargs) - def scatter(self, x, y, *a, **kwargs) -> None: + def scatter(self, x: list[float], y: list[float], *a, **kwargs) -> None: scatter(x, y, *a, **kwargs) - def pie(self, x, *a, **kw) -> None: + def pie(self, x: list[float], *a, **kw) -> None: pie(x, *a, **kw) - def violinplot(self, dataset, **kwargs) -> None: + def violinplot(self, dataset: list[float], **kwargs) -> None: violinplot(dataset, **kwargs) - def imshow(self, x, **kwargs) -> None: + def imshow(self, x: list[float], **kwargs) -> None: imshow(x, **kwargs) - def boxplot(self, x, **kwargs) -> None: + def boxplot(self, x: list[float], **kwargs) -> None: boxplot(x, **kwargs) import matplotlib as mpl diff --git a/hstest/testing/plotting/pandas_handler.py b/hstest/testing/plotting/pandas_handler.py index 05494c50..36e86dd7 100644 --- a/hstest/testing/plotting/pandas_handler.py +++ b/hstest/testing/plotting/pandas_handler.py @@ -1,7 +1,7 @@ from __future__ import annotations import contextlib -from typing import TYPE_CHECKING +from typing import ClassVar, Final, TYPE_CHECKING from hstest.testing.plotting.drawing.drawing_data import DrawingData @@ -23,199 +23,234 @@ from hstest.testing.plotting.matplotlib_handler import MatplotlibHandler if TYPE_CHECKING: + from collections.abc import Callable + from hstest.testing.runner.plot_testing_runner import DrawingsStorage +NUM_SHAPES: Final = 2 -class PandasHandler: - _saved = False - _replaced = False - _PlotAccessor = None +def get_line_drawings_with_normalized_data( + data: pd.DataFrame, x: str | None, y: str | None +) -> list[Drawing]: + if type(data) is pd.Series: + return [DrawingBuilder.get_line_drawing(data.index, data, DrawingLibrary.pandas, {})] - _series_plot = None - _dframe_plot = None + return [ + DrawingBuilder.get_line_drawing(data.index, data[column], DrawingLibrary.pandas, {}) + for column in data.columns + ] - _series_hist = None - _dframe_hist = None - _series_bar = None - _dframe_bar = None +def get_hexbin_drawings_with_normalized_data( + data: pd.DataFrame, x: str | None, y: str | None +) -> list[Drawing]: + drawings = [] + drawing = Drawing(DrawingLibrary.pandas, DrawingType.hexbin, None, {}) + drawings.append(drawing) + return drawings - _series_boxplot = None - _dframe_boxplot = None - plot_name_to_basic_name = { - # 'barh': DrawingType.bar, - "density": DrawingType.dis, - "kde": DrawingType.dis, - } +def get_area_drawings_with_normalized_data( + data: pd.DataFrame, x: str | None, y: str | None +) -> list[Drawing]: + drawings = [] + drawing = Drawing(DrawingLibrary.pandas, DrawingType.area, None, {}) + drawings.append(drawing) + return drawings - graph_type_to_normalized_data = { - "scatter": PandasHandler.get_scatter_drawings_with_normalized_data, - "line": PandasHandler.get_line_drawings_with_normalized_data, - "pie": PandasHandler.get_pie_drawings_with_normalized_data, - # 'bar': lambda data, x, y: PandasHandler.get_bar_drawings_with_normalized_data(data, x, y), - "box": PandasHandler.get_box_drawings_with_normalized_data, - "dis": PandasHandler.get_dis_drawings_with_normalized_data, - } - @staticmethod - def get_line_drawings_with_normalized_data(data, x, y): - drawings = [] +def get_dis_drawings_with_normalized_data( + data: pd.DataFrame, x: str | None, y: str | None +) -> list[Drawing]: + drawings = [] - if type(data) is pd.Series: - drawings.append( - DrawingBuilder.get_line_drawing(data.index, data, DrawingLibrary.pandas, {}) - ) - return drawings - - for column in data.columns: - drawings.append( - DrawingBuilder.get_line_drawing(data.index, data[column], DrawingLibrary.pandas, {}) - ) + if type(data) == pd.Series: + curr_data = {"x": data.to_numpy()} + drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) + drawings.append(drawing) return drawings - @staticmethod - def get_scatter_drawings_with_normalized_data(data, x, y): - return [DrawingBuilder.get_scatter_drawing(data[x], data[y], DrawingLibrary.pandas, {})] - - @staticmethod - def get_pie_drawings_with_normalized_data(data: pd.DataFrame, x, y): - if type(data) == pd.Series: - return [ - Drawing( - DrawingLibrary.pandas, - DrawingType.pie, - DrawingData(data.index.to_numpy(), data.to_numpy()), - {}, - ) - ] + if x: + curr_data = { + "x": np.array(data[x], dtype=object), + } - if y is not None: - return [ - Drawing( - DrawingLibrary.pandas, - DrawingType.pie, - DrawingData(data.index.to_numpy(), data[y].to_numpy()), - {}, - ) - ] + drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) + drawings.append(drawing) + if y: + curr_data = { + "x": np.array(data[y], dtype=object), + } - drawings = [] + drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) + drawings.append(drawing) + if not x and not y: for column in data.columns: if not is_numeric_dtype(data[column]): continue - drawings.append( - Drawing( - DrawingLibrary.pandas, - DrawingType.pie, - DrawingData(data.index.to_numpy(), data[column].to_numpy()), - {}, - ) - ) - return drawings - @staticmethod - def get_bar_drawings_with_normalized_data(data: pd.DataFrame, x, y): - drawings = [] - - x_arr = data[x].to_numpy() if x is not None else data.index.to_numpy() + curr_data = { # noqa: F841 + "x": data[column].to_numpy() + } - if y is not None: - drawing = DrawingBuilder.get_bar_drawing(x_arr, data[y], DrawingLibrary.pandas, {}) + drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) drawings.append(drawing) - return drawings + return drawings + + +def get_box_drawings_with_normalized_data( + data: pd.DataFrame, x: str | None, y: str | None +) -> list[Drawing]: + drawings = [] + # Columns are not specified + if x is None: for column in data.columns: if not is_numeric_dtype(data[column]): continue - drawing = DrawingBuilder.get_bar_drawing(x_arr, data[column], DrawingLibrary.pandas, {}) + + curr_data = {"x": np.array([column], dtype=object), "y": data[column].to_numpy()} + + drawing = Drawing(DrawingLibrary.pandas, DrawingType.box, None, {}) drawings.append(drawing) return drawings - @staticmethod - def get_box_drawings_with_normalized_data(data: pd.DataFrame, x, y): - drawings = [] + for column in x: + if not is_numeric_dtype(data[column]): + continue - # Columns are not specified - if x is None: - for column in data.columns: - if not is_numeric_dtype(data[column]): - continue + curr_data = { # noqa: F841 + "x": np.array([column], dtype=object), + "y": data[column].to_numpy(), + } - curr_data = {"x": np.array([column], dtype=object), "y": data[column].to_numpy()} + drawing = Drawing(DrawingLibrary.pandas, DrawingType.box, None, {}) + drawings.append(drawing) + return drawings - drawing = Drawing(DrawingLibrary.pandas, DrawingType.box, None, {}) - drawings.append(drawing) - return drawings - for column in x: - if not is_numeric_dtype(data[column]): - continue +def get_bar_drawings_with_normalized_data( + data: pd.DataFrame, x: str | None, y: str | None +) -> list[Drawing]: + drawings = [] - curr_data = { # noqa: F841 - "x": np.array([column], dtype=object), - "y": data[column].to_numpy(), - } + x_arr = data[x].to_numpy() if x is not None else data.index.to_numpy() - drawing = Drawing(DrawingLibrary.pandas, DrawingType.box, None, {}) - drawings.append(drawing) + if y is not None: + drawing = DrawingBuilder.get_bar_drawing(x_arr, data[y], DrawingLibrary.pandas, {}) + drawings.append(drawing) return drawings - @staticmethod - def get_dis_drawings_with_normalized_data(data, x, y): - drawings = [] + for column in data.columns: + if not is_numeric_dtype(data[column]): + continue + drawing = DrawingBuilder.get_bar_drawing(x_arr, data[column], DrawingLibrary.pandas, {}) + drawings.append(drawing) + return drawings + + +def get_pie_drawings_with_normalized_data( + data: pd.DataFrame, x: str | None, y: str | None +) -> list[Drawing]: + if type(data) == pd.Series: + return [ + Drawing( + DrawingLibrary.pandas, + DrawingType.pie, + DrawingData(data.index.to_numpy(), data.to_numpy()), + {}, + ) + ] + + if y is not None: + return [ + Drawing( + DrawingLibrary.pandas, + DrawingType.pie, + DrawingData(data.index.to_numpy(), data[y].to_numpy()), + {}, + ) + ] + + drawings = [] + + for column in data.columns: + if not is_numeric_dtype(data[column]): + continue + drawings.append( + Drawing( + DrawingLibrary.pandas, + DrawingType.pie, + DrawingData(data.index.to_numpy(), data[column].to_numpy()), + {}, + ) + ) + return drawings - if type(data) == pd.Series: - curr_data = {"x": data.to_numpy()} - drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) - drawings.append(drawing) - return drawings +def get_scatter_drawings_with_normalized_data( + data: pd.DataFrame, x: str | None, y: str | None +) -> list[Drawing]: + return [DrawingBuilder.get_scatter_drawing(data[x], data[y], DrawingLibrary.pandas, {})] - if x: - curr_data = { - "x": np.array(data[x], dtype=object), - } - drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) - drawings.append(drawing) - if y: - curr_data = { - "x": np.array(data[y], dtype=object), - } +class PandasHandler: + _saved = False + _replaced = False - drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) - drawings.append(drawing) + _PlotAccessor = None - if not x and not y: - for column in data.columns: - if not is_numeric_dtype(data[column]): - continue + _series_plot = None + _dframe_plot = None - curr_data = { # noqa: F841 - "x": data[column].to_numpy() - } + _series_hist = None + _dframe_hist = None - drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) - drawings.append(drawing) - return drawings + _series_bar = None + _dframe_bar = None - @staticmethod - def get_area_drawings_with_normalized_data(data, x, y): - drawings = [] - drawing = Drawing(DrawingLibrary.pandas, DrawingType.area, None, {}) - drawings.append(drawing) - return drawings + _series_boxplot = None + _dframe_boxplot = None + + plot_name_to_basic_name: ClassVar[dict[str, DrawingType]] = { + # 'barh': DrawingType.bar, # noqa: ERA001 + "density": DrawingType.dis, + "kde": DrawingType.dis, + } + + graph_type_to_normalized_data: ClassVar[ + dict[str, Callable[[pd.DataFrame, str | None, str | None], list[Drawing]]] + ] = { + "scatter": get_scatter_drawings_with_normalized_data, + "line": get_line_drawings_with_normalized_data, + "pie": get_pie_drawings_with_normalized_data, + # "bar": get_bar_drawings_with_normalized_data, # noqa: ERA001 + "box": get_box_drawings_with_normalized_data, + "dis": get_dis_drawings_with_normalized_data, + } @staticmethod - def get_hexbin_drawings_with_normalized_data(data, x, y): - drawings = [] - drawing = Drawing(DrawingLibrary.pandas, DrawingType.hexbin, None, {}) - drawings.append(drawing) - return drawings + def revert_plots() -> None: + if not PandasHandler._replaced: + return + + MatplotlibHandler.revert_plots() + + import pandas.plotting + from pandas.core.accessor import CachedAccessor + + pandas.Series.plot = CachedAccessor("plot", pandas.plotting.PlotAccessor) + pandas.DataFrame.plot = CachedAccessor("plot", pandas.plotting.PlotAccessor) + + pandas.Series.hist = PandasHandler._series_hist + pandas.DataFrame.hist = PandasHandler._dframe_hist + + pandas.DataFrame.boxplot = PandasHandler._dframe_boxplot + + PandasHandler._replaced = False @staticmethod def replace_plots(drawings: DrawingsStorage) -> None: @@ -226,8 +261,8 @@ def replace_plots(drawings: DrawingsStorage) -> None: return class CustomPlotAccessor(pandas.plotting.PlotAccessor): - def __call__(self, *args, **kw): - from pandas.plotting._core import _get_plot_backend + def __call__(self, *args, **kw) -> None: + from pandas.plotting._core import _get_plot_backend # noqa: PLC2701 plot_backend = _get_plot_backend(kw.pop("backend", None)) @@ -273,16 +308,22 @@ def __call__(self, *args, **kw): import pandas.plotting._core - def boxplot(self, column=None, **kwargs) -> None: - all_drawings = PandasHandler.get_box_drawings_with_normalized_data(self, column, None) + def boxplot(self: pandas.DataFrame, column: str | None = None, **kwargs) -> None: + all_drawings = get_box_drawings_with_normalized_data(self, column, None) drawings.extend(all_drawings) - def hist(data, column=None, _process_by=True, **kw): + def hist( + data: pandas.DataFrame | pandas.Series | np.ndarray, + column: str | None = None, + *, + _process_by: bool = True, + **kw, + ) -> None: for k in list(kw.keys()): if kw[k] is None: kw.pop(k) - if _process_by and "by" in kw and type(kw["by"]) == str: + if _process_by and "by" in kw and isinstance(kw["by"], str): with contextlib.suppress(Exception): kw["by"] = data[kw["by"]] @@ -301,17 +342,17 @@ def hist(data, column=None, _process_by=True, **kw): hist(data[col].to_numpy(), **kw) return None - elif type(data) == pandas.Series: + if type(data) == pandas.Series: return hist(data.to_numpy(), **kw) - elif type(data) != np.ndarray: + if type(data) != np.ndarray: data = np.array(data, dtype=object) - if len(data.shape) == 2: + if len(data.shape) == NUM_SHAPES: from matplotlib import cbook - data = np.array(cbook._reshape_2D(data, "x"), dtype=object) + data = np.array(cbook._reshape_2D(data, "x"), dtype=object) # noqa: SLF001 - if len(data.shape) == 2: + if len(data.shape) == NUM_SHAPES: for i in range(data.shape[1]): hist(data[:, i], **kw) return None @@ -334,45 +375,44 @@ def hist(data, column=None, _process_by=True, **kw): ) return None - def bar(data, x=None, y=None, **kw): + def bar(data: pandas.DataFrame, x: str | None = None, y: str | None = None, **kw) -> None: for k in list(kw.keys()): if kw[k] is None: kw.pop(k) - if type(data) == pandas.DataFrame: + if isinstance(data, pandas.DataFrame): if y is not None and x is not None: - if type(y) == str: + if isinstance(y, str): y = [y] for col in y: bar(None, data[x].array.to_numpy(), data[col].array.to_numpy(), **kw) return None - elif x is not None: + if x is not None: for col in data.columns: if col != x: bar(None, data[x].array.to_numpy(), data[col].array.to_numpy(), **kw) return None - elif y is not None: - if type(y) == str: + if y is not None: + if isinstance(y, str): y = [y] for col in y: bar(None, data[col].index.to_numpy(), data[col].array.to_numpy(), **kw) return None - else: - for col in data.columns: - bar(None, data[col].index.to_numpy(), data[col].array.to_numpy(), **kw) - return None + for col in data.columns: + bar(None, data[col].index.to_numpy(), data[col].array.to_numpy(), **kw) + return None - elif type(data) == pandas.Series: + if isinstance(data, pandas.Series): return bar(None, data.index.to_numpy(), data.array.to_numpy(), **kw) drawings.append(Drawing(DrawingLibrary.pandas, DrawingType.bar, DrawingData(x, y), kw)) return None def barh( - self, + self: pandas.Series, ) -> None: pass @@ -385,7 +425,7 @@ def barh( PandasHandler._series_hist = pandas.Series.hist PandasHandler._dframe_hist = pandas.DataFrame.hist - # PandasHandler._series_bar = pandas.Series.bar + # PandasHandler._series_bar = pandas.Series.bar # noqa: ERA001 PandasHandler._dframe_boxplot = pandas.DataFrame.boxplot @@ -398,23 +438,3 @@ def barh( pandas.DataFrame.boxplot = boxplot PandasHandler._replaced = True - - @staticmethod - def revert_plots() -> None: - if not PandasHandler._replaced: - return - - MatplotlibHandler.revert_plots() - - import pandas.plotting - from pandas.core.accessor import CachedAccessor - - pandas.Series.plot = CachedAccessor("plot", pandas.plotting.PlotAccessor) - pandas.DataFrame.plot = CachedAccessor("plot", pandas.plotting.PlotAccessor) - - pandas.Series.hist = PandasHandler._series_hist - pandas.DataFrame.hist = PandasHandler._dframe_hist - - pandas.DataFrame.boxplot = PandasHandler._dframe_boxplot - - PandasHandler._replaced = False diff --git a/hstest/testing/plotting/seaborn_handler.py b/hstest/testing/plotting/seaborn_handler.py index 3bf1b343..3ade1328 100644 --- a/hstest/testing/plotting/seaborn_handler.py +++ b/hstest/testing/plotting/seaborn_handler.py @@ -1,7 +1,7 @@ from __future__ import annotations from importlib import reload -from typing import TYPE_CHECKING +from typing import Final, TYPE_CHECKING from hstest.testing.plotting.drawing.drawing_data import DrawingData @@ -22,6 +22,8 @@ if TYPE_CHECKING: from hstest.testing.runner.plot_testing_runner import DrawingsStorage +NUM_SHAPES: Final = 2 + class SeabornHandler: _saved = False @@ -46,7 +48,7 @@ def replace_plots(drawings: DrawingsStorage) -> None: except ModuleNotFoundError: return - def displot(data=None, **kwargs) -> None: + def displot(data: pd.DataFrame = None, **kwargs) -> None: x = kwargs.get("x", None) y = kwargs.get("y", None) @@ -102,11 +104,16 @@ def displot(data=None, **kwargs) -> None: ) drawings.append(drawing) - def histplot(data=None, _process_hue=True, **kw): + def histplot( + data: pd.DataFrame | pd.Series | np.ndarray | None = None, + *, + _process_hue: bool = True, + **kw, + ) -> None: if data is None: return None - if _process_hue and "hue" in kw and type(kw["hue"]) == str: + if _process_hue and "hue" in kw and isinstance(kw["hue"], str): with contextlib.suppress(Exception): kw["hue"] = data[kw["hue"]] @@ -122,17 +129,18 @@ def histplot(data=None, _process_hue=True, **kw): for col in data.columns: histplot(data[col], **kw) return None - elif type(data) == pd.Series: + + if type(data) == pd.Series: return histplot(data.to_numpy(), **kw) - elif type(data) != np.ndarray: + if type(data) != np.ndarray: data = np.array(data, dtype=object) - if len(data.shape) == 2: + if len(data.shape) == NUM_SHAPES: from matplotlib import cbook - data = np.array(cbook._reshape_2D(data, "x"), dtype=object) + data = np.array(cbook._reshape_2D(data, "x"), dtype=object) # noqa: SLF001 - if len(data.shape) == 2: + if len(data.shape) == NUM_SHAPES: for i in range(data.shape[1]): histplot(data[:, i], **kw) return None @@ -155,7 +163,9 @@ def histplot(data=None, _process_hue=True, **kw): ) return None - def lineplot(*, data=None, x=None, y=None, **kwargs): + def lineplot( + *, x: str | None = None, y: str | None = None, data: pd.DataFrame = None, **kwargs + ) -> DrawingsStorage | None: x_array = data[x].to_numpy() if x is not None else data.index.to_numpy() if y is not None: @@ -185,7 +195,9 @@ def lineplot(*, data=None, x=None, y=None, **kwargs): ) return None - def lmplot(x=None, y=None, data=None, **kwargs) -> None: + def lmplot( + x: str | None = None, y: str | None = None, data: pd.DataFrame = None, **kwargs + ) -> None: curr_data = { # noqa: F841 "data": data, "x": x, @@ -201,7 +213,9 @@ def lmplot(x=None, y=None, data=None, **kwargs) -> None: ) drawings.append(drawing) - def scatterplot(x=None, y=None, data=None, **kwargs) -> None: + def scatterplot( + x: str | None = None, y: str | None = None, data: pd.DataFrame = None, **kwargs + ) -> None: if x is not None and y is not None: drawings.append( DrawingBuilder.get_scatter_drawing( @@ -228,7 +242,9 @@ def scatterplot(x=None, y=None, data=None, **kwargs) -> None: ) ) - def catplot(x=None, y=None, data=None, **kwargs) -> None: + def catplot( + x: str | None = None, y: str | None = None, data: pd.DataFrame = None, **kwargs + ) -> None: curr_data = { # noqa: F841 "data": data, "x": x, @@ -244,7 +260,9 @@ def catplot(x=None, y=None, data=None, **kwargs) -> None: ) drawings.append(drawing) - def barplot(x=None, y=None, data=None, **kwargs) -> None: + def barplot( + x: str | None = None, y: str | None = None, data: pd.DataFrame = None, **kwargs + ) -> None: x_arr = np.array([], dtype=object) y_arr = np.array([], dtype=object) @@ -260,7 +278,9 @@ def barplot(x=None, y=None, data=None, **kwargs) -> None: Drawing(DrawingLibrary.seaborn, DrawingType.bar, DrawingData(x_arr, y_arr), kwargs) ) - def violinplot(*, x=None, y=None, data=None, **kwargs) -> None: + def violinplot( + *, x: str | None = None, y: str | None = None, data: pd.DataFrame = None, **kwargs + ) -> None: if data is not None: if x is None and y is not None: data = data[y] @@ -268,13 +288,12 @@ def violinplot(*, x=None, y=None, data=None, **kwargs) -> None: data = data[x] elif x is not None and y is not None: data = pd.concat([data[x], data[y]], axis=1).reset_index() + elif x is None: + data = y + elif y is None: + data = x else: - if x is None: - data = y - elif y is None: - data = x - else: - data = pd.concat([x, y], axis=1).reset_index() + data = pd.concat([x, y], axis=1).reset_index() drawing = Drawing( DrawingLibrary.seaborn, @@ -285,7 +304,7 @@ def violinplot(*, x=None, y=None, data=None, **kwargs) -> None: drawings.append(drawing) - def heatmap(data=None, **kwargs) -> None: + def heatmap(data: pd.DataFrame = None, **kwargs) -> None: if data is None: return @@ -302,7 +321,9 @@ def heatmap(data=None, **kwargs) -> None: drawings.append(drawing) - def boxplot(x=None, y=None, data=None, **kwargs) -> None: + def boxplot( + x: str | None = None, y: str | None = None, data: pd.DataFrame = None, **kwargs + ) -> None: if data is None: curr_data = {"x": np.array(x, dtype=object), "y": np.array(y, dtype=object)} diff --git a/hstest/testing/process_wrapper.py b/hstest/testing/process_wrapper.py index 46745003..eecfdf92 100644 --- a/hstest/testing/process_wrapper.py +++ b/hstest/testing/process_wrapper.py @@ -1,9 +1,10 @@ from __future__ import annotations -import subprocess +import subprocess # noqa: S404 import sys from threading import Lock, Thread from time import sleep +from typing import IO from psutil import NoSuchProcess, Process @@ -21,7 +22,11 @@ class ProcessWrapper: initial_idle_wait_time = 150 def __init__( - self, *args, check_early_finish=False, register_output=True, register_io_handler=False + self, + *args, + check_early_finish: bool = False, + register_output: bool = True, + register_io_handler: bool = False, ) -> None: self.lock = Lock() @@ -49,7 +54,7 @@ def __init__( self.register_io_handler = register_io_handler self._group = None - def start(self): + def start(self) -> ProcessWrapper: command = " ".join(map(str, self.args)) if self.process is not None: @@ -68,7 +73,7 @@ def start(self): args = ["cmd", "/c", *args] self.process = subprocess.Popen( - args, + args, # noqa: S603 bufsize=0, universal_newlines=not self._use_byte_stream, stdout=subprocess.PIPE, @@ -76,7 +81,7 @@ def start(self): stdin=subprocess.PIPE, encoding="utf-8" if not self._use_byte_stream else None, ) - except Exception as e: + except Exception as e: # noqa: BLE001 from hstest import StageTest StageTest.curr_test_run.set_error_in_test( @@ -105,7 +110,14 @@ def check_alive(self) -> None: if self._alive and self.process.returncode is not None: self._alive = False - def check_pipe(self, read_pipe, write_pipe, write_stdout=False, write_stderr=False) -> None: + def check_pipe( + self, + read_pipe: IO, + write_pipe: IO, + *, + write_stdout: bool = False, + write_stderr: bool = False, + ) -> None: pipe_name = "stdout" if write_stdout else "stderr" with self.lock: @@ -240,7 +252,7 @@ def register_input_request(self) -> None: self.cpu_load_history = [] self.output_diff_history = [] - def is_finished(self, need_wait_output=True) -> bool: + def is_finished(self, *, need_wait_output: bool = True) -> bool: if not self.check_early_finish: return not self._alive diff --git a/hstest/testing/runner/async_dynamic_testing_runner.py b/hstest/testing/runner/async_dynamic_testing_runner.py index 84ea724e..b74f5eda 100644 --- a/hstest/testing/runner/async_dynamic_testing_runner.py +++ b/hstest/testing/runner/async_dynamic_testing_runner.py @@ -51,11 +51,10 @@ def _run_file(self, test_run: TestRun) -> CheckResult | None: future: Future = executor.submit(lambda: self._run_dynamic_test(test_run)) if time_limit <= 0 or debug_mode: return future.result() - else: - return future.result(timeout=time_limit / 1000) + return future.result(timeout=time_limit / 1000) except TimeoutError: test_run.set_error_in_test(TimeLimitException(time_limit)) - except BaseException as ex: + except BaseException as ex: # noqa: BLE001 test_run.set_error_in_test(ex) finally: test_run.invalidate_handlers() @@ -74,7 +73,7 @@ def test(self, test_run: TestRun) -> CheckResult | None: if error is None: try: return test_case.check_func(OutputHandler.get_output(), test_case.attach) - except BaseException as ex: + except BaseException as ex: # noqa: BLE001 error = ex test_run.set_error_in_test(error) diff --git a/hstest/testing/runner/django_application_runner.py b/hstest/testing/runner/django_application_runner.py index c1be0784..2d184800 100644 --- a/hstest/testing/runner/django_application_runner.py +++ b/hstest/testing/runner/django_application_runner.py @@ -2,6 +2,7 @@ import os import sys +from pathlib import Path from time import sleep from typing import TYPE_CHECKING @@ -38,15 +39,15 @@ def launch_django_application(self, test_case: TestCase) -> None: if source is None or not len(source): source = "manage" - full_source = source.replace(".", os.sep) + ".py" - full_path = os.path.abspath(full_source) + full_source = Path(source.replace(".", os.sep) + ".py") + full_path = full_source.resolve() - if not os.path.exists(full_path): - filename = os.path.basename(full_source) + if not full_path.exists(): + filename = full_source.name runnable = PythonSearcher().search(file_filter=FileFilter(file=lambda f: f == filename)) - full_path = os.path.abspath(runnable.folder + os.sep + runnable.file) + full_path = (Path(runnable.folder) / runnable.file).resolve() - self.full_path = full_path + self.full_path = full_path.name self.port = self.__find_free_port(test_case.attach.tryout_ports) if test_case.attach.use_database: @@ -100,7 +101,7 @@ def __find_free_port(self, ports: list[int]) -> int: def __prepare_database(self, test_database: str) -> None: os.environ["HYPERSKILL_TEST_DATABASE"] = test_database - with open(test_database, "w", encoding="locale"): + with Path(test_database).open("w", encoding="locale"): pass migrate = ProcessWrapper(sys.executable, self.full_path, "migrate", check_early_finish=True) @@ -147,8 +148,9 @@ def test(self, test_run: TestRun) -> CheckResult | None: try: result = test_case.dynamic_testing() self._check_errors() - return result - except BaseException as ex: + except BaseException as ex: # noqa: BLE001 test_run.set_error_in_test(ex) + else: + return result return CheckResult.from_error(test_run.error_in_test) diff --git a/hstest/testing/runner/flask_application_runner.py b/hstest/testing/runner/flask_application_runner.py index 33deac13..7909d7ce 100644 --- a/hstest/testing/runner/flask_application_runner.py +++ b/hstest/testing/runner/flask_application_runner.py @@ -4,7 +4,7 @@ import sys from pathlib import Path from time import sleep -from typing import TYPE_CHECKING +from typing import ClassVar, TYPE_CHECKING from hstest.common.process_utils import is_port_in_use from hstest.exception.outcomes import ErrorWithFeedback, UnexpectedError @@ -19,7 +19,7 @@ class FlaskApplicationRunner(TestRunner): - processes: list[tuple[str, ProcessWrapper]] = [] + processes: ClassVar[list[tuple[str, ProcessWrapper]]] = [] def launch_flask_applications(self, test_case: TestCase) -> None: if not isinstance(test_case.attach, FlaskSettings): @@ -45,8 +45,8 @@ def launch_flask_applications(self, test_case: TestCase) -> None: if not full_path.exists(): msg = ( - f'Cannot find file named "{os.path.basename(full_path)}" ' - f'in folder "{os.path.dirname(full_path)}". ' + f'Cannot find file named "{full_path.name}" ' + f'in folder "{full_path.parent}". ' f"Check if you deleted it." ) raise ErrorWithFeedback(msg) @@ -123,8 +123,9 @@ def test(self, test_run: TestRun) -> CheckResult | None: try: result = test_case.dynamic_testing() self._check_errors() - return result - except BaseException as ex: + except BaseException as ex: # noqa: BLE001 test_run.set_error_in_test(ex) + else: + return result return CheckResult.from_error(test_run.error_in_test) diff --git a/hstest/testing/runner/sql_runner.py b/hstest/testing/runner/sql_runner.py index 30c8a56f..7781fb63 100644 --- a/hstest/testing/runner/sql_runner.py +++ b/hstest/testing/runner/sql_runner.py @@ -25,7 +25,7 @@ def test(self, test_run: TestRun) -> CheckResult | None: try: return test_case.dynamic_testing() - except BaseException as ex: + except BaseException as ex: # noqa: BLE001 test_run.set_error_in_test(ex) return CheckResult.from_error(test_run.error_in_test) diff --git a/hstest/testing/test_run.py b/hstest/testing/test_run.py index 85b83418..a2d9a6dc 100644 --- a/hstest/testing/test_run.py +++ b/hstest/testing/test_run.py @@ -97,7 +97,7 @@ def test(self) -> CheckResult: result = None try: result = self._test_runner.test(self) - except BaseException as ex: + except BaseException as ex: # noqa: BLE001 self.set_error_in_test(ex) # stopThreads(testCase.getProcesses(), pool) # noqa: ERA001 diff --git a/poetry.lock b/poetry.lock index 4586dc4a..ca26cdb2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,65 +1,65 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "contourpy" -version = "1.2.0" +version = "1.2.1" description = "Python library for calculating contours of 2D quadrilateral grids" optional = false python-versions = ">=3.9" files = [ - {file = "contourpy-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8"}, - {file = "contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4"}, - {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f"}, - {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e"}, - {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9"}, - {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa"}, - {file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9"}, - {file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab"}, - {file = "contourpy-1.2.0-cp310-cp310-win32.whl", hash = "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488"}, - {file = "contourpy-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41"}, - {file = "contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727"}, - {file = "contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd"}, - {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a"}, - {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063"}, - {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e"}, - {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686"}, - {file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286"}, - {file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95"}, - {file = "contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6"}, - {file = "contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de"}, - {file = "contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0"}, - {file = "contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4"}, - {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779"}, - {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316"}, - {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399"}, - {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0"}, - {file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0"}, - {file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431"}, - {file = "contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f"}, - {file = "contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9"}, - {file = "contourpy-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5fd1810973a375ca0e097dee059c407913ba35723b111df75671a1976efa04bc"}, - {file = "contourpy-1.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:999c71939aad2780f003979b25ac5b8f2df651dac7b38fb8ce6c46ba5abe6ae9"}, - {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7caf9b241464c404613512d5594a6e2ff0cc9cb5615c9475cc1d9b514218ae8"}, - {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:266270c6f6608340f6c9836a0fb9b367be61dde0c9a9a18d5ece97774105ff3e"}, - {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbd50d0a0539ae2e96e537553aff6d02c10ed165ef40c65b0e27e744a0f10af8"}, - {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11f8d2554e52f459918f7b8e6aa20ec2a3bce35ce95c1f0ef4ba36fbda306df5"}, - {file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ce96dd400486e80ac7d195b2d800b03e3e6a787e2a522bfb83755938465a819e"}, - {file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d3364b999c62f539cd403f8123ae426da946e142312a514162adb2addd8d808"}, - {file = "contourpy-1.2.0-cp39-cp39-win32.whl", hash = "sha256:1c88dfb9e0c77612febebb6ac69d44a8d81e3dc60f993215425b62c1161353f4"}, - {file = "contourpy-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:78e6ad33cf2e2e80c5dfaaa0beec3d61face0fb650557100ee36db808bfa6843"}, - {file = "contourpy-1.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:be16975d94c320432657ad2402f6760990cb640c161ae6da1363051805fa8108"}, - {file = "contourpy-1.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b95a225d4948b26a28c08307a60ac00fb8671b14f2047fc5476613252a129776"}, - {file = "contourpy-1.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0d7e03c0f9a4f90dc18d4e77e9ef4ec7b7bbb437f7f675be8e530d65ae6ef956"}, - {file = "contourpy-1.2.0.tar.gz", hash = "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a"}, + {file = "contourpy-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd7c23df857d488f418439686d3b10ae2fbf9bc256cd045b37a8c16575ea1040"}, + {file = "contourpy-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b9eb0ca724a241683c9685a484da9d35c872fd42756574a7cfbf58af26677fd"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c75507d0a55378240f781599c30e7776674dbaf883a46d1c90f37e563453480"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11959f0ce4a6f7b76ec578576a0b61a28bdc0696194b6347ba3f1c53827178b9"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb3315a8a236ee19b6df481fc5f997436e8ade24a9f03dfdc6bd490fea20c6da"}, + {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39f3ecaf76cd98e802f094e0d4fbc6dc9c45a8d0c4d185f0f6c2234e14e5f75b"}, + {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:94b34f32646ca0414237168d68a9157cb3889f06b096612afdd296003fdd32fd"}, + {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:457499c79fa84593f22454bbd27670227874cd2ff5d6c84e60575c8b50a69619"}, + {file = "contourpy-1.2.1-cp310-cp310-win32.whl", hash = "sha256:ac58bdee53cbeba2ecad824fa8159493f0bf3b8ea4e93feb06c9a465d6c87da8"}, + {file = "contourpy-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:9cffe0f850e89d7c0012a1fb8730f75edd4320a0a731ed0c183904fe6ecfc3a9"}, + {file = "contourpy-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6022cecf8f44e36af10bd9118ca71f371078b4c168b6e0fab43d4a889985dbb5"}, + {file = "contourpy-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef5adb9a3b1d0c645ff694f9bca7702ec2c70f4d734f9922ea34de02294fdf72"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6150ffa5c767bc6332df27157d95442c379b7dce3a38dff89c0f39b63275696f"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c863140fafc615c14a4bf4efd0f4425c02230eb8ef02784c9a156461e62c965"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00e5388f71c1a0610e6fe56b5c44ab7ba14165cdd6d695429c5cd94021e390b2"}, + {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4492d82b3bc7fbb7e3610747b159869468079fe149ec5c4d771fa1f614a14df"}, + {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49e70d111fee47284d9dd867c9bb9a7058a3c617274900780c43e38d90fe1205"}, + {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b59c0ffceff8d4d3996a45f2bb6f4c207f94684a96bf3d9728dbb77428dd8cb8"}, + {file = "contourpy-1.2.1-cp311-cp311-win32.whl", hash = "sha256:7b4182299f251060996af5249c286bae9361fa8c6a9cda5efc29fe8bfd6062ec"}, + {file = "contourpy-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2855c8b0b55958265e8b5888d6a615ba02883b225f2227461aa9127c578a4922"}, + {file = "contourpy-1.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:62828cada4a2b850dbef89c81f5a33741898b305db244904de418cc957ff05dc"}, + {file = "contourpy-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:309be79c0a354afff9ff7da4aaed7c3257e77edf6c1b448a779329431ee79d7e"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e785e0f2ef0d567099b9ff92cbfb958d71c2d5b9259981cd9bee81bd194c9a4"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cac0a8f71a041aa587410424ad46dfa6a11f6149ceb219ce7dd48f6b02b87a7"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af3f4485884750dddd9c25cb7e3915d83c2db92488b38ccb77dd594eac84c4a0"}, + {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ce6889abac9a42afd07a562c2d6d4b2b7134f83f18571d859b25624a331c90b"}, + {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a1eea9aecf761c661d096d39ed9026574de8adb2ae1c5bd7b33558af884fb2ce"}, + {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:187fa1d4c6acc06adb0fae5544c59898ad781409e61a926ac7e84b8f276dcef4"}, + {file = "contourpy-1.2.1-cp312-cp312-win32.whl", hash = "sha256:c2528d60e398c7c4c799d56f907664673a807635b857df18f7ae64d3e6ce2d9f"}, + {file = "contourpy-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:1a07fc092a4088ee952ddae19a2b2a85757b923217b7eed584fdf25f53a6e7ce"}, + {file = "contourpy-1.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb6834cbd983b19f06908b45bfc2dad6ac9479ae04abe923a275b5f48f1a186b"}, + {file = "contourpy-1.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1d59e739ab0e3520e62a26c60707cc3ab0365d2f8fecea74bfe4de72dc56388f"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd3db01f59fdcbce5b22afad19e390260d6d0222f35a1023d9adc5690a889364"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a12a813949e5066148712a0626895c26b2578874e4cc63160bb007e6df3436fe"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe0ccca550bb8e5abc22f530ec0466136379c01321fd94f30a22231e8a48d985"}, + {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1d59258c3c67c865435d8fbeb35f8c59b8bef3d6f46c1f29f6123556af28445"}, + {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f32c38afb74bd98ce26de7cc74a67b40afb7b05aae7b42924ea990d51e4dac02"}, + {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d31a63bc6e6d87f77d71e1abbd7387ab817a66733734883d1fc0021ed9bfa083"}, + {file = "contourpy-1.2.1-cp39-cp39-win32.whl", hash = "sha256:ddcb8581510311e13421b1f544403c16e901c4e8f09083c881fab2be80ee31ba"}, + {file = "contourpy-1.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:10a37ae557aabf2509c79715cd20b62e4c7c28b8cd62dd7d99e5ed3ce28c3fd9"}, + {file = "contourpy-1.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a31f94983fecbac95e58388210427d68cd30fe8a36927980fab9c20062645609"}, + {file = "contourpy-1.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef2b055471c0eb466033760a521efb9d8a32b99ab907fc8358481a1dd29e3bd3"}, + {file = "contourpy-1.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b33d2bc4f69caedcd0a275329eb2198f560b325605810895627be5d4b876bf7f"}, + {file = "contourpy-1.2.1.tar.gz", hash = "sha256:4d8908b3bee1c889e547867ca4cdc54e5ab6be6d3e078556814a22457f49423c"}, ] [package.dependencies] -numpy = ">=1.20,<2.0" +numpy = ">=1.20" [package.extras] bokeh = ["bokeh", "selenium"] docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] -mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.6.1)", "types-Pillow"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.8.0)", "types-Pillow"] test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] @@ -80,60 +80,60 @@ tests = ["pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "fonttools" -version = "4.47.2" +version = "4.51.0" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" files = [ - {file = "fonttools-4.47.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b629108351d25512d4ea1a8393a2dba325b7b7d7308116b605ea3f8e1be88df"}, - {file = "fonttools-4.47.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c19044256c44fe299d9a73456aabee4b4d06c6b930287be93b533b4737d70aa1"}, - {file = "fonttools-4.47.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8be28c036b9f186e8c7eaf8a11b42373e7e4949f9e9f370202b9da4c4c3f56c"}, - {file = "fonttools-4.47.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f83a4daef6d2a202acb9bf572958f91cfde5b10c8ee7fb1d09a4c81e5d851fd8"}, - {file = "fonttools-4.47.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5a5318ba5365d992666ac4fe35365f93004109d18858a3e18ae46f67907670"}, - {file = "fonttools-4.47.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8f57ecd742545362a0f7186774b2d1c53423ed9ece67689c93a1055b236f638c"}, - {file = "fonttools-4.47.2-cp310-cp310-win32.whl", hash = "sha256:a1c154bb85dc9a4cf145250c88d112d88eb414bad81d4cb524d06258dea1bdc0"}, - {file = "fonttools-4.47.2-cp310-cp310-win_amd64.whl", hash = "sha256:3e2b95dce2ead58fb12524d0ca7d63a63459dd489e7e5838c3cd53557f8933e1"}, - {file = "fonttools-4.47.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:29495d6d109cdbabe73cfb6f419ce67080c3ef9ea1e08d5750240fd4b0c4763b"}, - {file = "fonttools-4.47.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0a1d313a415eaaba2b35d6cd33536560deeebd2ed758b9bfb89ab5d97dc5deac"}, - {file = "fonttools-4.47.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90f898cdd67f52f18049250a6474185ef6544c91f27a7bee70d87d77a8daf89c"}, - {file = "fonttools-4.47.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3480eeb52770ff75140fe7d9a2ec33fb67b07efea0ab5129c7e0c6a639c40c70"}, - {file = "fonttools-4.47.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0255dbc128fee75fb9be364806b940ed450dd6838672a150d501ee86523ac61e"}, - {file = "fonttools-4.47.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f791446ff297fd5f1e2247c188de53c1bfb9dd7f0549eba55b73a3c2087a2703"}, - {file = "fonttools-4.47.2-cp311-cp311-win32.whl", hash = "sha256:740947906590a878a4bde7dd748e85fefa4d470a268b964748403b3ab2aeed6c"}, - {file = "fonttools-4.47.2-cp311-cp311-win_amd64.whl", hash = "sha256:63fbed184979f09a65aa9c88b395ca539c94287ba3a364517698462e13e457c9"}, - {file = "fonttools-4.47.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4ec558c543609e71b2275c4894e93493f65d2f41c15fe1d089080c1d0bb4d635"}, - {file = "fonttools-4.47.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e040f905d542362e07e72e03612a6270c33d38281fd573160e1003e43718d68d"}, - {file = "fonttools-4.47.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dd58cc03016b281bd2c74c84cdaa6bd3ce54c5a7f47478b7657b930ac3ed8eb"}, - {file = "fonttools-4.47.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32ab2e9702dff0dd4510c7bb958f265a8d3dd5c0e2547e7b5f7a3df4979abb07"}, - {file = "fonttools-4.47.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a808f3c1d1df1f5bf39be869b6e0c263570cdafb5bdb2df66087733f566ea71"}, - {file = "fonttools-4.47.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac71e2e201df041a2891067dc36256755b1229ae167edbdc419b16da78732c2f"}, - {file = "fonttools-4.47.2-cp312-cp312-win32.whl", hash = "sha256:69731e8bea0578b3c28fdb43dbf95b9386e2d49a399e9a4ad736b8e479b08085"}, - {file = "fonttools-4.47.2-cp312-cp312-win_amd64.whl", hash = "sha256:b3e1304e5f19ca861d86a72218ecce68f391646d85c851742d265787f55457a4"}, - {file = "fonttools-4.47.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:254d9a6f7be00212bf0c3159e0a420eb19c63793b2c05e049eb337f3023c5ecc"}, - {file = "fonttools-4.47.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eabae77a07c41ae0b35184894202305c3ad211a93b2eb53837c2a1143c8bc952"}, - {file = "fonttools-4.47.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a86a5ab2873ed2575d0fcdf1828143cfc6b977ac448e3dc616bb1e3d20efbafa"}, - {file = "fonttools-4.47.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13819db8445a0cec8c3ff5f243af6418ab19175072a9a92f6cc8ca7d1452754b"}, - {file = "fonttools-4.47.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4e743935139aa485fe3253fc33fe467eab6ea42583fa681223ea3f1a93dd01e6"}, - {file = "fonttools-4.47.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d49ce3ea7b7173faebc5664872243b40cf88814ca3eb135c4a3cdff66af71946"}, - {file = "fonttools-4.47.2-cp38-cp38-win32.whl", hash = "sha256:94208ea750e3f96e267f394d5588579bb64cc628e321dbb1d4243ffbc291b18b"}, - {file = "fonttools-4.47.2-cp38-cp38-win_amd64.whl", hash = "sha256:0f750037e02beb8b3569fbff701a572e62a685d2a0e840d75816592280e5feae"}, - {file = "fonttools-4.47.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d71606c9321f6701642bd4746f99b6089e53d7e9817fc6b964e90d9c5f0ecc6"}, - {file = "fonttools-4.47.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:86e0427864c6c91cf77f16d1fb9bf1bbf7453e824589e8fb8461b6ee1144f506"}, - {file = "fonttools-4.47.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a00bd0e68e88987dcc047ea31c26d40a3c61185153b03457956a87e39d43c37"}, - {file = "fonttools-4.47.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5d77479fb885ef38a16a253a2f4096bc3d14e63a56d6246bfdb56365a12b20c"}, - {file = "fonttools-4.47.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5465df494f20a7d01712b072ae3ee9ad2887004701b95cb2cc6dcb9c2c97a899"}, - {file = "fonttools-4.47.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4c811d3c73b6abac275babb8aa439206288f56fdb2c6f8835e3d7b70de8937a7"}, - {file = "fonttools-4.47.2-cp39-cp39-win32.whl", hash = "sha256:5b60e3afa9635e3dfd3ace2757039593e3bd3cf128be0ddb7a1ff4ac45fa5a50"}, - {file = "fonttools-4.47.2-cp39-cp39-win_amd64.whl", hash = "sha256:7ee48bd9d6b7e8f66866c9090807e3a4a56cf43ffad48962725a190e0dd774c8"}, - {file = "fonttools-4.47.2-py3-none-any.whl", hash = "sha256:7eb7ad665258fba68fd22228a09f347469d95a97fb88198e133595947a20a184"}, - {file = "fonttools-4.47.2.tar.gz", hash = "sha256:7df26dd3650e98ca45f1e29883c96a0b9f5bb6af8d632a6a108bc744fa0bd9b3"}, + {file = "fonttools-4.51.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:84d7751f4468dd8cdd03ddada18b8b0857a5beec80bce9f435742abc9a851a74"}, + {file = "fonttools-4.51.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8b4850fa2ef2cfbc1d1f689bc159ef0f45d8d83298c1425838095bf53ef46308"}, + {file = "fonttools-4.51.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5b48a1121117047d82695d276c2af2ee3a24ffe0f502ed581acc2673ecf1037"}, + {file = "fonttools-4.51.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:180194c7fe60c989bb627d7ed5011f2bef1c4d36ecf3ec64daec8302f1ae0716"}, + {file = "fonttools-4.51.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:96a48e137c36be55e68845fc4284533bda2980f8d6f835e26bca79d7e2006438"}, + {file = "fonttools-4.51.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:806e7912c32a657fa39d2d6eb1d3012d35f841387c8fc6cf349ed70b7c340039"}, + {file = "fonttools-4.51.0-cp310-cp310-win32.whl", hash = "sha256:32b17504696f605e9e960647c5f64b35704782a502cc26a37b800b4d69ff3c77"}, + {file = "fonttools-4.51.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7e91abdfae1b5c9e3a543f48ce96013f9a08c6c9668f1e6be0beabf0a569c1b"}, + {file = "fonttools-4.51.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a8feca65bab31479d795b0d16c9a9852902e3a3c0630678efb0b2b7941ea9c74"}, + {file = "fonttools-4.51.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ac27f436e8af7779f0bb4d5425aa3535270494d3bc5459ed27de3f03151e4c2"}, + {file = "fonttools-4.51.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e19bd9e9964a09cd2433a4b100ca7f34e34731e0758e13ba9a1ed6e5468cc0f"}, + {file = "fonttools-4.51.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2b92381f37b39ba2fc98c3a45a9d6383bfc9916a87d66ccb6553f7bdd129097"}, + {file = "fonttools-4.51.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5f6bc991d1610f5c3bbe997b0233cbc234b8e82fa99fc0b2932dc1ca5e5afec0"}, + {file = "fonttools-4.51.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9696fe9f3f0c32e9a321d5268208a7cc9205a52f99b89479d1b035ed54c923f1"}, + {file = "fonttools-4.51.0-cp311-cp311-win32.whl", hash = "sha256:3bee3f3bd9fa1d5ee616ccfd13b27ca605c2b4270e45715bd2883e9504735034"}, + {file = "fonttools-4.51.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f08c901d3866a8905363619e3741c33f0a83a680d92a9f0e575985c2634fcc1"}, + {file = "fonttools-4.51.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4060acc2bfa2d8e98117828a238889f13b6f69d59f4f2d5857eece5277b829ba"}, + {file = "fonttools-4.51.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1250e818b5f8a679ad79660855528120a8f0288f8f30ec88b83db51515411fcc"}, + {file = "fonttools-4.51.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76f1777d8b3386479ffb4a282e74318e730014d86ce60f016908d9801af9ca2a"}, + {file = "fonttools-4.51.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b5ad456813d93b9c4b7ee55302208db2b45324315129d85275c01f5cb7e61a2"}, + {file = "fonttools-4.51.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:68b3fb7775a923be73e739f92f7e8a72725fd333eab24834041365d2278c3671"}, + {file = "fonttools-4.51.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8e2f1a4499e3b5ee82c19b5ee57f0294673125c65b0a1ff3764ea1f9db2f9ef5"}, + {file = "fonttools-4.51.0-cp312-cp312-win32.whl", hash = "sha256:278e50f6b003c6aed19bae2242b364e575bcb16304b53f2b64f6551b9c000e15"}, + {file = "fonttools-4.51.0-cp312-cp312-win_amd64.whl", hash = "sha256:b3c61423f22165541b9403ee39874dcae84cd57a9078b82e1dce8cb06b07fa2e"}, + {file = "fonttools-4.51.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1621ee57da887c17312acc4b0e7ac30d3a4fb0fec6174b2e3754a74c26bbed1e"}, + {file = "fonttools-4.51.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d9298be7a05bb4801f558522adbe2feea1b0b103d5294ebf24a92dd49b78e5"}, + {file = "fonttools-4.51.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee1af4be1c5afe4c96ca23badd368d8dc75f611887fb0c0dac9f71ee5d6f110e"}, + {file = "fonttools-4.51.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c18b49adc721a7d0b8dfe7c3130c89b8704baf599fb396396d07d4aa69b824a1"}, + {file = "fonttools-4.51.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de7c29bdbdd35811f14493ffd2534b88f0ce1b9065316433b22d63ca1cd21f14"}, + {file = "fonttools-4.51.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cadf4e12a608ef1d13e039864f484c8a968840afa0258b0b843a0556497ea9ed"}, + {file = "fonttools-4.51.0-cp38-cp38-win32.whl", hash = "sha256:aefa011207ed36cd280babfaa8510b8176f1a77261833e895a9d96e57e44802f"}, + {file = "fonttools-4.51.0-cp38-cp38-win_amd64.whl", hash = "sha256:865a58b6e60b0938874af0968cd0553bcd88e0b2cb6e588727117bd099eef836"}, + {file = "fonttools-4.51.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:60a3409c9112aec02d5fb546f557bca6efa773dcb32ac147c6baf5f742e6258b"}, + {file = "fonttools-4.51.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7e89853d8bea103c8e3514b9f9dc86b5b4120afb4583b57eb10dfa5afbe0936"}, + {file = "fonttools-4.51.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56fc244f2585d6c00b9bcc59e6593e646cf095a96fe68d62cd4da53dd1287b55"}, + {file = "fonttools-4.51.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d145976194a5242fdd22df18a1b451481a88071feadf251221af110ca8f00ce"}, + {file = "fonttools-4.51.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5b8cab0c137ca229433570151b5c1fc6af212680b58b15abd797dcdd9dd5051"}, + {file = "fonttools-4.51.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:54dcf21a2f2d06ded676e3c3f9f74b2bafded3a8ff12f0983160b13e9f2fb4a7"}, + {file = "fonttools-4.51.0-cp39-cp39-win32.whl", hash = "sha256:0118ef998a0699a96c7b28457f15546815015a2710a1b23a7bf6c1be60c01636"}, + {file = "fonttools-4.51.0-cp39-cp39-win_amd64.whl", hash = "sha256:599bdb75e220241cedc6faebfafedd7670335d2e29620d207dd0378a4e9ccc5a"}, + {file = "fonttools-4.51.0-py3-none-any.whl", hash = "sha256:15c94eeef6b095831067f72c825eb0e2d48bb4cea0647c1b05c981ecba2bf39f"}, + {file = "fonttools-4.51.0.tar.gz", hash = "sha256:dc0673361331566d7a663d7ce0f6fdcbfbdc1f59c6e3ed1165ad7202ca183c68"}, ] [package.extras] -all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] graphite = ["lz4 (>=1.7.4.2)"] interpolatable = ["munkres", "pycairo", "scipy"] -lxml = ["lxml (>=4.0,<5)"] +lxml = ["lxml (>=4.0)"] pathops = ["skia-pathops (>=0.5.0)"] plot = ["matplotlib"] repacker = ["uharfbuzz (>=0.23.0)"] @@ -258,39 +258,40 @@ files = [ [[package]] name = "matplotlib" -version = "3.8.2" +version = "3.9.0" description = "Python plotting package" optional = false python-versions = ">=3.9" files = [ - {file = "matplotlib-3.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:09796f89fb71a0c0e1e2f4bdaf63fb2cefc84446bb963ecdeb40dfee7dfa98c7"}, - {file = "matplotlib-3.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9c6976748a25e8b9be51ea028df49b8e561eed7809146da7a47dbecebab367"}, - {file = "matplotlib-3.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78e4f2cedf303869b782071b55fdde5987fda3038e9d09e58c91cc261b5ad18"}, - {file = "matplotlib-3.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e208f46cf6576a7624195aa047cb344a7f802e113bb1a06cfd4bee431de5e31"}, - {file = "matplotlib-3.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a569130ff53798ea5f50afce7406e91fdc471ca1e0e26ba976a8c734c9427a"}, - {file = "matplotlib-3.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:830f00640c965c5b7f6bc32f0d4ce0c36dfe0379f7dd65b07a00c801713ec40a"}, - {file = "matplotlib-3.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d86593ccf546223eb75a39b44c32788e6f6440d13cfc4750c1c15d0fcb850b63"}, - {file = "matplotlib-3.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a5430836811b7652991939012f43d2808a2db9b64ee240387e8c43e2e5578c8"}, - {file = "matplotlib-3.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9576723858a78751d5aacd2497b8aef29ffea6d1c95981505877f7ac28215c6"}, - {file = "matplotlib-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba9cbd8ac6cf422f3102622b20f8552d601bf8837e49a3afed188d560152788"}, - {file = "matplotlib-3.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:03f9d160a29e0b65c0790bb07f4f45d6a181b1ac33eb1bb0dd225986450148f0"}, - {file = "matplotlib-3.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:3773002da767f0a9323ba1a9b9b5d00d6257dbd2a93107233167cfb581f64717"}, - {file = "matplotlib-3.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:4c318c1e95e2f5926fba326f68177dee364aa791d6df022ceb91b8221bd0a627"}, - {file = "matplotlib-3.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:091275d18d942cf1ee9609c830a1bc36610607d8223b1b981c37d5c9fc3e46a4"}, - {file = "matplotlib-3.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b0f3b8ea0e99e233a4bcc44590f01604840d833c280ebb8fe5554fd3e6cfe8d"}, - {file = "matplotlib-3.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b1704a530395aaf73912be741c04d181f82ca78084fbd80bc737be04848331"}, - {file = "matplotlib-3.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533b0e3b0c6768eef8cbe4b583731ce25a91ab54a22f830db2b031e83cca9213"}, - {file = "matplotlib-3.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:0f4fc5d72b75e2c18e55eb32292659cf731d9d5b312a6eb036506304f4675630"}, - {file = "matplotlib-3.8.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:deaed9ad4da0b1aea77fe0aa0cebb9ef611c70b3177be936a95e5d01fa05094f"}, - {file = "matplotlib-3.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:172f4d0fbac3383d39164c6caafd3255ce6fa58f08fc392513a0b1d3b89c4f89"}, - {file = "matplotlib-3.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7d36c2209d9136cd8e02fab1c0ddc185ce79bc914c45054a9f514e44c787917"}, - {file = "matplotlib-3.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5864bdd7da445e4e5e011b199bb67168cdad10b501750367c496420f2ad00843"}, - {file = "matplotlib-3.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ef8345b48e95cee45ff25192ed1f4857273117917a4dcd48e3905619bcd9c9b8"}, - {file = "matplotlib-3.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:7c48d9e221b637c017232e3760ed30b4e8d5dfd081daf327e829bf2a72c731b4"}, - {file = "matplotlib-3.8.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa11b3c6928a1e496c1a79917d51d4cd5d04f8a2e75f21df4949eeefdf697f4b"}, - {file = "matplotlib-3.8.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1095fecf99eeb7384dabad4bf44b965f929a5f6079654b681193edf7169ec20"}, - {file = "matplotlib-3.8.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:bddfb1db89bfaa855912261c805bd0e10218923cc262b9159a49c29a7a1c1afa"}, - {file = "matplotlib-3.8.2.tar.gz", hash = "sha256:01a978b871b881ee76017152f1f1a0cbf6bd5f7b8ff8c96df0df1bd57d8755a1"}, + {file = "matplotlib-3.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2bcee1dffaf60fe7656183ac2190bd630842ff87b3153afb3e384d966b57fe56"}, + {file = "matplotlib-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f988bafb0fa39d1074ddd5bacd958c853e11def40800c5824556eb630f94d3b"}, + {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe428e191ea016bb278758c8ee82a8129c51d81d8c4bc0846c09e7e8e9057241"}, + {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaf3978060a106fab40c328778b148f590e27f6fa3cd15a19d6892575bce387d"}, + {file = "matplotlib-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e7f03e5cbbfacdd48c8ea394d365d91ee8f3cae7e6ec611409927b5ed997ee4"}, + {file = "matplotlib-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:13beb4840317d45ffd4183a778685e215939be7b08616f431c7795276e067463"}, + {file = "matplotlib-3.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:063af8587fceeac13b0936c42a2b6c732c2ab1c98d38abc3337e430e1ff75e38"}, + {file = "matplotlib-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a2fa6d899e17ddca6d6526cf6e7ba677738bf2a6a9590d702c277204a7c6152"}, + {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550cdda3adbd596078cca7d13ed50b77879104e2e46392dcd7c75259d8f00e85"}, + {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cce0f31b351e3551d1f3779420cf8f6ec0d4a8cf9c0237a3b549fd28eb4abb"}, + {file = "matplotlib-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c53aeb514ccbbcbab55a27f912d79ea30ab21ee0531ee2c09f13800efb272674"}, + {file = "matplotlib-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5be985db2596d761cdf0c2eaf52396f26e6a64ab46bd8cd810c48972349d1be"}, + {file = "matplotlib-3.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c79f3a585f1368da6049318bdf1f85568d8d04b2e89fc24b7e02cc9b62017382"}, + {file = "matplotlib-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bdd1ecbe268eb3e7653e04f451635f0fb0f77f07fd070242b44c076c9106da84"}, + {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d38e85a1a6d732f645f1403ce5e6727fd9418cd4574521d5803d3d94911038e5"}, + {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a490715b3b9984fa609116481b22178348c1a220a4499cda79132000a79b4db"}, + {file = "matplotlib-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8146ce83cbc5dc71c223a74a1996d446cd35cfb6a04b683e1446b7e6c73603b7"}, + {file = "matplotlib-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:d91a4ffc587bacf5c4ce4ecfe4bcd23a4b675e76315f2866e588686cc97fccdf"}, + {file = "matplotlib-3.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:616fabf4981a3b3c5a15cd95eba359c8489c4e20e03717aea42866d8d0465956"}, + {file = "matplotlib-3.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd53c79fd02f1c1808d2cfc87dd3cf4dbc63c5244a58ee7944497107469c8d8a"}, + {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06a478f0d67636554fa78558cfbcd7b9dba85b51f5c3b5a0c9be49010cf5f321"}, + {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81c40af649d19c85f8073e25e5806926986806fa6d54be506fbf02aef47d5a89"}, + {file = "matplotlib-3.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52146fc3bd7813cc784562cb93a15788be0b2875c4655e2cc6ea646bfa30344b"}, + {file = "matplotlib-3.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:0fc51eaa5262553868461c083d9adadb11a6017315f3a757fc45ec6ec5f02888"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bd4f2831168afac55b881db82a7730992aa41c4f007f1913465fb182d6fb20c0"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:290d304e59be2b33ef5c2d768d0237f5bd132986bdcc66f80bc9bcc300066a03"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff2e239c26be4f24bfa45860c20ffccd118d270c5b5d081fa4ea409b5469fcd"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:af4001b7cae70f7eaacfb063db605280058246de590fa7874f00f62259f2df7e"}, + {file = "matplotlib-3.9.0.tar.gz", hash = "sha256:e6d29ea6c19e34b30fb7d88b7081f869a03014f66fe06d62cc77d5a6ea88ed7a"}, ] [package.dependencies] @@ -298,46 +299,49 @@ contourpy = ">=1.0.1" cycler = ">=0.10" fonttools = ">=4.22.0" kiwisolver = ">=1.3.1" -numpy = ">=1.21,<2" +numpy = ">=1.23" packaging = ">=20.0" pillow = ">=8" pyparsing = ">=2.3.1" python-dateutil = ">=2.7" +[package.extras] +dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] + [[package]] name = "mypy" -version = "1.8.0" +version = "1.10.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, - {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, - {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, - {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, - {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, - {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, - {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, - {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, - {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, - {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, - {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, - {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, - {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, - {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, - {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, - {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, - {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, - {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, - {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, + {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"}, + {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"}, + {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"}, + {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"}, + {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, + {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, + {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, + {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, + {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, + {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, + {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, + {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"}, + {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"}, + {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"}, + {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"}, + {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"}, + {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"}, + {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"}, + {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"}, + {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"}, + {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"}, + {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, + {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, ] [package.dependencies] @@ -364,103 +368,103 @@ files = [ [[package]] name = "numpy" -version = "1.26.3" +version = "1.26.4" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" files = [ - {file = "numpy-1.26.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf"}, - {file = "numpy-1.26.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd"}, - {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d45b3ec2faed4baca41c76617fcdcfa4f684ff7a151ce6fc78ad3b6e85af0a6"}, - {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdd2b45bf079d9ad90377048e2747a0c82351989a2165821f0c96831b4a2a54b"}, - {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:211ddd1e94817ed2d175b60b6374120244a4dd2287f4ece45d49228b4d529178"}, - {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1240f767f69d7c4c8a29adde2310b871153df9b26b5cb2b54a561ac85146485"}, - {file = "numpy-1.26.3-cp310-cp310-win32.whl", hash = "sha256:21a9484e75ad018974a2fdaa216524d64ed4212e418e0a551a2d83403b0531d3"}, - {file = "numpy-1.26.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e1591f6ae98bcfac2a4bbf9221c0b92ab49762228f38287f6eeb5f3f55905ce"}, - {file = "numpy-1.26.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b831295e5472954104ecb46cd98c08b98b49c69fdb7040483aff799a755a7374"}, - {file = "numpy-1.26.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e87562b91f68dd8b1c39149d0323b42e0082db7ddb8e934ab4c292094d575d6"}, - {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c66d6fec467e8c0f975818c1796d25c53521124b7cfb760114be0abad53a0a2"}, - {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f25e2811a9c932e43943a2615e65fc487a0b6b49218899e62e426e7f0a57eeda"}, - {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af36e0aa45e25c9f57bf684b1175e59ea05d9a7d3e8e87b7ae1a1da246f2767e"}, - {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:51c7f1b344f302067b02e0f5b5d2daa9ed4a721cf49f070280ac202738ea7f00"}, - {file = "numpy-1.26.3-cp311-cp311-win32.whl", hash = "sha256:7ca4f24341df071877849eb2034948459ce3a07915c2734f1abb4018d9c49d7b"}, - {file = "numpy-1.26.3-cp311-cp311-win_amd64.whl", hash = "sha256:39763aee6dfdd4878032361b30b2b12593fb445ddb66bbac802e2113eb8a6ac4"}, - {file = "numpy-1.26.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a7081fd19a6d573e1a05e600c82a1c421011db7935ed0d5c483e9dd96b99cf13"}, - {file = "numpy-1.26.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12c70ac274b32bc00c7f61b515126c9205323703abb99cd41836e8125ea0043e"}, - {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f784e13e598e9594750b2ef6729bcd5a47f6cfe4a12cca13def35e06d8163e3"}, - {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f24750ef94d56ce6e33e4019a8a4d68cfdb1ef661a52cdaee628a56d2437419"}, - {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:77810ef29e0fb1d289d225cabb9ee6cf4d11978a00bb99f7f8ec2132a84e0166"}, - {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8ed07a90f5450d99dad60d3799f9c03c6566709bd53b497eb9ccad9a55867f36"}, - {file = "numpy-1.26.3-cp312-cp312-win32.whl", hash = "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511"}, - {file = "numpy-1.26.3-cp312-cp312-win_amd64.whl", hash = "sha256:da4b0c6c699a0ad73c810736303f7fbae483bcb012e38d7eb06a5e3b432c981b"}, - {file = "numpy-1.26.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1666f634cb3c80ccbd77ec97bc17337718f56d6658acf5d3b906ca03e90ce87f"}, - {file = "numpy-1.26.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18c3319a7d39b2c6a9e3bb75aab2304ab79a811ac0168a671a62e6346c29b03f"}, - {file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b7e807d6888da0db6e7e75838444d62495e2b588b99e90dd80c3459594e857b"}, - {file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4d362e17bcb0011738c2d83e0a65ea8ce627057b2fdda37678f4374a382a137"}, - {file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b8c275f0ae90069496068c714387b4a0eba5d531aace269559ff2b43655edd58"}, - {file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc0743f0302b94f397a4a65a660d4cd24267439eb16493fb3caad2e4389bccbb"}, - {file = "numpy-1.26.3-cp39-cp39-win32.whl", hash = "sha256:9bc6d1a7f8cedd519c4b7b1156d98e051b726bf160715b769106661d567b3f03"}, - {file = "numpy-1.26.3-cp39-cp39-win_amd64.whl", hash = "sha256:867e3644e208c8922a3be26fc6bbf112a035f50f0a86497f98f228c50c607bb2"}, - {file = "numpy-1.26.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3c67423b3703f8fbd90f5adaa37f85b5794d3366948efe9a5190a5f3a83fc34e"}, - {file = "numpy-1.26.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46f47ee566d98849323f01b349d58f2557f02167ee301e5e28809a8c0e27a2d0"}, - {file = "numpy-1.26.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a8474703bffc65ca15853d5fd4d06b18138ae90c17c8d12169968e998e448bb5"}, - {file = "numpy-1.26.3.tar.gz", hash = "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] [[package]] name = "packaging" -version = "23.2" +version = "24.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] name = "pandas" -version = "2.2.0" +version = "2.2.2" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" files = [ - {file = "pandas-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8108ee1712bb4fa2c16981fba7e68b3f6ea330277f5ca34fa8d557e986a11670"}, - {file = "pandas-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:736da9ad4033aeab51d067fc3bd69a0ba36f5a60f66a527b3d72e2030e63280a"}, - {file = "pandas-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38e0b4fc3ddceb56ec8a287313bc22abe17ab0eb184069f08fc6a9352a769b18"}, - {file = "pandas-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20404d2adefe92aed3b38da41d0847a143a09be982a31b85bc7dd565bdba0f4e"}, - {file = "pandas-2.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ea3ee3f125032bfcade3a4cf85131ed064b4f8dd23e5ce6fa16473e48ebcaf5"}, - {file = "pandas-2.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f9670b3ac00a387620489dfc1bca66db47a787f4e55911f1293063a78b108df1"}, - {file = "pandas-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a946f210383c7e6d16312d30b238fd508d80d927014f3b33fb5b15c2f895430"}, - {file = "pandas-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a1b438fa26b208005c997e78672f1aa8138f67002e833312e6230f3e57fa87d5"}, - {file = "pandas-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8ce2fbc8d9bf303ce54a476116165220a1fedf15985b09656b4b4275300e920b"}, - {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2707514a7bec41a4ab81f2ccce8b382961a29fbe9492eab1305bb075b2b1ff4f"}, - {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85793cbdc2d5bc32620dc8ffa715423f0c680dacacf55056ba13454a5be5de88"}, - {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cfd6c2491dc821b10c716ad6776e7ab311f7df5d16038d0b7458bc0b67dc10f3"}, - {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a146b9dcacc3123aa2b399df1a284de5f46287a4ab4fbfc237eac98a92ebcb71"}, - {file = "pandas-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbc1b53c0e1fdf16388c33c3cca160f798d38aea2978004dd3f4d3dec56454c9"}, - {file = "pandas-2.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a41d06f308a024981dcaa6c41f2f2be46a6b186b902c94c2674e8cb5c42985bc"}, - {file = "pandas-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:159205c99d7a5ce89ecfc37cb08ed179de7783737cea403b295b5eda8e9c56d1"}, - {file = "pandas-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1e1f3861ea9132b32f2133788f3b14911b68102d562715d71bd0013bc45440"}, - {file = "pandas-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:761cb99b42a69005dec2b08854fb1d4888fdf7b05db23a8c5a099e4b886a2106"}, - {file = "pandas-2.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a20628faaf444da122b2a64b1e5360cde100ee6283ae8effa0d8745153809a2e"}, - {file = "pandas-2.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f5be5d03ea2073627e7111f61b9f1f0d9625dc3c4d8dda72cc827b0c58a1d042"}, - {file = "pandas-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:a626795722d893ed6aacb64d2401d017ddc8a2341b49e0384ab9bf7112bdec30"}, - {file = "pandas-2.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9f66419d4a41132eb7e9a73dcec9486cf5019f52d90dd35547af11bc58f8637d"}, - {file = "pandas-2.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:57abcaeda83fb80d447f28ab0cc7b32b13978f6f733875ebd1ed14f8fbc0f4ab"}, - {file = "pandas-2.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e60f1f7dba3c2d5ca159e18c46a34e7ca7247a73b5dd1a22b6d59707ed6b899a"}, - {file = "pandas-2.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb61dc8567b798b969bcc1fc964788f5a68214d333cade8319c7ab33e2b5d88a"}, - {file = "pandas-2.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:52826b5f4ed658fa2b729264d63f6732b8b29949c7fd234510d57c61dbeadfcd"}, - {file = "pandas-2.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bde2bc699dbd80d7bc7f9cab1e23a95c4375de615860ca089f34e7c64f4a8de7"}, - {file = "pandas-2.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:3de918a754bbf2da2381e8a3dcc45eede8cd7775b047b923f9006d5f876802ae"}, - {file = "pandas-2.2.0.tar.gz", hash = "sha256:30b83f7c3eb217fb4d1b494a57a2fda5444f17834f5df2de6b2ffff68dc3c8e2"}, + {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, + {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, + {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, + {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, + {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, + {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, + {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, ] [package.dependencies] numpy = [ - {version = ">=1.22.4,<2", markers = "python_version < \"3.11\""}, - {version = ">=1.23.2,<2", markers = "python_version == \"3.11\""}, - {version = ">=1.26.0,<2", markers = "python_version >= \"3.12\""}, + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -485,6 +489,7 @@ parquet = ["pyarrow (>=10.0.1)"] performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] plot = ["matplotlib (>=3.6.3)"] postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] spss = ["pyreadstat (>=1.2.0)"] sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] @@ -492,79 +497,80 @@ xml = ["lxml (>=4.9.2)"] [[package]] name = "pillow" -version = "10.2.0" +version = "10.3.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" files = [ - {file = "pillow-10.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e"}, - {file = "pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2"}, - {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c"}, - {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0"}, - {file = "pillow-10.2.0-cp310-cp310-win32.whl", hash = "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023"}, - {file = "pillow-10.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72"}, - {file = "pillow-10.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad"}, - {file = "pillow-10.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5"}, - {file = "pillow-10.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311"}, - {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1"}, - {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757"}, - {file = "pillow-10.2.0-cp311-cp311-win32.whl", hash = "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068"}, - {file = "pillow-10.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56"}, - {file = "pillow-10.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1"}, - {file = "pillow-10.2.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef"}, - {file = "pillow-10.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04"}, - {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f"}, - {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb"}, - {file = "pillow-10.2.0-cp312-cp312-win32.whl", hash = "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f"}, - {file = "pillow-10.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9"}, - {file = "pillow-10.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48"}, - {file = "pillow-10.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9"}, - {file = "pillow-10.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d"}, - {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6"}, - {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe"}, - {file = "pillow-10.2.0-cp38-cp38-win32.whl", hash = "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e"}, - {file = "pillow-10.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39"}, - {file = "pillow-10.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67"}, - {file = "pillow-10.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13"}, - {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7"}, - {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591"}, - {file = "pillow-10.2.0-cp39-cp39-win32.whl", hash = "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516"}, - {file = "pillow-10.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8"}, - {file = "pillow-10.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6"}, - {file = "pillow-10.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"}, - {file = "pillow-10.2.0.tar.gz", hash = "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, + {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, + {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, + {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, + {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, + {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, + {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, + {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, + {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, + {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, + {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, + {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, + {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, + {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, + {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, + {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, ] [package.extras] @@ -602,13 +608,13 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] [[package]] name = "pyparsing" -version = "3.1.1" +version = "3.1.2" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.6.8" files = [ - {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"}, - {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"}, + {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, + {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, ] [package.extras] @@ -616,13 +622,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "python-dateutil" -version = "2.8.2" +version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] [package.dependencies] @@ -630,82 +636,82 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2023.3.post1" +version = "2024.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, - {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, ] [[package]] name = "ruff" -version = "0.1.14" +version = "0.4.4" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.1.14-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:96f76536df9b26622755c12ed8680f159817be2f725c17ed9305b472a757cdbb"}, - {file = "ruff-0.1.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab3f71f64498c7241123bb5a768544cf42821d2a537f894b22457a543d3ca7a9"}, - {file = "ruff-0.1.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7060156ecc572b8f984fd20fd8b0fcb692dd5d837b7606e968334ab7ff0090ab"}, - {file = "ruff-0.1.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a53d8e35313d7b67eb3db15a66c08434809107659226a90dcd7acb2afa55faea"}, - {file = "ruff-0.1.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bea9be712b8f5b4ebed40e1949379cfb2a7d907f42921cf9ab3aae07e6fba9eb"}, - {file = "ruff-0.1.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2270504d629a0b064247983cbc495bed277f372fb9eaba41e5cf51f7ba705a6a"}, - {file = "ruff-0.1.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80258bb3b8909b1700610dfabef7876423eed1bc930fe177c71c414921898efa"}, - {file = "ruff-0.1.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:653230dd00aaf449eb5ff25d10a6e03bc3006813e2cb99799e568f55482e5cae"}, - {file = "ruff-0.1.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b3acc6c4e6928459ba9eb7459dd4f0c4bf266a053c863d72a44c33246bfdbf"}, - {file = "ruff-0.1.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6b3dadc9522d0eccc060699a9816e8127b27addbb4697fc0c08611e4e6aeb8b5"}, - {file = "ruff-0.1.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1c8eca1a47b4150dc0fbec7fe68fc91c695aed798532a18dbb1424e61e9b721f"}, - {file = "ruff-0.1.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:62ce2ae46303ee896fc6811f63d6dabf8d9c389da0f3e3f2bce8bc7f15ef5488"}, - {file = "ruff-0.1.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b2027dde79d217b211d725fc833e8965dc90a16d0d3213f1298f97465956661b"}, - {file = "ruff-0.1.14-py3-none-win32.whl", hash = "sha256:722bafc299145575a63bbd6b5069cb643eaa62546a5b6398f82b3e4403329cab"}, - {file = "ruff-0.1.14-py3-none-win_amd64.whl", hash = "sha256:e3d241aa61f92b0805a7082bd89a9990826448e4d0398f0e2bc8f05c75c63d99"}, - {file = "ruff-0.1.14-py3-none-win_arm64.whl", hash = "sha256:269302b31ade4cde6cf6f9dd58ea593773a37ed3f7b97e793c8594b262466b67"}, - {file = "ruff-0.1.14.tar.gz", hash = "sha256:ad3f8088b2dfd884820289a06ab718cde7d38b94972212cc4ba90d5fbc9955f3"}, + {file = "ruff-0.4.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:29d44ef5bb6a08e235c8249294fa8d431adc1426bfda99ed493119e6f9ea1bf6"}, + {file = "ruff-0.4.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c4efe62b5bbb24178c950732ddd40712b878a9b96b1d02b0ff0b08a090cbd891"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8e2f1e8fc12d07ab521a9005d68a969e167b589cbcaee354cb61e9d9de9c15"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60ed88b636a463214905c002fa3eaab19795679ed55529f91e488db3fe8976ab"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b90fc5e170fc71c712cc4d9ab0e24ea505c6a9e4ebf346787a67e691dfb72e85"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8e7e6ebc10ef16dcdc77fd5557ee60647512b400e4a60bdc4849468f076f6eef"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ddb2c494fb79fc208cd15ffe08f32b7682519e067413dbaf5f4b01a6087bcd"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c51c928a14f9f0a871082603e25a1588059b7e08a920f2f9fa7157b5bf08cfe9"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5eb0a4bfd6400b7d07c09a7725e1a98c3b838be557fee229ac0f84d9aa49c36"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b1867ee9bf3acc21778dcb293db504692eda5f7a11a6e6cc40890182a9f9e595"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1aecced1269481ef2894cc495647392a34b0bf3e28ff53ed95a385b13aa45768"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da73eb616b3241a307b837f32756dc20a0b07e2bcb694fec73699c93d04a69e"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:958b4ea5589706a81065e2a776237de2ecc3e763342e5cc8e02a4a4d8a5e6f95"}, + {file = "ruff-0.4.4-py3-none-win32.whl", hash = "sha256:cb53473849f011bca6e754f2cdf47cafc9c4f4ff4570003a0dad0b9b6890e876"}, + {file = "ruff-0.4.4-py3-none-win_amd64.whl", hash = "sha256:424e5b72597482543b684c11def82669cc6b395aa8cc69acc1858b5ef3e5daae"}, + {file = "ruff-0.4.4-py3-none-win_arm64.whl", hash = "sha256:39df0537b47d3b597293edbb95baf54ff5b49589eb7ff41926d8243caa995ea6"}, + {file = "ruff-0.4.4.tar.gz", hash = "sha256:f87ea42d5cdebdc6a69761a9d0bc83ae9b3b30d0ad78952005ba6568d6c022af"}, ] [[package]] name = "scipy" -version = "1.12.0" +version = "1.13.0" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.9" files = [ - {file = "scipy-1.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:78e4402e140879387187f7f25d91cc592b3501a2e51dfb320f48dfb73565f10b"}, - {file = "scipy-1.12.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:f5f00ebaf8de24d14b8449981a2842d404152774c1a1d880c901bf454cb8e2a1"}, - {file = "scipy-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e53958531a7c695ff66c2e7bb7b79560ffdc562e2051644c5576c39ff8efb563"}, - {file = "scipy-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e32847e08da8d895ce09d108a494d9eb78974cf6de23063f93306a3e419960c"}, - {file = "scipy-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4c1020cad92772bf44b8e4cdabc1df5d87376cb219742549ef69fc9fd86282dd"}, - {file = "scipy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:75ea2a144096b5e39402e2ff53a36fecfd3b960d786b7efd3c180e29c39e53f2"}, - {file = "scipy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:408c68423f9de16cb9e602528be4ce0d6312b05001f3de61fe9ec8b1263cad08"}, - {file = "scipy-1.12.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5adfad5dbf0163397beb4aca679187d24aec085343755fcdbdeb32b3679f254c"}, - {file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3003652496f6e7c387b1cf63f4bb720951cfa18907e998ea551e6de51a04467"}, - {file = "scipy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b8066bce124ee5531d12a74b617d9ac0ea59245246410e19bca549656d9a40a"}, - {file = "scipy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8bee4993817e204d761dba10dbab0774ba5a8612e57e81319ea04d84945375ba"}, - {file = "scipy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a24024d45ce9a675c1fb8494e8e5244efea1c7a09c60beb1eeb80373d0fecc70"}, - {file = "scipy-1.12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e7e76cc48638228212c747ada851ef355c2bb5e7f939e10952bc504c11f4e372"}, - {file = "scipy-1.12.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f7ce148dffcd64ade37b2df9315541f9adad6efcaa86866ee7dd5db0c8f041c3"}, - {file = "scipy-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c39f92041f490422924dfdb782527a4abddf4707616e07b021de33467f917bc"}, - {file = "scipy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ebda398f86e56178c2fa94cad15bf457a218a54a35c2a7b4490b9f9cb2676c"}, - {file = "scipy-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:95e5c750d55cf518c398a8240571b0e0782c2d5a703250872f36eaf737751338"}, - {file = "scipy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e646d8571804a304e1da01040d21577685ce8e2db08ac58e543eaca063453e1c"}, - {file = "scipy-1.12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:913d6e7956c3a671de3b05ccb66b11bc293f56bfdef040583a7221d9e22a2e35"}, - {file = "scipy-1.12.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba1b0c7256ad75401c73e4b3cf09d1f176e9bd4248f0d3112170fb2ec4db067"}, - {file = "scipy-1.12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:730badef9b827b368f351eacae2e82da414e13cf8bd5051b4bdfd720271a5371"}, - {file = "scipy-1.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6546dc2c11a9df6926afcbdd8a3edec28566e4e785b915e849348c6dd9f3f490"}, - {file = "scipy-1.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:196ebad3a4882081f62a5bf4aeb7326aa34b110e533aab23e4374fcccb0890dc"}, - {file = "scipy-1.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:b360f1b6b2f742781299514e99ff560d1fe9bd1bff2712894b52abe528d1fd1e"}, - {file = "scipy-1.12.0.tar.gz", hash = "sha256:4bf5abab8a36d20193c698b0f1fc282c1d083c94723902c447e5d2f1780936a3"}, + {file = "scipy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba419578ab343a4e0a77c0ef82f088238a93eef141b2b8017e46149776dfad4d"}, + {file = "scipy-1.13.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:22789b56a999265431c417d462e5b7f2b487e831ca7bef5edeb56efe4c93f86e"}, + {file = "scipy-1.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05f1432ba070e90d42d7fd836462c50bf98bd08bed0aa616c359eed8a04e3922"}, + {file = "scipy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8434f6f3fa49f631fae84afee424e2483289dfc30a47755b4b4e6b07b2633a4"}, + {file = "scipy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:dcbb9ea49b0167de4167c40eeee6e167caeef11effb0670b554d10b1e693a8b9"}, + {file = "scipy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:1d2f7bb14c178f8b13ebae93f67e42b0a6b0fc50eba1cd8021c9b6e08e8fb1cd"}, + {file = "scipy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fbcf8abaf5aa2dc8d6400566c1a727aed338b5fe880cde64907596a89d576fa"}, + {file = "scipy-1.13.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5e4a756355522eb60fcd61f8372ac2549073c8788f6114449b37e9e8104f15a5"}, + {file = "scipy-1.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5acd8e1dbd8dbe38d0004b1497019b2dbbc3d70691e65d69615f8a7292865d7"}, + {file = "scipy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ff7dad5d24a8045d836671e082a490848e8639cabb3dbdacb29f943a678683d"}, + {file = "scipy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4dca18c3ffee287ddd3bc8f1dabaf45f5305c5afc9f8ab9cbfab855e70b2df5c"}, + {file = "scipy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:a2f471de4d01200718b2b8927f7d76b5d9bde18047ea0fa8bd15c5ba3f26a1d6"}, + {file = "scipy-1.13.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d0de696f589681c2802f9090fff730c218f7c51ff49bf252b6a97ec4a5d19e8b"}, + {file = "scipy-1.13.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:b2a3ff461ec4756b7e8e42e1c681077349a038f0686132d623fa404c0bee2551"}, + {file = "scipy-1.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf9fe63e7a4bf01d3645b13ff2aa6dea023d38993f42aaac81a18b1bda7a82a"}, + {file = "scipy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e7626dfd91cdea5714f343ce1176b6c4745155d234f1033584154f60ef1ff42"}, + {file = "scipy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:109d391d720fcebf2fbe008621952b08e52907cf4c8c7efc7376822151820820"}, + {file = "scipy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:8930ae3ea371d6b91c203b1032b9600d69c568e537b7988a3073dfe4d4774f21"}, + {file = "scipy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5407708195cb38d70fd2d6bb04b1b9dd5c92297d86e9f9daae1576bd9e06f602"}, + {file = "scipy-1.13.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:ac38c4c92951ac0f729c4c48c9e13eb3675d9986cc0c83943784d7390d540c78"}, + {file = "scipy-1.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c74543c4fbeb67af6ce457f6a6a28e5d3739a87f62412e4a16e46f164f0ae5"}, + {file = "scipy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28e286bf9ac422d6beb559bc61312c348ca9b0f0dae0d7c5afde7f722d6ea13d"}, + {file = "scipy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33fde20efc380bd23a78a4d26d59fc8704e9b5fd9b08841693eb46716ba13d86"}, + {file = "scipy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:45c08bec71d3546d606989ba6e7daa6f0992918171e2a6f7fbedfa7361c2de1e"}, + {file = "scipy-1.13.0.tar.gz", hash = "sha256:58569af537ea29d3f78e5abd18398459f195546bb3be23d16677fb26616cc11e"}, ] [package.dependencies] -numpy = ">=1.22.4,<1.29.0" +numpy = ">=1.22.4,<2.3" [package.extras] -dev = ["click", "cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] -doc = ["jupytext", "matplotlib (>2)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] -test = ["asv", "gmpy2", "hypothesis", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] +doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.12.0)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] +test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] [[package]] name = "seaborn" @@ -752,27 +758,27 @@ files = [ [[package]] name = "typing-extensions" -version = "4.9.0" +version = "4.11.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, - {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, ] [[package]] name = "tzdata" -version = "2023.4" +version = "2024.1" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ - {file = "tzdata-2023.4-py2.py3-none-any.whl", hash = "sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3"}, - {file = "tzdata-2023.4.tar.gz", hash = "sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9"}, + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "55a6ffb8a4095af0f7aa7c1b212b95a8c2f34f566418d6d2c91deb848e08b9b4" +content-hash = "afd25f7a11631c9a47ca60d9a7f7f6fd9b488f9da263d79150902aa6a3a1b3c5" diff --git a/pyproject.toml b/pyproject.toml index 2c9da63f..90598022 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,14 +4,17 @@ version = "0.1.0" description = "" authors = ["Hyperskill Team"] readme = "README.md" +packages = [ + { include = "hstest" }, +] [tool.poetry.dependencies] python = "^3.10" psutil-wheels = "^5.8.0" [tool.poetry.group.dev.dependencies] -mypy = "1.8.0" -ruff = "0.1.14" +mypy = "1.10.0" +ruff = "0.4.4" matplotlib = "^3.8.2" seaborn = "^0.13.2" pandas = "^2.2.0" @@ -22,6 +25,36 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.ruff] +line-length = 100 +target-version = "py310" +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "tests/outcomes", + "tests/projects", + "tests/sql", + "venv", +] + +[tool.ruff.lint] select = [ "ALL", ] @@ -52,7 +85,6 @@ ignore = [ "PLC1901", # `record['bio'] == ''` can be simplified to `not record['bio']` as an empty string is falsey "PLR0904", # Too many public methods "PLR0916", # Too many Boolean expressions - "PLR1706", # Consider using if-else expression (`self.obj.fullname if self.obj else ''`) "PLR6301", # Method could be a function, class method, or static method "PT", # Use a regular `assert` instead of unittest-style `assertEqual` "S101", # Use of `assert` detected @@ -75,48 +107,20 @@ ignore = [ "W191", # Indentation contains tabs ] -# Exclude a variety of commonly ignored directories. -exclude = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".hg", - ".mypy_cache", - ".nox", - ".pants.d", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "tests/outcomes", - "tests/projects", - "tests/sql", - "venv", -] -line-length = 100 -target-version = "py310" - -[tool.ruff.mccabe] -max-complexity = 41 +[tool.ruff.lint.mccabe] +max-complexity = 56 -[tool.ruff.pydocstyle] +[tool.ruff.lint.pydocstyle] convention = "google" [tool.ruff.lint.pylint] -max-args = 5 -max-branches = 15 -max-returns = 6 -max-statements = 72 +max-args = 11 +max-branches = 27 +max-returns = 7 +max-statements = 153 +max-nested-blocks = 7 -[tool.ruff.isort] +[tool.ruff.lint.isort] combine-as-imports = true order-by-type = false required-imports = ["from __future__ import annotations"] diff --git a/tests/testing.py b/tests/testing.py index 1c2779e6..a6da0cf3 100644 --- a/tests/testing.py +++ b/tests/testing.py @@ -40,16 +40,18 @@ def flush(self) -> None: def close(self) -> None: self.original.close() + MAX_REPEATS: Final = 5 + class UnitTesting: @staticmethod def test_all() -> bool: old_run = unittest.TestCase.run - def run(self: unittest.TestCase, - result: unittest.TestResult | None = None, - repeats: int = 0) -> unittest.TestResult: + def run( + self: unittest.TestCase, result: unittest.TestResult | None = None, repeats: int = 0 + ) -> unittest.TestResult: failures_before = 0 if result is None else len(result.failures) test_result = old_run(self, result=result) is_project_test = "tests.projects." in str(self) @@ -71,11 +73,8 @@ def run(self: unittest.TestCase, loader = unittest.TestLoader() for module in UnitTesting.find_modules(Path(__file__).parent): - if ( - ("outcomes" in module - and not module.endswith(".test")) - or ("projects" in module - and not module.endswith(".tests")) + if ("outcomes" in module and not module.endswith(".test")) or ( + "projects" in module and not module.endswith(".tests") ): continue try: From c6d7f50dfe4f86ca0d9895cf57bde169f47605c3 Mon Sep 17 00:00:00 2001 From: meanmail Date: Mon, 20 May 2024 10:59:39 +0200 Subject: [PATCH 03/57] Update workflow configurations for Github Actions Modified the `.github/workflows/ci.yml` and `.github/workflows/auto-format.yml` files to streamline operations. Added a concurrency control which allows concurrent jobs to run in the workflow, and updated the names of the listed branches by removing unnecessary quotes. Also renamed `test-ubuntu` to `test` while improving its descriptor in the CI workflow. (cherry picked from commit f525391f2c12b8d3cd1560640bf78ff695563348) --- .github/workflows/auto-format.yml | 4 ++-- .github/workflows/ci.yml | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/auto-format.yml b/.github/workflows/auto-format.yml index f4a2bfc3..6152b9c6 100644 --- a/.github/workflows/auto-format.yml +++ b/.github/workflows/auto-format.yml @@ -3,8 +3,8 @@ on: pull_request: push: branches: - - 'master' - - 'release' + - master + - release concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b802a710..d5868432 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,10 +3,14 @@ name: CI on: push: branches: - - 'master' - - 'release' + - master + - release pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true + jobs: lint: name: Lint with ruff @@ -32,7 +36,8 @@ jobs: - name: Mypy run: poetry run mypy . - test-ubuntu: + test: + name: Run unit test on ${{ matrix.os }} with Python ${{ matrix.python-version }} runs-on: ${{ matrix.os }} strategy: fail-fast: false From fdf6862382412e42de2afb82211055d8ba84fee9 Mon Sep 17 00:00:00 2001 From: meanmail Date: Mon, 20 May 2024 11:01:06 +0200 Subject: [PATCH 04/57] Update Poetry install command in CI config Updated the poetry install command in the continuous integration configuration (".github/workflows/ci.yml"). Removed the other flags and just added the "--sync" option for the Poetry install command to enforce a correct and complete installation of dependencies. (cherry picked from commit 9f8ec792bb68a8574e06eb8fc8bfbdf622c9f9a5) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5868432..92977100 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: cache: 'poetry' - name: Install dependencies - run: poetry install --only main,dev --no-interaction --no-ansi + run: poetry install --no-interaction --no-ansi --sync - name: Run unittests run: python tests/testing.py From 9828c5afa0b6d8f3a4f621bb5b16b9ddfbd0aa02 Mon Sep 17 00:00:00 2001 From: meanmail Date: Mon, 20 May 2024 11:08:54 +0200 Subject: [PATCH 05/57] Update poetry install command in workflow files The poetry install command in both '.github/workflows/actions/prepare/action.yml' and '.github/workflows/ci.yml' workflow files has been updated. The new command now includes '--no-root' option for better control of the installation process and removed the '--only main,dev' options to ensure all necessary dependencies are installed. (cherry picked from commit 048b9cc490549bad7663c1d920f283154ab4ba45) --- .github/workflows/actions/prepare/action.yml | 2 +- .github/workflows/ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/actions/prepare/action.yml b/.github/workflows/actions/prepare/action.yml index cc3133d8..46586e15 100644 --- a/.github/workflows/actions/prepare/action.yml +++ b/.github/workflows/actions/prepare/action.yml @@ -14,5 +14,5 @@ runs: cache: 'poetry' - name: Install dependencies - run: poetry install --only main,dev --no-interaction --no-ansi + run: poetry install --no-interaction --no-ansi --no-root --sync shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92977100..2a2171a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: cache: 'poetry' - name: Install dependencies - run: poetry install --no-interaction --no-ansi --sync + run: poetry install --no-interaction --no-ansi --no-root --sync - name: Run unittests run: python tests/testing.py From b848908f929ead20623b12a81bc2084edab637a9 Mon Sep 17 00:00:00 2001 From: polischuks Date: Wed, 14 May 2025 11:45:16 +0300 Subject: [PATCH 06/57] Refactor base_searcher and stage_test for improved exception handling and type annotations --- hstest/stage/stage_test.py | 2 +- hstest/testing/execution/searcher/base_searcher.py | 2 +- tests/projects/go/coffee_machine/stage1_ce/tests.py | 4 +++- tests/projects/go/coffee_machine/stage1_ex/tests.py | 4 +++- tests/projects/go/coffee_machine/stage1_wa/tests.py | 4 +++- tests/projects/go/coffee_machine/stage2/tests.py | 4 +++- tests/projects/go/coffee_machine/stage3/tests.py | 4 +++- tests/projects/go/coffee_machine/stage4/tests.py | 4 +++- tests/projects/go/coffee_machine/stage5/tests.py | 4 +++- tests/projects/javascript/coffee_machine/stage1/tests.py | 4 +++- tests/projects/javascript/coffee_machine/stage1_ce/tests.py | 4 +++- tests/projects/javascript/coffee_machine/stage1_ex/tests.py | 4 +++- tests/projects/javascript/coffee_machine/stage1_wa/tests.py | 4 +++- tests/projects/javascript/coffee_machine/stage2/tests.py | 4 +++- tests/projects/javascript/coffee_machine/stage3/tests.py | 4 +++- tests/projects/javascript/coffee_machine/stage4/tests.py | 4 +++- tests/projects/javascript/coffee_machine/stage5/tests.py | 4 +++- tests/projects/javascript/simple_chatty_bot/stage1/tests.py | 3 ++- tests/projects/javascript/simple_chatty_bot/stage2/tests.py | 4 +++- tests/projects/javascript/simple_chatty_bot/stage3/tests.py | 4 +++- tests/projects/javascript/simple_chatty_bot/stage4/tests.py | 4 +++- tests/projects/javascript/simple_chatty_bot/stage5/tests.py | 4 +++- tests/projects/python/coffee_machine/stage1/tests.py | 4 +++- tests/projects/python/coffee_machine/stage2/tests.py | 4 +++- tests/projects/python/coffee_machine/stage3/tests.py | 4 +++- tests/projects/python/coffee_machine/stage4/tests.py | 4 +++- tests/projects/python/coffee_machine/stage5/tests.py | 4 +++- tests/projects/shell/coffee_machine/stage1/tests.py | 4 +++- tests/projects/shell/coffee_machine/stage1_ex/tests.py | 4 +++- tests/projects/shell/coffee_machine/stage1_wa/tests.py | 4 +++- tests/projects/shell/coffee_machine/stage2/tests.py | 4 +++- 31 files changed, 88 insertions(+), 31 deletions(-) diff --git a/hstest/stage/stage_test.py b/hstest/stage/stage_test.py index 7f4fd7d8..4dd660ed 100644 --- a/hstest/stage/stage_test.py +++ b/hstest/stage/stage_test.py @@ -171,7 +171,7 @@ def run_tests(self, *, debug: bool = False, is_unittest: bool = False) -> tuple[ SystemHandler.tear_down() return passed(is_unittest=is_unittest) - except BaseException: # noqa: BLE001 + except BaseException as ex: # noqa: BLE001 if need_tear_down: try: StageTest.curr_test_run.tear_down() diff --git a/hstest/testing/execution/searcher/base_searcher.py b/hstest/testing/execution/searcher/base_searcher.py index 71c44a86..3806bb67 100644 --- a/hstest/testing/execution/searcher/base_searcher.py +++ b/hstest/testing/execution/searcher/base_searcher.py @@ -255,7 +255,7 @@ def _parse_source(self, source: str) -> tuple[Path, Path, Module]: source_file = source source_module = source[: -len(ext)].replace(os.sep, ".") - elif os.sep in source: + elif os.sep in str(source): if source.name.endswith(os.sep): source = source[: -len(os.sep)] diff --git a/tests/projects/go/coffee_machine/stage1_ce/tests.py b/tests/projects/go/coffee_machine/stage1_ce/tests.py index 436a5500..88db8eb0 100644 --- a/tests/projects/go/coffee_machine/stage1_ce/tests.py +++ b/tests/projects/go/coffee_machine/stage1_ce/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult from hstest.testing.unittest.user_error_test import UserErrorTest import os diff --git a/tests/projects/go/coffee_machine/stage1_ex/tests.py b/tests/projects/go/coffee_machine/stage1_ex/tests.py index 7d81161d..7e362182 100644 --- a/tests/projects/go/coffee_machine/stage1_ex/tests.py +++ b/tests/projects/go/coffee_machine/stage1_ex/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult from hstest.testing.unittest.user_error_test import UserErrorTest CheckResult.correct = lambda: CheckResult(True, '') diff --git a/tests/projects/go/coffee_machine/stage1_wa/tests.py b/tests/projects/go/coffee_machine/stage1_wa/tests.py index f2585377..d9b5a00c 100644 --- a/tests/projects/go/coffee_machine/stage1_wa/tests.py +++ b/tests/projects/go/coffee_machine/stage1_wa/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult from hstest.testing.unittest.user_error_test import UserErrorTest CheckResult.correct = lambda: CheckResult(True, '') diff --git a/tests/projects/go/coffee_machine/stage2/tests.py b/tests/projects/go/coffee_machine/stage2/tests.py index e2878fd7..b7a68717 100644 --- a/tests/projects/go/coffee_machine/stage2/tests.py +++ b/tests/projects/go/coffee_machine/stage2/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/go/coffee_machine/stage3/tests.py b/tests/projects/go/coffee_machine/stage3/tests.py index 3601e4c3..b4555fd1 100644 --- a/tests/projects/go/coffee_machine/stage3/tests.py +++ b/tests/projects/go/coffee_machine/stage3/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/go/coffee_machine/stage4/tests.py b/tests/projects/go/coffee_machine/stage4/tests.py index 213047e6..a94139fd 100644 --- a/tests/projects/go/coffee_machine/stage4/tests.py +++ b/tests/projects/go/coffee_machine/stage4/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/go/coffee_machine/stage5/tests.py b/tests/projects/go/coffee_machine/stage5/tests.py index a36bc59a..ffd9d82d 100644 --- a/tests/projects/go/coffee_machine/stage5/tests.py +++ b/tests/projects/go/coffee_machine/stage5/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/coffee_machine/stage1/tests.py b/tests/projects/javascript/coffee_machine/stage1/tests.py index fea90ed8..3fb72d77 100644 --- a/tests/projects/javascript/coffee_machine/stage1/tests.py +++ b/tests/projects/javascript/coffee_machine/stage1/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/coffee_machine/stage1_ce/tests.py b/tests/projects/javascript/coffee_machine/stage1_ce/tests.py index b455f2dd..067c9027 100644 --- a/tests/projects/javascript/coffee_machine/stage1_ce/tests.py +++ b/tests/projects/javascript/coffee_machine/stage1_ce/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult from hstest.testing.unittest.user_error_test import UserErrorTest CheckResult.correct = lambda: CheckResult(True, '') diff --git a/tests/projects/javascript/coffee_machine/stage1_ex/tests.py b/tests/projects/javascript/coffee_machine/stage1_ex/tests.py index 08c9cbe1..3978d31e 100644 --- a/tests/projects/javascript/coffee_machine/stage1_ex/tests.py +++ b/tests/projects/javascript/coffee_machine/stage1_ex/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult from hstest.testing.unittest.user_error_test import UserErrorTest CheckResult.correct = lambda: CheckResult(True, '') diff --git a/tests/projects/javascript/coffee_machine/stage1_wa/tests.py b/tests/projects/javascript/coffee_machine/stage1_wa/tests.py index f2585377..d9b5a00c 100644 --- a/tests/projects/javascript/coffee_machine/stage1_wa/tests.py +++ b/tests/projects/javascript/coffee_machine/stage1_wa/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult from hstest.testing.unittest.user_error_test import UserErrorTest CheckResult.correct = lambda: CheckResult(True, '') diff --git a/tests/projects/javascript/coffee_machine/stage2/tests.py b/tests/projects/javascript/coffee_machine/stage2/tests.py index e2878fd7..b7a68717 100644 --- a/tests/projects/javascript/coffee_machine/stage2/tests.py +++ b/tests/projects/javascript/coffee_machine/stage2/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/coffee_machine/stage3/tests.py b/tests/projects/javascript/coffee_machine/stage3/tests.py index 3601e4c3..b4555fd1 100644 --- a/tests/projects/javascript/coffee_machine/stage3/tests.py +++ b/tests/projects/javascript/coffee_machine/stage3/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/coffee_machine/stage4/tests.py b/tests/projects/javascript/coffee_machine/stage4/tests.py index 213047e6..a94139fd 100644 --- a/tests/projects/javascript/coffee_machine/stage4/tests.py +++ b/tests/projects/javascript/coffee_machine/stage4/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/coffee_machine/stage5/tests.py b/tests/projects/javascript/coffee_machine/stage5/tests.py index a36bc59a..ffd9d82d 100644 --- a/tests/projects/javascript/coffee_machine/stage5/tests.py +++ b/tests/projects/javascript/coffee_machine/stage5/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/simple_chatty_bot/stage1/tests.py b/tests/projects/javascript/simple_chatty_bot/stage1/tests.py index da7967c9..eba07e84 100644 --- a/tests/projects/javascript/simple_chatty_bot/stage1/tests.py +++ b/tests/projects/javascript/simple_chatty_bot/stage1/tests.py @@ -1,7 +1,8 @@ import re +from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/simple_chatty_bot/stage2/tests.py b/tests/projects/javascript/simple_chatty_bot/stage2/tests.py index 82562c96..da284ee8 100644 --- a/tests/projects/javascript/simple_chatty_bot/stage2/tests.py +++ b/tests/projects/javascript/simple_chatty_bot/stage2/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/simple_chatty_bot/stage3/tests.py b/tests/projects/javascript/simple_chatty_bot/stage3/tests.py index ea267bb5..ba749ce3 100644 --- a/tests/projects/javascript/simple_chatty_bot/stage3/tests.py +++ b/tests/projects/javascript/simple_chatty_bot/stage3/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/simple_chatty_bot/stage4/tests.py b/tests/projects/javascript/simple_chatty_bot/stage4/tests.py index 462b9a64..dca4349d 100644 --- a/tests/projects/javascript/simple_chatty_bot/stage4/tests.py +++ b/tests/projects/javascript/simple_chatty_bot/stage4/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/simple_chatty_bot/stage5/tests.py b/tests/projects/javascript/simple_chatty_bot/stage5/tests.py index 092cffbd..29ea3696 100644 --- a/tests/projects/javascript/simple_chatty_bot/stage5/tests.py +++ b/tests/projects/javascript/simple_chatty_bot/stage5/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/python/coffee_machine/stage1/tests.py b/tests/projects/python/coffee_machine/stage1/tests.py index fea90ed8..3fb72d77 100644 --- a/tests/projects/python/coffee_machine/stage1/tests.py +++ b/tests/projects/python/coffee_machine/stage1/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/python/coffee_machine/stage2/tests.py b/tests/projects/python/coffee_machine/stage2/tests.py index e2878fd7..b7a68717 100644 --- a/tests/projects/python/coffee_machine/stage2/tests.py +++ b/tests/projects/python/coffee_machine/stage2/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/python/coffee_machine/stage3/tests.py b/tests/projects/python/coffee_machine/stage3/tests.py index 3601e4c3..b4555fd1 100644 --- a/tests/projects/python/coffee_machine/stage3/tests.py +++ b/tests/projects/python/coffee_machine/stage3/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/python/coffee_machine/stage4/tests.py b/tests/projects/python/coffee_machine/stage4/tests.py index 213047e6..a94139fd 100644 --- a/tests/projects/python/coffee_machine/stage4/tests.py +++ b/tests/projects/python/coffee_machine/stage4/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/python/coffee_machine/stage5/tests.py b/tests/projects/python/coffee_machine/stage5/tests.py index a36bc59a..ffd9d82d 100644 --- a/tests/projects/python/coffee_machine/stage5/tests.py +++ b/tests/projects/python/coffee_machine/stage5/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/shell/coffee_machine/stage1/tests.py b/tests/projects/shell/coffee_machine/stage1/tests.py index 60bb2b04..eb24184f 100644 --- a/tests/projects/shell/coffee_machine/stage1/tests.py +++ b/tests/projects/shell/coffee_machine/stage1/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult from hstest.common.os_utils import is_windows CheckResult.correct = lambda: CheckResult(True, '') diff --git a/tests/projects/shell/coffee_machine/stage1_ex/tests.py b/tests/projects/shell/coffee_machine/stage1_ex/tests.py index 98652a9b..eac189e3 100644 --- a/tests/projects/shell/coffee_machine/stage1_ex/tests.py +++ b/tests/projects/shell/coffee_machine/stage1_ex/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult from hstest.testing.unittest.user_error_test import UserErrorTest from hstest.common.os_utils import is_windows diff --git a/tests/projects/shell/coffee_machine/stage1_wa/tests.py b/tests/projects/shell/coffee_machine/stage1_wa/tests.py index 7184b6f3..6432a648 100644 --- a/tests/projects/shell/coffee_machine/stage1_wa/tests.py +++ b/tests/projects/shell/coffee_machine/stage1_wa/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult from hstest.testing.unittest.user_error_test import UserErrorTest from hstest.common.os_utils import is_windows diff --git a/tests/projects/shell/coffee_machine/stage2/tests.py b/tests/projects/shell/coffee_machine/stage2/tests.py index 06a8cbc1..359acb82 100644 --- a/tests/projects/shell/coffee_machine/stage2/tests.py +++ b/tests/projects/shell/coffee_machine/stage2/tests.py @@ -1,5 +1,7 @@ +from typing import List + from hstest.stage_test import * -from hstest.test_case import TestCase +from hstest.test_case import TestCase, CheckResult from hstest.common.os_utils import is_windows CheckResult.correct = lambda: CheckResult(True, '') From 5c9c1769e76ea6d453093a23de65313afa8b2c3d Mon Sep 17 00:00:00 2001 From: polischuks Date: Wed, 14 May 2025 11:55:14 +0300 Subject: [PATCH 07/57] Refactor base_searcher to improve path handling and enhance readability --- hstest/testing/execution/searcher/base_searcher.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/hstest/testing/execution/searcher/base_searcher.py b/hstest/testing/execution/searcher/base_searcher.py index 3806bb67..b67dde29 100644 --- a/hstest/testing/execution/searcher/base_searcher.py +++ b/hstest/testing/execution/searcher/base_searcher.py @@ -256,16 +256,19 @@ def _parse_source(self, source: str) -> tuple[Path, Path, Module]: source_module = source[: -len(ext)].replace(os.sep, ".") elif os.sep in str(source): - if source.name.endswith(os.sep): - source = source[: -len(os.sep)] + source_str = str(source) + if source_str.endswith(os.sep): + source_str = source_str[:-len(os.sep)] + source = Path(source_str) source_folder = source source_file = None - source_module = source.name.replace(os.sep, ".") + source_module = source_str.replace(os.sep, ".") else: - source_folder = source.name.replace(".", os.sep) - source_file = source_folder + ext + folder_str = source.name.replace(".", os.sep) + source_folder = Path(folder_str) + source_file = Path(folder_str + ext) source_module = source return source_folder, source_file, source_module From f6b92565f27a2d0f045f6ff088eac9c758866ba9 Mon Sep 17 00:00:00 2001 From: polischuks Date: Wed, 14 May 2025 13:28:46 +0300 Subject: [PATCH 08/57] Refactor base_searcher to simplify source path handling and improve clarity --- .../testing/execution/searcher/base_searcher.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/hstest/testing/execution/searcher/base_searcher.py b/hstest/testing/execution/searcher/base_searcher.py index b67dde29..d4501d52 100644 --- a/hstest/testing/execution/searcher/base_searcher.py +++ b/hstest/testing/execution/searcher/base_searcher.py @@ -253,22 +253,19 @@ def _parse_source(self, source: str) -> tuple[Path, Path, Module]: if source.name.endswith(ext): source_folder = None source_file = source - source_module = source[: -len(ext)].replace(os.sep, ".") + source_module = str(source)[:-len(ext)].replace(os.sep, ".") elif os.sep in str(source): - source_str = str(source) - if source_str.endswith(os.sep): - source_str = source_str[:-len(os.sep)] - source = Path(source_str) + if source.name.endswith(os.sep): + source = str(source)[:-len(os.sep)] source_folder = source source_file = None - source_module = source_str.replace(os.sep, ".") + source_module = source.name.replace(os.sep, ".") else: - folder_str = source.name.replace(".", os.sep) - source_folder = Path(folder_str) - source_file = Path(folder_str + ext) + source_folder = source.name.replace(".", os.sep) + source_file = source_folder + ext source_module = source return source_folder, source_file, source_module From 1c56e476edf1b9708363aa03cd36d07151448427 Mon Sep 17 00:00:00 2001 From: polischuks Date: Thu, 13 Feb 2025 19:19:19 +0200 Subject: [PATCH 09/57] Improve syntax error messages in eval tests --- tests/outcomes/syntax_error/test_empty_eval/test.py | 7 +++++-- tests/outcomes/syntax_error/test_error_using_eval/test.py | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/outcomes/syntax_error/test_empty_eval/test.py b/tests/outcomes/syntax_error/test_empty_eval/test.py index e24dce10..c6a1769c 100644 --- a/tests/outcomes/syntax_error/test_empty_eval/test.py +++ b/tests/outcomes/syntax_error/test_empty_eval/test.py @@ -12,8 +12,11 @@ class TestEmptyEval(UserErrorTest): Traceback (most recent call last): File "main.py", line 1, in print(eval("")) - File "", line 0""", - "SyntaxError: " + ^^^^^^^^ + File "", line 0 + + SyntaxError: invalid syntax + """ ] def generate(self) -> List[TestCase]: diff --git a/tests/outcomes/syntax_error/test_error_using_eval/test.py b/tests/outcomes/syntax_error/test_error_using_eval/test.py index 95cab756..477bbcbb 100644 --- a/tests/outcomes/syntax_error/test_error_using_eval/test.py +++ b/tests/outcomes/syntax_error/test_error_using_eval/test.py @@ -12,9 +12,12 @@ class TestEmptyEval(UserErrorTest): Traceback (most recent call last): File "main.py", line 1, in print(eval(")")) + ^^^^^^^^^ File "", line 1 - """, - "SyntaxError: " + ) + ^ + SyntaxError: unmatched ')' + """ ] def generate(self) -> List[TestCase]: From 581f512304e5c8a931574edaecb622855f3d67e3 Mon Sep 17 00:00:00 2001 From: polischuks Date: Fri, 14 Feb 2025 09:34:26 +0200 Subject: [PATCH 10/57] Refactor error messages in test cases for clarity and consistency --- .../test_dynamic_method_exception/test.py | 35 ++++++++++--------- .../feedback_on_exception_test_1/test.py | 1 + .../lib/exception_in_user_code_2/test.py | 2 ++ .../lib/exception_in_user_code_3/test.py | 7 ++-- .../plot/bar/pandas/test_example/cleaning.py | 2 +- .../plot/test_seaborn/test_revert/main.py | 2 +- .../test_error_using_eval_and_print/test.py | 10 +++--- .../go/coffee_machine/stage1_ce/tests.py | 2 +- 8 files changed, 35 insertions(+), 26 deletions(-) diff --git a/tests/outcomes/dynamic_method/test_dynamic_method_exception/test.py b/tests/outcomes/dynamic_method/test_dynamic_method_exception/test.py index e14d20c1..ca6c43f8 100644 --- a/tests/outcomes/dynamic_method/test_dynamic_method_exception/test.py +++ b/tests/outcomes/dynamic_method/test_dynamic_method_exception/test.py @@ -5,23 +5,26 @@ class TestDynamicMethodException(UserErrorTest): - contain = """ - Exception in test #1 + contain = [ + """ + Exception in test #1 - Traceback (most recent call last): - File "main.py", line 3, in - print(0/0) - ZeroDivisionError: division by zero - - Please find below the output of your program during this failed test. - Note that the '>' character indicates the beginning of the input line. - - --- - - Server started! - > main - S1: main - """ # noqa: W293 + Traceback (most recent call last): + File "main.py", line 3, in + print(0/0) + ~^~ + ZeroDivisionError: division by zero + + Please find below the output of your program during this failed test. + Note that the '>' character indicates the beginning of the input line. + + --- + + Server started! + > main + S1: main + """ + ] @dynamic_test def test(self): diff --git a/tests/outcomes/feedback_on_exception/feedback_on_exception_test_1/test.py b/tests/outcomes/feedback_on_exception/feedback_on_exception_test_1/test.py index 91e51218..e5171a58 100644 --- a/tests/outcomes/feedback_on_exception/feedback_on_exception_test_1/test.py +++ b/tests/outcomes/feedback_on_exception/feedback_on_exception_test_1/test.py @@ -14,6 +14,7 @@ class FeedbackOnExceptionTest1(UserErrorTest): Traceback (most recent call last): File "main.py", line 2, in print(1 / 0) + ~~^~~ ZeroDivisionError: division by zero Please find below the output of your program during this failed test. diff --git a/tests/outcomes/lib/exception_in_user_code_2/test.py b/tests/outcomes/lib/exception_in_user_code_2/test.py index b7789d55..1580fc10 100644 --- a/tests/outcomes/lib/exception_in_user_code_2/test.py +++ b/tests/outcomes/lib/exception_in_user_code_2/test.py @@ -12,8 +12,10 @@ class ExceptionInUserCodeTest(UserErrorTest): Traceback (most recent call last): File "main.py", line 3, in print(0 / 0) + ~~^~~ File "main.py", line 5, in print(0 / 0) + ~~^~~ ZeroDivisionError: division by zero Please find below the output of your program during this failed test. diff --git a/tests/outcomes/lib/exception_in_user_code_3/test.py b/tests/outcomes/lib/exception_in_user_code_3/test.py index 9410655c..a140e13e 100644 --- a/tests/outcomes/lib/exception_in_user_code_3/test.py +++ b/tests/outcomes/lib/exception_in_user_code_3/test.py @@ -6,14 +6,15 @@ class ExceptionInUserCodeTest3(UserErrorTest): - contain = """ + contain = ''' Exception in test #1 Traceback (most recent call last): File "main.py", line 2, in - Coffee is ready!\"\"\", raise_error_here) + Coffee is ready!""", raise_error_here) + ^^^^^^^^^^^^^^^^ NameError: name 'raise_error_here' is not defined - """ + ''' def generate(self) -> List[TestCase]: return [TestCase()] diff --git a/tests/outcomes/plot/bar/pandas/test_example/cleaning.py b/tests/outcomes/plot/bar/pandas/test_example/cleaning.py index f65f04ac..6f67738b 100644 --- a/tests/outcomes/plot/bar/pandas/test_example/cleaning.py +++ b/tests/outcomes/plot/bar/pandas/test_example/cleaning.py @@ -5,7 +5,7 @@ df_ab = pd.read_csv('ab_test.csv') # Plot dates -df_ab['date'] = df_ab['date'].astype('datetime64') +df_ab["date"] = pd.to_datetime(df_ab["date"]) control = df_ab[df_ab['group'] == 'Control'].groupby(df_ab['date'].dt.day).size().rename('Control') experiment = df_ab[df_ab['group'] == 'Experimental'].groupby(df_ab['date'].dt.day).size().rename('Experimental') diff --git a/tests/outcomes/plot/test_seaborn/test_revert/main.py b/tests/outcomes/plot/test_seaborn/test_revert/main.py index 0528854b..b3d92d4e 100644 --- a/tests/outcomes/plot/test_seaborn/test_revert/main.py +++ b/tests/outcomes/plot/test_seaborn/test_revert/main.py @@ -35,7 +35,7 @@ def plot(): sns.violinplot(data=df, palette="Set3", bw=.2, cut=1, linewidth=1) flights_long = sns.load_dataset("flights") - flights = flights_long.pivot("month", "year", "passengers") + flights = flights_long.pivot(index="month", columns="year", values="passengers") sns.heatmap(flights, annot=True, fmt="d", linewidths=.5, ax=ax) tips = sns.load_dataset("tips") diff --git a/tests/outcomes/syntax_error/test_error_using_eval_and_print/test.py b/tests/outcomes/syntax_error/test_error_using_eval_and_print/test.py index a34e5fe1..8d3715a4 100644 --- a/tests/outcomes/syntax_error/test_error_using_eval_and_print/test.py +++ b/tests/outcomes/syntax_error/test_error_using_eval_and_print/test.py @@ -12,12 +12,14 @@ class TestEmptyEval(UserErrorTest): Traceback (most recent call last): File "main.py", line 2, in print(eval(")")) + ^^^^^^^^^ File "", line 1 - """, - "SyntaxError: ", - """ + ) + ^ + SyntaxError: unmatched ')' + Please find below the output of your program during this failed test. - + --- 123 diff --git a/tests/projects/go/coffee_machine/stage1_ce/tests.py b/tests/projects/go/coffee_machine/stage1_ce/tests.py index 88db8eb0..fd2135b2 100644 --- a/tests/projects/go/coffee_machine/stage1_ce/tests.py +++ b/tests/projects/go/coffee_machine/stage1_ce/tests.py @@ -23,7 +23,7 @@ class CoffeeMachineTest(UserErrorTest): contain = f""" Compilation error - .{os.sep}main.go:4:2: imported and not used: "fmt" + .{os.sep}main.go:4:2: "fmt" imported and not used .{os.sep}main.go:8:2: undefined: Println """ From 068528526cb1fcbddc7d4d8717202ab6fff68665 Mon Sep 17 00:00:00 2001 From: polischuks Date: Fri, 14 Feb 2025 09:42:35 +0200 Subject: [PATCH 11/57] Update GitHub Actions workflow to use Python 3.12 and latest action versions --- .github/workflows/main.yml | 59 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..fd01c669 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,59 @@ +name: Python package + +on: [ push ] + +jobs: + Lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Python version + run: python --version + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install -r requirements-dev.txt + npm install + - name: python version + run: python --version + - name: Lint + run: flake8 + test-ubuntu: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: ["ubuntu-20.04", "windows-latest", "macos-latest"] + python-version: [ "3.10", "3.11", "3.12" ] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Python version + run: python --version + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + - name: pip cache + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt', '**/requirements-dev.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install -r requirements-dev.txt + npm install + - name: Run unittests + run: python tests/testing.py From 8d293a287b403c359afe341ea7ebceca74abf6b3 Mon Sep 17 00:00:00 2001 From: polischuks Date: Fri, 14 Feb 2025 10:58:06 +0200 Subject: [PATCH 12/57] Add CI workflow for linting, type checking, and testing with multiple Python versions --- .github/workflows/actions/prepare/action.yml | 35 +++++++- .github/workflows/build-wheels.yml | 87 ++++++++++++++++++++ .github/workflows/ci.yml | 73 ++++++++++------ .github/workflows/main.yml | 18 ++-- 4 files changed, 177 insertions(+), 36 deletions(-) mode change 100644 => 100755 .github/workflows/actions/prepare/action.yml create mode 100755 .github/workflows/build-wheels.yml mode change 100644 => 100755 .github/workflows/ci.yml mode change 100644 => 100755 .github/workflows/main.yml diff --git a/.github/workflows/actions/prepare/action.yml b/.github/workflows/actions/prepare/action.yml old mode 100644 new mode 100755 index 46586e15..12d2ba84 --- a/.github/workflows/actions/prepare/action.yml +++ b/.github/workflows/actions/prepare/action.yml @@ -1,18 +1,47 @@ name: 'Prepare environment' description: 'Prepare environment' +inputs: + python-version: + description: 'Python version to use' + required: true + runs: using: "composite" steps: + - name: Setup PATH + run: echo "/home/runner/.local/bin" >> $GITHUB_PATH + shell: bash + - name: Install Poetry run: pipx install poetry==$(head -n 1 .poetry-version) shell: bash - uses: actions/setup-python@v5 with: - python-version-file: '.python-version' + python-version: ${{ inputs.python-version }} cache: 'poetry' - - name: Install dependencies - run: poetry install --no-interaction --no-ansi --no-root --sync + - name: Clear Poetry cache + run: poetry cache clear --all pypi shell: bash + + - name: Install dependencies and package + run: poetry install --no-interaction --no-ansi + shell: bash + + # Setup Node.js for JavaScript tests + - uses: actions/setup-node@v4 + with: + node-version: '20' + + # Install Node.js dependencies + - name: Install Node.js dependencies + run: npm install + shell: bash + + # Setup Go for Go language tests + - uses: actions/setup-go@v5 + with: + go-version: '1.21' + cache: false diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml new file mode 100755 index 00000000..9fd9b9d1 --- /dev/null +++ b/.github/workflows/build-wheels.yml @@ -0,0 +1,87 @@ +name: Build Wheels + +on: + push: + tags: + - 'v*' + workflow_dispatch: + +jobs: + build_wheels: + name: Build psutil wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + permissions: + contents: write + strategy: + matrix: + include: + # Linux builds + - os: ubuntu-latest + python-version: '3.10' + - os: ubuntu-latest + python-version: '3.11' + - os: ubuntu-latest + python-version: '3.12' + + # Windows builds + - os: windows-latest + python-version: '3.10' + - os: windows-latest + python-version: '3.11' + - os: windows-latest + python-version: '3.12' + + # macOS builds + - os: macos-latest + python-version: '3.10' + - os: macos-latest + python-version: '3.11' + - os: macos-latest + python-version: '3.12' + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install build dependencies + run: | + python -m pip install pip wheel + shell: bash + + - name: Build psutil wheel + run: | + # Create dist directory + mkdir -p dist + + # Build psutil wheel + pip wheel psutil==5.8.0 --wheel-dir dist/ + shell: bash + + - name: Upload to GitHub Actions + uses: actions/upload-artifact@v4 + with: + name: dist-${{ matrix.os }}-py${{ matrix.python-version }} + path: dist/* + + release: + needs: build_wheels + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') + permissions: + contents: write + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + pattern: dist-* + path: dist + merge-multiple: true + + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + files: dist/* diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml old mode 100644 new mode 100755 index 2a2171a4..e792b4c0 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,27 +14,41 @@ concurrency: jobs: lint: name: Lint with ruff - runs-on: [ self-hosted, small ] + runs-on: arc-runners-small timeout-minutes: 30 steps: - uses: actions/checkout@v4 - + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} - uses: ./.github/workflows/actions/prepare - + with: + python-version: "3.11" - name: Check files using the ruff formatter - run: poetry run ruff --fix --unsafe-fixes --preview . + run: | + poetry run ruff check --fix --unsafe-fixes --preview --exit-zero . + poetry run ruff format . + shell: bash + - name: Commit changes + uses: EndBug/add-and-commit@v9 + with: + fetch: false + default_author: github_actions + message: 'Backend: Auto format' + add: '.' mypy: name: Static Type Checking - runs-on: [ self-hosted, small ] + runs-on: arc-runners-small timeout-minutes: 30 steps: - uses: actions/checkout@v4 - - uses: ./.github/workflows/actions/prepare - + with: + python-version: "3.12" - name: Mypy run: poetry run mypy . + shell: bash test: name: Run unit test on ${{ matrix.os }} with Python ${{ matrix.python-version }} @@ -42,24 +56,33 @@ jobs: strategy: fail-fast: false matrix: - os: - - [ self-hosted, small ] - - windows-latest - - macos-latest - python-version: [ "3.10", "3.11", "3.12" ] + include: + # Self-hosted runner + - os: [ self-hosted, small ] + python-version: "3.10" + - os: [ self-hosted, small ] + python-version: "3.11" + - os: [ self-hosted, small ] + python-version: "3.12" + + # Windows + - os: windows-latest + python-version: "3.10.11" + - os: windows-latest + python-version: "3.11" + - os: windows-latest + python-version: "3.12" + + # macOS (arm64) + - os: macos-14 + python-version: "3.11" + - os: macos-14 + python-version: "3.12" steps: - - uses: actions/checkout@v3 - - - name: Install Poetry - run: pipx install poetry==$(head -n 1 .poetry-version) - - - uses: actions/setup-python@v5 + - uses: actions/checkout@v4 + - uses: ./.github/workflows/actions/prepare with: python-version: ${{ matrix.python-version }} - cache: 'poetry' - - - name: Install dependencies - run: poetry install --no-interaction --no-ansi --no-root --sync - - - name: Run unittests - run: python tests/testing.py + - name: Run Tests + run: poetry run python tests/testing.py + shell: bash diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml old mode 100644 new mode 100755 index fd01c669..869ce9e9 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,13 +4,14 @@ on: [ push ] jobs: Lint: + if: false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Set up Python 3.12 - uses: actions/setup-python@v5 + - uses: actions/checkout@v3 + - name: Set up Python 3.7 + uses: actions/setup-python@v4 with: - python-version: "3.12" + python-version: "3.7" - name: Python version run: python --version - name: Install dependencies @@ -24,16 +25,17 @@ jobs: - name: Lint run: flake8 test-ubuntu: + if: false runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: ["ubuntu-20.04", "windows-latest", "macos-latest"] - python-version: [ "3.10", "3.11", "3.12" ] + python-version: [ "3.6", "3.7", "3.8", "3.9", "3.10", "3.11.0-rc.2" ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Python version @@ -43,7 +45,7 @@ jobs: run: | echo "::set-output name=dir::$(pip cache dir)" - name: pip cache - uses: actions/cache@v4 + uses: actions/cache@v3 with: path: ${{ steps.pip-cache.outputs.dir }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt', '**/requirements-dev.txt') }} From 2427c0496b4eebf41b1e1529dd4fa06e1950da94 Mon Sep 17 00:00:00 2001 From: polischuks Date: Fri, 14 Feb 2025 11:21:49 +0200 Subject: [PATCH 13/57] Migrate project to Poetry for dependency management and update Python version requirements --- .poetry-version | 2 +- .python-version | 2 +- poetry.lock | 1174 ++++++++++++++++++++++++++++------------------- pyproject.toml | 32 +- 4 files changed, 741 insertions(+), 469 deletions(-) mode change 100644 => 100755 .poetry-version mode change 100644 => 100755 .python-version mode change 100644 => 100755 poetry.lock mode change 100644 => 100755 pyproject.toml diff --git a/.poetry-version b/.poetry-version old mode 100644 new mode 100755 index 943f9cbc..10bf840e --- a/.poetry-version +++ b/.poetry-version @@ -1 +1 @@ -1.7.1 +2.0.1 \ No newline at end of file diff --git a/.python-version b/.python-version old mode 100644 new mode 100755 index 9919bf8c..920121e4 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.10.13 +3.10.13 \ No newline at end of file diff --git a/poetry.lock b/poetry.lock old mode 100644 new mode 100755 index ca26cdb2..41f9202b --- a/poetry.lock +++ b/poetry.lock @@ -1,67 +1,79 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. [[package]] name = "contourpy" -version = "1.2.1" +version = "1.3.1" description = "Python library for calculating contours of 2D quadrilateral grids" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "contourpy-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd7c23df857d488f418439686d3b10ae2fbf9bc256cd045b37a8c16575ea1040"}, - {file = "contourpy-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b9eb0ca724a241683c9685a484da9d35c872fd42756574a7cfbf58af26677fd"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c75507d0a55378240f781599c30e7776674dbaf883a46d1c90f37e563453480"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11959f0ce4a6f7b76ec578576a0b61a28bdc0696194b6347ba3f1c53827178b9"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb3315a8a236ee19b6df481fc5f997436e8ade24a9f03dfdc6bd490fea20c6da"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39f3ecaf76cd98e802f094e0d4fbc6dc9c45a8d0c4d185f0f6c2234e14e5f75b"}, - {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:94b34f32646ca0414237168d68a9157cb3889f06b096612afdd296003fdd32fd"}, - {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:457499c79fa84593f22454bbd27670227874cd2ff5d6c84e60575c8b50a69619"}, - {file = "contourpy-1.2.1-cp310-cp310-win32.whl", hash = "sha256:ac58bdee53cbeba2ecad824fa8159493f0bf3b8ea4e93feb06c9a465d6c87da8"}, - {file = "contourpy-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:9cffe0f850e89d7c0012a1fb8730f75edd4320a0a731ed0c183904fe6ecfc3a9"}, - {file = "contourpy-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6022cecf8f44e36af10bd9118ca71f371078b4c168b6e0fab43d4a889985dbb5"}, - {file = "contourpy-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef5adb9a3b1d0c645ff694f9bca7702ec2c70f4d734f9922ea34de02294fdf72"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6150ffa5c767bc6332df27157d95442c379b7dce3a38dff89c0f39b63275696f"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c863140fafc615c14a4bf4efd0f4425c02230eb8ef02784c9a156461e62c965"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00e5388f71c1a0610e6fe56b5c44ab7ba14165cdd6d695429c5cd94021e390b2"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4492d82b3bc7fbb7e3610747b159869468079fe149ec5c4d771fa1f614a14df"}, - {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49e70d111fee47284d9dd867c9bb9a7058a3c617274900780c43e38d90fe1205"}, - {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b59c0ffceff8d4d3996a45f2bb6f4c207f94684a96bf3d9728dbb77428dd8cb8"}, - {file = "contourpy-1.2.1-cp311-cp311-win32.whl", hash = "sha256:7b4182299f251060996af5249c286bae9361fa8c6a9cda5efc29fe8bfd6062ec"}, - {file = "contourpy-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2855c8b0b55958265e8b5888d6a615ba02883b225f2227461aa9127c578a4922"}, - {file = "contourpy-1.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:62828cada4a2b850dbef89c81f5a33741898b305db244904de418cc957ff05dc"}, - {file = "contourpy-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:309be79c0a354afff9ff7da4aaed7c3257e77edf6c1b448a779329431ee79d7e"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e785e0f2ef0d567099b9ff92cbfb958d71c2d5b9259981cd9bee81bd194c9a4"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cac0a8f71a041aa587410424ad46dfa6a11f6149ceb219ce7dd48f6b02b87a7"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af3f4485884750dddd9c25cb7e3915d83c2db92488b38ccb77dd594eac84c4a0"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ce6889abac9a42afd07a562c2d6d4b2b7134f83f18571d859b25624a331c90b"}, - {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a1eea9aecf761c661d096d39ed9026574de8adb2ae1c5bd7b33558af884fb2ce"}, - {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:187fa1d4c6acc06adb0fae5544c59898ad781409e61a926ac7e84b8f276dcef4"}, - {file = "contourpy-1.2.1-cp312-cp312-win32.whl", hash = "sha256:c2528d60e398c7c4c799d56f907664673a807635b857df18f7ae64d3e6ce2d9f"}, - {file = "contourpy-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:1a07fc092a4088ee952ddae19a2b2a85757b923217b7eed584fdf25f53a6e7ce"}, - {file = "contourpy-1.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb6834cbd983b19f06908b45bfc2dad6ac9479ae04abe923a275b5f48f1a186b"}, - {file = "contourpy-1.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1d59e739ab0e3520e62a26c60707cc3ab0365d2f8fecea74bfe4de72dc56388f"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd3db01f59fdcbce5b22afad19e390260d6d0222f35a1023d9adc5690a889364"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a12a813949e5066148712a0626895c26b2578874e4cc63160bb007e6df3436fe"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe0ccca550bb8e5abc22f530ec0466136379c01321fd94f30a22231e8a48d985"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1d59258c3c67c865435d8fbeb35f8c59b8bef3d6f46c1f29f6123556af28445"}, - {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f32c38afb74bd98ce26de7cc74a67b40afb7b05aae7b42924ea990d51e4dac02"}, - {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d31a63bc6e6d87f77d71e1abbd7387ab817a66733734883d1fc0021ed9bfa083"}, - {file = "contourpy-1.2.1-cp39-cp39-win32.whl", hash = "sha256:ddcb8581510311e13421b1f544403c16e901c4e8f09083c881fab2be80ee31ba"}, - {file = "contourpy-1.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:10a37ae557aabf2509c79715cd20b62e4c7c28b8cd62dd7d99e5ed3ce28c3fd9"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a31f94983fecbac95e58388210427d68cd30fe8a36927980fab9c20062645609"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef2b055471c0eb466033760a521efb9d8a32b99ab907fc8358481a1dd29e3bd3"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b33d2bc4f69caedcd0a275329eb2198f560b325605810895627be5d4b876bf7f"}, - {file = "contourpy-1.2.1.tar.gz", hash = "sha256:4d8908b3bee1c889e547867ca4cdc54e5ab6be6d3e078556814a22457f49423c"}, + {file = "contourpy-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab"}, + {file = "contourpy-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3"}, + {file = "contourpy-1.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277"}, + {file = "contourpy-1.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595"}, + {file = "contourpy-1.3.1-cp310-cp310-win32.whl", hash = "sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697"}, + {file = "contourpy-1.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e"}, + {file = "contourpy-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b"}, + {file = "contourpy-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c"}, + {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291"}, + {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f"}, + {file = "contourpy-1.3.1-cp311-cp311-win32.whl", hash = "sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375"}, + {file = "contourpy-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9"}, + {file = "contourpy-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509"}, + {file = "contourpy-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9"}, + {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b"}, + {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d"}, + {file = "contourpy-1.3.1-cp312-cp312-win32.whl", hash = "sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e"}, + {file = "contourpy-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d"}, + {file = "contourpy-1.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2"}, + {file = "contourpy-1.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c"}, + {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3"}, + {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1"}, + {file = "contourpy-1.3.1-cp313-cp313-win32.whl", hash = "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82"}, + {file = "contourpy-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd"}, + {file = "contourpy-1.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30"}, + {file = "contourpy-1.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda"}, + {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242"}, + {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1"}, + {file = "contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1"}, + {file = "contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53"}, + {file = "contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699"}, ] [package.dependencies] -numpy = ">=1.20" +numpy = ">=1.23" [package.extras] bokeh = ["bokeh", "selenium"] docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] -mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.8.0)", "types-Pillow"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.11.1)", "types-Pillow"] test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] -test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] +test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"] [[package]] name = "cycler" @@ -69,6 +81,8 @@ version = "0.12.1" description = "Composable style cycles" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, @@ -80,53 +94,63 @@ tests = ["pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "fonttools" -version = "4.51.0" +version = "4.56.0" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "fonttools-4.51.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:84d7751f4468dd8cdd03ddada18b8b0857a5beec80bce9f435742abc9a851a74"}, - {file = "fonttools-4.51.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8b4850fa2ef2cfbc1d1f689bc159ef0f45d8d83298c1425838095bf53ef46308"}, - {file = "fonttools-4.51.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5b48a1121117047d82695d276c2af2ee3a24ffe0f502ed581acc2673ecf1037"}, - {file = "fonttools-4.51.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:180194c7fe60c989bb627d7ed5011f2bef1c4d36ecf3ec64daec8302f1ae0716"}, - {file = "fonttools-4.51.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:96a48e137c36be55e68845fc4284533bda2980f8d6f835e26bca79d7e2006438"}, - {file = "fonttools-4.51.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:806e7912c32a657fa39d2d6eb1d3012d35f841387c8fc6cf349ed70b7c340039"}, - {file = "fonttools-4.51.0-cp310-cp310-win32.whl", hash = "sha256:32b17504696f605e9e960647c5f64b35704782a502cc26a37b800b4d69ff3c77"}, - {file = "fonttools-4.51.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7e91abdfae1b5c9e3a543f48ce96013f9a08c6c9668f1e6be0beabf0a569c1b"}, - {file = "fonttools-4.51.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a8feca65bab31479d795b0d16c9a9852902e3a3c0630678efb0b2b7941ea9c74"}, - {file = "fonttools-4.51.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ac27f436e8af7779f0bb4d5425aa3535270494d3bc5459ed27de3f03151e4c2"}, - {file = "fonttools-4.51.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e19bd9e9964a09cd2433a4b100ca7f34e34731e0758e13ba9a1ed6e5468cc0f"}, - {file = "fonttools-4.51.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2b92381f37b39ba2fc98c3a45a9d6383bfc9916a87d66ccb6553f7bdd129097"}, - {file = "fonttools-4.51.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5f6bc991d1610f5c3bbe997b0233cbc234b8e82fa99fc0b2932dc1ca5e5afec0"}, - {file = "fonttools-4.51.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9696fe9f3f0c32e9a321d5268208a7cc9205a52f99b89479d1b035ed54c923f1"}, - {file = "fonttools-4.51.0-cp311-cp311-win32.whl", hash = "sha256:3bee3f3bd9fa1d5ee616ccfd13b27ca605c2b4270e45715bd2883e9504735034"}, - {file = "fonttools-4.51.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f08c901d3866a8905363619e3741c33f0a83a680d92a9f0e575985c2634fcc1"}, - {file = "fonttools-4.51.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4060acc2bfa2d8e98117828a238889f13b6f69d59f4f2d5857eece5277b829ba"}, - {file = "fonttools-4.51.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1250e818b5f8a679ad79660855528120a8f0288f8f30ec88b83db51515411fcc"}, - {file = "fonttools-4.51.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76f1777d8b3386479ffb4a282e74318e730014d86ce60f016908d9801af9ca2a"}, - {file = "fonttools-4.51.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b5ad456813d93b9c4b7ee55302208db2b45324315129d85275c01f5cb7e61a2"}, - {file = "fonttools-4.51.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:68b3fb7775a923be73e739f92f7e8a72725fd333eab24834041365d2278c3671"}, - {file = "fonttools-4.51.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8e2f1a4499e3b5ee82c19b5ee57f0294673125c65b0a1ff3764ea1f9db2f9ef5"}, - {file = "fonttools-4.51.0-cp312-cp312-win32.whl", hash = "sha256:278e50f6b003c6aed19bae2242b364e575bcb16304b53f2b64f6551b9c000e15"}, - {file = "fonttools-4.51.0-cp312-cp312-win_amd64.whl", hash = "sha256:b3c61423f22165541b9403ee39874dcae84cd57a9078b82e1dce8cb06b07fa2e"}, - {file = "fonttools-4.51.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1621ee57da887c17312acc4b0e7ac30d3a4fb0fec6174b2e3754a74c26bbed1e"}, - {file = "fonttools-4.51.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d9298be7a05bb4801f558522adbe2feea1b0b103d5294ebf24a92dd49b78e5"}, - {file = "fonttools-4.51.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee1af4be1c5afe4c96ca23badd368d8dc75f611887fb0c0dac9f71ee5d6f110e"}, - {file = "fonttools-4.51.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c18b49adc721a7d0b8dfe7c3130c89b8704baf599fb396396d07d4aa69b824a1"}, - {file = "fonttools-4.51.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de7c29bdbdd35811f14493ffd2534b88f0ce1b9065316433b22d63ca1cd21f14"}, - {file = "fonttools-4.51.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cadf4e12a608ef1d13e039864f484c8a968840afa0258b0b843a0556497ea9ed"}, - {file = "fonttools-4.51.0-cp38-cp38-win32.whl", hash = "sha256:aefa011207ed36cd280babfaa8510b8176f1a77261833e895a9d96e57e44802f"}, - {file = "fonttools-4.51.0-cp38-cp38-win_amd64.whl", hash = "sha256:865a58b6e60b0938874af0968cd0553bcd88e0b2cb6e588727117bd099eef836"}, - {file = "fonttools-4.51.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:60a3409c9112aec02d5fb546f557bca6efa773dcb32ac147c6baf5f742e6258b"}, - {file = "fonttools-4.51.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7e89853d8bea103c8e3514b9f9dc86b5b4120afb4583b57eb10dfa5afbe0936"}, - {file = "fonttools-4.51.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56fc244f2585d6c00b9bcc59e6593e646cf095a96fe68d62cd4da53dd1287b55"}, - {file = "fonttools-4.51.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d145976194a5242fdd22df18a1b451481a88071feadf251221af110ca8f00ce"}, - {file = "fonttools-4.51.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5b8cab0c137ca229433570151b5c1fc6af212680b58b15abd797dcdd9dd5051"}, - {file = "fonttools-4.51.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:54dcf21a2f2d06ded676e3c3f9f74b2bafded3a8ff12f0983160b13e9f2fb4a7"}, - {file = "fonttools-4.51.0-cp39-cp39-win32.whl", hash = "sha256:0118ef998a0699a96c7b28457f15546815015a2710a1b23a7bf6c1be60c01636"}, - {file = "fonttools-4.51.0-cp39-cp39-win_amd64.whl", hash = "sha256:599bdb75e220241cedc6faebfafedd7670335d2e29620d207dd0378a4e9ccc5a"}, - {file = "fonttools-4.51.0-py3-none-any.whl", hash = "sha256:15c94eeef6b095831067f72c825eb0e2d48bb4cea0647c1b05c981ecba2bf39f"}, - {file = "fonttools-4.51.0.tar.gz", hash = "sha256:dc0673361331566d7a663d7ce0f6fdcbfbdc1f59c6e3ed1165ad7202ca183c68"}, + {file = "fonttools-4.56.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:331954d002dbf5e704c7f3756028e21db07097c19722569983ba4d74df014000"}, + {file = "fonttools-4.56.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8d1613abd5af2f93c05867b3a3759a56e8bf97eb79b1da76b2bc10892f96ff16"}, + {file = "fonttools-4.56.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:705837eae384fe21cee5e5746fd4f4b2f06f87544fa60f60740007e0aa600311"}, + {file = "fonttools-4.56.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc871904a53a9d4d908673c6faa15689874af1c7c5ac403a8e12d967ebd0c0dc"}, + {file = "fonttools-4.56.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:38b947de71748bab150259ee05a775e8a0635891568e9fdb3cdd7d0e0004e62f"}, + {file = "fonttools-4.56.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:86b2a1013ef7a64d2e94606632683f07712045ed86d937c11ef4dde97319c086"}, + {file = "fonttools-4.56.0-cp310-cp310-win32.whl", hash = "sha256:133bedb9a5c6376ad43e6518b7e2cd2f866a05b1998f14842631d5feb36b5786"}, + {file = "fonttools-4.56.0-cp310-cp310-win_amd64.whl", hash = "sha256:17f39313b649037f6c800209984a11fc256a6137cbe5487091c6c7187cae4685"}, + {file = "fonttools-4.56.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ef04bc7827adb7532be3d14462390dd71287644516af3f1e67f1e6ff9c6d6df"}, + {file = "fonttools-4.56.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ffda9b8cd9cb8b301cae2602ec62375b59e2e2108a117746f12215145e3f786c"}, + {file = "fonttools-4.56.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e993e8db36306cc3f1734edc8ea67906c55f98683d6fd34c3fc5593fdbba4c"}, + {file = "fonttools-4.56.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:003548eadd674175510773f73fb2060bb46adb77c94854af3e0cc5bc70260049"}, + {file = "fonttools-4.56.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd9825822e7bb243f285013e653f6741954d8147427aaa0324a862cdbf4cbf62"}, + {file = "fonttools-4.56.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b23d30a2c0b992fb1c4f8ac9bfde44b5586d23457759b6cf9a787f1a35179ee0"}, + {file = "fonttools-4.56.0-cp311-cp311-win32.whl", hash = "sha256:47b5e4680002ae1756d3ae3b6114e20aaee6cc5c69d1e5911f5ffffd3ee46c6b"}, + {file = "fonttools-4.56.0-cp311-cp311-win_amd64.whl", hash = "sha256:14a3e3e6b211660db54ca1ef7006401e4a694e53ffd4553ab9bc87ead01d0f05"}, + {file = "fonttools-4.56.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6f195c14c01bd057bc9b4f70756b510e009c83c5ea67b25ced3e2c38e6ee6e9"}, + {file = "fonttools-4.56.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa760e5fe8b50cbc2d71884a1eff2ed2b95a005f02dda2fa431560db0ddd927f"}, + {file = "fonttools-4.56.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d54a45d30251f1d729e69e5b675f9a08b7da413391a1227781e2a297fa37f6d2"}, + {file = "fonttools-4.56.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:661a8995d11e6e4914a44ca7d52d1286e2d9b154f685a4d1f69add8418961563"}, + {file = "fonttools-4.56.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d94449ad0a5f2a8bf5d2f8d71d65088aee48adbe45f3c5f8e00e3ad861ed81a"}, + {file = "fonttools-4.56.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f59746f7953f69cc3290ce2f971ab01056e55ddd0fb8b792c31a8acd7fee2d28"}, + {file = "fonttools-4.56.0-cp312-cp312-win32.whl", hash = "sha256:bce60f9a977c9d3d51de475af3f3581d9b36952e1f8fc19a1f2254f1dda7ce9c"}, + {file = "fonttools-4.56.0-cp312-cp312-win_amd64.whl", hash = "sha256:300c310bb725b2bdb4f5fc7e148e190bd69f01925c7ab437b9c0ca3e1c7cd9ba"}, + {file = "fonttools-4.56.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f20e2c0dfab82983a90f3d00703ac0960412036153e5023eed2b4641d7d5e692"}, + {file = "fonttools-4.56.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f36a0868f47b7566237640c026c65a86d09a3d9ca5df1cd039e30a1da73098a0"}, + {file = "fonttools-4.56.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62b4c6802fa28e14dba010e75190e0e6228513573f1eeae57b11aa1a39b7e5b1"}, + {file = "fonttools-4.56.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a05d1f07eb0a7d755fbe01fee1fd255c3a4d3730130cf1bfefb682d18fd2fcea"}, + {file = "fonttools-4.56.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0073b62c3438cf0058488c002ea90489e8801d3a7af5ce5f7c05c105bee815c3"}, + {file = "fonttools-4.56.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cad98c94833465bcf28f51c248aaf07ca022efc6a3eba750ad9c1e0256d278"}, + {file = "fonttools-4.56.0-cp313-cp313-win32.whl", hash = "sha256:d0cb73ccf7f6d7ca8d0bc7ea8ac0a5b84969a41c56ac3ac3422a24df2680546f"}, + {file = "fonttools-4.56.0-cp313-cp313-win_amd64.whl", hash = "sha256:62cc1253827d1e500fde9dbe981219fea4eb000fd63402283472d38e7d8aa1c6"}, + {file = "fonttools-4.56.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3fd3fccb7b9adaaecfa79ad51b759f2123e1aba97f857936ce044d4f029abd71"}, + {file = "fonttools-4.56.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:193b86e9f769320bc98ffdb42accafb5d0c8c49bd62884f1c0702bc598b3f0a2"}, + {file = "fonttools-4.56.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e81c1cc80c1d8bf071356cc3e0e25071fbba1c75afc48d41b26048980b3c771"}, + {file = "fonttools-4.56.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9270505a19361e81eecdbc2c251ad1e1a9a9c2ad75fa022ccdee533f55535dc"}, + {file = "fonttools-4.56.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:53f5e9767978a4daf46f28e09dbeb7d010319924ae622f7b56174b777258e5ba"}, + {file = "fonttools-4.56.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9da650cb29bc098b8cfd15ef09009c914b35c7986c8fa9f08b51108b7bc393b4"}, + {file = "fonttools-4.56.0-cp38-cp38-win32.whl", hash = "sha256:965d0209e6dbdb9416100123b6709cb13f5232e2d52d17ed37f9df0cc31e2b35"}, + {file = "fonttools-4.56.0-cp38-cp38-win_amd64.whl", hash = "sha256:654ac4583e2d7c62aebc6fc6a4c6736f078f50300e18aa105d87ce8925cfac31"}, + {file = "fonttools-4.56.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca7962e8e5fc047cc4e59389959843aafbf7445b6c08c20d883e60ced46370a5"}, + {file = "fonttools-4.56.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1af375734018951c31c0737d04a9d5fd0a353a0253db5fbed2ccd44eac62d8c"}, + {file = "fonttools-4.56.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:442ad4122468d0e47d83bc59d0e91b474593a8c813839e1872e47c7a0cb53b10"}, + {file = "fonttools-4.56.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cf4f8d2a30b454ac682e12c61831dcb174950c406011418e739de592bbf8f76"}, + {file = "fonttools-4.56.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:96a4271f63a615bcb902b9f56de00ea225d6896052c49f20d0c91e9f43529a29"}, + {file = "fonttools-4.56.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6c1d38642ca2dddc7ae992ef5d026e5061a84f10ff2b906be5680ab089f55bb8"}, + {file = "fonttools-4.56.0-cp39-cp39-win32.whl", hash = "sha256:2d351275f73ebdd81dd5b09a8b8dac7a30f29a279d41e1c1192aedf1b6dced40"}, + {file = "fonttools-4.56.0-cp39-cp39-win_amd64.whl", hash = "sha256:d6ca96d1b61a707ba01a43318c9c40aaf11a5a568d1e61146fafa6ab20890793"}, + {file = "fonttools-4.56.0-py3-none-any.whl", hash = "sha256:1088182f68c303b50ca4dc0c82d42083d176cba37af1937e1a976a31149d4d14"}, + {file = "fonttools-4.56.0.tar.gz", hash = "sha256:a114d1567e1a1586b7e9e7fc2ff686ca542a82769a296cef131e4c4af51e58f4"}, ] [package.extras] @@ -145,153 +169,138 @@ woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] [[package]] name = "kiwisolver" -version = "1.4.5" +version = "1.4.8" description = "A fast implementation of the Cassowary constraint solver" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, - {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, - {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, - {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, - {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, - {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, - {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, - {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, - {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, - {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, - {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, - {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, + {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db"}, + {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b"}, + {file = "kiwisolver-1.4.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce2cf1e5688edcb727fdf7cd1bbd0b6416758996826a8be1d958f91880d0809d"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c8bf637892dc6e6aad2bc6d4d69d08764166e5e3f69d469e55427b6ac001b19d"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:034d2c891f76bd3edbdb3ea11140d8510dca675443da7304205a2eaa45d8334c"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47b28d1dfe0793d5e96bce90835e17edf9a499b53969b03c6c47ea5985844c3"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb158fe28ca0c29f2260cca8c43005329ad58452c36f0edf298204de32a9a3ed"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5536185fce131780ebd809f8e623bf4030ce1b161353166c49a3c74c287897f"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:369b75d40abedc1da2c1f4de13f3482cb99e3237b38726710f4a793432b1c5ff"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:641f2ddf9358c80faa22e22eb4c9f54bd3f0e442e038728f500e3b978d00aa7d"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d561d2d8883e0819445cfe58d7ddd673e4015c3c57261d7bdcd3710d0d14005c"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1732e065704b47c9afca7ffa272f845300a4eb959276bf6970dc07265e73b605"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bcb1ebc3547619c3b58a39e2448af089ea2ef44b37988caf432447374941574e"}, + {file = "kiwisolver-1.4.8-cp310-cp310-win_amd64.whl", hash = "sha256:89c107041f7b27844179ea9c85d6da275aa55ecf28413e87624d033cf1f6b751"}, + {file = "kiwisolver-1.4.8-cp310-cp310-win_arm64.whl", hash = "sha256:b5773efa2be9eb9fcf5415ea3ab70fc785d598729fd6057bea38d539ead28271"}, + {file = "kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84"}, + {file = "kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561"}, + {file = "kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67"}, + {file = "kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34"}, + {file = "kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2"}, + {file = "kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502"}, + {file = "kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31"}, + {file = "kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8"}, + {file = "kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50"}, + {file = "kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476"}, + {file = "kiwisolver-1.4.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1c8ceb754339793c24aee1c9fb2485b5b1f5bb1c2c214ff13368431e51fc9a09"}, + {file = "kiwisolver-1.4.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a62808ac74b5e55a04a408cda6156f986cefbcf0ada13572696b507cc92fa1"}, + {file = "kiwisolver-1.4.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68269e60ee4929893aad82666821aaacbd455284124817af45c11e50a4b42e3c"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d142fba9c464bc3bbfeff15c96eab0e7310343d6aefb62a79d51421fcc5f1b"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc373e0eef45b59197de815b1b28ef89ae3955e7722cc9710fb91cd77b7f47"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77e6f57a20b9bd4e1e2cedda4d0b986ebd0216236f0106e55c28aea3d3d69b16"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08e77738ed7538f036cd1170cbed942ef749137b1311fa2bbe2a7fda2f6bf3cc"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc2ace710ba7c1dfd1a3b42530b62b9ceed115f19a1656adefce7b1782a37794"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3452046c37c7692bd52b0e752b87954ef86ee2224e624ef7ce6cb21e8c41cc1b"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e9a60b50fe8b2ec6f448fe8d81b07e40141bfced7f896309df271a0b92f80f3"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:918139571133f366e8362fa4a297aeba86c7816b7ecf0bc79168080e2bd79957"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e063ef9f89885a1d68dd8b2e18f5ead48653176d10a0e324e3b0030e3a69adeb"}, + {file = "kiwisolver-1.4.8-cp313-cp313-win_amd64.whl", hash = "sha256:a17b7c4f5b2c51bb68ed379defd608a03954a1845dfed7cc0117f1cc8a9b7fd2"}, + {file = "kiwisolver-1.4.8-cp313-cp313-win_arm64.whl", hash = "sha256:3cd3bc628b25f74aedc6d374d5babf0166a92ff1317f46267f12d2ed54bc1d30"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:370fd2df41660ed4e26b8c9d6bbcad668fbe2560462cba151a721d49e5b6628c"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:84a2f830d42707de1d191b9490ac186bf7997a9495d4e9072210a1296345f7dc"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7a3ad337add5148cf51ce0b55642dc551c0b9d6248458a757f98796ca7348712"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7506488470f41169b86d8c9aeff587293f530a23a23a49d6bc64dab66bedc71e"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f0121b07b356a22fb0414cec4666bbe36fd6d0d759db3d37228f496ed67c880"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6d6bd87df62c27d4185de7c511c6248040afae67028a8a22012b010bc7ad062"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:291331973c64bb9cce50bbe871fb2e675c4331dab4f31abe89f175ad7679a4d7"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:893f5525bb92d3d735878ec00f781b2de998333659507d29ea4466208df37bed"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b47a465040146981dc9db8647981b8cb96366fbc8d452b031e4f8fdffec3f26d"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:99cea8b9dd34ff80c521aef46a1dddb0dcc0283cf18bde6d756f1e6f31772165"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e7a019419b7b510f0f7c9dceff8c5eae2392037eae483a7f9162625233802b0a"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:286b18e86682fd2217a48fc6be6b0f20c1d0ed10958d8dc53453ad58d7be0bf8"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4191ee8dfd0be1c3666ccbac178c5a05d5f8d689bbe3fc92f3c4abec817f8fe0"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd2785b9391f2873ad46088ed7599a6a71e762e1ea33e87514b1a441ed1da1c"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c07b29089b7ba090b6f1a669f1411f27221c3662b3a1b7010e67b59bb5a6f10b"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:65ea09a5a3faadd59c2ce96dc7bf0f364986a315949dc6374f04396b0d60e09b"}, + {file = "kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e"}, ] [[package]] name = "matplotlib" -version = "3.9.0" +version = "3.10.0" description = "Python plotting package" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "matplotlib-3.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2bcee1dffaf60fe7656183ac2190bd630842ff87b3153afb3e384d966b57fe56"}, - {file = "matplotlib-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f988bafb0fa39d1074ddd5bacd958c853e11def40800c5824556eb630f94d3b"}, - {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe428e191ea016bb278758c8ee82a8129c51d81d8c4bc0846c09e7e8e9057241"}, - {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaf3978060a106fab40c328778b148f590e27f6fa3cd15a19d6892575bce387d"}, - {file = "matplotlib-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e7f03e5cbbfacdd48c8ea394d365d91ee8f3cae7e6ec611409927b5ed997ee4"}, - {file = "matplotlib-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:13beb4840317d45ffd4183a778685e215939be7b08616f431c7795276e067463"}, - {file = "matplotlib-3.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:063af8587fceeac13b0936c42a2b6c732c2ab1c98d38abc3337e430e1ff75e38"}, - {file = "matplotlib-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a2fa6d899e17ddca6d6526cf6e7ba677738bf2a6a9590d702c277204a7c6152"}, - {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550cdda3adbd596078cca7d13ed50b77879104e2e46392dcd7c75259d8f00e85"}, - {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cce0f31b351e3551d1f3779420cf8f6ec0d4a8cf9c0237a3b549fd28eb4abb"}, - {file = "matplotlib-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c53aeb514ccbbcbab55a27f912d79ea30ab21ee0531ee2c09f13800efb272674"}, - {file = "matplotlib-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5be985db2596d761cdf0c2eaf52396f26e6a64ab46bd8cd810c48972349d1be"}, - {file = "matplotlib-3.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c79f3a585f1368da6049318bdf1f85568d8d04b2e89fc24b7e02cc9b62017382"}, - {file = "matplotlib-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bdd1ecbe268eb3e7653e04f451635f0fb0f77f07fd070242b44c076c9106da84"}, - {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d38e85a1a6d732f645f1403ce5e6727fd9418cd4574521d5803d3d94911038e5"}, - {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a490715b3b9984fa609116481b22178348c1a220a4499cda79132000a79b4db"}, - {file = "matplotlib-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8146ce83cbc5dc71c223a74a1996d446cd35cfb6a04b683e1446b7e6c73603b7"}, - {file = "matplotlib-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:d91a4ffc587bacf5c4ce4ecfe4bcd23a4b675e76315f2866e588686cc97fccdf"}, - {file = "matplotlib-3.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:616fabf4981a3b3c5a15cd95eba359c8489c4e20e03717aea42866d8d0465956"}, - {file = "matplotlib-3.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd53c79fd02f1c1808d2cfc87dd3cf4dbc63c5244a58ee7944497107469c8d8a"}, - {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06a478f0d67636554fa78558cfbcd7b9dba85b51f5c3b5a0c9be49010cf5f321"}, - {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81c40af649d19c85f8073e25e5806926986806fa6d54be506fbf02aef47d5a89"}, - {file = "matplotlib-3.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52146fc3bd7813cc784562cb93a15788be0b2875c4655e2cc6ea646bfa30344b"}, - {file = "matplotlib-3.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:0fc51eaa5262553868461c083d9adadb11a6017315f3a757fc45ec6ec5f02888"}, - {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bd4f2831168afac55b881db82a7730992aa41c4f007f1913465fb182d6fb20c0"}, - {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:290d304e59be2b33ef5c2d768d0237f5bd132986bdcc66f80bc9bcc300066a03"}, - {file = "matplotlib-3.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff2e239c26be4f24bfa45860c20ffccd118d270c5b5d081fa4ea409b5469fcd"}, - {file = "matplotlib-3.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:af4001b7cae70f7eaacfb063db605280058246de590fa7874f00f62259f2df7e"}, - {file = "matplotlib-3.9.0.tar.gz", hash = "sha256:e6d29ea6c19e34b30fb7d88b7081f869a03014f66fe06d62cc77d5a6ea88ed7a"}, + {file = "matplotlib-3.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2c5829a5a1dd5a71f0e31e6e8bb449bc0ee9dbfb05ad28fc0c6b55101b3a4be6"}, + {file = "matplotlib-3.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2a43cbefe22d653ab34bb55d42384ed30f611bcbdea1f8d7f431011a2e1c62e"}, + {file = "matplotlib-3.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:607b16c8a73943df110f99ee2e940b8a1cbf9714b65307c040d422558397dac5"}, + {file = "matplotlib-3.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01d2b19f13aeec2e759414d3bfe19ddfb16b13a1250add08d46d5ff6f9be83c6"}, + {file = "matplotlib-3.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e6c6461e1fc63df30bf6f80f0b93f5b6784299f721bc28530477acd51bfc3d1"}, + {file = "matplotlib-3.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:994c07b9d9fe8d25951e3202a68c17900679274dadfc1248738dcfa1bd40d7f3"}, + {file = "matplotlib-3.10.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:fd44fc75522f58612ec4a33958a7e5552562b7705b42ef1b4f8c0818e304a363"}, + {file = "matplotlib-3.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c58a9622d5dbeb668f407f35f4e6bfac34bb9ecdcc81680c04d0258169747997"}, + {file = "matplotlib-3.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:845d96568ec873be63f25fa80e9e7fae4be854a66a7e2f0c8ccc99e94a8bd4ef"}, + {file = "matplotlib-3.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5439f4c5a3e2e8eab18e2f8c3ef929772fd5641876db71f08127eed95ab64683"}, + {file = "matplotlib-3.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4673ff67a36152c48ddeaf1135e74ce0d4bce1bbf836ae40ed39c29edf7e2765"}, + {file = "matplotlib-3.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:7e8632baebb058555ac0cde75db885c61f1212e47723d63921879806b40bec6a"}, + {file = "matplotlib-3.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4659665bc7c9b58f8c00317c3c2a299f7f258eeae5a5d56b4c64226fca2f7c59"}, + {file = "matplotlib-3.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d44cb942af1693cced2604c33a9abcef6205601c445f6d0dc531d813af8a2f5a"}, + {file = "matplotlib-3.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a994f29e968ca002b50982b27168addfd65f0105610b6be7fa515ca4b5307c95"}, + {file = "matplotlib-3.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b0558bae37f154fffda54d779a592bc97ca8b4701f1c710055b609a3bac44c8"}, + {file = "matplotlib-3.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:503feb23bd8c8acc75541548a1d709c059b7184cde26314896e10a9f14df5f12"}, + {file = "matplotlib-3.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:c40ba2eb08b3f5de88152c2333c58cee7edcead0a2a0d60fcafa116b17117adc"}, + {file = "matplotlib-3.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96f2886f5c1e466f21cc41b70c5a0cd47bfa0015eb2d5793c88ebce658600e25"}, + {file = "matplotlib-3.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:12eaf48463b472c3c0f8dbacdbf906e573013df81a0ab82f0616ea4b11281908"}, + {file = "matplotlib-3.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fbbabc82fde51391c4da5006f965e36d86d95f6ee83fb594b279564a4c5d0d2"}, + {file = "matplotlib-3.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad2e15300530c1a94c63cfa546e3b7864bd18ea2901317bae8bbf06a5ade6dcf"}, + {file = "matplotlib-3.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3547d153d70233a8496859097ef0312212e2689cdf8d7ed764441c77604095ae"}, + {file = "matplotlib-3.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c55b20591ced744aa04e8c3e4b7543ea4d650b6c3c4b208c08a05b4010e8b442"}, + {file = "matplotlib-3.10.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ade1003376731a971e398cc4ef38bb83ee8caf0aee46ac6daa4b0506db1fd06"}, + {file = "matplotlib-3.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:95b710fea129c76d30be72c3b38f330269363fbc6e570a5dd43580487380b5ff"}, + {file = "matplotlib-3.10.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdbaf909887373c3e094b0318d7ff230b2ad9dcb64da7ade654182872ab2593"}, + {file = "matplotlib-3.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d907fddb39f923d011875452ff1eca29a9e7f21722b873e90db32e5d8ddff12e"}, + {file = "matplotlib-3.10.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3b427392354d10975c1d0f4ee18aa5844640b512d5311ef32efd4dd7db106ede"}, + {file = "matplotlib-3.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5fd41b0ec7ee45cd960a8e71aea7c946a28a0b8a4dcee47d2856b2af051f334c"}, + {file = "matplotlib-3.10.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:81713dd0d103b379de4516b861d964b1d789a144103277769238c732229d7f03"}, + {file = "matplotlib-3.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:359f87baedb1f836ce307f0e850d12bb5f1936f70d035561f90d41d305fdacea"}, + {file = "matplotlib-3.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae80dc3a4add4665cf2faa90138384a7ffe2a4e37c58d83e115b54287c4f06ef"}, + {file = "matplotlib-3.10.0.tar.gz", hash = "sha256:b886d02a581b96704c9d1ffe55709e49b4d2d52709ccebc4be42db856e511278"}, ] [package.dependencies] @@ -306,42 +315,44 @@ pyparsing = ">=2.3.1" python-dateutil = ">=2.7" [package.extras] -dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] +dev = ["meson-python (>=0.13.1,<0.17.0)", "pybind11 (>=2.13.2,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7)"] [[package]] name = "mypy" -version = "1.10.0" +version = "1.10.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"}, - {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"}, - {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"}, - {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"}, - {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"}, - {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, - {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, - {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, - {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, - {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, - {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, - {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, - {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, - {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, - {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, - {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"}, - {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"}, - {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"}, - {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"}, - {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"}, - {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"}, - {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"}, - {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"}, - {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"}, - {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"}, - {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, - {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, + {file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"}, + {file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"}, + {file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"}, + {file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"}, + {file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"}, + {file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"}, + {file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"}, + {file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"}, + {file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"}, + {file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"}, + {file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"}, + {file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"}, + {file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"}, + {file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"}, + {file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"}, + {file = "mypy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:604282c886497645ffb87b8f35a57ec773a4a2721161e709a4422c1636ddde5c"}, + {file = "mypy-1.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37fd87cab83f09842653f08de066ee68f1182b9b5282e4634cdb4b407266bade"}, + {file = "mypy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8addf6313777dbb92e9564c5d32ec122bf2c6c39d683ea64de6a1fd98b90fe37"}, + {file = "mypy-1.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cc3ca0a244eb9a5249c7c583ad9a7e881aa5d7b73c35652296ddcdb33b2b9c7"}, + {file = "mypy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:1b3a2ffce52cc4dbaeee4df762f20a2905aa171ef157b82192f2e2f368eec05d"}, + {file = "mypy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe85ed6836165d52ae8b88f99527d3d1b2362e0cb90b005409b8bed90e9059b3"}, + {file = "mypy-1.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2ae450d60d7d020d67ab440c6e3fae375809988119817214440033f26ddf7bf"}, + {file = "mypy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be84c06e6abd72f960ba9a71561c14137a583093ffcf9bbfaf5e613d63fa531"}, + {file = "mypy-1.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2189ff1e39db399f08205e22a797383613ce1cb0cb3b13d8bcf0170e45b96cc3"}, + {file = "mypy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:97a131ee36ac37ce9581f4220311247ab6cba896b4395b9c87af0675a13a755f"}, + {file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"}, + {file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"}, ] [package.dependencies] @@ -361,6 +372,8 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -368,58 +381,81 @@ files = [ [[package]] name = "numpy" -version = "1.26.4" +version = "2.2.2" description = "Fundamental package for array computing in Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, - {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, - {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, - {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, - {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, - {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, - {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, - {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, - {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, - {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, - {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, + {file = "numpy-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e"}, + {file = "numpy-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e"}, + {file = "numpy-2.2.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:40c7ff5da22cd391944a28c6a9c638a5eef77fcf71d6e3a79e1d9d9e82752715"}, + {file = "numpy-2.2.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:995f9e8181723852ca458e22de5d9b7d3ba4da3f11cc1cb113f093b271d7965a"}, + {file = "numpy-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78ea78450fd96a498f50ee096f69c75379af5138f7881a51355ab0e11286c97"}, + {file = "numpy-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fbe72d347fbc59f94124125e73fc4976a06927ebc503ec5afbfb35f193cd957"}, + {file = "numpy-2.2.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8e6da5cffbbe571f93588f562ed130ea63ee206d12851b60819512dd3e1ba50d"}, + {file = "numpy-2.2.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:09d6a2032faf25e8d0cadde7fd6145118ac55d2740132c1d845f98721b5ebcfd"}, + {file = "numpy-2.2.2-cp310-cp310-win32.whl", hash = "sha256:159ff6ee4c4a36a23fe01b7c3d07bd8c14cc433d9720f977fcd52c13c0098160"}, + {file = "numpy-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:64bd6e1762cd7f0986a740fee4dff927b9ec2c5e4d9a28d056eb17d332158014"}, + {file = "numpy-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:642199e98af1bd2b6aeb8ecf726972d238c9877b0f6e8221ee5ab945ec8a2189"}, + {file = "numpy-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6d9fc9d812c81e6168b6d405bf00b8d6739a7f72ef22a9214c4241e0dc70b323"}, + {file = "numpy-2.2.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:c7d1fd447e33ee20c1f33f2c8e6634211124a9aabde3c617687d8b739aa69eac"}, + {file = "numpy-2.2.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:451e854cfae0febe723077bd0cf0a4302a5d84ff25f0bfece8f29206c7bed02e"}, + {file = "numpy-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd249bc894af67cbd8bad2c22e7cbcd46cf87ddfca1f1289d1e7e54868cc785c"}, + {file = "numpy-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02935e2c3c0c6cbe9c7955a8efa8908dd4221d7755644c59d1bba28b94fd334f"}, + {file = "numpy-2.2.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a972cec723e0563aa0823ee2ab1df0cb196ed0778f173b381c871a03719d4826"}, + {file = "numpy-2.2.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d6d6a0910c3b4368d89dde073e630882cdb266755565155bc33520283b2d9df8"}, + {file = "numpy-2.2.2-cp311-cp311-win32.whl", hash = "sha256:860fd59990c37c3ef913c3ae390b3929d005243acca1a86facb0773e2d8d9e50"}, + {file = "numpy-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:da1eeb460ecce8d5b8608826595c777728cdf28ce7b5a5a8c8ac8d949beadcf2"}, + {file = "numpy-2.2.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467"}, + {file = "numpy-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a"}, + {file = "numpy-2.2.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825"}, + {file = "numpy-2.2.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37"}, + {file = "numpy-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748"}, + {file = "numpy-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0"}, + {file = "numpy-2.2.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278"}, + {file = "numpy-2.2.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba"}, + {file = "numpy-2.2.2-cp312-cp312-win32.whl", hash = "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283"}, + {file = "numpy-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb"}, + {file = "numpy-2.2.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc"}, + {file = "numpy-2.2.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369"}, + {file = "numpy-2.2.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd"}, + {file = "numpy-2.2.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be"}, + {file = "numpy-2.2.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84"}, + {file = "numpy-2.2.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff"}, + {file = "numpy-2.2.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0"}, + {file = "numpy-2.2.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de"}, + {file = "numpy-2.2.2-cp313-cp313-win32.whl", hash = "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9"}, + {file = "numpy-2.2.2-cp313-cp313-win_amd64.whl", hash = "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369"}, + {file = "numpy-2.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391"}, + {file = "numpy-2.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39"}, + {file = "numpy-2.2.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317"}, + {file = "numpy-2.2.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49"}, + {file = "numpy-2.2.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2"}, + {file = "numpy-2.2.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7"}, + {file = "numpy-2.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb"}, + {file = "numpy-2.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648"}, + {file = "numpy-2.2.2-cp313-cp313t-win32.whl", hash = "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4"}, + {file = "numpy-2.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576"}, + {file = "numpy-2.2.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b0531f0b0e07643eb089df4c509d30d72c9ef40defa53e41363eca8a8cc61495"}, + {file = "numpy-2.2.2-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:e9e82dcb3f2ebbc8cb5ce1102d5f1c5ed236bf8a11730fb45ba82e2841ec21df"}, + {file = "numpy-2.2.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0d4142eb40ca6f94539e4db929410f2a46052a0fe7a2c1c59f6179c39938d2a"}, + {file = "numpy-2.2.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:356ca982c188acbfa6af0d694284d8cf20e95b1c3d0aefa8929376fea9146f60"}, + {file = "numpy-2.2.2.tar.gz", hash = "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f"}, ] [[package]] name = "packaging" -version = "24.0" +version = "24.2" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -428,6 +464,8 @@ version = "2.2.2" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, @@ -497,124 +535,276 @@ xml = ["lxml (>=4.9.2)"] [[package]] name = "pillow" -version = "10.3.0" +version = "11.1.0" description = "Python Imaging Library (Fork)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, - {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, - {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, - {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, - {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, - {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, - {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, - {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, - {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, - {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, - {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, - {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, - {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, - {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, - {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, - {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, - {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, - {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, - {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, - {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, - {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, - {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, - {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, - {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, - {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, - {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, - {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, - {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, - {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, - {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, - {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, - {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, - {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, - {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, - {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, + {file = "pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8"}, + {file = "pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192"}, + {file = "pillow-11.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07dba04c5e22824816b2615ad7a7484432d7f540e6fa86af60d2de57b0fcee2"}, + {file = "pillow-11.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e267b0ed063341f3e60acd25c05200df4193e15a4a5807075cd71225a2386e26"}, + {file = "pillow-11.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bd165131fd51697e22421d0e467997ad31621b74bfc0b75956608cb2906dda07"}, + {file = "pillow-11.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:abc56501c3fd148d60659aae0af6ddc149660469082859fa7b066a298bde9482"}, + {file = "pillow-11.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:54ce1c9a16a9561b6d6d8cb30089ab1e5eb66918cb47d457bd996ef34182922e"}, + {file = "pillow-11.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73ddde795ee9b06257dac5ad42fcb07f3b9b813f8c1f7f870f402f4dc54b5269"}, + {file = "pillow-11.1.0-cp310-cp310-win32.whl", hash = "sha256:3a5fe20a7b66e8135d7fd617b13272626a28278d0e578c98720d9ba4b2439d49"}, + {file = "pillow-11.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6123aa4a59d75f06e9dd3dac5bf8bc9aa383121bb3dd9a7a612e05eabc9961a"}, + {file = "pillow-11.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:a76da0a31da6fcae4210aa94fd779c65c75786bc9af06289cd1c184451ef7a65"}, + {file = "pillow-11.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457"}, + {file = "pillow-11.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35"}, + {file = "pillow-11.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3cd561ded2cf2bbae44d4605837221b987c216cff94f49dfeed63488bb228d2"}, + {file = "pillow-11.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f189805c8be5ca5add39e6f899e6ce2ed824e65fb45f3c28cb2841911da19070"}, + {file = "pillow-11.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dd0052e9db3474df30433f83a71b9b23bd9e4ef1de13d92df21a52c0303b8ab6"}, + {file = "pillow-11.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:837060a8599b8f5d402e97197d4924f05a2e0d68756998345c829c33186217b1"}, + {file = "pillow-11.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa8dd43daa836b9a8128dbe7d923423e5ad86f50a7a14dc688194b7be5c0dea2"}, + {file = "pillow-11.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0a2f91f8a8b367e7a57c6e91cd25af510168091fb89ec5146003e424e1558a96"}, + {file = "pillow-11.1.0-cp311-cp311-win32.whl", hash = "sha256:c12fc111ef090845de2bb15009372175d76ac99969bdf31e2ce9b42e4b8cd88f"}, + {file = "pillow-11.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd43429d0d7ed6533b25fc993861b8fd512c42d04514a0dd6337fb3ccf22761"}, + {file = "pillow-11.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f7955ecf5609dee9442cbface754f2c6e541d9e6eda87fad7f7a989b0bdb9d71"}, + {file = "pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a"}, + {file = "pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b"}, + {file = "pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3"}, + {file = "pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a"}, + {file = "pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1"}, + {file = "pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f"}, + {file = "pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91"}, + {file = "pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c"}, + {file = "pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6"}, + {file = "pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf"}, + {file = "pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5"}, + {file = "pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc"}, + {file = "pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0"}, + {file = "pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1"}, + {file = "pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec"}, + {file = "pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5"}, + {file = "pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114"}, + {file = "pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352"}, + {file = "pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3"}, + {file = "pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9"}, + {file = "pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c"}, + {file = "pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65"}, + {file = "pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861"}, + {file = "pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081"}, + {file = "pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c"}, + {file = "pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547"}, + {file = "pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab"}, + {file = "pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9"}, + {file = "pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe"}, + {file = "pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756"}, + {file = "pillow-11.1.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:bf902d7413c82a1bfa08b06a070876132a5ae6b2388e2712aab3a7cbc02205c6"}, + {file = "pillow-11.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c1eec9d950b6fe688edee07138993e54ee4ae634c51443cfb7c1e7613322718e"}, + {file = "pillow-11.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e275ee4cb11c262bd108ab2081f750db2a1c0b8c12c1897f27b160c8bd57bbc"}, + {file = "pillow-11.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4db853948ce4e718f2fc775b75c37ba2efb6aaea41a1a5fc57f0af59eee774b2"}, + {file = "pillow-11.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:ab8a209b8485d3db694fa97a896d96dd6533d63c22829043fd9de627060beade"}, + {file = "pillow-11.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:54251ef02a2309b5eec99d151ebf5c9904b77976c8abdcbce7891ed22df53884"}, + {file = "pillow-11.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5bb94705aea800051a743aa4874bb1397d4695fb0583ba5e425ee0328757f196"}, + {file = "pillow-11.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89dbdb3e6e9594d512780a5a1c42801879628b38e3efc7038094430844e271d8"}, + {file = "pillow-11.1.0-cp39-cp39-win32.whl", hash = "sha256:e5449ca63da169a2e6068dd0e2fcc8d91f9558aba89ff6d02121ca8ab11e79e5"}, + {file = "pillow-11.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:3362c6ca227e65c54bf71a5f88b3d4565ff1bcbc63ae72c34b07bbb1cc59a43f"}, + {file = "pillow-11.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:b20be51b37a75cc54c2c55def3fa2c65bb94ba859dde241cd0a4fd302de5ae0a"}, + {file = "pillow-11.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8c730dc3a83e5ac137fbc92dfcfe1511ce3b2b5d7578315b63dbbb76f7f51d90"}, + {file = "pillow-11.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d33d2fae0e8b170b6a6c57400e077412240f6f5bb2a342cf1ee512a787942bb"}, + {file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8d65b38173085f24bc07f8b6c505cbb7418009fa1a1fcb111b1f4961814a442"}, + {file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:015c6e863faa4779251436db398ae75051469f7c903b043a48f078e437656f83"}, + {file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d44ff19eea13ae4acdaaab0179fa68c0c6f2f45d66a4d8ec1eda7d6cecbcc15f"}, + {file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d3d8da4a631471dfaf94c10c85f5277b1f8e42ac42bade1ac67da4b4a7359b73"}, + {file = "pillow-11.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4637b88343166249fe8aa94e7c4a62a180c4b3898283bb5d3d2fd5fe10d8e4e0"}, + {file = "pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20"}, ] [package.extras] -docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] fpx = ["olefile"] mic = ["olefile"] -tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "trove-classifiers (>=2024.10.12)"] typing = ["typing-extensions"] xmp = ["defusedxml"] [[package]] -name = "psutil-wheels" +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"linux\" and python_version < \"3.11\"" +files = [ + {file = "psutil-5.8.0-cp310-cp310-linux_x86_64.whl", hash = "sha256:4dd2052113d323b3fe9fd93e9915ab364bdb9ea3bd4995d7a0bf62c492713167"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-linux_x86_64.whl" + +[[package]] +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"darwin\" and python_version < \"3.11\"" +files = [ + {file = "psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b611197383b14311c87919eaa8fbeebf15eab5fdeb22d4c7b67f03772a79716"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl" + +[[package]] +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"win32\" and python_version < \"3.11\"" +files = [ + {file = "psutil-5.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:07fd5212435883f325b925480b326bb5a2537e085e5552acd9877b91da4566ec"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-win_amd64.whl" + +[[package]] +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"linux\" and python_version == \"3.11\"" +files = [ + {file = "psutil-5.8.0-cp311-cp311-linux_x86_64.whl", hash = "sha256:b3e5c23e33e96c582e5e3a99e2d2e432621784880e229c4a4e81655932ebd25f"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-linux_x86_64.whl" + +[[package]] +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"darwin\" and python_version == \"3.11\"" +files = [ + {file = "psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f126690bbb74f08cccaaf5defe9d9d4e0b629855e8eb24e07ba6f6087337edf8"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl" + +[[package]] +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"win32\" and python_version == \"3.11\"" +files = [ + {file = "psutil-5.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:b175a13518b8b3c6671f0e3e626706e2eff936a2519c38ca0a5f73138736099a"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-win_amd64.whl" + +[[package]] +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"linux\" and python_version >= \"3.12\" and python_version < \"3.13\"" +files = [ + {file = "psutil-5.8.0-cp312-cp312-linux_x86_64.whl", hash = "sha256:adf09ab4d9caaf3ac048a2d9486e14eba84e6b231df24b46ce7d495372672f05"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-linux_x86_64.whl" + +[[package]] +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"darwin\" and python_version >= \"3.12\" and python_version < \"3.13\"" +files = [ + {file = "psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f7d30e6a6ca11ea83f50039b9dfefc9a1ef2c66028cc0db9071ac1edd9d9bbb9"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl" + +[[package]] +name = "psutil" version = "5.8.0" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"win32\" and python_version >= \"3.12\" and python_version < \"3.13\"" files = [ - {file = "psutil-wheels-5.8.0.tar.gz", hash = "sha256:9fb80725195402a66e5db947f239d032500cde75ca5d8625326d797a65341d6f"}, - {file = "psutil_wheels-5.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2cfbb317f3ee0c8ac9dd5d82e6913b0216222d2b22ea65cbc2f8072dabb167d4"}, - {file = "psutil_wheels-5.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ea1f7f6bcc536669a22c07429dde993bc707f45339137b085394faada25fc813"}, - {file = "psutil_wheels-5.8.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d13d705fb5026d3ae476c7988601430dfaa6143e695058a3182146adc0457b7f"}, - {file = "psutil_wheels-5.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:3f0ca7c4c9031e04b18e52cd4c6f17e196bb7896071dd1eacaeb352948b47517"}, - {file = "psutil_wheels-5.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:902ab2a221529cd7c0d9fa2f865fdd22bc45df87db825437aeee0dcaeed9b787"}, - {file = "psutil_wheels-5.8.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:96838ad949609621e369d327834ade3b3e1b0fa3f450e0a7460855a3cf41a6d6"}, - {file = "psutil_wheels-5.8.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:933c4079c8121f8f0d3d1525671e3b6182d804d54c7819b6a7dddeac5605ba69"}, - {file = "psutil_wheels-5.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:5dd57fb06b081bf2e3cebe89ca92f6ef606ecc5e50ac7ecb2dc7a68262d6cd91"}, - {file = "psutil_wheels-5.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:05a4b136c395273066ecd63d64200868fc57561c65f6dda988b28d08f4a60f69"}, - {file = "psutil_wheels-5.8.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c7c13e8264fa26f0bde4ddc15f2959d04c2a8f7537c41541d1503dd159b01a86"}, - {file = "psutil_wheels-5.8.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b07890d22db82a135b8d5149ba1736e0fde998605cfa73c4d030bbfc77e890b6"}, - {file = "psutil_wheels-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:84bb63b669aa918b4a62226276b1c1f952e57a461debfb7b9eed848c41e7cbda"}, + {file = "psutil-5.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:2b478fe4266e25e7b0caea5eeec6df836b2a37308cb24efbce30f915b1f03cfe"}, ] [package.extras] test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-win_amd64.whl" + [[package]] name = "pyparsing" -version = "3.1.2" +version = "3.2.1" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false -python-versions = ">=3.6.8" +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, - {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, + {file = "pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1"}, + {file = "pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a"}, ] [package.extras] @@ -626,6 +816,8 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -636,82 +828,104 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2024.1" +version = "2025.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, - {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, + {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, + {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, ] [[package]] name = "ruff" -version = "0.4.4" +version = "0.6.0" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "ruff-0.4.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:29d44ef5bb6a08e235c8249294fa8d431adc1426bfda99ed493119e6f9ea1bf6"}, - {file = "ruff-0.4.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c4efe62b5bbb24178c950732ddd40712b878a9b96b1d02b0ff0b08a090cbd891"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8e2f1e8fc12d07ab521a9005d68a969e167b589cbcaee354cb61e9d9de9c15"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60ed88b636a463214905c002fa3eaab19795679ed55529f91e488db3fe8976ab"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b90fc5e170fc71c712cc4d9ab0e24ea505c6a9e4ebf346787a67e691dfb72e85"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8e7e6ebc10ef16dcdc77fd5557ee60647512b400e4a60bdc4849468f076f6eef"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ddb2c494fb79fc208cd15ffe08f32b7682519e067413dbaf5f4b01a6087bcd"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c51c928a14f9f0a871082603e25a1588059b7e08a920f2f9fa7157b5bf08cfe9"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5eb0a4bfd6400b7d07c09a7725e1a98c3b838be557fee229ac0f84d9aa49c36"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b1867ee9bf3acc21778dcb293db504692eda5f7a11a6e6cc40890182a9f9e595"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1aecced1269481ef2894cc495647392a34b0bf3e28ff53ed95a385b13aa45768"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da73eb616b3241a307b837f32756dc20a0b07e2bcb694fec73699c93d04a69e"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:958b4ea5589706a81065e2a776237de2ecc3e763342e5cc8e02a4a4d8a5e6f95"}, - {file = "ruff-0.4.4-py3-none-win32.whl", hash = "sha256:cb53473849f011bca6e754f2cdf47cafc9c4f4ff4570003a0dad0b9b6890e876"}, - {file = "ruff-0.4.4-py3-none-win_amd64.whl", hash = "sha256:424e5b72597482543b684c11def82669cc6b395aa8cc69acc1858b5ef3e5daae"}, - {file = "ruff-0.4.4-py3-none-win_arm64.whl", hash = "sha256:39df0537b47d3b597293edbb95baf54ff5b49589eb7ff41926d8243caa995ea6"}, - {file = "ruff-0.4.4.tar.gz", hash = "sha256:f87ea42d5cdebdc6a69761a9d0bc83ae9b3b30d0ad78952005ba6568d6c022af"}, + {file = "ruff-0.6.0-py3-none-linux_armv6l.whl", hash = "sha256:92dcce923e5df265781e5fc76f9a1edad52201a7aafe56e586b90988d5239013"}, + {file = "ruff-0.6.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:31b90ff9dc79ed476c04e957ba7e2b95c3fceb76148f2079d0d68a908d2cfae7"}, + {file = "ruff-0.6.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d834a9ec9f8287dd6c3297058b3a265ed6b59233db22593379ee38ebc4b9768"}, + {file = "ruff-0.6.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2089267692696aba342179471831a085043f218706e642564812145df8b8d0d"}, + {file = "ruff-0.6.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aa62b423ee4bbd8765f2c1dbe8f6aac203e0583993a91453dc0a449d465c84da"}, + {file = "ruff-0.6.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7344e1a964b16b1137ea361d6516ce4ee61a0403fa94252a1913ecc1311adcae"}, + {file = "ruff-0.6.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:487f3a35c3f33bf82be212ce15dc6278ea854e35573a3f809442f73bec8b2760"}, + {file = "ruff-0.6.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75db409984077a793cf344d499165298a6f65449e905747ac65983b12e3e64b1"}, + {file = "ruff-0.6.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84908bd603533ecf1db456d8fc2665d1f4335d722e84bc871d3bbd2d1116c272"}, + {file = "ruff-0.6.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f1749a0aef3ec41ed91a0e2127a6ae97d2e2853af16dbd4f3c00d7a3af726c5"}, + {file = "ruff-0.6.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:016fea751e2bcfbbd2f8cb19b97b37b3fd33148e4df45b526e87096f4e17354f"}, + {file = "ruff-0.6.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6ae80f141b53b2e36e230017e64f5ea2def18fac14334ffceaae1b780d70c4f7"}, + {file = "ruff-0.6.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eaaaf33ea4b3f63fd264d6a6f4a73fa224bbfda4b438ffea59a5340f4afa2bb5"}, + {file = "ruff-0.6.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7667ddd1fc688150a7ca4137140867584c63309695a30016880caf20831503a0"}, + {file = "ruff-0.6.0-py3-none-win32.whl", hash = "sha256:ae48365aae60d40865a412356f8c6f2c0be1c928591168111eaf07eaefa6bea3"}, + {file = "ruff-0.6.0-py3-none-win_amd64.whl", hash = "sha256:774032b507c96f0c803c8237ce7d2ef3934df208a09c40fa809c2931f957fe5e"}, + {file = "ruff-0.6.0-py3-none-win_arm64.whl", hash = "sha256:a5366e8c3ae6b2dc32821749b532606c42e609a99b0ae1472cf601da931a048c"}, + {file = "ruff-0.6.0.tar.gz", hash = "sha256:272a81830f68f9bd19d49eaf7fa01a5545c5a2e86f32a9935bb0e4bb9a1db5b8"}, ] [[package]] name = "scipy" -version = "1.13.0" +version = "1.15.1" description = "Fundamental algorithms for scientific computing in Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "scipy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba419578ab343a4e0a77c0ef82f088238a93eef141b2b8017e46149776dfad4d"}, - {file = "scipy-1.13.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:22789b56a999265431c417d462e5b7f2b487e831ca7bef5edeb56efe4c93f86e"}, - {file = "scipy-1.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05f1432ba070e90d42d7fd836462c50bf98bd08bed0aa616c359eed8a04e3922"}, - {file = "scipy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8434f6f3fa49f631fae84afee424e2483289dfc30a47755b4b4e6b07b2633a4"}, - {file = "scipy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:dcbb9ea49b0167de4167c40eeee6e167caeef11effb0670b554d10b1e693a8b9"}, - {file = "scipy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:1d2f7bb14c178f8b13ebae93f67e42b0a6b0fc50eba1cd8021c9b6e08e8fb1cd"}, - {file = "scipy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fbcf8abaf5aa2dc8d6400566c1a727aed338b5fe880cde64907596a89d576fa"}, - {file = "scipy-1.13.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5e4a756355522eb60fcd61f8372ac2549073c8788f6114449b37e9e8104f15a5"}, - {file = "scipy-1.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5acd8e1dbd8dbe38d0004b1497019b2dbbc3d70691e65d69615f8a7292865d7"}, - {file = "scipy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ff7dad5d24a8045d836671e082a490848e8639cabb3dbdacb29f943a678683d"}, - {file = "scipy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4dca18c3ffee287ddd3bc8f1dabaf45f5305c5afc9f8ab9cbfab855e70b2df5c"}, - {file = "scipy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:a2f471de4d01200718b2b8927f7d76b5d9bde18047ea0fa8bd15c5ba3f26a1d6"}, - {file = "scipy-1.13.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d0de696f589681c2802f9090fff730c218f7c51ff49bf252b6a97ec4a5d19e8b"}, - {file = "scipy-1.13.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:b2a3ff461ec4756b7e8e42e1c681077349a038f0686132d623fa404c0bee2551"}, - {file = "scipy-1.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf9fe63e7a4bf01d3645b13ff2aa6dea023d38993f42aaac81a18b1bda7a82a"}, - {file = "scipy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e7626dfd91cdea5714f343ce1176b6c4745155d234f1033584154f60ef1ff42"}, - {file = "scipy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:109d391d720fcebf2fbe008621952b08e52907cf4c8c7efc7376822151820820"}, - {file = "scipy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:8930ae3ea371d6b91c203b1032b9600d69c568e537b7988a3073dfe4d4774f21"}, - {file = "scipy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5407708195cb38d70fd2d6bb04b1b9dd5c92297d86e9f9daae1576bd9e06f602"}, - {file = "scipy-1.13.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:ac38c4c92951ac0f729c4c48c9e13eb3675d9986cc0c83943784d7390d540c78"}, - {file = "scipy-1.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c74543c4fbeb67af6ce457f6a6a28e5d3739a87f62412e4a16e46f164f0ae5"}, - {file = "scipy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28e286bf9ac422d6beb559bc61312c348ca9b0f0dae0d7c5afde7f722d6ea13d"}, - {file = "scipy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33fde20efc380bd23a78a4d26d59fc8704e9b5fd9b08841693eb46716ba13d86"}, - {file = "scipy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:45c08bec71d3546d606989ba6e7daa6f0992918171e2a6f7fbedfa7361c2de1e"}, - {file = "scipy-1.13.0.tar.gz", hash = "sha256:58569af537ea29d3f78e5abd18398459f195546bb3be23d16677fb26616cc11e"}, + {file = "scipy-1.15.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:c64ded12dcab08afff9e805a67ff4480f5e69993310e093434b10e85dc9d43e1"}, + {file = "scipy-1.15.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5b190b935e7db569960b48840e5bef71dc513314cc4e79a1b7d14664f57fd4ff"}, + {file = "scipy-1.15.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:4b17d4220df99bacb63065c76b0d1126d82bbf00167d1730019d2a30d6ae01ea"}, + {file = "scipy-1.15.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:63b9b6cd0333d0eb1a49de6f834e8aeaefe438df8f6372352084535ad095219e"}, + {file = "scipy-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f151e9fb60fbf8e52426132f473221a49362091ce7a5e72f8aa41f8e0da4f25"}, + {file = "scipy-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21e10b1dd56ce92fba3e786007322542361984f8463c6d37f6f25935a5a6ef52"}, + {file = "scipy-1.15.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5dff14e75cdbcf07cdaa1c7707db6017d130f0af9ac41f6ce443a93318d6c6e0"}, + {file = "scipy-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:f82fcf4e5b377f819542fbc8541f7b5fbcf1c0017d0df0bc22c781bf60abc4d8"}, + {file = "scipy-1.15.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:5bd8d27d44e2c13d0c1124e6a556454f52cd3f704742985f6b09e75e163d20d2"}, + {file = "scipy-1.15.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:be3deeb32844c27599347faa077b359584ba96664c5c79d71a354b80a0ad0ce0"}, + {file = "scipy-1.15.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:5eb0ca35d4b08e95da99a9f9c400dc9f6c21c424298a0ba876fdc69c7afacedf"}, + {file = "scipy-1.15.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:74bb864ff7640dea310a1377d8567dc2cb7599c26a79ca852fc184cc851954ac"}, + {file = "scipy-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:667f950bf8b7c3a23b4199db24cb9bf7512e27e86d0e3813f015b74ec2c6e3df"}, + {file = "scipy-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395be70220d1189756068b3173853029a013d8c8dd5fd3d1361d505b2aa58fa7"}, + {file = "scipy-1.15.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ce3a000cd28b4430426db2ca44d96636f701ed12e2b3ca1f2b1dd7abdd84b39a"}, + {file = "scipy-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:3fe1d95944f9cf6ba77aa28b82dd6bb2a5b52f2026beb39ecf05304b8392864b"}, + {file = "scipy-1.15.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c09aa9d90f3500ea4c9b393ee96f96b0ccb27f2f350d09a47f533293c78ea776"}, + {file = "scipy-1.15.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:0ac102ce99934b162914b1e4a6b94ca7da0f4058b6d6fd65b0cef330c0f3346f"}, + {file = "scipy-1.15.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:09c52320c42d7f5c7748b69e9f0389266fd4f82cf34c38485c14ee976cb8cb04"}, + {file = "scipy-1.15.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:cdde8414154054763b42b74fe8ce89d7f3d17a7ac5dd77204f0e142cdc9239e9"}, + {file = "scipy-1.15.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c9d8fc81d6a3b6844235e6fd175ee1d4c060163905a2becce8e74cb0d7554ce"}, + {file = "scipy-1.15.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fb57b30f0017d4afa5fe5f5b150b8f807618819287c21cbe51130de7ccdaed2"}, + {file = "scipy-1.15.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:491d57fe89927fa1aafbe260f4cfa5ffa20ab9f1435025045a5315006a91b8f5"}, + {file = "scipy-1.15.1-cp312-cp312-win_amd64.whl", hash = "sha256:900f3fa3db87257510f011c292a5779eb627043dd89731b9c461cd16ef76ab3d"}, + {file = "scipy-1.15.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:100193bb72fbff37dbd0bf14322314fc7cbe08b7ff3137f11a34d06dc0ee6b85"}, + {file = "scipy-1.15.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:2114a08daec64980e4b4cbdf5bee90935af66d750146b1d2feb0d3ac30613692"}, + {file = "scipy-1.15.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6b3e71893c6687fc5e29208d518900c24ea372a862854c9888368c0b267387ab"}, + {file = "scipy-1.15.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:837299eec3d19b7e042923448d17d95a86e43941104d33f00da7e31a0f715d3c"}, + {file = "scipy-1.15.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82add84e8a9fb12af5c2c1a3a3f1cb51849d27a580cb9e6bd66226195142be6e"}, + {file = "scipy-1.15.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:070d10654f0cb6abd295bc96c12656f948e623ec5f9a4eab0ddb1466c000716e"}, + {file = "scipy-1.15.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:55cc79ce4085c702ac31e49b1e69b27ef41111f22beafb9b49fea67142b696c4"}, + {file = "scipy-1.15.1-cp313-cp313-win_amd64.whl", hash = "sha256:c352c1b6d7cac452534517e022f8f7b8d139cd9f27e6fbd9f3cbd0bfd39f5bef"}, + {file = "scipy-1.15.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0458839c9f873062db69a03de9a9765ae2e694352c76a16be44f93ea45c28d2b"}, + {file = "scipy-1.15.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:af0b61c1de46d0565b4b39c6417373304c1d4f5220004058bdad3061c9fa8a95"}, + {file = "scipy-1.15.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:71ba9a76c2390eca6e359be81a3e879614af3a71dfdabb96d1d7ab33da6f2364"}, + {file = "scipy-1.15.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14eaa373c89eaf553be73c3affb11ec6c37493b7eaaf31cf9ac5dffae700c2e0"}, + {file = "scipy-1.15.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f735bc41bd1c792c96bc426dece66c8723283695f02df61dcc4d0a707a42fc54"}, + {file = "scipy-1.15.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2722a021a7929d21168830790202a75dbb20b468a8133c74a2c0230c72626b6c"}, + {file = "scipy-1.15.1-cp313-cp313t-win_amd64.whl", hash = "sha256:bc7136626261ac1ed988dca56cfc4ab5180f75e0ee52e58f1e6aa74b5f3eacd5"}, + {file = "scipy-1.15.1.tar.gz", hash = "sha256:033a75ddad1463970c96a88063a1df87ccfddd526437136b6ee81ff0312ebdf6"}, ] [package.dependencies] -numpy = ">=1.22.4,<2.3" +numpy = ">=1.23.5,<2.5" [package.extras] -dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] -doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.12.0)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] -test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] +doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.16.5)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.0.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"] +test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] [[package]] name = "seaborn" @@ -719,6 +933,8 @@ version = "0.13.2" description = "Statistical data visualization" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "seaborn-0.13.2-py3-none-any.whl", hash = "sha256:636f8336facf092165e27924f223d3c62ca560b1f2bb5dff7ab7fad265361987"}, {file = "seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7"}, @@ -736,49 +952,87 @@ stats = ["scipy (>=1.7)", "statsmodels (>=0.12)"] [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] name = "tomli" -version = "2.0.1" +version = "2.2.1" description = "A lil' TOML parser" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version < \"3.11\"" files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] name = "typing-extensions" -version = "4.11.0" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "tzdata" -version = "2024.1" +version = "2025.1" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" +groups = ["main"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, + {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"}, + {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"}, ] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.10" -content-hash = "afd25f7a11631c9a47ca60d9a7f7f6fd9b488f9da263d79150902aa6a3a1b3c5" +content-hash = "7e3c8eb5addedd6ebfe1b203e5ed4eed02b3edcaf6205533426065894382a014" diff --git a/pyproject.toml b/pyproject.toml old mode 100644 new mode 100755 index 90598022..77ea996d --- a/pyproject.toml +++ b/pyproject.toml @@ -10,15 +10,26 @@ packages = [ [tool.poetry.dependencies] python = "^3.10" -psutil-wheels = "^5.8.0" -[tool.poetry.group.dev.dependencies] -mypy = "1.10.0" -ruff = "0.4.4" -matplotlib = "^3.8.2" -seaborn = "^0.13.2" -pandas = "^2.2.0" +# psutil with platform and Python version markers +psutil = [ + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-win_amd64.whl", markers = "sys_platform == 'win32' and python_version >= '3.10' and python_version < '3.11'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-win_amd64.whl", markers = "sys_platform == 'win32' and python_version >= '3.11' and python_version < '3.12'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-win_amd64.whl", markers = "sys_platform == 'win32' and python_version >= '3.12' and python_version < '3.13'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-linux_x86_64.whl", markers = "sys_platform == 'linux' and python_version >= '3.10' and python_version < '3.11'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-linux_x86_64.whl", markers = "sys_platform == 'linux' and python_version >= '3.11' and python_version < '3.12'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-linux_x86_64.whl", markers = "sys_platform == 'linux' and python_version >= '3.12' and python_version < '3.13'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl", markers = "sys_platform == 'darwin' and python_version >= '3.10' and python_version < '3.11'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl", markers = "sys_platform == 'darwin' and python_version >= '3.11' and python_version < '3.12'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl", markers = "sys_platform == 'darwin' and python_version >= '3.12' and python_version < '3.13'" } +] + +mypy = "1.10.1" +pandas = "2.2.2" +ruff = "0.6.0" +seaborn = "0.13.2" scipy = "^1.12.0" +matplotlib = "^3.9.2" [build-system] requires = ["poetry-core"] @@ -149,3 +160,10 @@ exclude = [ "tests/sql", "venv", ] + +[tool.cibuildwheel] +test-command = "pytest {project}/tests" +test-extras = ["test"] +test-skip = ["*universal2:arm64"] +# Временно пропускаем сборку для PyPy +skip = ["pp*"] From d7fea2629e9b8d004255af2799e6738649afb533 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 14 Feb 2025 11:23:13 +0200 Subject: [PATCH 14/57] Backend: Auto format --- hstest/common/file_utils.py | 36 +- hstest/common/os_utils.py | 6 +- hstest/common/process_utils.py | 11 +- hstest/common/reflection_utils.py | 66 ++- hstest/common/utils.py | 11 +- hstest/dynamic/dynamic_test.py | 10 +- hstest/dynamic/input/dynamic_testing.py | 19 +- hstest/dynamic/output/output_handler.py | 4 +- hstest/dynamic/output/output_mock.py | 40 +- hstest/dynamic/security/thread_group.py | 6 +- hstest/dynamic/security/thread_handler.py | 17 +- hstest/exception/outcomes.py | 10 +- hstest/outcomes/error_outcome.py | 10 +- hstest/outcomes/outcome.py | 6 +- hstest/stage/django_test.py | 6 +- hstest/stage/flask_test.py | 12 +- hstest/stage/plotting_test.py | 2 +- hstest/stage/sql_test.py | 12 +- hstest/stage/stage_test.py | 40 +- hstest/test_case/attach/django_settings.py | 4 +- hstest/test_case/attach/flask_settings.py | 6 +- hstest/test_case/check_result.py | 6 +- hstest/test_case/test_case.py | 32 +- .../execution/filtering/file_filter.py | 2 +- .../testing/execution/main_module_executor.py | 5 +- .../testing/execution/process/cpp_executor.py | 14 +- .../testing/execution/process/go_executor.py | 13 +- .../execution/process/javascript_executor.py | 2 +- .../execution/process/python_executor.py | 2 +- .../execution/process/shell_executor.py | 2 +- hstest/testing/execution/process_executor.py | 26 +- hstest/testing/execution/program_executor.py | 11 +- .../execution/runnable/runnable_file.py | 9 +- .../execution/searcher/base_searcher.py | 79 ++-- .../execution/searcher/cpp_searcher.py | 4 +- .../testing/execution/searcher/go_searcher.py | 4 +- .../execution/searcher/javascript_searcher.py | 4 +- .../execution/searcher/python_searcher.py | 8 +- .../execution/searcher/shell_searcher.py | 4 +- .../execution/searcher/sql_searcher.py | 4 +- .../testing/plotting/drawing/drawing_data.py | 8 +- .../plotting/drawing_data_normalizer.py | 28 +- hstest/testing/plotting/matplotlib_handler.py | 50 +-- hstest/testing/plotting/pandas_handler.py | 381 +++++++++--------- hstest/testing/plotting/seaborn_handler.py | 52 +-- hstest/testing/process_wrapper.py | 26 +- .../runner/async_dynamic_testing_runner.py | 4 +- .../runner/django_application_runner.py | 20 +- .../runner/flask_application_runner.py | 20 +- hstest/testing/runner/sql_runner.py | 44 +- hstest/testing/tested_program.py | 6 +- hstest/testing/unittest/expected_fail_test.py | 19 +- setup.py | 26 ++ tests/testing.py | 39 +- 54 files changed, 598 insertions(+), 690 deletions(-) create mode 100644 setup.py diff --git a/hstest/common/file_utils.py b/hstest/common/file_utils.py index 9504e514..9da6ea14 100644 --- a/hstest/common/file_utils.py +++ b/hstest/common/file_utils.py @@ -2,42 +2,42 @@ import contextlib import os -from pathlib import Path from hstest.exception.testing import FileDeletionError def create_files(files: dict[str, str]) -> None: for file, content in files.items(): - Path(file).write_text(content, encoding="locale") + with open(file, "w", encoding="utf-8") as f: + f.write(content) def delete_files(files: dict[str, str]) -> None: - for file in map(Path, files): - if file.is_file(): + for file in files: + if os.path.isfile(file): try: - file.unlink() - except PermissionError as ex: - raise FileDeletionError from ex + os.remove(file) + except PermissionError: + raise FileDeletionError -def safe_delete(filename: str) -> None: - with contextlib.suppress(BaseException): - Path(filename).unlink(missing_ok=True) +def safe_delete(filename) -> None: + if os.path.exists(filename): + with contextlib.suppress(BaseException): + os.remove(filename) -def walk_user_files(curr_folder: Path) -> tuple[Path, list[str], list[str]]: - curr_folder = curr_folder.resolve() - test_folder = curr_folder / "test" +def walk_user_files(folder): + curr_folder = os.path.abspath(folder) + test_folder = os.path.join(curr_folder, "test") for folder, dirs, files in os.walk(curr_folder): - folder_ = Path(folder) - if folder_.is_relative_to(test_folder): + if folder.startswith(test_folder): continue - if folder_ == curr_folder: - for file in ("test.py", "tests.py"): + if folder == curr_folder: + for file in "test.py", "tests.py": if file in files: files.remove(file) - yield Path(folder_), dirs, files + yield folder, dirs, files diff --git a/hstest/common/os_utils.py b/hstest/common/os_utils.py index 08498421..4c346919 100644 --- a/hstest/common/os_utils.py +++ b/hstest/common/os_utils.py @@ -3,13 +3,13 @@ import platform -def is_windows() -> bool: +def is_windows(): return platform.system() == "Windows" -def is_mac() -> bool: +def is_mac(): return platform.system() == "Darwin" -def is_linux() -> bool: +def is_linux(): return platform.system() == "Linux" diff --git a/hstest/common/process_utils.py b/hstest/common/process_utils.py index f2d66e0e..0c29949f 100644 --- a/hstest/common/process_utils.py +++ b/hstest/common/process_utils.py @@ -3,13 +3,10 @@ import threading import weakref from concurrent.futures import ThreadPoolExecutor -from concurrent.futures.thread import _worker # noqa: PLC2701 -from typing import Any, TYPE_CHECKING +from concurrent.futures.thread import _worker +from typing import TYPE_CHECKING if TYPE_CHECKING: - import queue - from concurrent.futures.process import _WorkItem - from hstest.dynamic.security.thread_group import ThreadGroup @@ -25,7 +22,7 @@ def _adjust_thread_count(self) -> None: # When the executor gets lost, the weakref callback will wake up # the worker threads. - def weakref_cb(_: int, q: queue.SimpleQueue[_WorkItem[Any]] = self._work_queue) -> None: + def weakref_cb(_, q=self._work_queue) -> None: q.put(None) num_threads = len(self._threads) @@ -45,7 +42,7 @@ def weakref_cb(_: int, q: queue.SimpleQueue[_WorkItem[Any]] = self._work_queue) self._threads.add(t) -def is_port_in_use(port: int) -> bool: +def is_port_in_use(port): import socket with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: diff --git a/hstest/common/reflection_utils.py b/hstest/common/reflection_utils.py index f08f2404..9816ebc6 100644 --- a/hstest/common/reflection_utils.py +++ b/hstest/common/reflection_utils.py @@ -1,18 +1,13 @@ from __future__ import annotations import inspect +import itertools import os -from itertools import pairwise -from pathlib import Path -from typing import TYPE_CHECKING from hstest.exception.failure_handler import get_traceback_stack -if TYPE_CHECKING: - from hstest import StageTest - -def is_tests(stage: object) -> bool: +def is_tests(stage): package = inspect.getmodule(stage).__package__ file = inspect.getmodule(stage).__file__ return ( @@ -24,18 +19,17 @@ def is_tests(stage: object) -> bool: ) -def setup_cwd(stage: StageTest) -> None: +def setup_cwd(stage) -> None: if stage.is_tests: - test_file = Path(inspect.getmodule(stage).__file__) - test_folder = test_file.parent + test_file = inspect.getmodule(stage).__file__ + test_folder = os.path.dirname(test_file) os.chdir(test_folder) - cwd = Path.cwd() - if cwd.name == "test": - os.chdir(cwd.parent) + if os.path.basename(os.getcwd()) == "test": + os.chdir(os.path.dirname(os.getcwd())) -def get_stacktrace(ex: BaseException, *, hide_internals: bool = False) -> str: +def get_stacktrace(ex: BaseException, hide_internals=False) -> str: traceback_stack = get_traceback_stack(ex) if not hide_internals: @@ -45,7 +39,7 @@ def get_stacktrace(ex: BaseException, *, hide_internals: bool = False) -> str: if ex.filename.startswith("<"): # "", or "" user_dir = ex.filename else: - user_dir = Path(ex.filename).parent.name + os.sep + user_dir = os.path.dirname(ex.filename) + os.sep else: user_dir = "" @@ -85,7 +79,7 @@ def str_to_stacktrace(str_trace: str) -> str: traceback_stack = [] - for line_from, line_to in pairwise(traceback_lines): + for line_from, line_to in itertools.pairwise(traceback_lines): actual_lines = lines[line_from:line_to] needed_lines = [line for line in actual_lines if line.startswith(" ")] traceback_stack += ["\n".join(needed_lines) + "\n"] @@ -147,9 +141,9 @@ def clean_stacktrace( if user_file.startswith("<"): continue - dir_name = Path(tr[start_index:end_index]).parent - if dir_name.is_dir(): - dir_names += [dir_name.resolve()] + dir_name = os.path.dirname(tr[start_index:end_index]) + if os.path.isdir(dir_name): + dir_names += [os.path.abspath(dir_name)] if dir_names: from hstest.common.os_utils import is_windows @@ -169,25 +163,21 @@ def clean_stacktrace( cleaned_traceback = [] for trace in full_traceback[1:-1]: - # Trace line that starts with 4 is a line with SyntaxError - # avoid including lines that are always in the stacktrace - # but include , because it's definitely user's code - if (not trace.startswith(" " * 4) and user_dir in trace) or ( - ("<" in trace and ">" in trace and "" in trace and " lines that are always in the stacktrace + # but include , because it's definitely user's code + if not user_dir.startswith("<"): + if user_dir in trace: + trace = trace.replace(user_dir, "") else: - cleaned_traceback.append(trace) - else: - cleaned_traceback.append(trace) + folder_name = os.path.basename(user_dir[:-1]) + if folder_name in trace: + index = trace.index(folder_name) + trace = ' File "' + trace[index + len(folder_name + os.sep) :] + + cleaned_traceback += [trace] return full_traceback[0] + "".join(cleaned_traceback) + full_traceback[-1] diff --git a/hstest/common/utils.py b/hstest/common/utils.py index 39ad6808..49acea4d 100644 --- a/hstest/common/utils.py +++ b/hstest/common/utils.py @@ -11,20 +11,19 @@ success_msg = "#educational_plugin test OK" -def failed(message: str, *, is_unittest: bool) -> tuple[int, str]: +def failed(message: str, is_unittest: bool): """Reports failure.""" if not is_unittest: lines = message.splitlines() - print("\n" + failed_msg_start + lines[0]) # noqa: T201 - for line in lines[1:]: - print(failed_msg_continue + line) # noqa: T201 + for _line in lines[1:]: + pass return -1, message -def passed(*, is_unittest: bool) -> tuple[int, str]: +def passed(is_unittest: bool): """Reports success.""" if not is_unittest: - print("\n" + success_msg) # noqa: T201 + pass return 0, "test OK" diff --git a/hstest/dynamic/dynamic_test.py b/hstest/dynamic/dynamic_test.py index 55ad36fb..344b69d4 100644 --- a/hstest/dynamic/dynamic_test.py +++ b/hstest/dynamic/dynamic_test.py @@ -8,7 +8,7 @@ def dynamic_test( - func: Any | None = None, + func=None, *, order: int = 0, time_limit: int = DEFAULT_TIME_LIMIT, @@ -16,16 +16,16 @@ def dynamic_test( feedback: str = "", repeat: int = 1, files: dict[str, str] | None = None, -) -> Any: +): """Decorator for creating dynamic tests.""" class DynamicTestingMethod: - def __init__(self, fn: Any) -> None: + def __init__(self, fn) -> None: self.fn = fn - def __set_name__(self, owner: StageTest, name: str) -> None: + def __set_name__(self, owner, name): # do something with owner, i.e. - # print(f"Decorating {self.fn} and using {owner}") # noqa: ERA001 + # print(f"Decorating {self.fn} and using {owner}") self.fn.class_name = owner.__name__ # then replace ourself with the original method diff --git a/hstest/dynamic/input/dynamic_testing.py b/hstest/dynamic/input/dynamic_testing.py index 52c68da5..774b0c59 100644 --- a/hstest/dynamic/input/dynamic_testing.py +++ b/hstest/dynamic/input/dynamic_testing.py @@ -72,7 +72,7 @@ def check_errors(self) -> None: raise UnexpectedError(msg) if self.files is not None: - if not isinstance(self.files, dict): + if type(self.files) != dict: msg = ( f"'Files' parameter in dynamic test should be of type " f'"dict", found {type(self.files)}.' @@ -80,20 +80,20 @@ def check_errors(self) -> None: raise UnexpectedError(msg) for k, v in self.files.items(): - if not isinstance(k, str): + if type(k) != str: msg = ( f"All keys in 'files' parameter in dynamic test should be " f'of type "str", found {type(k)}.' ) raise UnexpectedError(msg) - if not isinstance(v, str): + if type(v) != str: msg = ( f"All values in 'files' parameter in dynamic test should be " f'of type "str", found {type(v)}.' ) raise UnexpectedError(msg) - def get_tests(self, obj: StageTest) -> list[DynamicTesting]: + def get_tests(self, obj) -> list[DynamicTesting]: tests = [] for _i in range(self.repeat): for args in self.args_list: @@ -131,15 +131,14 @@ def eject_next_input(self, curr_output: str) -> str | None: new_input = obj elif isinstance(obj, CheckResult): if obj.is_correct: - raise TestPassed # noqa: TRY301 - raise WrongAnswer(obj.feedback) # noqa: TRY301 + raise TestPassed + raise WrongAnswer(obj.feedback) else: - msg = ( + raise UnexpectedError( "Dynamic input should return " - f"str or CheckResult objects only. Found: {type(obj)}" + + f"str or CheckResult objects only. Found: {type(obj)}" ) - raise UnexpectedError(msg) # noqa: TRY301 - except BaseException as ex: # noqa: BLE001 + except BaseException as ex: from hstest.stage_test import StageTest StageTest.curr_test_run.set_error_in_test(ex) diff --git a/hstest/dynamic/output/output_handler.py b/hstest/dynamic/output/output_handler.py index 39b49897..98004fcc 100644 --- a/hstest/dynamic/output/output_handler.py +++ b/hstest/dynamic/output/output_handler.py @@ -22,7 +22,7 @@ class OutputHandler: _mock_err: OutputMock = None @staticmethod - def print(obj: Any) -> None: + def print(obj) -> None: if True: return @@ -41,7 +41,7 @@ def print(obj: Any) -> None: OutputHandler.get_real_out().write(full) OutputHandler.get_real_out().flush() else: - print(full, end="") # noqa: T201 + pass @staticmethod def get_real_out() -> io.TextIOWrapper: diff --git a/hstest/dynamic/output/output_mock.py b/hstest/dynamic/output/output_mock.py index 656f914a..38303c9a 100644 --- a/hstest/dynamic/output/output_mock.py +++ b/hstest/dynamic/output/output_mock.py @@ -20,23 +20,9 @@ def __init__(self, condition: Condition) -> None: self.output: list[str] = [] -class RealOutputMock: - def __init__(self, out: io.TextIOWrapper) -> None: - self.out = out - - def write(self, text: str) -> None: - if not ignore_stdout: - self.out.write(text) - - def flush(self) -> None: - self.out.flush() - - def close(self) -> None: - self.out.close() - - class OutputMock: - """Original stream is used to actually see the test in the console and nothing else. + """original stream is used to actually see + the test in the console and nothing else. cloned stream is used to collect all output from the test and redirect to check function @@ -48,7 +34,21 @@ class OutputMock: but also injected input from the test """ - def __init__(self, real_out: io.TextIOWrapper, *, is_stderr: bool = False) -> None: + def __init__(self, real_out: io.TextIOWrapper, is_stderr: bool = False) -> None: + class RealOutputMock: + def __init__(self, out: io.TextIOWrapper) -> None: + self.out = out + + def write(self, text) -> None: + if not ignore_stdout: + self.out.write(text) + + def flush(self) -> None: + self.out.flush() + + def close(self) -> None: + self.out.close() + self._original: RealOutputMock = RealOutputMock(real_out) self._cloned: list[str] = [] # used in check function self._dynamic: list[str] = [] # used to append inputs @@ -56,7 +56,7 @@ def __init__(self, real_out: io.TextIOWrapper, *, is_stderr: bool = False) -> No self._is_stderr = is_stderr @property - def original(self) -> RealOutputMock: + def original(self): return self._original @property @@ -73,7 +73,7 @@ def partial(self, obj: Any) -> str: output.clear() return result - def write(self, text: str) -> None: + def write(self, text) -> None: partial_handler = self.__get_partial_handler() if partial_handler is None: @@ -120,7 +120,7 @@ def uninstall_output_handler(self, obj: Any) -> None: raise UnexpectedError(msg) del self._partial[obj] - def __get_partial_handler(self) -> list[str] | None: + def __get_partial_handler(self): for handler in self._partial.values(): if handler.condition(): return handler.output diff --git a/hstest/dynamic/security/thread_group.py b/hstest/dynamic/security/thread_group.py index 32bdec36..dccbd3b2 100644 --- a/hstest/dynamic/security/thread_group.py +++ b/hstest/dynamic/security/thread_group.py @@ -17,16 +17,16 @@ def __init__(self, name: str | None = None) -> None: curr = current_thread() if hasattr(curr, "_group"): - self._parent: ThreadGroup | None = curr._group # noqa: SLF001 + self._parent: ThreadGroup | None = curr._group else: self._parent: ThreadGroup | None = None @property - def name(self) -> str: + def name(self): return self._name @property - def parent(self) -> ThreadGroup | None: + def parent(self): return self._parent def add(self, thread: Thread) -> None: diff --git a/hstest/dynamic/security/thread_handler.py b/hstest/dynamic/security/thread_handler.py index 6a0d72e2..5c2d701e 100644 --- a/hstest/dynamic/security/thread_handler.py +++ b/hstest/dynamic/security/thread_handler.py @@ -1,7 +1,7 @@ from __future__ import annotations from threading import current_thread, Thread -from typing import Any, TYPE_CHECKING +from typing import TYPE_CHECKING from hstest.dynamic.security.thread_group import ThreadGroup @@ -19,26 +19,19 @@ def install_thread_group(cls) -> None: cls._old_init = Thread.__init__ Thread.__init__ = ThreadHandler.init cls._group = ThreadGroup("Main") - current_thread()._group = cls._group # noqa: SLF001 + current_thread()._group = cls._group @classmethod def uninstall_thread_group(cls) -> None: if cls._old_init is not None: Thread.__init__ = cls._old_init cls._old_init = None - del current_thread()._group # noqa: SLF001 + del current_thread()._group cls._group = None @staticmethod def init( - self: Thread, # noqa: PLW0211 - group: ThreadGroup | None = None, - target: Callable[..., Any] | None = None, - name: str | None = None, - args: tuple[Any, ...] | None = (), - kwargs: dict[str, Any] | None = None, - *, - daemon: bool | None = None, + self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None ) -> None: ThreadHandler._old_init(self, None, target, name, args, kwargs, daemon=daemon) @@ -49,7 +42,7 @@ def init( curr = current_thread() if hasattr(curr, "_group"): - self._group = curr._group # noqa: SLF001 + self._group = curr._group else: self._group = ThreadGroup(self._name) diff --git a/hstest/exception/outcomes.py b/hstest/exception/outcomes.py index 71bdace7..7000582d 100644 --- a/hstest/exception/outcomes.py +++ b/hstest/exception/outcomes.py @@ -5,19 +5,19 @@ class OutcomeError(BaseException): pass -class SyntaxException(OutcomeError): # noqa: N818 +class SyntaxException(OutcomeError): def __init__(self, exception: SyntaxError, file: str) -> None: self.file: str = file self.exception: SyntaxError = exception -class ExceptionWithFeedback(OutcomeError): # noqa: N818 +class ExceptionWithFeedback(OutcomeError): def __init__(self, error_text: str, real_exception: BaseException | None) -> None: self.error_text: str = error_text self.real_exception: BaseException = real_exception -class ErrorWithFeedback(OutcomeError): # noqa: N818 +class ErrorWithFeedback(OutcomeError): def __init__(self, error_text: str) -> None: self.error_text = error_text @@ -38,10 +38,10 @@ def __init__(self, error_text: str) -> None: self.error_text = error_text -class TestPassed(OutcomeError): # noqa: N818 +class TestPassed(OutcomeError): pass -class WrongAnswer(OutcomeError): # noqa: N818 +class WrongAnswer(OutcomeError): def __init__(self, feedback: str) -> None: self.feedback = feedback diff --git a/hstest/outcomes/error_outcome.py b/hstest/outcomes/error_outcome.py index f182e90a..fdf3d78e 100644 --- a/hstest/outcomes/error_outcome.py +++ b/hstest/outcomes/error_outcome.py @@ -33,20 +33,20 @@ def __init__(self, test_num: int, cause: BaseException) -> None: def _init_permission_error(self, _: FileDeletionError) -> None: self.error_text = ( "The file you opened " - "can't be deleted after the end of the test. " - "Probably you didn't close it." + + "can't be deleted after the end of the test. " + + "Probably you didn't close it." ) def _init_time_limit_exception(self, ex: TimeLimitException) -> None: time_limit: int = ex.time_limit_ms time_unit: str = "milliseconds" - if time_limit > 1999: # noqa: PLR2004 + if time_limit > 1999: time_limit //= 1000 time_unit = "seconds" self.error_text = ( "In this test, the program is running for a long time, " - f"more than {time_limit} {time_unit}. Most likely, " - "the program has gone into an infinite loop." + + f"more than {time_limit} {time_unit}. Most likely, " + + "the program has gone into an infinite loop." ) def get_type(self) -> str: diff --git a/hstest/outcomes/outcome.py b/hstest/outcomes/outcome.py index 0460e1ed..15ca9a37 100644 --- a/hstest/outcomes/outcome.py +++ b/hstest/outcomes/outcome.py @@ -76,7 +76,7 @@ def __str__(self) -> str: return result.strip() @staticmethod - def __get_args() -> str: + def __get_args(): arguments = "" from hstest.stage_test import StageTest @@ -99,7 +99,7 @@ def __get_args() -> str: return arguments @staticmethod - def __trim_lines(full_out: str) -> str: + def __trim_lines(full_out): result = "" max_lines_in_output = 250 @@ -119,7 +119,7 @@ def __trim_lines(full_out: str) -> str: return result.strip() @staticmethod - def get_outcome(ex: BaseException, curr_test: int) -> Outcome: + def get_outcome(ex: BaseException, curr_test: int): from hstest.outcomes.compilation_error_outcome import CompilationErrorOutcome from hstest.outcomes.error_outcome import ErrorOutcome from hstest.outcomes.exception_outcome import ExceptionOutcome diff --git a/hstest/stage/django_test.py b/hstest/stage/django_test.py index 445eeefb..82f460a0 100644 --- a/hstest/stage/django_test.py +++ b/hstest/stage/django_test.py @@ -17,7 +17,7 @@ class DjangoTest(StageTest): test_database: str = attach.test_database use_database: bool = attach.use_database - def __init__(self, args: str = "", *, source: str = "") -> None: + def __init__(self, args="", *, source: str = "") -> None: super().__init__(args, source=source) self.attach.use_database = self.use_database loop_detector.working = False @@ -25,7 +25,7 @@ def __init__(self, args: str = "", *, source: str = "") -> None: def read_page(self, link: str) -> str: """Deprecated, use get(...) instead.""" - return clean_text(urlopen(link).read().decode()) # noqa: S310 + return clean_text(urlopen(link).read().decode()) def get_url(self, link: str = "") -> str: if link.startswith("/"): @@ -35,4 +35,4 @@ def get_url(self, link: str = "") -> str: def get(self, link: str) -> str: if not link.startswith("http://"): link = self.get_url(link) - return clean_text(urlopen(link).read().decode()) # noqa: S310 + return clean_text(urlopen(link).read().decode()) diff --git a/hstest/stage/flask_test.py b/hstest/stage/flask_test.py index 12865eef..3cc4b568 100644 --- a/hstest/stage/flask_test.py +++ b/hstest/stage/flask_test.py @@ -15,7 +15,7 @@ class FlaskTest(StageTest): runner = FlaskApplicationRunner() attach: FlaskSettings = FlaskSettings() - def __init__(self, args: str = "", *, source: str = "") -> None: + def __init__(self, args="", *, source: str = "") -> None: super().__init__(args, source=source) loop_detector.working = False Settings.do_reset_output = False @@ -23,19 +23,19 @@ def __init__(self, args: str = "", *, source: str = "") -> None: if self.source_name: sources = self.source_name - if not isinstance(sources, str): + if type(sources) != list: sources = [sources] for item in sources: - if isinstance(item, str): + if type(item) == str: self.attach.sources += [(item, None)] - elif isinstance(item, tuple): + elif type(item) == tuple: if len(item) == 1: self.attach.sources += [(item[0], None)] else: self.attach.sources += [item] - def get_url(self, link: str = "", *, source: str | None = None) -> str: + def get_url(self, link: str = "", *, source: str | None = None): if link.startswith("/"): link = link[1:] @@ -62,4 +62,4 @@ def get(self, link: str, *, source: str | None = None) -> str: if not link.startswith("http://"): link = self.get_url(link, source=source) - return clean_text(urlopen(link).read().decode()) # noqa: S310 + return clean_text(urlopen(link).read().decode()) diff --git a/hstest/stage/plotting_test.py b/hstest/stage/plotting_test.py index f472f975..fcc1e2f2 100644 --- a/hstest/stage/plotting_test.py +++ b/hstest/stage/plotting_test.py @@ -10,7 +10,7 @@ class PlottingTest(StageTest): - def __init__(self, args: str = "", *, source: str = "") -> None: + def __init__(self, args="", *, source: str = "") -> None: super().__init__(args, source=source) self._all_drawings: list[Drawing] = [] self._new_drawings: list[Drawing] = [] diff --git a/hstest/stage/sql_test.py b/hstest/stage/sql_test.py index c077c4a7..615f486e 100644 --- a/hstest/stage/sql_test.py +++ b/hstest/stage/sql_test.py @@ -1,21 +1,19 @@ from __future__ import annotations -from typing import Any, ClassVar - from hstest.exception.outcomes import WrongAnswer from hstest.stage.stage_test import StageTest from hstest.testing.runner.sql_runner import SQLRunner class SQLTest(StageTest): - queries: ClassVar[dict[str, str]] = {} - db: Any = None + queries: dict[str, str] = {} + db: any = None def __init__(self, source: str = "") -> None: super().__init__(source) self.runner = SQLRunner(self) - def execute(self, query_name: str) -> Any: + def execute(self, query_name: str): cursor = self.db.cursor() if query_name not in self.queries: @@ -25,7 +23,7 @@ def execute(self, query_name: str) -> Any: return cursor.execute(self.queries[query_name]) except Exception as ex: msg = f"Error while running '{query_name}': \n\n{ex}" - raise WrongAnswer(msg) from ex + raise WrongAnswer(msg) - def execute_and_fetch_all(self, query_name: str) -> list[tuple]: + def execute_and_fetch_all(self, query_name: str): return self.execute(query_name).fetchall() diff --git a/hstest/stage/stage_test.py b/hstest/stage/stage_test.py index 4dd660ed..d4589944 100644 --- a/hstest/stage/stage_test.py +++ b/hstest/stage/stage_test.py @@ -1,9 +1,9 @@ from __future__ import annotations import contextlib +import os import unittest -from pathlib import Path -from typing import Any, ClassVar, TYPE_CHECKING +from typing import Any, TYPE_CHECKING from hstest.common.file_utils import walk_user_files from hstest.common.reflection_utils import is_tests, setup_cwd @@ -32,7 +32,7 @@ class DirMeta(type): - def __dir__(cls) -> list[str]: + def __dir__(cls): from hstest.testing.unittest.expected_fail_test import ExpectedFailTest from hstest.testing.unittest.unexepected_error_test import UnexpectedErrorTest from hstest.testing.unittest.user_error_test import UserErrorTest @@ -62,7 +62,7 @@ class StageTest(unittest.TestCase, metaclass=DirMeta): curr_test_run: TestRun | None = None curr_test_global: int = 0 - def __init__(self, args: str = "", *, source: str = "") -> None: + def __init__(self, args="", *, source: str = "") -> None: super().__init__("test_run_unittest") self.is_tests = False @@ -80,7 +80,7 @@ def after_all_tests(self) -> None: pass def _init_runner(self) -> TestRunner: - for _folder, _dirs, files in walk_user_files(Path.cwd()): + for _folder, _dirs, files in walk_user_files(os.getcwd()): for f in files: if f.endswith(".cpp"): return AsyncDynamicTestingRunner(CppExecutor) @@ -112,13 +112,15 @@ def _init_tests(self) -> list[TestRun]: msg = "No tests found" raise UnexpectedError(msg) + curr_test: int = 0 test_count = len(test_cases) - for curr_test, test_case in enumerate(test_cases, start=1): + for test_case in test_cases: test_case.source_name = self.source_name if test_case.check_func is None: test_case.check_func = self.check if test_case.attach is None: test_case.attach = self.attach + curr_test += 1 test_runs += [TestRun(curr_test, test_count, test_case, self.runner)] return test_runs @@ -129,7 +131,7 @@ def __print_test_num(self, num: int) -> None: RED_BOLD + f"\nStart test {num}{total_tests}" + RESET + "\n" ) - def run_tests(self, *, debug: bool = False, is_unittest: bool = False) -> tuple[int, str]: + def run_tests(self, *, debug=False, is_unittest: bool = False) -> tuple[int, str]: curr_test: int = 0 need_tear_down: bool = False try: @@ -162,20 +164,20 @@ def run_tests(self, *, debug: bool = False, is_unittest: bool = False) -> tuple[ if not result.is_correct: full_feedback = result.feedback + "\n\n" + test_run.test_case.feedback - raise WrongAnswer(full_feedback.strip()) # noqa: TRY301 + raise WrongAnswer(full_feedback.strip()) if test_run.is_last_test(): need_tear_down = False test_run.tear_down() SystemHandler.tear_down() - return passed(is_unittest=is_unittest) + return passed(is_unittest) - except BaseException as ex: # noqa: BLE001 + except BaseException: if need_tear_down: try: StageTest.curr_test_run.tear_down() - except BaseException as new_ex: # noqa: BLE001 + except BaseException as new_ex: if isinstance(new_ex, OutcomeError): ex = new_ex @@ -183,27 +185,27 @@ def run_tests(self, *, debug: bool = False, is_unittest: bool = False) -> tuple[ try: report = build + "\n\n" + get_report() - except Exception: # noqa: BLE001 + except Exception: report = build try: outcome: Outcome = Outcome.get_outcome(ex, curr_test) fail_text = str(outcome) - except BaseException as new_ex: # noqa: BLE001 + except BaseException as new_ex: try: outcome: Outcome = Outcome.get_outcome(new_ex, curr_test) fail_text = str(outcome) - except BaseException as new_ex2: # noqa: BLE001 + except BaseException as new_ex2: try: traceback = "" for e in new_ex2, new_ex, ex: try: text = get_exception_text(e) - except Exception: # noqa: BLE001 + except Exception: try: text = f"{type(e)}: {e!s}" - except Exception: # noqa: BLE001 + except Exception: text = "Broken exception" if len(text): @@ -211,14 +213,14 @@ def run_tests(self, *, debug: bool = False, is_unittest: bool = False) -> tuple[ fail_text = "Unexpected error\n\n" + report + "\n\n" + traceback - except BaseException: # noqa: BLE001 + except BaseException: # no code execution here allowed so not to throw an exception fail_text = "Unexpected error\n\nCannot check the submission\n\n" + report with contextlib.suppress(BaseException): SystemHandler.tear_down() - return failed(fail_text, is_unittest=is_unittest) + return failed(fail_text, is_unittest) finally: StageTest.curr_test_run = None @@ -227,7 +229,7 @@ def run_tests(self, *, debug: bool = False, is_unittest: bool = False) -> tuple[ StageTest.source = None self.after_all_tests() - _dynamic_methods: ClassVar[dict[type[StageTest], list[DynamicTestElement]]] = {} + _dynamic_methods: dict[type[StageTest], list[DynamicTestElement]] = {} @classmethod def dynamic_methods(cls) -> list[DynamicTestElement]: diff --git a/hstest/test_case/attach/django_settings.py b/hstest/test_case/attach/django_settings.py index bd8e0c42..ad2bf579 100644 --- a/hstest/test_case/attach/django_settings.py +++ b/hstest/test_case/attach/django_settings.py @@ -1,10 +1,8 @@ from __future__ import annotations -from typing import ClassVar - class DjangoSettings: port: int = None use_database: bool = False test_database: str = "db.test.sqlite3" - tryout_ports: ClassVar[list[int]] = list(range(8000, 8101)) + tryout_ports: list[int] = list(range(8000, 8101)) diff --git a/hstest/test_case/attach/flask_settings.py b/hstest/test_case/attach/flask_settings.py index eb794728..97aa4324 100644 --- a/hstest/test_case/attach/flask_settings.py +++ b/hstest/test_case/attach/flask_settings.py @@ -1,8 +1,6 @@ from __future__ import annotations -from typing import ClassVar - class FlaskSettings: - sources: ClassVar[list[tuple[str, int]]] = [] - tryout_ports: ClassVar[list[int]] = list(range(8000, 8101)) + sources: list[tuple[str, int]] = [] + tryout_ports: list[int] = list(range(8000, 8101)) diff --git a/hstest/test_case/check_result.py b/hstest/test_case/check_result.py index f3fe0c24..cb291d49 100644 --- a/hstest/test_case/check_result.py +++ b/hstest/test_case/check_result.py @@ -4,7 +4,7 @@ class CheckResult: - def __init__(self, result: bool, feedback: str) -> None: # noqa: FBT001 + def __init__(self, result: bool, feedback: str) -> None: self._result: bool = result self._feedback: str = feedback @@ -18,11 +18,11 @@ def feedback(self) -> str: @staticmethod def correct() -> CheckResult: - return CheckResult(result=True, feedback="") + return CheckResult(True, "") @staticmethod def wrong(feedback: str) -> CheckResult: - return CheckResult(result=False, feedback=feedback) + return CheckResult(False, feedback) @staticmethod def from_error(error: BaseException) -> CheckResult | None: diff --git a/hstest/test_case/test_case.py b/hstest/test_case/test_case.py index 1451e70b..8a10e852 100644 --- a/hstest/test_case/test_case.py +++ b/hstest/test_case/test_case.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, TYPE_CHECKING +from typing import Any, TYPE_CHECKING, Union from hstest.dynamic.input.dynamic_input_func import DynamicInputFunction from hstest.dynamic.input.dynamic_testing import to_dynamic_testing @@ -15,15 +15,15 @@ SimpleStepikTest = str AdvancedStepikTest = tuple[str, Any] - StepikTest = SimpleStepikTest | AdvancedStepikTest + StepikTest = Union[SimpleStepikTest, AdvancedStepikTest] CheckFunction = Callable[[str, Any], CheckResult] PredefinedInput = str - RuntimeEvaluatedInput = [ - PredefinedInput | InputFunction | tuple[int, InputFunction] | DynamicInputFunction + RuntimeEvaluatedInput = Union[ + PredefinedInput, InputFunction, tuple[int, InputFunction], DynamicInputFunction ] - DynamicInput = PredefinedInput | list[RuntimeEvaluatedInput] + DynamicInput = Union[PredefinedInput, list[RuntimeEvaluatedInput]] DEFAULT_TIME_LIMIT: int = 15000 @@ -66,7 +66,7 @@ def __init__( if attach is not None: msg = "Attach is not None " "but copying from stdin is specified" raise UnexpectedError(msg) - if not isinstance(stdin, str): + if type(stdin) != str: msg = ( "To copy stdin to attach stdin should be of type str " f"but found type {type(stdin)}" @@ -74,29 +74,29 @@ def __init__( raise UnexpectedError(msg) self.attach = stdin - if isinstance(stdin, str): + if type(stdin) == str: self.input = stdin - self.input_funcs = [DynamicInputFunction(1, lambda _: stdin)] + self.input_funcs = [DynamicInputFunction(1, lambda x: stdin)] else: - if not isinstance(stdin, list): + if type(stdin) != list: msg = "Stdin should be either of type str or list " f"but found type {type(stdin)}" raise UnexpectedError(msg) - for elem in stdin: - if isinstance(elem, DynamicInputFunction): + for elem in stdin: # type: RuntimeEvaluatedInput + if type(elem) == DynamicInputFunction: self.input_funcs += [elem] - elif isinstance(elem, str): - self.input_funcs += [DynamicInputFunction(1, lambda _, inp=elem: inp)] + elif type(elem) == str: + self.input_funcs += [DynamicInputFunction(1, lambda x, inp=elem: inp)] elif str(type(elem)) in {"", ""}: self.input_funcs += [DynamicInputFunction(1, elem)] elif type(elem) in {tuple, list}: - if len(elem) == 2: # noqa: PLR2004 + if len(elem) == 2: trigger_count: int = elem[0] input_function: InputFunction = elem[1] - if not isinstance(trigger_count, int): + if type(trigger_count) != int: msg = ( f"Stdin element's 1st element should be of type int, " f"found {type(trigger_count)}" @@ -152,6 +152,6 @@ def __init__(self, *, stdin: str, stdout: str, feedback: str, **kwargs) -> None: super().__init__(stdin=stdin, attach=stdout, feedback=feedback, **kwargs) self.check_func = self._custom_check - def _custom_check(self, reply: str, expected: str) -> CheckResult: + def _custom_check(self, reply: str, expected: str): is_correct = reply.strip() == expected.strip() return CheckResult(is_correct, "") diff --git a/hstest/testing/execution/filtering/file_filter.py b/hstest/testing/execution/filtering/file_filter.py index 2d8280e4..e002b364 100644 --- a/hstest/testing/execution/filtering/file_filter.py +++ b/hstest/testing/execution/filtering/file_filter.py @@ -32,7 +32,7 @@ def __init__( self.filtered: set[File] = set() @staticmethod - def regex_filter(regex: str) -> Filter: + def regex_filter(regex: str): return lambda s: re.compile(regex, re.MULTILINE).search(s) is not None def init_filter(self, folder: Folder, sources: Sources) -> None: diff --git a/hstest/testing/execution/main_module_executor.py b/hstest/testing/execution/main_module_executor.py index f18566ed..363c668f 100644 --- a/hstest/testing/execution/main_module_executor.py +++ b/hstest/testing/execution/main_module_executor.py @@ -3,7 +3,6 @@ import os import runpy import sys -from pathlib import Path from typing import TYPE_CHECKING from hstest.common.process_utils import DaemonThreadPoolExecutor @@ -27,7 +26,7 @@ def __init__(self, source_name: str | None = None) -> None: self.__executor: DaemonThreadPoolExecutor | None = None self.__task: Future | None = None self.__group = None - self.working_directory_before = Path.cwd().resolve() + self.working_directory_before = os.path.abspath(os.getcwd()) def _invoke_method(self, *args: str) -> None: from hstest.stage_test import StageTest @@ -42,7 +41,7 @@ def _invoke_method(self, *args: str) -> None: self._machine.set_state(ProgramState.FINISHED) - except BaseException as ex: # noqa: BLE001 + except BaseException as ex: if StageTest.curr_test_run.error_in_test is None: # ExitException is thrown in case of exit() or quit() # consider them like normal exit diff --git a/hstest/testing/execution/process/cpp_executor.py b/hstest/testing/execution/process/cpp_executor.py index 4b74146a..9e4a3ac7 100644 --- a/hstest/testing/execution/process/cpp_executor.py +++ b/hstest/testing/execution/process/cpp_executor.py @@ -1,7 +1,6 @@ from __future__ import annotations import os -from pathlib import Path from hstest.common.os_utils import is_windows from hstest.testing.execution.process_executor import ProcessExecutor @@ -12,16 +11,16 @@ class CppExecutor(ProcessExecutor): def __init__(self, source_name: str | None = None) -> None: super().__init__(CppSearcher().find(source_name)) - self.without_extension = os.path.splitext(self.runnable.file)[0] # noqa: PTH122 + self.without_extension = os.path.splitext(self.runnable.file)[0] if is_windows(): self.executable = self.without_extension - self.file_name = Path(self.executable + ".exe") + self.file_name = self.executable + ".exe" else: self.executable = f"./{self.without_extension}" - self.file_name = Path(self.without_extension) + self.file_name = self.without_extension - def _compilation_command(self) -> list[str]: + def _compilation_command(self): return [ "g++", "-std=c++20", @@ -36,8 +35,9 @@ def _compilation_command(self) -> list[str]: def _filter_compilation_error(self, error: str) -> str: return error - def _execution_command(self, *args: str) -> list[str]: + def _execution_command(self, *args: str): return [self.executable, *list(args)] def _cleanup(self) -> None: - self.file_name.unlink(missing_ok=True) + if os.path.exists(self.file_name): + os.remove(self.file_name) diff --git a/hstest/testing/execution/process/go_executor.py b/hstest/testing/execution/process/go_executor.py index d1c0befc..e5d202dc 100644 --- a/hstest/testing/execution/process/go_executor.py +++ b/hstest/testing/execution/process/go_executor.py @@ -1,6 +1,6 @@ from __future__ import annotations -from pathlib import Path +import os from hstest.common.os_utils import is_windows from hstest.testing.execution.process_executor import ProcessExecutor @@ -15,20 +15,21 @@ def __init__(self, source_name: str | None = None) -> None: if is_windows(): self.executable = self.without_go - self.file_name = Path(self.executable + ".exe") + self.file_name = self.executable + ".exe" else: self.executable = f"./{self.without_go}" - self.file_name = Path(self.without_go) + self.file_name = self.without_go - def _compilation_command(self) -> list[str]: + def _compilation_command(self): return ["go", "build", self.runnable.file] def _filter_compilation_error(self, error: str) -> str: error_lines = [line for line in error.splitlines() if not line.startswith("#")] return "\n".join(error_lines) - def _execution_command(self, *args: str) -> list[str]: + def _execution_command(self, *args: str): return [self.executable, *list(args)] def _cleanup(self) -> None: - self.file_name.unlink(missing_ok=True) + if os.path.exists(self.file_name): + os.remove(self.file_name) diff --git a/hstest/testing/execution/process/javascript_executor.py b/hstest/testing/execution/process/javascript_executor.py index 5efa9940..c90d3258 100644 --- a/hstest/testing/execution/process/javascript_executor.py +++ b/hstest/testing/execution/process/javascript_executor.py @@ -8,5 +8,5 @@ class JavascriptExecutor(ProcessExecutor): def __init__(self, source_name: str | None = None) -> None: super().__init__(JavascriptSearcher().find(source_name)) - def _execution_command(self, *args: str) -> list[str]: + def _execution_command(self, *args: str): return ["node", self.runnable.file, *list(args)] diff --git a/hstest/testing/execution/process/python_executor.py b/hstest/testing/execution/process/python_executor.py index f504a3cb..b121b145 100644 --- a/hstest/testing/execution/process/python_executor.py +++ b/hstest/testing/execution/process/python_executor.py @@ -8,5 +8,5 @@ class PythonExecutor(ProcessExecutor): def __init__(self, source_name: str | None = None) -> None: super().__init__(PythonSearcher().find(source_name)) - def _execution_command(self, *args: str) -> list[str]: + def _execution_command(self, *args: str): return ["python", "-u", self.runnable.file, *list(args)] diff --git a/hstest/testing/execution/process/shell_executor.py b/hstest/testing/execution/process/shell_executor.py index 724a1062..2868866b 100644 --- a/hstest/testing/execution/process/shell_executor.py +++ b/hstest/testing/execution/process/shell_executor.py @@ -8,5 +8,5 @@ class ShellExecutor(ProcessExecutor): def __init__(self, source_name: str | None = None) -> None: super().__init__(ShellSearcher().find(source_name)) - def _execution_command(self, *args: str) -> list[str]: + def _execution_command(self, *args: str): return ["bash", self.runnable.file, *list(args)] diff --git a/hstest/testing/execution/process_executor.py b/hstest/testing/execution/process_executor.py index 834028f6..62391259 100644 --- a/hstest/testing/execution/process_executor.py +++ b/hstest/testing/execution/process_executor.py @@ -2,7 +2,6 @@ import contextlib import os -from pathlib import Path from threading import Thread from time import sleep from typing import TYPE_CHECKING @@ -31,7 +30,7 @@ def __init__(self, runnable: RunnableFile) -> None: self.continue_executing = True self.runnable: RunnableFile = runnable self.__group: ThreadGroup | None = None - self.working_directory_before = Path.cwd().resolve() + self.working_directory_before = os.path.abspath(os.getcwd()) def _compilation_command(self, *args: str) -> list[str]: return [] @@ -113,16 +112,15 @@ def __handle_process(self, *args: str) -> None: OutputHandler.print(f"Handle process - written to stdin: {next_input!r}") except ExitException: OutputHandler.print("Handle process - EXIT EXCEPTION, stop input") - if self._wait_if_terminated() and isinstance( - StageTest.curr_test_run.error_in_test, OutOfInputError - ): - StageTest.curr_test_run.set_error_in_test(None) - OutputHandler.print( - "Handle process - Abort stopping input, everything is OK" - ) - break + if self._wait_if_terminated(): + if type(StageTest.curr_test_run.error_in_test) == OutOfInputError: + StageTest.curr_test_run.set_error_in_test(None) + OutputHandler.print( + "Handle process - Abort stopping input, everything is OK" + ) + break self.stop_input() - except BaseException as ex: # noqa: BLE001 + except BaseException as ex: OutputHandler.print(f"Handle process - SOME EXCEPTION {ex}") OutputHandler.print("Handle process - TERMINATE") @@ -149,8 +147,8 @@ def __handle_process(self, *args: str) -> None: OutputHandler.print("Handle process - finishing execution") - def _wait_if_terminated(self) -> bool: - return try_many_times(100, 10, lambda: self.process.is_finished(need_wait_output=False)) + def _wait_if_terminated(self): + return try_many_times(100, 10, lambda: self.process.is_finished(False)) def _launch(self, *args: str) -> None: self.__group = ThreadGroup() @@ -177,7 +175,7 @@ def _terminate(self) -> None: sleep(0.001) def tear_down(self) -> None: - working_directory_before = Path.cwd().resolve() + working_directory_before = os.path.abspath(os.getcwd()) os.chdir(self.runnable.folder) with contextlib.suppress(BaseException): diff --git a/hstest/testing/execution/program_executor.py b/hstest/testing/execution/program_executor.py index c127de14..2ad71f20 100644 --- a/hstest/testing/execution/program_executor.py +++ b/hstest/testing/execution/program_executor.py @@ -70,7 +70,7 @@ def execute(self, stdin: str) -> str: StageTest.curr_test_run.set_error_in_test( ErrorWithFeedback( f"The program {self} has unexpectedly terminated.\n" - "It finished execution too early, should continue running." + + "It finished execution too early, should continue running." ) ) raise TestedProgramFinishedEarly @@ -80,11 +80,10 @@ def execute(self, stdin: str) -> str: return "" if not self.is_waiting_input(): - msg = ( + raise UnexpectedError( f"Program {self} is not waiting for the input " - f'(state == "{self._machine.state}")' + + f'(state == "{self._machine.state}")' ) - raise UnexpectedError(msg) if self.__no_more_input: msg = f"Can't pass input to the program {self} - input was prohibited." @@ -122,7 +121,7 @@ def request_input(self) -> str | None: self._input = None return input_local - def set_return_output_after_execution(self, *, value: bool) -> None: + def set_return_output_after_execution(self, value: bool) -> None: self.__return_output_after_execution = value def is_finished(self) -> bool: @@ -153,7 +152,7 @@ def stop_background(self) -> None: self.__in_background = False self._machine.wait_state(ProgramState.WAITING) - def is_in_background(self) -> bool: + def is_in_background(self): return self.__in_background def tear_down(self) -> None: diff --git a/hstest/testing/execution/runnable/runnable_file.py b/hstest/testing/execution/runnable/runnable_file.py index a246eaa3..5c90d645 100644 --- a/hstest/testing/execution/runnable/runnable_file.py +++ b/hstest/testing/execution/runnable/runnable_file.py @@ -3,15 +3,10 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from pathlib import Path - - from hstest.testing.execution.filtering.file_filter import File + from hstest.testing.execution.filtering.file_filter import File, Folder class RunnableFile: - def __init__(self, folder: Path, file: File) -> None: + def __init__(self, folder: Folder, file: File) -> None: self.folder = folder self.file = file - - def path(self) -> Path: - return self.folder / self.file diff --git a/hstest/testing/execution/searcher/base_searcher.py b/hstest/testing/execution/searcher/base_searcher.py index d4501d52..4fe59cc4 100644 --- a/hstest/testing/execution/searcher/base_searcher.py +++ b/hstest/testing/execution/searcher/base_searcher.py @@ -2,11 +2,10 @@ import os import re -from pathlib import Path from hstest.common.file_utils import walk_user_files from hstest.exception.outcomes import ErrorWithFeedback, UnexpectedError -from hstest.testing.execution.filtering.file_filter import File, FileFilter, Module, Source +from hstest.testing.execution.filtering.file_filter import File, FileFilter, Folder, Module, Source from hstest.testing.execution.filtering.main_filter import MainFilter from hstest.testing.execution.runnable.runnable_file import RunnableFile @@ -20,32 +19,33 @@ def extension(self) -> str: msg = 'Property "extension" should be implemented' raise NotImplementedError(msg) - def search(self, where: Path | None = None) -> RunnableFile: + def search(self, where_to_search: str | None = None) -> RunnableFile: msg = 'Method "search" should be implemented' raise NotImplementedError(msg) @staticmethod - def _get_contents(folder: Path, files: list[Path]) -> dict[Path, Source]: + def _get_contents(folder: Folder, files: list[File]) -> dict[File, Source]: contents = {} for file in files: - path = (folder / file).resolve() + path = os.path.abspath(os.path.join(folder, file)) if path in file_contents_cached: contents[file] = file_contents_cached[path] - elif path.exists(): - try: - file_content = Path(path).read_text(encoding="locale") - except UnicodeDecodeError: - # binary bile, no need to process - continue - contents[file] = file_content - file_contents_cached[path] = contents[file] + elif os.path.exists(path): + with open(path, encoding="utf-8") as f: + try: + file_content = f.read() + except UnicodeDecodeError: + # binary bile, no need to process + continue + contents[file] = file_content + file_contents_cached[path] = contents[file] return contents def _search_non_cached( self, - where: Path, + where_to_search: str, *, file_filter: FileFilter, pre_main_filter: FileFilter, @@ -56,7 +56,7 @@ def _search_non_cached( if not force_content_filters: force_content_filters = [] - curr_folder = where.resolve() + curr_folder = os.path.abspath(where_to_search) for folder, _dirs, files in walk_user_files(curr_folder): contents = self._get_contents(folder, files) @@ -96,10 +96,7 @@ def _search_non_cached( for forced_filter in force_content_filters if isinstance(forced_filter, MainFilter) ] - msg = ( - f"The runnable file should contain all the following lines: " - f"{should_contain}" - ) + msg = f"The runnable file should contain all the following lines: {should_contain}" raise ErrorWithFeedback(msg) if len(filtered_files) == 1: @@ -156,7 +153,7 @@ def _search_non_cached( def _search( self, - where: Path | None = None, + where_to_search: str | None = None, *, file_filter: FileFilter = None, pre_main_filter: FileFilter = None, @@ -168,11 +165,11 @@ def _search( msg = f'File extension "{self.extension}" should start with a dot' raise UnexpectedError(msg) - if where is None: - where = Path.cwd() + if where_to_search is None: + where_to_search = os.getcwd() do_caching = False - cache_key = self.extension, where + cache_key = self.extension, where_to_search if file_filter is None: if cache_key in search_cached: @@ -191,7 +188,7 @@ def _search( post_main_filter = FileFilter() result = self._search_non_cached( - where, + where_to_search, file_filter=file_filter, pre_main_filter=pre_main_filter, main_filter=main_filter, @@ -206,20 +203,20 @@ def _search( def _simple_search( self, - where: Path, + where_to_search: str, main_desc: str, main_regex: str, force_content_filters: list[MainFilter] | None = None, ) -> RunnableFile: main_searcher = re.compile(main_regex, re.MULTILINE) return self._search( - where, + where_to_search, main_filter=MainFilter(main_desc, source=lambda s: main_searcher.search(s) is not None), force_content_filters=force_content_filters, ) - def _base_search(self, where: Path) -> RunnableFile: - return self._simple_search(where, main_desc="", main_regex="") + def _base_search(self, where_to_search: str) -> RunnableFile: + return self._simple_search(where_to_search, main_desc="", main_regex="") def find(self, source: str | None) -> RunnableFile: if source in {None, ""}: @@ -229,42 +226,42 @@ def find(self, source: str | None) -> RunnableFile: source_folder, source_file, source_module = self._parse_source(source) - if source_folder is not None and source_folder.is_dir(): + if source_folder is not None and os.path.isdir(source_folder): return self.search(source_folder) - if source_file is not None and source_file.is_file(): + if source_file is not None and os.path.isfile(source_file): path, _sep, file = source_module.rpartition(".") - folder = Path(path.replace(".", os.sep)).resolve() + folder = os.path.abspath(path.replace(".", os.sep)) return RunnableFile(folder, file + ext) path, _, _ = source_module.rpartition(".") - folder = Path(path.replace(".", os.sep)).resolve() + folder = os.path.abspath(path.replace(".", os.sep)) msg = ( "Cannot find a file to execute your code.\n" f'Are your project files located at "{folder}"?' ) raise ErrorWithFeedback(msg) - def _parse_source(self, source: str) -> tuple[Path, Path, Module]: + def _parse_source(self, source: str) -> tuple[Folder, File, Module]: ext = self.extension - source = Path(source.replace("/", os.sep).replace("\\", os.sep)) + source = source.replace("/", os.sep).replace("\\", os.sep) - if source.name.endswith(ext): + if source.endswith(ext): source_folder = None source_file = source - source_module = str(source)[:-len(ext)].replace(os.sep, ".") + source_module = source[: -len(ext)].replace(os.sep, ".") - elif os.sep in str(source): - if source.name.endswith(os.sep): - source = str(source)[:-len(os.sep)] + elif os.sep in source: + if source.endswith(os.sep): + source = source[: -len(os.sep)] source_folder = source source_file = None - source_module = source.name.replace(os.sep, ".") + source_module = source.replace(os.sep, ".") else: - source_folder = source.name.replace(".", os.sep) + source_folder = source.replace(".", os.sep) source_file = source_folder + ext source_module = source diff --git a/hstest/testing/execution/searcher/cpp_searcher.py b/hstest/testing/execution/searcher/cpp_searcher.py index e730104e..c0173070 100644 --- a/hstest/testing/execution/searcher/cpp_searcher.py +++ b/hstest/testing/execution/searcher/cpp_searcher.py @@ -7,8 +7,6 @@ from hstest.testing.execution.searcher.base_searcher import BaseSearcher if TYPE_CHECKING: - from pathlib import Path - from hstest.testing.execution.runnable.runnable_file import RunnableFile @@ -17,7 +15,7 @@ class CppSearcher(BaseSearcher): def extension(self) -> str: return ".cpp" - def search(self, where: Path | None = None) -> RunnableFile: + def search(self, where: str | None = None) -> RunnableFile: main_func_searcher = re.compile(r"(^|\n)\s*int\s+main\s*\(.*\)", re.MULTILINE) return self._search( diff --git a/hstest/testing/execution/searcher/go_searcher.py b/hstest/testing/execution/searcher/go_searcher.py index ae605fab..838dfc5d 100644 --- a/hstest/testing/execution/searcher/go_searcher.py +++ b/hstest/testing/execution/searcher/go_searcher.py @@ -7,8 +7,6 @@ from hstest.testing.execution.searcher.base_searcher import BaseSearcher if TYPE_CHECKING: - from pathlib import Path - from hstest.testing.execution.runnable.runnable_file import RunnableFile @@ -17,7 +15,7 @@ class GoSearcher(BaseSearcher): def extension(self) -> str: return ".go" - def search(self, where: Path | None = None) -> RunnableFile: + def search(self, where: str | None = None) -> RunnableFile: package_searcher = re.compile(r"^\s*package\s*main", re.MULTILINE) main_func_searcher = re.compile(r"(^|\n)\s*func\s+main\s*\(\s*\)", re.MULTILINE) diff --git a/hstest/testing/execution/searcher/javascript_searcher.py b/hstest/testing/execution/searcher/javascript_searcher.py index b2fcfdfe..f1ec811f 100644 --- a/hstest/testing/execution/searcher/javascript_searcher.py +++ b/hstest/testing/execution/searcher/javascript_searcher.py @@ -5,8 +5,6 @@ from hstest.testing.execution.searcher.base_searcher import BaseSearcher if TYPE_CHECKING: - from pathlib import Path - from hstest.testing.execution.runnable.runnable_file import RunnableFile @@ -15,5 +13,5 @@ class JavascriptSearcher(BaseSearcher): def extension(self) -> str: return ".js" - def search(self, where: Path | None = None) -> RunnableFile: + def search(self, where: str | None = None) -> RunnableFile: return self._simple_search(where, "function main()", r"(^|\n) *function +main +\( *\)") diff --git a/hstest/testing/execution/searcher/python_searcher.py b/hstest/testing/execution/searcher/python_searcher.py index 88aab919..1afd32a0 100644 --- a/hstest/testing/execution/searcher/python_searcher.py +++ b/hstest/testing/execution/searcher/python_searcher.py @@ -1,7 +1,7 @@ from __future__ import annotations +import os import re -from pathlib import Path from typing import TYPE_CHECKING from hstest.dynamic.output.output_handler import OutputHandler @@ -27,14 +27,14 @@ def search( def init_regexes(_: Folder, sources: Sources) -> None: import_regexes = {} - for file in sources: + for file, source in sources.items(): is_imported[file] = False import_regexes[file] = [ re.compile(rf"(^|\n)import +[\w., ]*\b{file[:-3]}\b[\w., ]*", re.MULTILINE), re.compile(rf"(^|\n)from +\.? *\b{file[:-3]}\b +import +", re.MULTILINE), ] - for source in sources.values(): + for file, source in sources.items(): for f, (r1, r2) in import_regexes.items(): if r1.search(source) is not None or r2.search(source) is not None: is_imported[f] = True @@ -49,7 +49,7 @@ def init_regexes(_: Folder, sources: Sources) -> None: ) def find(self, source: str | None) -> PythonRunnableFile: - OutputHandler.print(f"PythonSearcher source = {source}, cwd = {Path.cwd()}") + OutputHandler.print(f"PythonSearcher source = {source}, cwd = {os.getcwd()}") runnable = super().find(source) OutputHandler.print(f"PythonSearcher found runnable: {runnable.folder}/{runnable.file}") return PythonRunnableFile( diff --git a/hstest/testing/execution/searcher/shell_searcher.py b/hstest/testing/execution/searcher/shell_searcher.py index b262b170..63f5a8a4 100644 --- a/hstest/testing/execution/searcher/shell_searcher.py +++ b/hstest/testing/execution/searcher/shell_searcher.py @@ -5,8 +5,6 @@ from hstest.testing.execution.searcher.base_searcher import BaseSearcher if TYPE_CHECKING: - from pathlib import Path - from hstest.testing.execution.runnable.runnable_file import RunnableFile @@ -15,5 +13,5 @@ class ShellSearcher(BaseSearcher): def extension(self) -> str: return ".sh" - def search(self, where: Path | None = None) -> RunnableFile: + def search(self, where: str | None = None) -> RunnableFile: return self._simple_search(where, "# main", r"(^|\n)# *main") diff --git a/hstest/testing/execution/searcher/sql_searcher.py b/hstest/testing/execution/searcher/sql_searcher.py index 4e98c216..1f52c04f 100644 --- a/hstest/testing/execution/searcher/sql_searcher.py +++ b/hstest/testing/execution/searcher/sql_searcher.py @@ -5,8 +5,6 @@ from hstest.testing.execution.searcher.base_searcher import BaseSearcher if TYPE_CHECKING: - from pathlib import Path - from hstest.testing.execution.runnable.runnable_file import RunnableFile @@ -15,5 +13,5 @@ class SQLSearcher(BaseSearcher): def extension(self) -> str: return ".sql" - def search(self, where: Path | None = None) -> RunnableFile: + def search(self, where: str | None = None) -> RunnableFile: return self._base_search(where) diff --git a/hstest/testing/plotting/drawing/drawing_data.py b/hstest/testing/plotting/drawing/drawing_data.py index ba4f6805..6296a0bd 100644 --- a/hstest/testing/plotting/drawing/drawing_data.py +++ b/hstest/testing/plotting/drawing/drawing_data.py @@ -6,13 +6,13 @@ class DrawingData: def __init__(self, x: np.ndarray, y: np.ndarray) -> None: try: - if not isinstance(x, list | None): + if type(x) != list and x is not None: x = list(x) - if not isinstance(y, list | None): + if type(y) != list and y is not None: y = list(y) - except Exception as e: + except Exception: msg = "The data argument should be an array" - raise ValueError(msg) from e + raise ValueError(msg) if x is not None and y is not None and len(x) != len(y): msg = "Arrays should be the same length" diff --git a/hstest/testing/plotting/drawing_data_normalizer.py b/hstest/testing/plotting/drawing_data_normalizer.py index f03dddca..a0e0639c 100644 --- a/hstest/testing/plotting/drawing_data_normalizer.py +++ b/hstest/testing/plotting/drawing_data_normalizer.py @@ -5,15 +5,15 @@ class DrawingDataNormalizer: @staticmethod - def normalize_x_y_data(x: list[float], y: list[float]) -> np.ndarray: + def normalize_x_y_data(x, y) -> np.ndarray: try: - if not isinstance(x, list): + if type(x) != list: x = list(x) - if not isinstance(y, list): + if type(y) != list: y = list(y) - except Exception as e: + except Exception: msg = "The data argument should be an array" - raise ValueError(msg) from e + raise ValueError(msg) if len(x) != len(y): msg = "Arrays should be the same length" @@ -27,16 +27,16 @@ def normalize_x_y_data(x: list[float], y: list[float]) -> np.ndarray: return np.array(result_data, dtype=object) @staticmethod - def normalize_hist_data(data: list[float] | str) -> np.ndarray: - if isinstance(data, str): + def normalize_hist_data(data) -> np.ndarray: + if type(data) == str: data = [data] - if not isinstance(data, list): + if type(data) != list: try: data = list(data) - except Exception as e: + except Exception: msg = "The data argument should be an array" - raise ValueError(msg) from e + raise ValueError(msg) return np.array(data, dtype=object) @@ -79,17 +79,17 @@ def normalize_hist_data(data: list[float] | str) -> np.ndarray: return None @staticmethod - def normalize_bar_data(x: list[float], y: list[float]) -> np.ndarray: + def normalize_bar_data(x, y) -> np.ndarray: return DrawingDataNormalizer.normalize_x_y_data(x, y) @staticmethod - def normalize_line_data(x: list[float], y: list[float]) -> np.ndarray: + def normalize_line_data(x, y) -> np.ndarray: return DrawingDataNormalizer.normalize_x_y_data(x, y) @staticmethod - def normalize_scatter_data(x: list[float], y: list[float]) -> np.ndarray: + def normalize_scatter_data(x, y) -> np.ndarray: return DrawingDataNormalizer.normalize_x_y_data(x, y) @staticmethod - def normalize_pie_data(x: list[float], y: list[float]) -> np.ndarray: + def normalize_pie_data(x, y) -> np.ndarray: return DrawingDataNormalizer.normalize_x_y_data(x, y) diff --git a/hstest/testing/plotting/matplotlib_handler.py b/hstest/testing/plotting/matplotlib_handler.py index 7481c2a9..a8dbdde2 100644 --- a/hstest/testing/plotting/matplotlib_handler.py +++ b/hstest/testing/plotting/matplotlib_handler.py @@ -3,7 +3,7 @@ import contextlib from copy import deepcopy from importlib import reload -from typing import Final, TYPE_CHECKING +from typing import TYPE_CHECKING from hstest.testing.plotting.drawing.drawing_data import DrawingData @@ -20,8 +20,6 @@ if TYPE_CHECKING: from hstest.testing.runner.plot_testing_runner import DrawingsStorage -NUM_SHAPES: Final = 2 - class MatplotlibHandler: _saved = False @@ -52,7 +50,7 @@ def replace_plots(drawings: DrawingsStorage) -> None: def custom_show_func(*args, **kwargs) -> None: pass - def hist(x: list[float], *args, data: list[float] | None = None, **kw) -> None: + def hist(x, *args, data=None, **kw): if data is not None: with contextlib.suppress(Exception): x = data[x] @@ -64,17 +62,17 @@ def hist(x: list[float], *args, data: list[float] | None = None, **kw) -> None: return None if type(x) == pd.Series: return hist(x.to_numpy(), *args, **kw) - except Exception: # noqa: BLE001, S110 + except Exception: pass if type(x) != np.ndarray: x = np.array(x, dtype=object) - if len(x.shape) == NUM_SHAPES: + if len(x.shape) == 2: from matplotlib import cbook - x = np.array(cbook._reshape_2D(x, "x"), dtype=object) # noqa: SLF001 + x = np.array(cbook._reshape_2D(x, "x"), dtype=object) - if len(x.shape) == NUM_SHAPES: + if len(x.shape) == 2: for i in range(x.shape[1]): hist(x[:, i], *args, **kw) return None @@ -89,9 +87,7 @@ def hist(x: list[float], *args, data: list[float] | None = None, **kw) -> None: ) return None - def bar( - x: list[float], height: list[float], *args, data: list[float] | None = None, **kw - ) -> None: + def bar(x, height, *args, data=None, **kw): if data is not None: with contextlib.suppress(Exception): x = data[x] @@ -107,7 +103,7 @@ def bar( return bar(x.to_numpy(), height, *args, **kw) if type(height) == pd.Series: return bar(x, height.to_numpy(), *args, **kw) - except Exception: # noqa: BLE001, S110 + except Exception: pass if type(height) in {int, float}: @@ -118,9 +114,7 @@ def bar( ) return None - def barh( - x: list[float], width: list[float], *args, data: list[float] | None = None, **kw - ) -> None: + def barh(x, width, *args, data=None, **kw): return bar(x, width, *args, data=data, **kw) def plot(*args, **kwargs) -> None: @@ -144,7 +138,7 @@ def plot(*args, **kwargs) -> None: ) ) - def scatter(x: list[float], y: list[float], *a, **kwargs) -> None: + def scatter(x, y, *a, **kwargs) -> None: drawings.append( DrawingBuilder.get_scatter_drawing( x, @@ -154,7 +148,7 @@ def scatter(x: list[float], y: list[float], *a, **kwargs) -> None: ) ) - def pie(x: list[float], *a, **kw) -> None: + def pie(x, *a, **kw) -> None: # Normalize with other plot libraries y = x @@ -167,7 +161,7 @@ def pie(x: list[float], *a, **kw) -> None: Drawing(DrawingLibrary.matplotlib, DrawingType.pie, DrawingData(x, y), kw) ) - def violinplot(dataset: list[float], *, data: list[float] | None = None, **kwargs) -> None: + def violinplot(dataset, *, data=None, **kwargs) -> None: if data is not None: with contextlib.suppress(Exception): dataset = data[dataset] @@ -176,7 +170,7 @@ def violinplot(dataset: list[float], *, data: list[float] | None = None, **kwarg drawings.append(drawing) - def imshow(x: list[float], **kwargs) -> None: + def imshow(x, **kwargs) -> None: curr_data = { # noqa: F841 "x": np.array(x, dtype=object) } @@ -189,7 +183,7 @@ def imshow(x: list[float], **kwargs) -> None: ) drawings.append(drawing) - def boxplot(x: list[float], **kwargs) -> None: + def boxplot(x, **kwargs) -> None: curr_data = { # noqa: F841 "x": np.array([None], dtype=object), "y": np.array(x, dtype=object), @@ -206,31 +200,31 @@ def boxplot(x: list[float], **kwargs) -> None: import matplotlib as mpl class CustomMatplotlibAxes(mpl.axes.Axes): - def hist(self, x: list[float], *a, **kw) -> None: + def hist(self, x, *a, **kw) -> None: hist(x, *a, **kw) - def bar(self, x: list[float], height: list[float], *a, **kw) -> None: + def bar(self, x, height, *a, **kw) -> None: bar(x, height, *a, **kw) - def barh(self, y: list[float], width: list[float], *a, **kw) -> None: + def barh(self, y, width, *a, **kw) -> None: barh(y, width, *a, **kw) def plot(self, *args, **kwargs) -> None: plot(*args, *kwargs) - def scatter(self, x: list[float], y: list[float], *a, **kwargs) -> None: + def scatter(self, x, y, *a, **kwargs) -> None: scatter(x, y, *a, **kwargs) - def pie(self, x: list[float], *a, **kw) -> None: + def pie(self, x, *a, **kw) -> None: pie(x, *a, **kw) - def violinplot(self, dataset: list[float], **kwargs) -> None: + def violinplot(self, dataset, **kwargs) -> None: violinplot(dataset, **kwargs) - def imshow(self, x: list[float], **kwargs) -> None: + def imshow(self, x, **kwargs) -> None: imshow(x, **kwargs) - def boxplot(self, x: list[float], **kwargs) -> None: + def boxplot(self, x, **kwargs) -> None: boxplot(x, **kwargs) import matplotlib as mpl diff --git a/hstest/testing/plotting/pandas_handler.py b/hstest/testing/plotting/pandas_handler.py index 36e86dd7..cedd38a7 100644 --- a/hstest/testing/plotting/pandas_handler.py +++ b/hstest/testing/plotting/pandas_handler.py @@ -1,7 +1,7 @@ from __future__ import annotations import contextlib -from typing import ClassVar, Final, TYPE_CHECKING +from typing import TYPE_CHECKING from hstest.testing.plotting.drawing.drawing_data import DrawingData @@ -23,234 +23,199 @@ from hstest.testing.plotting.matplotlib_handler import MatplotlibHandler if TYPE_CHECKING: - from collections.abc import Callable - from hstest.testing.runner.plot_testing_runner import DrawingsStorage -NUM_SHAPES: Final = 2 +class PandasHandler: + _saved = False + _replaced = False -def get_line_drawings_with_normalized_data( - data: pd.DataFrame, x: str | None, y: str | None -) -> list[Drawing]: - if type(data) is pd.Series: - return [DrawingBuilder.get_line_drawing(data.index, data, DrawingLibrary.pandas, {})] + _PlotAccessor = None - return [ - DrawingBuilder.get_line_drawing(data.index, data[column], DrawingLibrary.pandas, {}) - for column in data.columns - ] + _series_plot = None + _dframe_plot = None + _series_hist = None + _dframe_hist = None -def get_hexbin_drawings_with_normalized_data( - data: pd.DataFrame, x: str | None, y: str | None -) -> list[Drawing]: - drawings = [] - drawing = Drawing(DrawingLibrary.pandas, DrawingType.hexbin, None, {}) - drawings.append(drawing) - return drawings + _series_bar = None + _dframe_bar = None + _series_boxplot = None + _dframe_boxplot = None -def get_area_drawings_with_normalized_data( - data: pd.DataFrame, x: str | None, y: str | None -) -> list[Drawing]: - drawings = [] - drawing = Drawing(DrawingLibrary.pandas, DrawingType.area, None, {}) - drawings.append(drawing) - return drawings + plot_name_to_basic_name = { + # 'barh': DrawingType.bar, + "density": DrawingType.dis, + "kde": DrawingType.dis, + } + graph_type_to_normalized_data = { + "scatter": PandasHandler.get_scatter_drawings_with_normalized_data, + "line": PandasHandler.get_line_drawings_with_normalized_data, + "pie": PandasHandler.get_pie_drawings_with_normalized_data, + # 'bar': lambda data, x, y: PandasHandler.get_bar_drawings_with_normalized_data(data, x, y), + "box": PandasHandler.get_box_drawings_with_normalized_data, + "dis": PandasHandler.get_dis_drawings_with_normalized_data, + } -def get_dis_drawings_with_normalized_data( - data: pd.DataFrame, x: str | None, y: str | None -) -> list[Drawing]: - drawings = [] + @staticmethod + def get_line_drawings_with_normalized_data(data, x, y): + drawings = [] - if type(data) == pd.Series: - curr_data = {"x": data.to_numpy()} + if type(data) is pd.Series: + drawings.append( + DrawingBuilder.get_line_drawing(data.index, data, DrawingLibrary.pandas, {}) + ) + return drawings + + for column in data.columns: + drawings.append( + DrawingBuilder.get_line_drawing(data.index, data[column], DrawingLibrary.pandas, {}) + ) - drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) - drawings.append(drawing) return drawings - if x: - curr_data = { - "x": np.array(data[x], dtype=object), - } + @staticmethod + def get_scatter_drawings_with_normalized_data(data, x, y): + return [DrawingBuilder.get_scatter_drawing(data[x], data[y], DrawingLibrary.pandas, {})] - drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) - drawings.append(drawing) - if y: - curr_data = { - "x": np.array(data[y], dtype=object), - } + @staticmethod + def get_pie_drawings_with_normalized_data(data: pd.DataFrame, x, y): + if type(data) == pd.Series: + return [ + Drawing( + DrawingLibrary.pandas, + DrawingType.pie, + DrawingData(data.index.to_numpy(), data.to_numpy()), + {}, + ) + ] - drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) - drawings.append(drawing) + if y is not None: + return [ + Drawing( + DrawingLibrary.pandas, + DrawingType.pie, + DrawingData(data.index.to_numpy(), data[y].to_numpy()), + {}, + ) + ] + + drawings = [] - if not x and not y: for column in data.columns: if not is_numeric_dtype(data[column]): continue + drawings.append( + Drawing( + DrawingLibrary.pandas, + DrawingType.pie, + DrawingData(data.index.to_numpy(), data[column].to_numpy()), + {}, + ) + ) + return drawings - curr_data = { # noqa: F841 - "x": data[column].to_numpy() - } - - drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) - drawings.append(drawing) - return drawings + @staticmethod + def get_bar_drawings_with_normalized_data(data: pd.DataFrame, x, y): + drawings = [] + x_arr = data[x].to_numpy() if x is not None else data.index.to_numpy() -def get_box_drawings_with_normalized_data( - data: pd.DataFrame, x: str | None, y: str | None -) -> list[Drawing]: - drawings = [] + if y is not None: + drawing = DrawingBuilder.get_bar_drawing(x_arr, data[y], DrawingLibrary.pandas, {}) + drawings.append(drawing) + return drawings - # Columns are not specified - if x is None: for column in data.columns: if not is_numeric_dtype(data[column]): continue - - curr_data = {"x": np.array([column], dtype=object), "y": data[column].to_numpy()} - - drawing = Drawing(DrawingLibrary.pandas, DrawingType.box, None, {}) + drawing = DrawingBuilder.get_bar_drawing(x_arr, data[column], DrawingLibrary.pandas, {}) drawings.append(drawing) return drawings - for column in x: - if not is_numeric_dtype(data[column]): - continue + @staticmethod + def get_box_drawings_with_normalized_data(data: pd.DataFrame, x, y): + drawings = [] - curr_data = { # noqa: F841 - "x": np.array([column], dtype=object), - "y": data[column].to_numpy(), - } + # Columns are not specified + if x is None: + for column in data.columns: + if not is_numeric_dtype(data[column]): + continue - drawing = Drawing(DrawingLibrary.pandas, DrawingType.box, None, {}) - drawings.append(drawing) - return drawings + curr_data = {"x": np.array([column], dtype=object), "y": data[column].to_numpy()} + drawing = Drawing(DrawingLibrary.pandas, DrawingType.box, None, {}) + drawings.append(drawing) + return drawings -def get_bar_drawings_with_normalized_data( - data: pd.DataFrame, x: str | None, y: str | None -) -> list[Drawing]: - drawings = [] + for column in x: + if not is_numeric_dtype(data[column]): + continue - x_arr = data[x].to_numpy() if x is not None else data.index.to_numpy() + curr_data = { # noqa: F841 + "x": np.array([column], dtype=object), + "y": data[column].to_numpy(), + } - if y is not None: - drawing = DrawingBuilder.get_bar_drawing(x_arr, data[y], DrawingLibrary.pandas, {}) - drawings.append(drawing) + drawing = Drawing(DrawingLibrary.pandas, DrawingType.box, None, {}) + drawings.append(drawing) return drawings - for column in data.columns: - if not is_numeric_dtype(data[column]): - continue - drawing = DrawingBuilder.get_bar_drawing(x_arr, data[column], DrawingLibrary.pandas, {}) - drawings.append(drawing) - return drawings - - -def get_pie_drawings_with_normalized_data( - data: pd.DataFrame, x: str | None, y: str | None -) -> list[Drawing]: - if type(data) == pd.Series: - return [ - Drawing( - DrawingLibrary.pandas, - DrawingType.pie, - DrawingData(data.index.to_numpy(), data.to_numpy()), - {}, - ) - ] - - if y is not None: - return [ - Drawing( - DrawingLibrary.pandas, - DrawingType.pie, - DrawingData(data.index.to_numpy(), data[y].to_numpy()), - {}, - ) - ] - - drawings = [] - - for column in data.columns: - if not is_numeric_dtype(data[column]): - continue - drawings.append( - Drawing( - DrawingLibrary.pandas, - DrawingType.pie, - DrawingData(data.index.to_numpy(), data[column].to_numpy()), - {}, - ) - ) - return drawings - - -def get_scatter_drawings_with_normalized_data( - data: pd.DataFrame, x: str | None, y: str | None -) -> list[Drawing]: - return [DrawingBuilder.get_scatter_drawing(data[x], data[y], DrawingLibrary.pandas, {})] - + @staticmethod + def get_dis_drawings_with_normalized_data(data, x, y): + drawings = [] -class PandasHandler: - _saved = False - _replaced = False + if type(data) == pd.Series: + curr_data = {"x": data.to_numpy()} - _PlotAccessor = None + drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) + drawings.append(drawing) + return drawings - _series_plot = None - _dframe_plot = None + if x: + curr_data = { + "x": np.array(data[x], dtype=object), + } - _series_hist = None - _dframe_hist = None + drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) + drawings.append(drawing) + if y: + curr_data = { + "x": np.array(data[y], dtype=object), + } - _series_bar = None - _dframe_bar = None + drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) + drawings.append(drawing) - _series_boxplot = None - _dframe_boxplot = None + if not x and not y: + for column in data.columns: + if not is_numeric_dtype(data[column]): + continue - plot_name_to_basic_name: ClassVar[dict[str, DrawingType]] = { - # 'barh': DrawingType.bar, # noqa: ERA001 - "density": DrawingType.dis, - "kde": DrawingType.dis, - } + curr_data = { # noqa: F841 + "x": data[column].to_numpy() + } - graph_type_to_normalized_data: ClassVar[ - dict[str, Callable[[pd.DataFrame, str | None, str | None], list[Drawing]]] - ] = { - "scatter": get_scatter_drawings_with_normalized_data, - "line": get_line_drawings_with_normalized_data, - "pie": get_pie_drawings_with_normalized_data, - # "bar": get_bar_drawings_with_normalized_data, # noqa: ERA001 - "box": get_box_drawings_with_normalized_data, - "dis": get_dis_drawings_with_normalized_data, - } + drawing = Drawing(DrawingLibrary.pandas, DrawingType.dis, None, {}) + drawings.append(drawing) + return drawings @staticmethod - def revert_plots() -> None: - if not PandasHandler._replaced: - return - - MatplotlibHandler.revert_plots() - - import pandas.plotting - from pandas.core.accessor import CachedAccessor - - pandas.Series.plot = CachedAccessor("plot", pandas.plotting.PlotAccessor) - pandas.DataFrame.plot = CachedAccessor("plot", pandas.plotting.PlotAccessor) - - pandas.Series.hist = PandasHandler._series_hist - pandas.DataFrame.hist = PandasHandler._dframe_hist - - pandas.DataFrame.boxplot = PandasHandler._dframe_boxplot + def get_area_drawings_with_normalized_data(data, x, y): + drawings = [] + drawing = Drawing(DrawingLibrary.pandas, DrawingType.area, None, {}) + drawings.append(drawing) + return drawings - PandasHandler._replaced = False + @staticmethod + def get_hexbin_drawings_with_normalized_data(data, x, y): + drawings = [] + drawing = Drawing(DrawingLibrary.pandas, DrawingType.hexbin, None, {}) + drawings.append(drawing) + return drawings @staticmethod def replace_plots(drawings: DrawingsStorage) -> None: @@ -261,8 +226,8 @@ def replace_plots(drawings: DrawingsStorage) -> None: return class CustomPlotAccessor(pandas.plotting.PlotAccessor): - def __call__(self, *args, **kw) -> None: - from pandas.plotting._core import _get_plot_backend # noqa: PLC2701 + def __call__(self, *args, **kw): + from pandas.plotting._core import _get_plot_backend plot_backend = _get_plot_backend(kw.pop("backend", None)) @@ -308,22 +273,16 @@ def __call__(self, *args, **kw) -> None: import pandas.plotting._core - def boxplot(self: pandas.DataFrame, column: str | None = None, **kwargs) -> None: - all_drawings = get_box_drawings_with_normalized_data(self, column, None) + def boxplot(self, column=None, **kwargs) -> None: + all_drawings = PandasHandler.get_box_drawings_with_normalized_data(self, column, None) drawings.extend(all_drawings) - def hist( - data: pandas.DataFrame | pandas.Series | np.ndarray, - column: str | None = None, - *, - _process_by: bool = True, - **kw, - ) -> None: + def hist(data, column=None, _process_by=True, **kw): for k in list(kw.keys()): if kw[k] is None: kw.pop(k) - if _process_by and "by" in kw and isinstance(kw["by"], str): + if _process_by and "by" in kw and type(kw["by"]) == str: with contextlib.suppress(Exception): kw["by"] = data[kw["by"]] @@ -347,12 +306,12 @@ def hist( if type(data) != np.ndarray: data = np.array(data, dtype=object) - if len(data.shape) == NUM_SHAPES: + if len(data.shape) == 2: from matplotlib import cbook - data = np.array(cbook._reshape_2D(data, "x"), dtype=object) # noqa: SLF001 + data = np.array(cbook._reshape_2D(data, "x"), dtype=object) - if len(data.shape) == NUM_SHAPES: + if len(data.shape) == 2: for i in range(data.shape[1]): hist(data[:, i], **kw) return None @@ -375,14 +334,14 @@ def hist( ) return None - def bar(data: pandas.DataFrame, x: str | None = None, y: str | None = None, **kw) -> None: + def bar(data, x=None, y=None, **kw): for k in list(kw.keys()): if kw[k] is None: kw.pop(k) - if isinstance(data, pandas.DataFrame): + if type(data) == pandas.DataFrame: if y is not None and x is not None: - if isinstance(y, str): + if type(y) == str: y = [y] for col in y: bar(None, data[x].array.to_numpy(), data[col].array.to_numpy(), **kw) @@ -395,7 +354,7 @@ def bar(data: pandas.DataFrame, x: str | None = None, y: str | None = None, **kw return None if y is not None: - if isinstance(y, str): + if type(y) == str: y = [y] for col in y: bar(None, data[col].index.to_numpy(), data[col].array.to_numpy(), **kw) @@ -405,14 +364,14 @@ def bar(data: pandas.DataFrame, x: str | None = None, y: str | None = None, **kw bar(None, data[col].index.to_numpy(), data[col].array.to_numpy(), **kw) return None - if isinstance(data, pandas.Series): + if type(data) == pandas.Series: return bar(None, data.index.to_numpy(), data.array.to_numpy(), **kw) drawings.append(Drawing(DrawingLibrary.pandas, DrawingType.bar, DrawingData(x, y), kw)) return None def barh( - self: pandas.Series, + self, ) -> None: pass @@ -425,7 +384,7 @@ def barh( PandasHandler._series_hist = pandas.Series.hist PandasHandler._dframe_hist = pandas.DataFrame.hist - # PandasHandler._series_bar = pandas.Series.bar # noqa: ERA001 + # PandasHandler._series_bar = pandas.Series.bar PandasHandler._dframe_boxplot = pandas.DataFrame.boxplot @@ -438,3 +397,23 @@ def barh( pandas.DataFrame.boxplot = boxplot PandasHandler._replaced = True + + @staticmethod + def revert_plots() -> None: + if not PandasHandler._replaced: + return + + MatplotlibHandler.revert_plots() + + import pandas.plotting + from pandas.core.accessor import CachedAccessor + + pandas.Series.plot = CachedAccessor("plot", pandas.plotting.PlotAccessor) + pandas.DataFrame.plot = CachedAccessor("plot", pandas.plotting.PlotAccessor) + + pandas.Series.hist = PandasHandler._series_hist + pandas.DataFrame.hist = PandasHandler._dframe_hist + + pandas.DataFrame.boxplot = PandasHandler._dframe_boxplot + + PandasHandler._replaced = False diff --git a/hstest/testing/plotting/seaborn_handler.py b/hstest/testing/plotting/seaborn_handler.py index 3ade1328..ed181955 100644 --- a/hstest/testing/plotting/seaborn_handler.py +++ b/hstest/testing/plotting/seaborn_handler.py @@ -1,7 +1,7 @@ from __future__ import annotations from importlib import reload -from typing import Final, TYPE_CHECKING +from typing import TYPE_CHECKING from hstest.testing.plotting.drawing.drawing_data import DrawingData @@ -22,8 +22,6 @@ if TYPE_CHECKING: from hstest.testing.runner.plot_testing_runner import DrawingsStorage -NUM_SHAPES: Final = 2 - class SeabornHandler: _saved = False @@ -48,7 +46,7 @@ def replace_plots(drawings: DrawingsStorage) -> None: except ModuleNotFoundError: return - def displot(data: pd.DataFrame = None, **kwargs) -> None: + def displot(data=None, **kwargs) -> None: x = kwargs.get("x", None) y = kwargs.get("y", None) @@ -104,16 +102,11 @@ def displot(data: pd.DataFrame = None, **kwargs) -> None: ) drawings.append(drawing) - def histplot( - data: pd.DataFrame | pd.Series | np.ndarray | None = None, - *, - _process_hue: bool = True, - **kw, - ) -> None: + def histplot(data=None, _process_hue=True, **kw): if data is None: return None - if _process_hue and "hue" in kw and isinstance(kw["hue"], str): + if _process_hue and "hue" in kw and type(kw["hue"]) == str: with contextlib.suppress(Exception): kw["hue"] = data[kw["hue"]] @@ -129,18 +122,17 @@ def histplot( for col in data.columns: histplot(data[col], **kw) return None - if type(data) == pd.Series: return histplot(data.to_numpy(), **kw) if type(data) != np.ndarray: data = np.array(data, dtype=object) - if len(data.shape) == NUM_SHAPES: + if len(data.shape) == 2: from matplotlib import cbook - data = np.array(cbook._reshape_2D(data, "x"), dtype=object) # noqa: SLF001 + data = np.array(cbook._reshape_2D(data, "x"), dtype=object) - if len(data.shape) == NUM_SHAPES: + if len(data.shape) == 2: for i in range(data.shape[1]): histplot(data[:, i], **kw) return None @@ -163,9 +155,7 @@ def histplot( ) return None - def lineplot( - *, x: str | None = None, y: str | None = None, data: pd.DataFrame = None, **kwargs - ) -> DrawingsStorage | None: + def lineplot(*, data=None, x=None, y=None, **kwargs): x_array = data[x].to_numpy() if x is not None else data.index.to_numpy() if y is not None: @@ -195,9 +185,7 @@ def lineplot( ) return None - def lmplot( - x: str | None = None, y: str | None = None, data: pd.DataFrame = None, **kwargs - ) -> None: + def lmplot(x=None, y=None, data=None, **kwargs) -> None: curr_data = { # noqa: F841 "data": data, "x": x, @@ -213,9 +201,7 @@ def lmplot( ) drawings.append(drawing) - def scatterplot( - x: str | None = None, y: str | None = None, data: pd.DataFrame = None, **kwargs - ) -> None: + def scatterplot(x=None, y=None, data=None, **kwargs) -> None: if x is not None and y is not None: drawings.append( DrawingBuilder.get_scatter_drawing( @@ -242,9 +228,7 @@ def scatterplot( ) ) - def catplot( - x: str | None = None, y: str | None = None, data: pd.DataFrame = None, **kwargs - ) -> None: + def catplot(x=None, y=None, data=None, **kwargs) -> None: curr_data = { # noqa: F841 "data": data, "x": x, @@ -260,9 +244,7 @@ def catplot( ) drawings.append(drawing) - def barplot( - x: str | None = None, y: str | None = None, data: pd.DataFrame = None, **kwargs - ) -> None: + def barplot(x=None, y=None, data=None, **kwargs) -> None: x_arr = np.array([], dtype=object) y_arr = np.array([], dtype=object) @@ -278,9 +260,7 @@ def barplot( Drawing(DrawingLibrary.seaborn, DrawingType.bar, DrawingData(x_arr, y_arr), kwargs) ) - def violinplot( - *, x: str | None = None, y: str | None = None, data: pd.DataFrame = None, **kwargs - ) -> None: + def violinplot(*, x=None, y=None, data=None, **kwargs) -> None: if data is not None: if x is None and y is not None: data = data[y] @@ -304,7 +284,7 @@ def violinplot( drawings.append(drawing) - def heatmap(data: pd.DataFrame = None, **kwargs) -> None: + def heatmap(data=None, **kwargs) -> None: if data is None: return @@ -321,9 +301,7 @@ def heatmap(data: pd.DataFrame = None, **kwargs) -> None: drawings.append(drawing) - def boxplot( - x: str | None = None, y: str | None = None, data: pd.DataFrame = None, **kwargs - ) -> None: + def boxplot(x=None, y=None, data=None, **kwargs) -> None: if data is None: curr_data = {"x": np.array(x, dtype=object), "y": np.array(y, dtype=object)} diff --git a/hstest/testing/process_wrapper.py b/hstest/testing/process_wrapper.py index eecfdf92..46745003 100644 --- a/hstest/testing/process_wrapper.py +++ b/hstest/testing/process_wrapper.py @@ -1,10 +1,9 @@ from __future__ import annotations -import subprocess # noqa: S404 +import subprocess import sys from threading import Lock, Thread from time import sleep -from typing import IO from psutil import NoSuchProcess, Process @@ -22,11 +21,7 @@ class ProcessWrapper: initial_idle_wait_time = 150 def __init__( - self, - *args, - check_early_finish: bool = False, - register_output: bool = True, - register_io_handler: bool = False, + self, *args, check_early_finish=False, register_output=True, register_io_handler=False ) -> None: self.lock = Lock() @@ -54,7 +49,7 @@ def __init__( self.register_io_handler = register_io_handler self._group = None - def start(self) -> ProcessWrapper: + def start(self): command = " ".join(map(str, self.args)) if self.process is not None: @@ -73,7 +68,7 @@ def start(self) -> ProcessWrapper: args = ["cmd", "/c", *args] self.process = subprocess.Popen( - args, # noqa: S603 + args, bufsize=0, universal_newlines=not self._use_byte_stream, stdout=subprocess.PIPE, @@ -81,7 +76,7 @@ def start(self) -> ProcessWrapper: stdin=subprocess.PIPE, encoding="utf-8" if not self._use_byte_stream else None, ) - except Exception as e: # noqa: BLE001 + except Exception as e: from hstest import StageTest StageTest.curr_test_run.set_error_in_test( @@ -110,14 +105,7 @@ def check_alive(self) -> None: if self._alive and self.process.returncode is not None: self._alive = False - def check_pipe( - self, - read_pipe: IO, - write_pipe: IO, - *, - write_stdout: bool = False, - write_stderr: bool = False, - ) -> None: + def check_pipe(self, read_pipe, write_pipe, write_stdout=False, write_stderr=False) -> None: pipe_name = "stdout" if write_stdout else "stderr" with self.lock: @@ -252,7 +240,7 @@ def register_input_request(self) -> None: self.cpu_load_history = [] self.output_diff_history = [] - def is_finished(self, *, need_wait_output: bool = True) -> bool: + def is_finished(self, need_wait_output=True) -> bool: if not self.check_early_finish: return not self._alive diff --git a/hstest/testing/runner/async_dynamic_testing_runner.py b/hstest/testing/runner/async_dynamic_testing_runner.py index b74f5eda..974a910d 100644 --- a/hstest/testing/runner/async_dynamic_testing_runner.py +++ b/hstest/testing/runner/async_dynamic_testing_runner.py @@ -54,7 +54,7 @@ def _run_file(self, test_run: TestRun) -> CheckResult | None: return future.result(timeout=time_limit / 1000) except TimeoutError: test_run.set_error_in_test(TimeLimitException(time_limit)) - except BaseException as ex: # noqa: BLE001 + except BaseException as ex: test_run.set_error_in_test(ex) finally: test_run.invalidate_handlers() @@ -73,7 +73,7 @@ def test(self, test_run: TestRun) -> CheckResult | None: if error is None: try: return test_case.check_func(OutputHandler.get_output(), test_case.attach) - except BaseException as ex: # noqa: BLE001 + except BaseException as ex: error = ex test_run.set_error_in_test(error) diff --git a/hstest/testing/runner/django_application_runner.py b/hstest/testing/runner/django_application_runner.py index 2d184800..6e7529cd 100644 --- a/hstest/testing/runner/django_application_runner.py +++ b/hstest/testing/runner/django_application_runner.py @@ -2,7 +2,6 @@ import os import sys -from pathlib import Path from time import sleep from typing import TYPE_CHECKING @@ -39,15 +38,15 @@ def launch_django_application(self, test_case: TestCase) -> None: if source is None or not len(source): source = "manage" - full_source = Path(source.replace(".", os.sep) + ".py") - full_path = full_source.resolve() + full_source = source.replace(".", os.sep) + ".py" + full_path = os.path.abspath(full_source) - if not full_path.exists(): - filename = full_source.name + if not os.path.exists(full_path): + filename = os.path.basename(full_source) runnable = PythonSearcher().search(file_filter=FileFilter(file=lambda f: f == filename)) - full_path = (Path(runnable.folder) / runnable.file).resolve() + full_path = os.path.abspath(runnable.folder + os.sep + runnable.file) - self.full_path = full_path.name + self.full_path = full_path self.port = self.__find_free_port(test_case.attach.tryout_ports) if test_case.attach.use_database: @@ -101,7 +100,7 @@ def __find_free_port(self, ports: list[int]) -> int: def __prepare_database(self, test_database: str) -> None: os.environ["HYPERSKILL_TEST_DATABASE"] = test_database - with Path(test_database).open("w", encoding="locale"): + with open(test_database, "w", encoding="utf-8"): pass migrate = ProcessWrapper(sys.executable, self.full_path, "migrate", check_early_finish=True) @@ -148,9 +147,8 @@ def test(self, test_run: TestRun) -> CheckResult | None: try: result = test_case.dynamic_testing() self._check_errors() - except BaseException as ex: # noqa: BLE001 - test_run.set_error_in_test(ex) - else: return result + except BaseException as ex: + test_run.set_error_in_test(ex) return CheckResult.from_error(test_run.error_in_test) diff --git a/hstest/testing/runner/flask_application_runner.py b/hstest/testing/runner/flask_application_runner.py index 7909d7ce..5b870bf4 100644 --- a/hstest/testing/runner/flask_application_runner.py +++ b/hstest/testing/runner/flask_application_runner.py @@ -2,9 +2,8 @@ import os import sys -from pathlib import Path from time import sleep -from typing import ClassVar, TYPE_CHECKING +from typing import TYPE_CHECKING from hstest.common.process_utils import is_port_in_use from hstest.exception.outcomes import ErrorWithFeedback, UnexpectedError @@ -19,7 +18,7 @@ class FlaskApplicationRunner(TestRunner): - processes: ClassVar[list[tuple[str, ProcessWrapper]]] = [] + processes: list[tuple[str, ProcessWrapper]] = [] def launch_flask_applications(self, test_case: TestCase) -> None: if not isinstance(test_case.attach, FlaskSettings): @@ -40,13 +39,13 @@ def launch_flask_applications(self, test_case: TestCase) -> None: for source in sources: filename, port = source - full_source = Path(filename.replace(".", os.sep) + ".py") - full_path = full_source.resolve() + full_source = filename.replace(".", os.sep) + ".py" + full_path = os.path.abspath(full_source) - if not full_path.exists(): + if not os.path.exists(full_path): msg = ( - f'Cannot find file named "{full_path.name}" ' - f'in folder "{full_path.parent}". ' + f'Cannot find file named "{os.path.basename(full_path)}" ' + f'in folder "{os.path.dirname(full_path)}". ' f"Check if you deleted it." ) raise ErrorWithFeedback(msg) @@ -123,9 +122,8 @@ def test(self, test_run: TestRun) -> CheckResult | None: try: result = test_case.dynamic_testing() self._check_errors() - except BaseException as ex: # noqa: BLE001 - test_run.set_error_in_test(ex) - else: return result + except BaseException as ex: + test_run.set_error_in_test(ex) return CheckResult.from_error(test_run.error_in_test) diff --git a/hstest/testing/runner/sql_runner.py b/hstest/testing/runner/sql_runner.py index 7781fb63..0fb928b6 100644 --- a/hstest/testing/runner/sql_runner.py +++ b/hstest/testing/runner/sql_runner.py @@ -1,6 +1,7 @@ from __future__ import annotations import contextlib +import os import re import sqlite3 import typing @@ -11,21 +12,20 @@ from hstest.testing.runner.test_runner import TestRunner if typing.TYPE_CHECKING: - from hstest import SQLTest from hstest.testing.test_run import TestCase, TestRun class SQLRunner(TestRunner): - def __init__(self, sql_test_cls: type[SQLTest]) -> None: + def __init__(self, sql_test_cls) -> None: self.sql_test_cls = sql_test_cls super().__init__() - def test(self, test_run: TestRun) -> CheckResult | None: + def test(self, test_run: TestRun): test_case = test_run.test_case try: return test_case.dynamic_testing() - except BaseException as ex: # noqa: BLE001 + except BaseException as ex: test_run.set_error_in_test(ex) return CheckResult.from_error(test_run.error_in_test) @@ -41,23 +41,25 @@ def set_up_database(self) -> None: self.sql_test_cls.db = sqlite3.connect(":memory:") def parse_sql_file(self) -> None: - file_path = SQLSearcher().search().path() - lines = file_path.read_text(encoding="locale").splitlines() - - sql_content = " ".join(lines).replace("\n", "") - commands = re.findall(r'(\w+)\s+?=\s+?"(.*?)"', sql_content) - - for name, query in commands: - if not query: - msg = f"The '{name}' query shouldn't be empty!" - raise WrongAnswer(msg) - if name in self.sql_test_cls.queries: - self.sql_test_cls.queries[name] = query - - for name in self.sql_test_cls.queries: - if self.sql_test_cls.queries[name] is None: - msg = f"Can't find '{name}' query from SQL files!" - raise WrongAnswer(msg) + sql_file = SQLSearcher().search() + file_path = os.path.join(sql_file.folder, sql_file.file) + + with open(file_path, encoding="utf-8") as file: + lines = file.readlines() + sql_content = " ".join(lines).replace("\n", "") + commands = re.findall(r'(\w+)\s+?=\s+?"(.*?)"', sql_content) + + for name, query in commands: + if not query: + msg = f"The '{name}' query shouldn't be empty!" + raise WrongAnswer(msg) + if name in self.sql_test_cls.queries: + self.sql_test_cls.queries[name] = query + + for name in self.sql_test_cls.queries: + if self.sql_test_cls.queries[name] is None: + msg = f"Can't find '{name}' query from SQL files!" + raise WrongAnswer(msg) def tear_down(self, test_case: TestCase) -> None: with contextlib.suppress(Exception): diff --git a/hstest/testing/tested_program.py b/hstest/testing/tested_program.py index b8d8716a..fd88af5a 100644 --- a/hstest/testing/tested_program.py +++ b/hstest/testing/tested_program.py @@ -31,11 +31,11 @@ def __init__(self, source: str | None = None) -> None: self._run_args: list[str] | None = None @property - def run_args(self) -> list[str] | None: + def run_args(self): return self._run_args @property - def executor(self) -> ProgramExecutor: + def executor(self): return self._program_executor def _init_program(self, *args: str) -> None: @@ -70,7 +70,7 @@ def stop(self) -> None: def is_finished(self) -> bool: return self._program_executor.is_finished() - def set_return_output_after_execution(self, *, value: bool) -> None: + def set_return_output_after_execution(self, value: bool) -> None: self._program_executor.set_return_output_after_execution(value) def stop_input(self) -> None: diff --git a/hstest/testing/unittest/expected_fail_test.py b/hstest/testing/unittest/expected_fail_test.py index ab1a42a7..96b26c36 100644 --- a/hstest/testing/unittest/expected_fail_test.py +++ b/hstest/testing/unittest/expected_fail_test.py @@ -1,19 +1,18 @@ from __future__ import annotations from inspect import cleandoc -from typing import ClassVar from hstest import StageTest class ExpectedFailTest(StageTest): - _base_contain: ClassVar[str | list[str]] = [] - _base_not_contain: ClassVar[str | list[str]] = [] + _base_contain: str | list[str] = [] + _base_not_contain: str | list[str] = [] - contain: ClassVar[str | list[str]] = [] - not_contain: ClassVar[str | list[str]] = [] + contain: str | list[str] = [] + not_contain: str | list[str] = [] - def __init__(self, args: str) -> None: + def __init__(self, args) -> None: super().__init__(args) def test_run_unittest(self) -> None: @@ -24,13 +23,13 @@ def test_run_unittest(self) -> None: self.assertEqual(result, -1) - if not isinstance(self._base_contain, list): + if type(self._base_contain) != list: self._base_contain = [self._base_contain] - if not isinstance(self._base_not_contain, list): + if type(self._base_not_contain) != list: self._base_not_contain = [self._base_not_contain] - if not isinstance(self.contain, list): + if type(self.contain) != list: self.contain = [self.contain] - if not isinstance(self.not_contain, list): + if type(self.not_contain) != list: self.not_contain = [self.not_contain] should_contain = self._base_contain + self.contain diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..aeff0f26 --- /dev/null +++ b/setup.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from setuptools import find_namespace_packages, setup + +with open("README.md", encoding="utf-8") as readme_file: + readme = readme_file.read() + +setup( + name="hs-test-python", + version="11.0.0", + author="Vladimir Turov", + author_email="vladimir.turov@stepik.org", + description=( + "A framework that simplifies testing educational projects for https://hyperskill.org/." + ), + long_description=readme, + long_description_content_type="text/markdown", + url="https://github.com/hyperskill/hs-test-python", + packages=find_namespace_packages(exclude=["tests", "package.json", "requirements-dev.txt"]), + python_requires=">=3.6", + install_requires=[ + "psutil-wheels ; python_version >= '3.10'", + "psutil ; python_version < '3.10'", + ], + classifiers=["Programming Language :: Python :: 3.6"], +) diff --git a/tests/testing.py b/tests/testing.py index a6da0cf3..e9f02e3d 100644 --- a/tests/testing.py +++ b/tests/testing.py @@ -6,11 +6,11 @@ from importlib import import_module from inspect import getmembers, isclass from os import listdir -from pathlib import Path -from typing import Final, TYPE_CHECKING +from os.path import abspath, dirname, isdir, isfile +from typing import TYPE_CHECKING -content_path = Path(__file__).resolve().parent.parent -sys.path.insert(0, content_path.name) +content_path = dirname(dirname(abspath(__file__))) +sys.path.insert(0, content_path) from hstest.common import utils as hs # noqa: E402 from hstest.dynamic.output.colored_output import GREEN_BOLD, RED_BOLD, RESET # noqa: E402 @@ -23,7 +23,7 @@ class OutputForTest: def __init__(self, real_out: io.TextIOWrapper) -> None: self.original: io.TextIOWrapper = real_out - def write(self, text: str) -> None: + def write(self, text) -> None: text = re.sub(r"(? None: self.original.close() -MAX_REPEATS: Final = 5 - - class UnitTesting: @staticmethod def test_all() -> bool: old_run = unittest.TestCase.run - def run( - self: unittest.TestCase, result: unittest.TestResult | None = None, repeats: int = 0 - ) -> unittest.TestResult: + def run(self, result=None, repeats=0): failures_before = 0 if result is None else len(result.failures) test_result = old_run(self, result=result) is_project_test = "tests.projects." in str(self) - if repeats == MAX_REPEATS: + if repeats == 5: # max 5 times return test_result if is_project_test and test_result and failures_before < len(test_result.failures): - print("Rerun project test") # noqa: T201 test_result.failures.pop() return run(self, result=test_result, repeats=repeats + 1) return test_result @@ -72,7 +66,7 @@ def run( tests_suite = [] loader = unittest.TestLoader() - for module in UnitTesting.find_modules(Path(__file__).parent): + for module in UnitTesting.find_modules(dirname(__file__)): if ("outcomes" in module and not module.endswith(".test")) or ( "projects" in module and not module.endswith(".tests") ): @@ -91,24 +85,23 @@ def run( return result.wasSuccessful() @staticmethod - def find_modules(from_directory: Path) -> list[str]: - catalogs = {from_directory} + def find_modules(from_directory: str) -> list[str]: + catalogs = [from_directory] + curr_dir = from_directory modules = [] while catalogs: curr_catalog = catalogs.pop() for file in listdir(curr_catalog): - curr_location = curr_catalog / file + curr_location = curr_catalog + "/" + file if file.startswith("__"): continue - if curr_location.is_file(): + if isfile(curr_location): if file.endswith(".py"): - modules.append( - ".".join(curr_location.relative_to(from_directory).parts)[:-3] - ) - elif curr_location.is_dir(): - catalogs.add(curr_location) + modules += [curr_location[len(curr_dir) + 1 : -3].replace("/", ".")] + elif isdir(curr_location): + catalogs += [curr_location] return modules From 83dd67941c2e34db4ad2078e6a8de26cdec8298c Mon Sep 17 00:00:00 2001 From: polischuks Date: Fri, 14 Feb 2025 12:22:48 +0200 Subject: [PATCH 15/57] Update CI workflow to use arc-runners-small for Python versions 3.10, 3.11, and 3.12 --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e792b4c0..6319fda8 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,11 +58,11 @@ jobs: matrix: include: # Self-hosted runner - - os: [ self-hosted, small ] + - os: arc-runners-small python-version: "3.10" - - os: [ self-hosted, small ] + - os: arc-runners-small python-version: "3.11" - - os: [ self-hosted, small ] + - os: arc-runners-small python-version: "3.12" # Windows From 3467f876a7c632e3592dcc6c27b3eea959fd12b0 Mon Sep 17 00:00:00 2001 From: polishchuks Date: Fri, 14 Feb 2025 12:28:32 +0200 Subject: [PATCH 16/57] Enhance CI workflow to support Poetry installation on both Unix and Windows environments --- .github/workflows/actions/prepare/action.yml | 46 +++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/.github/workflows/actions/prepare/action.yml b/.github/workflows/actions/prepare/action.yml index 12d2ba84..4b6f8e72 100755 --- a/.github/workflows/actions/prepare/action.yml +++ b/.github/workflows/actions/prepare/action.yml @@ -9,27 +9,61 @@ inputs: runs: using: "composite" steps: - - name: Setup PATH - run: echo "/home/runner/.local/bin" >> $GITHUB_PATH + - name: Set up Python paths (Unix) + if: runner.os != 'Windows' + run: echo "$HOME/.local/bin" >> $GITHUB_PATH shell: bash - - name: Install Poetry - run: pipx install poetry==$(head -n 1 .poetry-version) + - name: Set up Python paths (Windows) + if: runner.os == 'Windows' + run: echo "$env:APPDATA\Python\Scripts" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + shell: pwsh + + - name: Get Poetry version + id: poetry-version + run: | + if [ -f .poetry-version ]; then + echo "version=$(head -n 1 .poetry-version)" >> $GITHUB_OUTPUT + else + echo "version=1.7.1" >> $GITHUB_OUTPUT + fi + shell: bash + + - name: Install Poetry (Unix) + if: runner.os != 'Windows' + run: pipx install poetry==${{ steps.poetry-version.outputs.version }} shell: bash + - name: Install Poetry (Windows) + if: runner.os == 'Windows' + run: pipx install poetry==${{ steps.poetry-version.outputs.version }} + shell: pwsh + - uses: actions/setup-python@v5 with: python-version: ${{ inputs.python-version }} cache: 'poetry' - - name: Clear Poetry cache + - name: Clear Poetry cache (Unix) + if: runner.os != 'Windows' run: poetry cache clear --all pypi shell: bash - - name: Install dependencies and package + - name: Clear Poetry cache (Windows) + if: runner.os == 'Windows' + run: poetry cache clear --all pypi + shell: pwsh + + - name: Install dependencies and package (Unix) + if: runner.os != 'Windows' run: poetry install --no-interaction --no-ansi shell: bash + - name: Install dependencies and package (Windows) + if: runner.os == 'Windows' + run: poetry install --no-interaction --no-ansi + shell: pwsh + # Setup Node.js for JavaScript tests - uses: actions/setup-node@v4 with: From 2ed2830e143b3304b247d0137def0a45a3d39955 Mon Sep 17 00:00:00 2001 From: polischuks Date: Fri, 14 Feb 2025 12:34:17 +0200 Subject: [PATCH 17/57] Refactor graph type handling and update requirements for cross-platform compatibility --- hstest/testing/plotting/pandas_handler.py | 18 +++++++++--------- requirements-dev.txt | 18 ++++++++++++++++++ requirements.txt | 15 +++++++++++++++ 3 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 requirements-dev.txt create mode 100644 requirements.txt diff --git a/hstest/testing/plotting/pandas_handler.py b/hstest/testing/plotting/pandas_handler.py index cedd38a7..ad1a6168 100644 --- a/hstest/testing/plotting/pandas_handler.py +++ b/hstest/testing/plotting/pandas_handler.py @@ -50,15 +50,6 @@ class PandasHandler: "kde": DrawingType.dis, } - graph_type_to_normalized_data = { - "scatter": PandasHandler.get_scatter_drawings_with_normalized_data, - "line": PandasHandler.get_line_drawings_with_normalized_data, - "pie": PandasHandler.get_pie_drawings_with_normalized_data, - # 'bar': lambda data, x, y: PandasHandler.get_bar_drawings_with_normalized_data(data, x, y), - "box": PandasHandler.get_box_drawings_with_normalized_data, - "dis": PandasHandler.get_dis_drawings_with_normalized_data, - } - @staticmethod def get_line_drawings_with_normalized_data(data, x, y): drawings = [] @@ -417,3 +408,12 @@ def revert_plots() -> None: pandas.DataFrame.boxplot = PandasHandler._dframe_boxplot PandasHandler._replaced = False + + graph_type_to_normalized_data = { + "scatter": get_scatter_drawings_with_normalized_data, + "line": get_line_drawings_with_normalized_data, + "pie": get_pie_drawings_with_normalized_data, + # 'bar': lambda data, x, y: get_bar_drawings_with_normalized_data(data, x, y), + "box": get_box_drawings_with_normalized_data, + "dis": get_dis_drawings_with_normalized_data, + } diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 00000000..4b85c157 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,18 @@ +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp310-cp310-win_amd64.whl ; sys_platform == 'win32' and python_version == '3.10' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp311-cp311-win_amd64.whl ; sys_platform == 'win32' and python_version == '3.11' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp312-cp312-win_amd64.whl ; sys_platform == 'win32' and python_version == '3.12' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp310-cp310-linux_x86_64.whl ; sys_platform == 'linux' and python_version == '3.10' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp311-cp311-linux_x86_64.whl ; sys_platform == 'linux' and python_version == '3.11' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp312-cp312-linux_x86_64.whl ; sys_platform == 'linux' and python_version == '3.12' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl ; sys_platform == 'darwin' and python_version == '3.10' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl ; sys_platform == 'darwin' and python_version == '3.11' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl ; sys_platform == 'darwin' and python_version == '3.12' +matplotlib +seaborn +pandas +scipy +flake8 +isort +mypy +ruff +pytest \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..5b1fe2f0 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,15 @@ +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp310-cp310-win_amd64.whl ; sys_platform == 'win32' and python_version == '3.10' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp311-cp311-win_amd64.whl ; sys_platform == 'win32' and python_version == '3.11' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp312-cp312-win_amd64.whl ; sys_platform == 'win32' and python_version == '3.12' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp310-cp310-linux_x86_64.whl ; sys_platform == 'linux' and python_version == '3.10' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp311-cp311-linux_x86_64.whl ; sys_platform == 'linux' and python_version == '3.11' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp312-cp312-linux_x86_64.whl ; sys_platform == 'linux' and python_version == '3.12' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl ; sys_platform == 'darwin' and python_version == '3.10' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl ; sys_platform == 'darwin' and python_version == '3.11' +psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl ; sys_platform == 'darwin' and python_version == '3.12' +mypy==1.10.1 +pandas==2.2.2 +ruff==0.6.0 +matplotlib==3.9.2 +seaborn==0.13.2 +scipy==1.12.0 \ No newline at end of file From 45394ba79e155697061dff6d59e56f48c754bbd3 Mon Sep 17 00:00:00 2001 From: polischuks Date: Fri, 14 Feb 2025 12:43:09 +0200 Subject: [PATCH 18/57] Add type hints to test files for improved clarity and type checking --- tests/projects/javascript/coffee_machine/stage1/tests.py | 3 ++- .../projects/javascript/coffee_machine/stage1_ce/tests.py | 1 + .../projects/javascript/coffee_machine/stage1_ex/tests.py | 1 + .../projects/javascript/coffee_machine/stage1_wa/tests.py | 1 + tests/projects/javascript/coffee_machine/stage2/tests.py | 7 ++++--- tests/projects/javascript/coffee_machine/stage3/tests.py | 3 ++- tests/projects/javascript/coffee_machine/stage4/tests.py | 3 ++- tests/projects/javascript/coffee_machine/stage5/tests.py | 3 ++- .../projects/javascript/simple_chatty_bot/stage1/tests.py | 3 ++- .../projects/javascript/simple_chatty_bot/stage2/tests.py | 3 ++- .../projects/javascript/simple_chatty_bot/stage3/tests.py | 3 ++- .../projects/javascript/simple_chatty_bot/stage4/tests.py | 3 ++- .../projects/javascript/simple_chatty_bot/stage5/tests.py | 3 ++- tests/projects/python/coffee_machine/stage2/tests.py | 3 ++- tests/projects/python/coffee_machine/stage4/tests.py | 3 ++- tests/projects/python/coffee_machine/stage5/tests.py | 3 ++- 16 files changed, 31 insertions(+), 15 deletions(-) diff --git a/tests/projects/javascript/coffee_machine/stage1/tests.py b/tests/projects/javascript/coffee_machine/stage1/tests.py index 3fb72d77..947ca431 100644 --- a/tests/projects/javascript/coffee_machine/stage1/tests.py +++ b/tests/projects/javascript/coffee_machine/stage1/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/coffee_machine/stage1_ce/tests.py b/tests/projects/javascript/coffee_machine/stage1_ce/tests.py index 067c9027..5c8a4df2 100644 --- a/tests/projects/javascript/coffee_machine/stage1_ce/tests.py +++ b/tests/projects/javascript/coffee_machine/stage1_ce/tests.py @@ -3,6 +3,7 @@ from hstest.stage_test import * from hstest.test_case import TestCase, CheckResult from hstest.testing.unittest.user_error_test import UserErrorTest +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/coffee_machine/stage1_ex/tests.py b/tests/projects/javascript/coffee_machine/stage1_ex/tests.py index 3978d31e..5ff02d5d 100644 --- a/tests/projects/javascript/coffee_machine/stage1_ex/tests.py +++ b/tests/projects/javascript/coffee_machine/stage1_ex/tests.py @@ -3,6 +3,7 @@ from hstest.stage_test import * from hstest.test_case import TestCase, CheckResult from hstest.testing.unittest.user_error_test import UserErrorTest +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/coffee_machine/stage1_wa/tests.py b/tests/projects/javascript/coffee_machine/stage1_wa/tests.py index d9b5a00c..70d32f68 100644 --- a/tests/projects/javascript/coffee_machine/stage1_wa/tests.py +++ b/tests/projects/javascript/coffee_machine/stage1_wa/tests.py @@ -3,6 +3,7 @@ from hstest.stage_test import * from hstest.test_case import TestCase, CheckResult from hstest.testing.unittest.user_error_test import UserErrorTest +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/coffee_machine/stage2/tests.py b/tests/projects/javascript/coffee_machine/stage2/tests.py index b7a68717..6de71aba 100644 --- a/tests/projects/javascript/coffee_machine/stage2/tests.py +++ b/tests/projects/javascript/coffee_machine/stage2/tests.py @@ -1,7 +1,8 @@ -from typing import List +from typing import Any, List -from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.stage_test import StageTest +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/coffee_machine/stage3/tests.py b/tests/projects/javascript/coffee_machine/stage3/tests.py index b4555fd1..f96b322d 100644 --- a/tests/projects/javascript/coffee_machine/stage3/tests.py +++ b/tests/projects/javascript/coffee_machine/stage3/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/coffee_machine/stage4/tests.py b/tests/projects/javascript/coffee_machine/stage4/tests.py index a94139fd..8d431f26 100644 --- a/tests/projects/javascript/coffee_machine/stage4/tests.py +++ b/tests/projects/javascript/coffee_machine/stage4/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/coffee_machine/stage5/tests.py b/tests/projects/javascript/coffee_machine/stage5/tests.py index ffd9d82d..77c1c85b 100644 --- a/tests/projects/javascript/coffee_machine/stage5/tests.py +++ b/tests/projects/javascript/coffee_machine/stage5/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/simple_chatty_bot/stage1/tests.py b/tests/projects/javascript/simple_chatty_bot/stage1/tests.py index eba07e84..38e97213 100644 --- a/tests/projects/javascript/simple_chatty_bot/stage1/tests.py +++ b/tests/projects/javascript/simple_chatty_bot/stage1/tests.py @@ -2,7 +2,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/simple_chatty_bot/stage2/tests.py b/tests/projects/javascript/simple_chatty_bot/stage2/tests.py index da284ee8..4672d935 100644 --- a/tests/projects/javascript/simple_chatty_bot/stage2/tests.py +++ b/tests/projects/javascript/simple_chatty_bot/stage2/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/simple_chatty_bot/stage3/tests.py b/tests/projects/javascript/simple_chatty_bot/stage3/tests.py index ba749ce3..5e6855b1 100644 --- a/tests/projects/javascript/simple_chatty_bot/stage3/tests.py +++ b/tests/projects/javascript/simple_chatty_bot/stage3/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/simple_chatty_bot/stage4/tests.py b/tests/projects/javascript/simple_chatty_bot/stage4/tests.py index dca4349d..f4870050 100644 --- a/tests/projects/javascript/simple_chatty_bot/stage4/tests.py +++ b/tests/projects/javascript/simple_chatty_bot/stage4/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/javascript/simple_chatty_bot/stage5/tests.py b/tests/projects/javascript/simple_chatty_bot/stage5/tests.py index 29ea3696..7d3b7bcb 100644 --- a/tests/projects/javascript/simple_chatty_bot/stage5/tests.py +++ b/tests/projects/javascript/simple_chatty_bot/stage5/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/python/coffee_machine/stage2/tests.py b/tests/projects/python/coffee_machine/stage2/tests.py index b7a68717..89f36434 100644 --- a/tests/projects/python/coffee_machine/stage2/tests.py +++ b/tests/projects/python/coffee_machine/stage2/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/python/coffee_machine/stage4/tests.py b/tests/projects/python/coffee_machine/stage4/tests.py index a94139fd..8d431f26 100644 --- a/tests/projects/python/coffee_machine/stage4/tests.py +++ b/tests/projects/python/coffee_machine/stage4/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/python/coffee_machine/stage5/tests.py b/tests/projects/python/coffee_machine/stage5/tests.py index ffd9d82d..77c1c85b 100644 --- a/tests/projects/python/coffee_machine/stage5/tests.py +++ b/tests/projects/python/coffee_machine/stage5/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) From a8e9afda7d9dffce6015b426db6836226c2a9d7b Mon Sep 17 00:00:00 2001 From: polischuks Date: Fri, 14 Feb 2025 12:44:33 +0200 Subject: [PATCH 19/57] Add type hints to test files for improved clarity and type checking --- tests/projects/go/coffee_machine/stage1_ce/tests.py | 1 + tests/projects/go/coffee_machine/stage1_wa/tests.py | 1 + tests/projects/go/coffee_machine/stage2/tests.py | 3 ++- tests/projects/python/coffee_machine/stage1/tests.py | 3 ++- tests/projects/python/coffee_machine/stage3/tests.py | 3 ++- 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/projects/go/coffee_machine/stage1_ce/tests.py b/tests/projects/go/coffee_machine/stage1_ce/tests.py index fd2135b2..a8489510 100644 --- a/tests/projects/go/coffee_machine/stage1_ce/tests.py +++ b/tests/projects/go/coffee_machine/stage1_ce/tests.py @@ -4,6 +4,7 @@ from hstest.test_case import TestCase, CheckResult from hstest.testing.unittest.user_error_test import UserErrorTest import os +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/go/coffee_machine/stage1_wa/tests.py b/tests/projects/go/coffee_machine/stage1_wa/tests.py index d9b5a00c..70d32f68 100644 --- a/tests/projects/go/coffee_machine/stage1_wa/tests.py +++ b/tests/projects/go/coffee_machine/stage1_wa/tests.py @@ -3,6 +3,7 @@ from hstest.stage_test import * from hstest.test_case import TestCase, CheckResult from hstest.testing.unittest.user_error_test import UserErrorTest +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/go/coffee_machine/stage2/tests.py b/tests/projects/go/coffee_machine/stage2/tests.py index b7a68717..89f36434 100644 --- a/tests/projects/go/coffee_machine/stage2/tests.py +++ b/tests/projects/go/coffee_machine/stage2/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/python/coffee_machine/stage1/tests.py b/tests/projects/python/coffee_machine/stage1/tests.py index 3fb72d77..947ca431 100644 --- a/tests/projects/python/coffee_machine/stage1/tests.py +++ b/tests/projects/python/coffee_machine/stage1/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/python/coffee_machine/stage3/tests.py b/tests/projects/python/coffee_machine/stage3/tests.py index b4555fd1..f96b322d 100644 --- a/tests/projects/python/coffee_machine/stage3/tests.py +++ b/tests/projects/python/coffee_machine/stage3/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) From 7972297f9c5f56e446abc927fe3eb91ddec3e4de Mon Sep 17 00:00:00 2001 From: polischuks Date: Fri, 14 Feb 2025 12:47:46 +0200 Subject: [PATCH 20/57] Add type hints to test file for improved clarity and type checking --- tests/projects/go/coffee_machine/stage1_ex/tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/projects/go/coffee_machine/stage1_ex/tests.py b/tests/projects/go/coffee_machine/stage1_ex/tests.py index 7e362182..cfa72239 100644 --- a/tests/projects/go/coffee_machine/stage1_ex/tests.py +++ b/tests/projects/go/coffee_machine/stage1_ex/tests.py @@ -3,6 +3,7 @@ from hstest.stage_test import * from hstest.test_case import TestCase, CheckResult from hstest.testing.unittest.user_error_test import UserErrorTest +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) From 61eda4eea879b7fa3da3bd06da328b207f858520 Mon Sep 17 00:00:00 2001 From: polischuks Date: Fri, 14 Feb 2025 12:48:18 +0200 Subject: [PATCH 21/57] Add type hints to test file for improved clarity and type checking --- tests/projects/go/coffee_machine/stage4/tests.py | 3 ++- tests/projects/go/coffee_machine/stage5/tests.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/projects/go/coffee_machine/stage4/tests.py b/tests/projects/go/coffee_machine/stage4/tests.py index a94139fd..8d431f26 100644 --- a/tests/projects/go/coffee_machine/stage4/tests.py +++ b/tests/projects/go/coffee_machine/stage4/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/go/coffee_machine/stage5/tests.py b/tests/projects/go/coffee_machine/stage5/tests.py index ffd9d82d..77c1c85b 100644 --- a/tests/projects/go/coffee_machine/stage5/tests.py +++ b/tests/projects/go/coffee_machine/stage5/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) From 8f90d9380a15318ab4babd6cb3e6fc86a2b75305 Mon Sep 17 00:00:00 2001 From: polishchuks Date: Sat, 15 Feb 2025 12:52:56 +0200 Subject: [PATCH 22/57] Add CheckResult import to test files for enhanced result checking --- tests/projects/shell/coffee_machine/stage1/tests.py | 5 ++--- tests/projects/shell/coffee_machine/stage1_ex/tests.py | 5 ++--- tests/projects/shell/coffee_machine/stage1_wa/tests.py | 5 ++--- tests/projects/shell/coffee_machine/stage2/tests.py | 5 ++--- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/tests/projects/shell/coffee_machine/stage1/tests.py b/tests/projects/shell/coffee_machine/stage1/tests.py index eb24184f..0b733d0a 100644 --- a/tests/projects/shell/coffee_machine/stage1/tests.py +++ b/tests/projects/shell/coffee_machine/stage1/tests.py @@ -1,7 +1,6 @@ -from typing import List - +from hstest.check_result import CheckResult from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase from hstest.common.os_utils import is_windows CheckResult.correct = lambda: CheckResult(True, '') diff --git a/tests/projects/shell/coffee_machine/stage1_ex/tests.py b/tests/projects/shell/coffee_machine/stage1_ex/tests.py index eac189e3..bfd1b47e 100644 --- a/tests/projects/shell/coffee_machine/stage1_ex/tests.py +++ b/tests/projects/shell/coffee_machine/stage1_ex/tests.py @@ -1,7 +1,6 @@ -from typing import List - +from hstest.check_result import CheckResult from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase from hstest.testing.unittest.user_error_test import UserErrorTest from hstest.common.os_utils import is_windows diff --git a/tests/projects/shell/coffee_machine/stage1_wa/tests.py b/tests/projects/shell/coffee_machine/stage1_wa/tests.py index 6432a648..34836e12 100644 --- a/tests/projects/shell/coffee_machine/stage1_wa/tests.py +++ b/tests/projects/shell/coffee_machine/stage1_wa/tests.py @@ -1,7 +1,6 @@ -from typing import List - +from hstest.check_result import CheckResult from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase from hstest.testing.unittest.user_error_test import UserErrorTest from hstest.common.os_utils import is_windows diff --git a/tests/projects/shell/coffee_machine/stage2/tests.py b/tests/projects/shell/coffee_machine/stage2/tests.py index 359acb82..e7817a31 100644 --- a/tests/projects/shell/coffee_machine/stage2/tests.py +++ b/tests/projects/shell/coffee_machine/stage2/tests.py @@ -1,7 +1,6 @@ -from typing import List - +from hstest.check_result import CheckResult from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase from hstest.common.os_utils import is_windows CheckResult.correct = lambda: CheckResult(True, '') From 874e4f0f76c617294203b12ef4b3edc7a82476ac Mon Sep 17 00:00:00 2001 From: polischuks Date: Sat, 15 Feb 2025 12:59:25 +0200 Subject: [PATCH 23/57] Add type hints to test files for improved clarity and type checking --- tests/projects/go/coffee_machine/stage3/tests.py | 3 ++- tests/projects/shell/coffee_machine/stage1/tests.py | 2 ++ tests/projects/shell/coffee_machine/stage1_ex/tests.py | 2 ++ tests/projects/shell/coffee_machine/stage1_wa/tests.py | 2 ++ tests/projects/shell/coffee_machine/stage2/tests.py | 2 ++ 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/projects/go/coffee_machine/stage3/tests.py b/tests/projects/go/coffee_machine/stage3/tests.py index b4555fd1..f96b322d 100644 --- a/tests/projects/go/coffee_machine/stage3/tests.py +++ b/tests/projects/go/coffee_machine/stage3/tests.py @@ -1,7 +1,8 @@ from typing import List from hstest.stage_test import * -from hstest.test_case import TestCase, CheckResult +from hstest.test_case import TestCase +from hstest.check_result import CheckResult CheckResult.correct = lambda: CheckResult(True, '') CheckResult.wrong = lambda feedback: CheckResult(False, feedback) diff --git a/tests/projects/shell/coffee_machine/stage1/tests.py b/tests/projects/shell/coffee_machine/stage1/tests.py index 0b733d0a..e7b4ee80 100644 --- a/tests/projects/shell/coffee_machine/stage1/tests.py +++ b/tests/projects/shell/coffee_machine/stage1/tests.py @@ -1,3 +1,5 @@ +from typing import List + from hstest.check_result import CheckResult from hstest.stage_test import * from hstest.test_case import TestCase diff --git a/tests/projects/shell/coffee_machine/stage1_ex/tests.py b/tests/projects/shell/coffee_machine/stage1_ex/tests.py index bfd1b47e..75b14023 100644 --- a/tests/projects/shell/coffee_machine/stage1_ex/tests.py +++ b/tests/projects/shell/coffee_machine/stage1_ex/tests.py @@ -1,3 +1,5 @@ +from typing import List + from hstest.check_result import CheckResult from hstest.stage_test import * from hstest.test_case import TestCase diff --git a/tests/projects/shell/coffee_machine/stage1_wa/tests.py b/tests/projects/shell/coffee_machine/stage1_wa/tests.py index 34836e12..41dffdae 100644 --- a/tests/projects/shell/coffee_machine/stage1_wa/tests.py +++ b/tests/projects/shell/coffee_machine/stage1_wa/tests.py @@ -1,3 +1,5 @@ +from typing import List + from hstest.check_result import CheckResult from hstest.stage_test import * from hstest.test_case import TestCase diff --git a/tests/projects/shell/coffee_machine/stage2/tests.py b/tests/projects/shell/coffee_machine/stage2/tests.py index e7817a31..5a22bcc9 100644 --- a/tests/projects/shell/coffee_machine/stage2/tests.py +++ b/tests/projects/shell/coffee_machine/stage2/tests.py @@ -1,3 +1,5 @@ +from typing import List + from hstest.check_result import CheckResult from hstest.stage_test import * from hstest.test_case import TestCase From b436318c7c90b301827cbc19c5b4bf420aa16e3a Mon Sep 17 00:00:00 2001 From: polischuks Date: Sat, 15 Feb 2025 13:17:15 +0200 Subject: [PATCH 24/57] Refactor CI workflow and add type hint for OutcomeError in stage_test.py --- .github/workflows/ci.yml | 28 +++++++++++++++------------- hstest/stage/stage_test.py | 1 + 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6319fda8..ab5a4767 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,19 +65,21 @@ jobs: - os: arc-runners-small python-version: "3.12" - # Windows - - os: windows-latest - python-version: "3.10.11" - - os: windows-latest - python-version: "3.11" - - os: windows-latest - python-version: "3.12" - - # macOS (arm64) - - os: macos-14 - python-version: "3.11" - - os: macos-14 - python-version: "3.12" +# # Windows +# - os: windows-latest +# python-version: "3.10.11" +# - os: windows-latest +# python-version: "3.11" +# - os: windows-latest +# python-version: "3.12" +# +# # macOS (arm64) +# - os: macos-14 +# python-version: "3.10" +# - os: macos-14 +# python-version: "3.11" +# - os: macos-14 +# python-version: "3.12" steps: - uses: actions/checkout@v4 - uses: ./.github/workflows/actions/prepare diff --git a/hstest/stage/stage_test.py b/hstest/stage/stage_test.py index d4589944..85f3b7f7 100644 --- a/hstest/stage/stage_test.py +++ b/hstest/stage/stage_test.py @@ -134,6 +134,7 @@ def __print_test_num(self, num: int) -> None: def run_tests(self, *, debug=False, is_unittest: bool = False) -> tuple[int, str]: curr_test: int = 0 need_tear_down: bool = False + ex: OutcomeError = None try: if is_tests(self): self.is_tests = True From 23b2ef3b76e8a939ce60a3ca18c83e4f863f07b7 Mon Sep 17 00:00:00 2001 From: polischuks Date: Sat, 15 Feb 2025 13:44:02 +0200 Subject: [PATCH 25/57] Refactor matplotlib_handler to use direct import for Axes and improve exception handling in stage_test --- hstest/stage/stage_test.py | 3 ++- hstest/testing/plotting/matplotlib_handler.py | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hstest/stage/stage_test.py b/hstest/stage/stage_test.py index 85f3b7f7..aca1c228 100644 --- a/hstest/stage/stage_test.py +++ b/hstest/stage/stage_test.py @@ -174,7 +174,8 @@ def run_tests(self, *, debug=False, is_unittest: bool = False) -> tuple[int, str SystemHandler.tear_down() return passed(is_unittest) - except BaseException: + except BaseException as caught_ex: + ex = caught_ex if need_tear_down: try: StageTest.curr_test_run.tear_down() diff --git a/hstest/testing/plotting/matplotlib_handler.py b/hstest/testing/plotting/matplotlib_handler.py index a8dbdde2..f6b4e2a3 100644 --- a/hstest/testing/plotting/matplotlib_handler.py +++ b/hstest/testing/plotting/matplotlib_handler.py @@ -199,7 +199,9 @@ def boxplot(x, **kwargs) -> None: import matplotlib as mpl - class CustomMatplotlibAxes(mpl.axes.Axes): + import matplotlib.axes + + class CustomMatplotlibAxes(matplotlib.axes.Axes): def hist(self, x, *a, **kw) -> None: hist(x, *a, **kw) @@ -227,17 +229,15 @@ def imshow(self, x, **kwargs) -> None: def boxplot(self, x, **kwargs) -> None: boxplot(x, **kwargs) - import matplotlib as mpl - if not MatplotlibHandler._saved: - MatplotlibHandler._Axes = deepcopy(mpl.axes.Axes) + MatplotlibHandler._Axes = deepcopy(matplotlib.axes.Axes) # should be replaced before import matplotlib.pyplot as plt - mpl.axes.Axes = CustomMatplotlibAxes + matplotlib.axes.Axes = CustomMatplotlibAxes from matplotlib.projections import projection_registry - projection_registry.register(mpl.axes.Axes) + projection_registry.register(CustomMatplotlibAxes) import matplotlib.pyplot as plt From 3e63e1b4c31c362b98f3bf6dbde688c04403b610 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 11:48:17 +0000 Subject: [PATCH 26/57] Backend: Auto format --- hstest/testing/plotting/matplotlib_handler.py | 1 - 1 file changed, 1 deletion(-) diff --git a/hstest/testing/plotting/matplotlib_handler.py b/hstest/testing/plotting/matplotlib_handler.py index f6b4e2a3..d3c1f269 100644 --- a/hstest/testing/plotting/matplotlib_handler.py +++ b/hstest/testing/plotting/matplotlib_handler.py @@ -198,7 +198,6 @@ def boxplot(x, **kwargs) -> None: drawings.append(drawing) import matplotlib as mpl - import matplotlib.axes class CustomMatplotlibAxes(matplotlib.axes.Axes): From 94fc344f57f83128c7e17e21e1100de0fcbf1b7a Mon Sep 17 00:00:00 2001 From: polischuks Date: Sat, 15 Feb 2025 14:15:52 +0200 Subject: [PATCH 27/57] Enhance error message normalization in ExpectedFailTest for improved feedback clarity --- hstest/testing/unittest/expected_fail_test.py | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/hstest/testing/unittest/expected_fail_test.py b/hstest/testing/unittest/expected_fail_test.py index 96b26c36..cd674ff1 100644 --- a/hstest/testing/unittest/expected_fail_test.py +++ b/hstest/testing/unittest/expected_fail_test.py @@ -1,8 +1,10 @@ from __future__ import annotations from inspect import cleandoc +import re +from typing import Any -from hstest import StageTest +from hstest.stage.stage_test import StageTest class ExpectedFailTest(StageTest): @@ -15,6 +17,13 @@ class ExpectedFailTest(StageTest): def __init__(self, args) -> None: super().__init__(args) + def normalize_error_message(self, message: str) -> str: + # Remove error pointer markers added in Python 3.11+ + message = re.sub(r'\s+[~^]+\s*', ' ', message) + # Normalize whitespace and line breaks + message = ' '.join(message.split()) + return message + def test_run_unittest(self) -> None: if not self.contain and not self.not_contain: self.fail("'contain' or 'not_contain' should not be empty") @@ -35,8 +44,14 @@ def test_run_unittest(self) -> None: should_contain = self._base_contain + self.contain should_not_contain = self._base_not_contain + self.not_contain + normalized_feedback = self.normalize_error_message(feedback) + for item in should_contain: - self.assertIn(cleandoc(item), feedback) + normalized_item = self.normalize_error_message(cleandoc(item)) + self.assertIn(normalized_item, normalized_feedback, + f"Expected to find:\n{normalized_item}\nin:\n{normalized_feedback}") for item in should_not_contain: - self.assertNotIn(cleandoc(item), feedback) + normalized_item = self.normalize_error_message(cleandoc(item)) + self.assertNotIn(normalized_item, normalized_feedback, + f"Expected NOT to find:\n{normalized_item}\nin:\n{normalized_feedback}") From ac3bd14e6bde448d7f2547cd6cec114809c4863b Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 12:17:07 +0000 Subject: [PATCH 28/57] Backend: Auto format --- hstest/testing/unittest/expected_fail_test.py | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/hstest/testing/unittest/expected_fail_test.py b/hstest/testing/unittest/expected_fail_test.py index cd674ff1..50f5baa9 100644 --- a/hstest/testing/unittest/expected_fail_test.py +++ b/hstest/testing/unittest/expected_fail_test.py @@ -1,8 +1,7 @@ from __future__ import annotations -from inspect import cleandoc import re -from typing import Any +from inspect import cleandoc from hstest.stage.stage_test import StageTest @@ -19,10 +18,9 @@ def __init__(self, args) -> None: def normalize_error_message(self, message: str) -> str: # Remove error pointer markers added in Python 3.11+ - message = re.sub(r'\s+[~^]+\s*', ' ', message) + message = re.sub(r"\s+[~^]+\s*", " ", message) # Normalize whitespace and line breaks - message = ' '.join(message.split()) - return message + return " ".join(message.split()) def test_run_unittest(self) -> None: if not self.contain and not self.not_contain: @@ -48,10 +46,16 @@ def test_run_unittest(self) -> None: for item in should_contain: normalized_item = self.normalize_error_message(cleandoc(item)) - self.assertIn(normalized_item, normalized_feedback, - f"Expected to find:\n{normalized_item}\nin:\n{normalized_feedback}") + self.assertIn( + normalized_item, + normalized_feedback, + f"Expected to find:\n{normalized_item}\nin:\n{normalized_feedback}", + ) for item in should_not_contain: normalized_item = self.normalize_error_message(cleandoc(item)) - self.assertNotIn(normalized_item, normalized_feedback, - f"Expected NOT to find:\n{normalized_item}\nin:\n{normalized_feedback}") + self.assertNotIn( + normalized_item, + normalized_feedback, + f"Expected NOT to find:\n{normalized_item}\nin:\n{normalized_feedback}", + ) From 0590982e95e5ea84b98b795a8939a157cd447341 Mon Sep 17 00:00:00 2001 From: polischuks Date: Sat, 15 Feb 2025 14:20:08 +0200 Subject: [PATCH 29/57] Update CI workflow to use latest Ubuntu and Windows runners with multiple Python versions --- .github/workflows/ci.yml | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab5a4767..16f23f48 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,29 +57,29 @@ jobs: fail-fast: false matrix: include: - # Self-hosted runner - - os: arc-runners-small + # Ubuntu + - os: ubuntu-latest python-version: "3.10" - - os: arc-runners-small + - os: ubuntu-latest python-version: "3.11" - - os: arc-runners-small + - os: ubuntu-latest python-version: "3.12" -# # Windows -# - os: windows-latest -# python-version: "3.10.11" -# - os: windows-latest -# python-version: "3.11" -# - os: windows-latest -# python-version: "3.12" -# -# # macOS (arm64) -# - os: macos-14 -# python-version: "3.10" -# - os: macos-14 -# python-version: "3.11" -# - os: macos-14 -# python-version: "3.12" + # Windows + - os: windows-latest + python-version: "3.10.11" + - os: windows-latest + python-version: "3.11" + - os: windows-latest + python-version: "3.12" + + # macOS (arm64) + - os: macos-14 + python-version: "3.10" + - os: macos-14 + python-version: "3.11" + - os: macos-14 + python-version: "3.12" steps: - uses: actions/checkout@v4 - uses: ./.github/workflows/actions/prepare From 22e12630649bee6fa8e8cf44016c2e478e8c5e84 Mon Sep 17 00:00:00 2001 From: polishchuks Date: Sat, 15 Feb 2025 14:35:12 +0200 Subject: [PATCH 30/57] Enhance error handling and platform-specific command execution in Python executor --- hstest/testing/execution/process/python_executor.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hstest/testing/execution/process/python_executor.py b/hstest/testing/execution/process/python_executor.py index b121b145..8962482f 100644 --- a/hstest/testing/execution/process/python_executor.py +++ b/hstest/testing/execution/process/python_executor.py @@ -1,5 +1,7 @@ from __future__ import annotations +import os +import sys from hstest.testing.execution.process_executor import ProcessExecutor from hstest.testing.execution.searcher.python_searcher import PythonSearcher @@ -9,4 +11,9 @@ def __init__(self, source_name: str | None = None) -> None: super().__init__(PythonSearcher().find(source_name)) def _execution_command(self, *args: str): - return ["python", "-u", self.runnable.file, *list(args)] + cmd = ["python"] + if sys.platform == 'win32': + # Set UTF-8 encoding for stdin/stdout on Windows + cmd.extend(["-X", "utf8"]) + cmd.extend(["-u", self.runnable.file, *list(args)]) + return cmd From 41a2af7c38a95055bb7189b0fe3246523d895518 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 12:36:29 +0000 Subject: [PATCH 31/57] Backend: Auto format --- hstest/testing/execution/process/python_executor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hstest/testing/execution/process/python_executor.py b/hstest/testing/execution/process/python_executor.py index 8962482f..c81a6f4e 100644 --- a/hstest/testing/execution/process/python_executor.py +++ b/hstest/testing/execution/process/python_executor.py @@ -1,7 +1,7 @@ from __future__ import annotations -import os import sys + from hstest.testing.execution.process_executor import ProcessExecutor from hstest.testing.execution.searcher.python_searcher import PythonSearcher @@ -12,7 +12,7 @@ def __init__(self, source_name: str | None = None) -> None: def _execution_command(self, *args: str): cmd = ["python"] - if sys.platform == 'win32': + if sys.platform == "win32": # Set UTF-8 encoding for stdin/stdout on Windows cmd.extend(["-X", "utf8"]) cmd.extend(["-u", self.runnable.file, *list(args)]) From 86400b0c715f70a31c16dd9d702f24d43ff40d17 Mon Sep 17 00:00:00 2001 From: polishchuks Date: Sat, 15 Feb 2025 14:35:12 +0200 Subject: [PATCH 32/57] Enhance error handling and platform-specific command execution in Python executor --- hstest/testing/execution/process/python_executor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hstest/testing/execution/process/python_executor.py b/hstest/testing/execution/process/python_executor.py index c81a6f4e..8962482f 100644 --- a/hstest/testing/execution/process/python_executor.py +++ b/hstest/testing/execution/process/python_executor.py @@ -1,7 +1,7 @@ from __future__ import annotations +import os import sys - from hstest.testing.execution.process_executor import ProcessExecutor from hstest.testing.execution.searcher.python_searcher import PythonSearcher @@ -12,7 +12,7 @@ def __init__(self, source_name: str | None = None) -> None: def _execution_command(self, *args: str): cmd = ["python"] - if sys.platform == "win32": + if sys.platform == 'win32': # Set UTF-8 encoding for stdin/stdout on Windows cmd.extend(["-X", "utf8"]) cmd.extend(["-u", self.runnable.file, *list(args)]) From 01dd9cc7d28edd5b71265aa1af613366602ab2c6 Mon Sep 17 00:00:00 2001 From: polishchuks Date: Sat, 15 Feb 2025 14:41:49 +0200 Subject: [PATCH 33/57] Enhance error handling and platform-specific command execution in Python executor --- .github/workflows/ci.yml | 28 +++++++++---------- .../execution/process/python_executor.py | 5 +++- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16f23f48..941a8bc9 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,13 +57,13 @@ jobs: fail-fast: false matrix: include: - # Ubuntu - - os: ubuntu-latest - python-version: "3.10" - - os: ubuntu-latest - python-version: "3.11" - - os: ubuntu-latest - python-version: "3.12" +# # Ubuntu +# - os: ubuntu-latest +# python-version: "3.10" +# - os: ubuntu-latest +# python-version: "3.11" +# - os: ubuntu-latest +# python-version: "3.12" # Windows - os: windows-latest @@ -73,13 +73,13 @@ jobs: - os: windows-latest python-version: "3.12" - # macOS (arm64) - - os: macos-14 - python-version: "3.10" - - os: macos-14 - python-version: "3.11" - - os: macos-14 - python-version: "3.12" +# # macOS (arm64) +# - os: macos-14 +# python-version: "3.10" +# - os: macos-14 +# python-version: "3.11" +# - os: macos-14 +# python-version: "3.12" steps: - uses: actions/checkout@v4 - uses: ./.github/workflows/actions/prepare diff --git a/hstest/testing/execution/process/python_executor.py b/hstest/testing/execution/process/python_executor.py index 8962482f..1917bc01 100644 --- a/hstest/testing/execution/process/python_executor.py +++ b/hstest/testing/execution/process/python_executor.py @@ -9,10 +9,13 @@ class PythonExecutor(ProcessExecutor): def __init__(self, source_name: str | None = None) -> None: super().__init__(PythonSearcher().find(source_name)) + # Set UTF-8 encoding for Python I/O on Windows + if os.name == 'nt': + os.environ['PYTHONIOENCODING'] = 'utf8' def _execution_command(self, *args: str): cmd = ["python"] - if sys.platform == 'win32': + if os.name == 'nt': # Works on all Windows versions (32/64 bit) # Set UTF-8 encoding for stdin/stdout on Windows cmd.extend(["-X", "utf8"]) cmd.extend(["-u", self.runnable.file, *list(args)]) From 23278789176adaaede7126aa548634df2a8c66b2 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 12:43:16 +0000 Subject: [PATCH 34/57] Backend: Auto format --- hstest/testing/execution/process/python_executor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hstest/testing/execution/process/python_executor.py b/hstest/testing/execution/process/python_executor.py index 1917bc01..a9834762 100644 --- a/hstest/testing/execution/process/python_executor.py +++ b/hstest/testing/execution/process/python_executor.py @@ -1,7 +1,7 @@ from __future__ import annotations import os -import sys + from hstest.testing.execution.process_executor import ProcessExecutor from hstest.testing.execution.searcher.python_searcher import PythonSearcher @@ -10,12 +10,12 @@ class PythonExecutor(ProcessExecutor): def __init__(self, source_name: str | None = None) -> None: super().__init__(PythonSearcher().find(source_name)) # Set UTF-8 encoding for Python I/O on Windows - if os.name == 'nt': - os.environ['PYTHONIOENCODING'] = 'utf8' + if os.name == "nt": + os.environ["PYTHONIOENCODING"] = "utf8" def _execution_command(self, *args: str): cmd = ["python"] - if os.name == 'nt': # Works on all Windows versions (32/64 bit) + if os.name == "nt": # Works on all Windows versions (32/64 bit) # Set UTF-8 encoding for stdin/stdout on Windows cmd.extend(["-X", "utf8"]) cmd.extend(["-u", self.runnable.file, *list(args)]) From df3c96d2cb6f4c122ec01879ae92eedf7e1ed987 Mon Sep 17 00:00:00 2001 From: polischuks Date: Sat, 15 Feb 2025 14:47:43 +0200 Subject: [PATCH 35/57] Refactor python_executor to use is_windows utility for OS checks --- hstest/testing/execution/process/python_executor.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hstest/testing/execution/process/python_executor.py b/hstest/testing/execution/process/python_executor.py index a9834762..32d3721d 100644 --- a/hstest/testing/execution/process/python_executor.py +++ b/hstest/testing/execution/process/python_executor.py @@ -2,6 +2,7 @@ import os +from hstest.common.os_utils import is_windows from hstest.testing.execution.process_executor import ProcessExecutor from hstest.testing.execution.searcher.python_searcher import PythonSearcher @@ -10,12 +11,12 @@ class PythonExecutor(ProcessExecutor): def __init__(self, source_name: str | None = None) -> None: super().__init__(PythonSearcher().find(source_name)) # Set UTF-8 encoding for Python I/O on Windows - if os.name == "nt": + if is_windows(): os.environ["PYTHONIOENCODING"] = "utf8" def _execution_command(self, *args: str): cmd = ["python"] - if os.name == "nt": # Works on all Windows versions (32/64 bit) + if is_windows(): # Works on all Windows versions (32/64 bit) # Set UTF-8 encoding for stdin/stdout on Windows cmd.extend(["-X", "utf8"]) cmd.extend(["-u", self.runnable.file, *list(args)]) From 72f89a9ce840be245f7e4f56027342dfc5c704d1 Mon Sep 17 00:00:00 2001 From: polishchuks Date: Sat, 15 Feb 2025 14:54:17 +0200 Subject: [PATCH 36/57] Enhance error handling and Windows compatibility in failure handler and process wrapper --- hstest/testing/process_wrapper.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hstest/testing/process_wrapper.py b/hstest/testing/process_wrapper.py index 46745003..5e28e0ba 100644 --- a/hstest/testing/process_wrapper.py +++ b/hstest/testing/process_wrapper.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os import subprocess import sys from threading import Lock, Thread @@ -67,6 +68,14 @@ def start(self): args = ["cmd", "/c", *args] + # Set environment variables for proper encoding on Windows + env = os.environ.copy() + if is_windows(): + env.update({ + 'PYTHONIOENCODING': 'utf-8', + 'PYTHONLEGACYWINDOWSSTDIO': '0' # Disable legacy stdio behavior on Windows + }) + self.process = subprocess.Popen( args, bufsize=0, @@ -75,6 +84,8 @@ def start(self): stderr=subprocess.PIPE, stdin=subprocess.PIPE, encoding="utf-8" if not self._use_byte_stream else None, + errors='replace', # Handle encoding errors gracefully + env=env ) except Exception as e: from hstest import StageTest From fca0849327f3c2a41a8ff2a3248a936abc5749c2 Mon Sep 17 00:00:00 2001 From: polishchuks Date: Sat, 15 Feb 2025 14:54:30 +0200 Subject: [PATCH 37/57] Enhance traceback formatting in failure handler and update CI workflow for Windows compatibility --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 941a8bc9..9aa74f8d 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,11 +67,11 @@ jobs: # Windows - os: windows-latest - python-version: "3.10.11" - - os: windows-latest - python-version: "3.11" - - os: windows-latest - python-version: "3.12" + python-version: "3.10" +# - os: windows-latest +# python-version: "3.11" +# - os: windows-latest +# python-version: "3.12" # # macOS (arm64) # - os: macos-14 From 57417b429995104de21423877745afad86070052 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 12:55:49 +0000 Subject: [PATCH 38/57] Backend: Auto format --- hstest/testing/process_wrapper.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/hstest/testing/process_wrapper.py b/hstest/testing/process_wrapper.py index 5e28e0ba..f1f8b397 100644 --- a/hstest/testing/process_wrapper.py +++ b/hstest/testing/process_wrapper.py @@ -71,10 +71,12 @@ def start(self): # Set environment variables for proper encoding on Windows env = os.environ.copy() if is_windows(): - env.update({ - 'PYTHONIOENCODING': 'utf-8', - 'PYTHONLEGACYWINDOWSSTDIO': '0' # Disable legacy stdio behavior on Windows - }) + env.update( + { + "PYTHONIOENCODING": "utf-8", + "PYTHONLEGACYWINDOWSSTDIO": "0", # Disable legacy stdio behavior on Windows + } + ) self.process = subprocess.Popen( args, @@ -84,8 +86,8 @@ def start(self): stderr=subprocess.PIPE, stdin=subprocess.PIPE, encoding="utf-8" if not self._use_byte_stream else None, - errors='replace', # Handle encoding errors gracefully - env=env + errors="replace", # Handle encoding errors gracefully + env=env, ) except Exception as e: from hstest import StageTest From c4e070c37a405d490121870f835abbc581184d92 Mon Sep 17 00:00:00 2001 From: polishchuks Date: Sat, 15 Feb 2025 15:06:27 +0200 Subject: [PATCH 39/57] Enhance input handling in process wrapper to ensure newline termination and improve error handling --- hstest/testing/process_wrapper.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/hstest/testing/process_wrapper.py b/hstest/testing/process_wrapper.py index f1f8b397..4ff0b799 100644 --- a/hstest/testing/process_wrapper.py +++ b/hstest/testing/process_wrapper.py @@ -275,9 +275,21 @@ def is_finished(self, need_wait_output=True) -> bool: return not self._alive def provide_input(self, stdin: str) -> None: - if self._use_byte_stream: - stdin = stdin.encode() - self.process.stdin.write(stdin) + if not stdin.endswith('\n'): + stdin += '\n' + try: + if self._use_byte_stream: + stdin = stdin.encode('utf-8') + self.process.stdin.write(stdin) + self.process.stdin.flush() + else: + self.process.stdin.write(stdin) + self.process.stdin.flush() + except IOError as e: + # Handle pipe errors gracefully + if not self._alive: + return + raise e def terminate(self) -> None: OutputHandler.print("Terminate called") From bbaa816b5430d24b6173e0475ff16585415d4ae0 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 13:07:43 +0000 Subject: [PATCH 40/57] Backend: Auto format --- hstest/testing/process_wrapper.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hstest/testing/process_wrapper.py b/hstest/testing/process_wrapper.py index 4ff0b799..c4988181 100644 --- a/hstest/testing/process_wrapper.py +++ b/hstest/testing/process_wrapper.py @@ -275,21 +275,21 @@ def is_finished(self, need_wait_output=True) -> bool: return not self._alive def provide_input(self, stdin: str) -> None: - if not stdin.endswith('\n'): - stdin += '\n' + if not stdin.endswith("\n"): + stdin += "\n" try: if self._use_byte_stream: - stdin = stdin.encode('utf-8') + stdin = stdin.encode("utf-8") self.process.stdin.write(stdin) self.process.stdin.flush() else: self.process.stdin.write(stdin) self.process.stdin.flush() - except IOError as e: + except OSError: # Handle pipe errors gracefully if not self._alive: return - raise e + raise def terminate(self) -> None: OutputHandler.print("Terminate called") From 6bc29d9d34f352ce732c3200de9a1543a0f00f87 Mon Sep 17 00:00:00 2001 From: polishchuks Date: Sat, 15 Feb 2025 15:14:26 +0200 Subject: [PATCH 41/57] Enhance CI workflow to include Python 3.11 and 3.12 for Windows compatibility --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9aa74f8d..4475bf50 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,10 +68,10 @@ jobs: # Windows - os: windows-latest python-version: "3.10" -# - os: windows-latest -# python-version: "3.11" -# - os: windows-latest -# python-version: "3.12" + - os: windows-latest + python-version: "3.11" + - os: windows-latest + python-version: "3.12" # # macOS (arm64) # - os: macos-14 From c6b600578880fb39314344ca153df70e816b3d48 Mon Sep 17 00:00:00 2001 From: polishchuks Date: Sat, 15 Feb 2025 15:20:56 +0200 Subject: [PATCH 42/57] Enhance CI workflow to configure environment variables for Windows compatibility --- .github/workflows/ci.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4475bf50..5a3c8131 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,6 +85,18 @@ jobs: - uses: ./.github/workflows/actions/prepare with: python-version: ${{ matrix.python-version }} + # Set environment variables for Windows + - name: Configure Windows environment + if: runner.os == 'Windows' + run: | + echo "PYTHONIOENCODING=utf-8" >> $GITHUB_ENV + echo "PYTHONUTF8=1" >> $GITHUB_ENV + echo "PYTHONLEGACYWINDOWSSTDIO=0" >> $GITHUB_ENV + shell: bash - name: Run Tests run: poetry run python tests/testing.py shell: bash + env: + PYTHONIOENCODING: utf-8 + PYTHONUTF8: 1 + PYTHONLEGACYWINDOWSSTDIO: 0 From 0a721dfc4dbf5ccc412b974ecdde2865c236fa74 Mon Sep 17 00:00:00 2001 From: polishchuks Date: Sat, 15 Feb 2025 15:25:25 +0200 Subject: [PATCH 43/57] Enhance CI workflow to uncomment Python versions for Ubuntu and macOS --- .github/workflows/ci.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a3c8131..736e1781 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,13 +57,13 @@ jobs: fail-fast: false matrix: include: -# # Ubuntu -# - os: ubuntu-latest -# python-version: "3.10" -# - os: ubuntu-latest -# python-version: "3.11" -# - os: ubuntu-latest -# python-version: "3.12" + # Ubuntu + - os: ubuntu-latest + python-version: "3.10" + - os: ubuntu-latest + python-version: "3.11" + - os: ubuntu-latest + python-version: "3.12" # Windows - os: windows-latest @@ -73,13 +73,13 @@ jobs: - os: windows-latest python-version: "3.12" -# # macOS (arm64) -# - os: macos-14 -# python-version: "3.10" -# - os: macos-14 -# python-version: "3.11" -# - os: macos-14 -# python-version: "3.12" + # macOS (arm64) + - os: macos-14 + python-version: "3.10" + - os: macos-14 + python-version: "3.11" + - os: macos-14 + python-version: "3.12" steps: - uses: actions/checkout@v4 - uses: ./.github/workflows/actions/prepare From 7f1dd9d1ae22243f2a94656a8f3b6fffe556b260 Mon Sep 17 00:00:00 2001 From: polischuks Date: Sat, 15 Feb 2025 17:45:29 +0200 Subject: [PATCH 44/57] Add GitHub Actions workflow for automatic code formatting with ruff --- .github/workflows/auto-format.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/auto-format.yml b/.github/workflows/auto-format.yml index 6152b9c6..5e492ce7 100644 --- a/.github/workflows/auto-format.yml +++ b/.github/workflows/auto-format.yml @@ -3,8 +3,8 @@ on: pull_request: push: branches: - - master - - release + - 'master' + - 'release' concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} @@ -23,7 +23,7 @@ jobs: - uses: ./.github/workflows/actions/prepare - - run: poetry run ruff check --fix --unsafe-fixes --preview --exit-zero . + - run: poetry run ruff --fix --unsafe-fixes --preview --exit-zero . - run: poetry run ruff format . - name: Commit changes From ab8fb5006efce1831f99d80921afc737724f2bc3 Mon Sep 17 00:00:00 2001 From: polischuks Date: Sat, 15 Feb 2025 18:06:52 +0200 Subject: [PATCH 45/57] Enhance CI workflow to update wheel URLs and manage releases automatically --- .github/workflows/build-wheels.yml | 58 ++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 9fd9b9d1..87f4c0b0 100755 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -74,6 +74,11 @@ jobs: permissions: contents: write steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: release + - name: Download all artifacts uses: actions/download-artifact@v4 with: @@ -81,7 +86,56 @@ jobs: path: dist merge-multiple: true + - name: Get tag version + id: get_version + run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + shell: bash + + - name: Update wheel URLs in pyproject.toml + run: | + # Get the version without the 'v' prefix + VERSION="${{ steps.get_version.outputs.VERSION }}" + + # Update URLs in pyproject.toml + sed -i "s|/releases/download/v[0-9]\+\.[0-9]\+\.[0-9]\+/|/releases/download/${VERSION}/|g" pyproject.toml + + # Update poetry.lock + poetry lock --no-update + + # Commit changes + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git add pyproject.toml poetry.lock + git commit -m "Update wheel URLs to ${VERSION}" + + # Force update the release branch + git push origin release --force + + # Create/update tag to point to the new commit + git tag -f "${VERSION}" + git push origin "${VERSION}" --force + shell: bash + - name: Create Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: - files: dist/* + files: | + dist/* + pyproject.toml + body: | + Release ${{ steps.get_version.outputs.VERSION }} + + This release includes: + - Updated wheel builds for Python 3.10, 3.11, and 3.12 + - Updated pyproject.toml with correct wheel URLs + draft: false + prerelease: false + target_commitish: release + + - name: Merge release into master + run: | + git checkout master + git pull origin master + git merge release --no-ff -m "Merge release ${VERSION} into master" + git push origin master + shell: bash From 057b3ffa3b9693f2c4850d63ce7c8c9f91322a67 Mon Sep 17 00:00:00 2001 From: polischuks Date: Sat, 15 Feb 2025 18:21:09 +0200 Subject: [PATCH 46/57] Refactor CI workflows to improve job names and streamline formatting steps --- .github/workflows/auto-author-assign.yml | 2 +- .github/workflows/auto-format.yml | 35 ------------------------ .github/workflows/ci.yml | 21 ++++++++++---- 3 files changed, 16 insertions(+), 42 deletions(-) delete mode 100644 .github/workflows/auto-format.yml diff --git a/.github/workflows/auto-author-assign.yml b/.github/workflows/auto-author-assign.yml index 62d531bb..4694f847 100644 --- a/.github/workflows/auto-author-assign.yml +++ b/.github/workflows/auto-author-assign.yml @@ -9,7 +9,7 @@ permissions: jobs: assign-author: - runs-on: [self-hosted, small] + runs-on: arc-runners-small timeout-minutes: 30 if: ${{ !github.event.pull_request.assignee }} steps: diff --git a/.github/workflows/auto-format.yml b/.github/workflows/auto-format.yml deleted file mode 100644 index 5e492ce7..00000000 --- a/.github/workflows/auto-format.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Format code -on: - pull_request: - push: - branches: - - 'master' - - 'release' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref_name }} - cancel-in-progress: true - -jobs: - format_backend: - name: Format with ruff - runs-on: [ self-hosted, small ] - timeout-minutes: 30 - steps: - - uses: actions/checkout@v4 - with: - repository: ${{ github.event.pull_request.head.repo.full_name }} - ref: ${{ github.event.pull_request.head.ref }} - - - uses: ./.github/workflows/actions/prepare - - - run: poetry run ruff --fix --unsafe-fixes --preview --exit-zero . - - run: poetry run ruff format . - - - name: Commit changes - uses: EndBug/add-and-commit@v9 - with: - fetch: false - default_author: github_actions - message: 'Backend: Auto format' - add: '.' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 736e1781..c407ae15 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ concurrency: jobs: lint: - name: Lint with ruff + name: Format and Lint runs-on: arc-runners-small timeout-minutes: 30 steps: @@ -21,14 +21,16 @@ jobs: with: repository: ${{ github.event.pull_request.head.repo.full_name }} ref: ${{ github.event.pull_request.head.ref }} + - uses: ./.github/workflows/actions/prepare with: python-version: "3.11" - - name: Check files using the ruff formatter - run: | - poetry run ruff check --fix --unsafe-fixes --preview --exit-zero . - poetry run ruff format . - shell: bash + + # Format code + - run: poetry run ruff --fix --unsafe-fixes --preview --exit-zero . + - run: poetry run ruff format . + + # Commit formatting changes - name: Commit changes uses: EndBug/add-and-commit@v9 with: @@ -37,8 +39,14 @@ jobs: message: 'Backend: Auto format' add: '.' + # Check code style + - name: Check files using ruff + run: poetry run ruff check --no-fix . + shell: bash + mypy: name: Static Type Checking + needs: lint runs-on: arc-runners-small timeout-minutes: 30 steps: @@ -52,6 +60,7 @@ jobs: test: name: Run unit test on ${{ matrix.os }} with Python ${{ matrix.python-version }} + needs: lint runs-on: ${{ matrix.os }} strategy: fail-fast: false From 782aab9e118444f8f728b520dbda343bc9ad7bc8 Mon Sep 17 00:00:00 2001 From: polischuks Date: Sat, 15 Feb 2025 18:26:47 +0200 Subject: [PATCH 47/57] Update CI workflow to rename lint job and streamline ruff formatting steps --- .github/workflows/ci.yml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c407ae15..493b529c 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ concurrency: jobs: lint: - name: Format and Lint + name: Lint with ruff runs-on: arc-runners-small timeout-minutes: 30 steps: @@ -25,12 +25,11 @@ jobs: - uses: ./.github/workflows/actions/prepare with: python-version: "3.11" - - # Format code - - run: poetry run ruff --fix --unsafe-fixes --preview --exit-zero . - - run: poetry run ruff format . - - # Commit formatting changes + - name: Check files using the ruff formatter + run: | + poetry run ruff check --fix --unsafe-fixes --preview --exit-zero . + poetry run ruff format . + shell: bash - name: Commit changes uses: EndBug/add-and-commit@v9 with: @@ -39,11 +38,6 @@ jobs: message: 'Backend: Auto format' add: '.' - # Check code style - - name: Check files using ruff - run: poetry run ruff check --no-fix . - shell: bash - mypy: name: Static Type Checking needs: lint From 3f9a9b2535a6c2dc17428da21518c5e5fc5eea8f Mon Sep 17 00:00:00 2001 From: polischuks Date: Mon, 24 Feb 2025 14:01:51 +0200 Subject: [PATCH 48/57] fix: --- poetry.lock | 203 +++++++++++++++++++++---------------------------- pyproject.toml | 20 ++--- 2 files changed, 94 insertions(+), 129 deletions(-) diff --git a/poetry.lock b/poetry.lock index 41f9202b..44134160 100755 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,18 @@ # This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "sys_platform == \"win32\" and (python_version <= \"3.11\" or python_version >= \"3.12\")" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + [[package]] name = "contourpy" version = "1.3.1" @@ -92,6 +105,22 @@ files = [ docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] tests = ["pytest", "pytest-cov", "pytest-xdist"] +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version < \"3.11\"" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "fonttools" version = "4.56.0" @@ -167,6 +196,19 @@ ufo = ["fs (>=2.2.0,<3)"] unicode = ["unicodedata2 (>=15.1.0)"] woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + [[package]] name = "kiwisolver" version = "1.4.8" @@ -323,7 +365,7 @@ version = "1.10.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["dev"] markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"}, @@ -372,7 +414,7 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" -groups = ["main"] +groups = ["dev"] markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, @@ -451,7 +493,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "dev"] markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, @@ -624,118 +666,21 @@ typing = ["typing-extensions"] xmp = ["defusedxml"] [[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "sys_platform == \"linux\" and python_version < \"3.11\"" -files = [ - {file = "psutil-5.8.0-cp310-cp310-linux_x86_64.whl", hash = "sha256:4dd2052113d323b3fe9fd93e9915ab364bdb9ea3bd4995d7a0bf62c492713167"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-linux_x86_64.whl" - -[[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "sys_platform == \"darwin\" and python_version < \"3.11\"" -files = [ - {file = "psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b611197383b14311c87919eaa8fbeebf15eab5fdeb22d4c7b67f03772a79716"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl" - -[[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "sys_platform == \"win32\" and python_version < \"3.11\"" -files = [ - {file = "psutil-5.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:07fd5212435883f325b925480b326bb5a2537e085e5552acd9877b91da4566ec"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-win_amd64.whl" - -[[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "sys_platform == \"linux\" and python_version == \"3.11\"" -files = [ - {file = "psutil-5.8.0-cp311-cp311-linux_x86_64.whl", hash = "sha256:b3e5c23e33e96c582e5e3a99e2d2e432621784880e229c4a4e81655932ebd25f"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-linux_x86_64.whl" - -[[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "sys_platform == \"darwin\" and python_version == \"3.11\"" -files = [ - {file = "psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f126690bbb74f08cccaaf5defe9d9d4e0b629855e8eb24e07ba6f6087337edf8"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl" - -[[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "sys_platform == \"win32\" and python_version == \"3.11\"" +python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "psutil-5.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:b175a13518b8b3c6671f0e3e626706e2eff936a2519c38ca0a5f73138736099a"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-win_amd64.whl" +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] [[package]] name = "psutil" @@ -744,7 +689,7 @@ description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" groups = ["main"] -markers = "sys_platform == \"linux\" and python_version >= \"3.12\" and python_version < \"3.13\"" +markers = "sys_platform == \"linux\" and python_version <= \"3.11\" or sys_platform == \"linux\" and python_version >= \"3.12\"" files = [ {file = "psutil-5.8.0-cp312-cp312-linux_x86_64.whl", hash = "sha256:adf09ab4d9caaf3ac048a2d9486e14eba84e6b231df24b46ce7d495372672f05"}, ] @@ -763,7 +708,7 @@ description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" groups = ["main"] -markers = "sys_platform == \"darwin\" and python_version >= \"3.12\" and python_version < \"3.13\"" +markers = "sys_platform == \"darwin\" and python_version <= \"3.11\" or sys_platform == \"darwin\" and python_version >= \"3.12\"" files = [ {file = "psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f7d30e6a6ca11ea83f50039b9dfefc9a1ef2c66028cc0db9071ac1edd9d9bbb9"}, ] @@ -782,7 +727,7 @@ description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" groups = ["main"] -markers = "sys_platform == \"win32\" and python_version >= \"3.12\" and python_version < \"3.13\"" +markers = "sys_platform == \"win32\" and python_version <= \"3.11\" or sys_platform == \"win32\" and python_version >= \"3.12\"" files = [ {file = "psutil-5.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:2b478fe4266e25e7b0caea5eeec6df836b2a37308cb24efbce30f915b1f03cfe"}, ] @@ -810,6 +755,30 @@ files = [ [package.extras] diagrams = ["jinja2", "railroad-diagrams"] +[[package]] +name = "pytest" +version = "8.3.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" +files = [ + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -845,7 +814,7 @@ version = "0.6.0" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["dev"] markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "ruff-0.6.0-py3-none-linux_armv6l.whl", hash = "sha256:92dcce923e5df265781e5fc76f9a1edad52201a7aafe56e586b90988d5239013"}, @@ -969,7 +938,7 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["dev"] markers = "python_version < \"3.11\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, @@ -1012,7 +981,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["dev"] markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, @@ -1035,4 +1004,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "7e3c8eb5addedd6ebfe1b203e5ed4eed02b3edcaf6205533426065894382a014" +content-hash = "daa83e8ce35032cbf7b4a92791638db5d7aef9ad7f16b40610f801009bc6c2f0" diff --git a/pyproject.toml b/pyproject.toml index 77ea996d..6596d816 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,24 +13,21 @@ python = "^3.10" # psutil with platform and Python version markers psutil = [ - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-win_amd64.whl", markers = "sys_platform == 'win32' and python_version >= '3.10' and python_version < '3.11'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-win_amd64.whl", markers = "sys_platform == 'win32' and python_version >= '3.11' and python_version < '3.12'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-win_amd64.whl", markers = "sys_platform == 'win32' and python_version >= '3.12' and python_version < '3.13'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-linux_x86_64.whl", markers = "sys_platform == 'linux' and python_version >= '3.10' and python_version < '3.11'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-linux_x86_64.whl", markers = "sys_platform == 'linux' and python_version >= '3.11' and python_version < '3.12'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-linux_x86_64.whl", markers = "sys_platform == 'linux' and python_version >= '3.12' and python_version < '3.13'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl", markers = "sys_platform == 'darwin' and python_version >= '3.10' and python_version < '3.11'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl", markers = "sys_platform == 'darwin' and python_version >= '3.11' and python_version < '3.12'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl", markers = "sys_platform == 'darwin' and python_version >= '3.12' and python_version < '3.13'" } + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-win_amd64.whl", markers = "sys_platform == 'win32'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-linux_x86_64.whl", markers = "sys_platform == 'linux'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl", markers = "sys_platform == 'darwin'" } ] -mypy = "1.10.1" pandas = "2.2.2" -ruff = "0.6.0" seaborn = "0.13.2" scipy = "^1.12.0" matplotlib = "^3.9.2" +[tool.poetry.group.dev.dependencies] +mypy = "1.10.1" +ruff = "0.6.0" +pytest = "8.3.4" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" @@ -165,5 +162,4 @@ exclude = [ test-command = "pytest {project}/tests" test-extras = ["test"] test-skip = ["*universal2:arm64"] -# Временно пропускаем сборку для PyPy skip = ["pp*"] From 02db04be7f8d40b6bbfd4a1e73307876f93397eb Mon Sep 17 00:00:00 2001 From: polischuks Date: Mon, 24 Feb 2025 14:28:14 +0200 Subject: [PATCH 49/57] fix: --- poetry.lock | 122 +++++++++++++++++++++++++++++++++++++++++++++++-- pyproject.toml | 12 +++-- 2 files changed, 127 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index 44134160..0f6817b6 100755 --- a/poetry.lock +++ b/poetry.lock @@ -689,7 +689,121 @@ description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" groups = ["main"] -markers = "sys_platform == \"linux\" and python_version <= \"3.11\" or sys_platform == \"linux\" and python_version >= \"3.12\"" +markers = "sys_platform == \"linux\" and python_version < \"3.11\"" +files = [ + {file = "psutil-5.8.0-cp310-cp310-linux_x86_64.whl", hash = "sha256:4dd2052113d323b3fe9fd93e9915ab364bdb9ea3bd4995d7a0bf62c492713167"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-linux_x86_64.whl" + +[[package]] +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"darwin\" and python_version < \"3.11\"" +files = [ + {file = "psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b611197383b14311c87919eaa8fbeebf15eab5fdeb22d4c7b67f03772a79716"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl" + +[[package]] +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"win32\" and python_version < \"3.11\"" +files = [ + {file = "psutil-5.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:07fd5212435883f325b925480b326bb5a2537e085e5552acd9877b91da4566ec"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-win_amd64.whl" + +[[package]] +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"linux\" and python_version == \"3.11\"" +files = [ + {file = "psutil-5.8.0-cp311-cp311-linux_x86_64.whl", hash = "sha256:b3e5c23e33e96c582e5e3a99e2d2e432621784880e229c4a4e81655932ebd25f"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-linux_x86_64.whl" + +[[package]] +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"darwin\" and python_version == \"3.11\"" +files = [ + {file = "psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f126690bbb74f08cccaaf5defe9d9d4e0b629855e8eb24e07ba6f6087337edf8"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl" + +[[package]] +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"win32\" and python_version == \"3.11\"" +files = [ + {file = "psutil-5.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:b175a13518b8b3c6671f0e3e626706e2eff936a2519c38ca0a5f73138736099a"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] + +[package.source] +type = "url" +url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-win_amd64.whl" + +[[package]] +name = "psutil" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +markers = "sys_platform == \"linux\" and python_version >= \"3.12\" and python_version < \"3.13\"" files = [ {file = "psutil-5.8.0-cp312-cp312-linux_x86_64.whl", hash = "sha256:adf09ab4d9caaf3ac048a2d9486e14eba84e6b231df24b46ce7d495372672f05"}, ] @@ -708,7 +822,7 @@ description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" groups = ["main"] -markers = "sys_platform == \"darwin\" and python_version <= \"3.11\" or sys_platform == \"darwin\" and python_version >= \"3.12\"" +markers = "sys_platform == \"darwin\" and python_version >= \"3.12\" and python_version < \"3.13\"" files = [ {file = "psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f7d30e6a6ca11ea83f50039b9dfefc9a1ef2c66028cc0db9071ac1edd9d9bbb9"}, ] @@ -727,7 +841,7 @@ description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" groups = ["main"] -markers = "sys_platform == \"win32\" and python_version <= \"3.11\" or sys_platform == \"win32\" and python_version >= \"3.12\"" +markers = "sys_platform == \"win32\" and python_version >= \"3.12\" and python_version < \"3.13\"" files = [ {file = "psutil-5.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:2b478fe4266e25e7b0caea5eeec6df836b2a37308cb24efbce30f915b1f03cfe"}, ] @@ -1004,4 +1118,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "daa83e8ce35032cbf7b4a92791638db5d7aef9ad7f16b40610f801009bc6c2f0" +content-hash = "104c9363ce679a986c71e1cb3e686696eddd22abcbae318e251ff44e4121bf29" diff --git a/pyproject.toml b/pyproject.toml index 6596d816..8cceb948 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,9 +13,15 @@ python = "^3.10" # psutil with platform and Python version markers psutil = [ - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-win_amd64.whl", markers = "sys_platform == 'win32'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-linux_x86_64.whl", markers = "sys_platform == 'linux'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl", markers = "sys_platform == 'darwin'" } + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-win_amd64.whl", markers = "sys_platform == 'win32' and python_version >= '3.10' and python_version < '3.11'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-win_amd64.whl", markers = "sys_platform == 'win32' and python_version >= '3.11' and python_version < '3.12'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-win_amd64.whl", markers = "sys_platform == 'win32' and python_version >= '3.12' and python_version < '3.13'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-linux_x86_64.whl", markers = "sys_platform == 'linux' and python_version >= '3.10' and python_version < '3.11'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-linux_x86_64.whl", markers = "sys_platform == 'linux' and python_version >= '3.11' and python_version < '3.12'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-linux_x86_64.whl", markers = "sys_platform == 'linux' and python_version >= '3.12' and python_version < '3.13'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl", markers = "sys_platform == 'darwin' and python_version >= '3.10' and python_version < '3.11'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl", markers = "sys_platform == 'darwin' and python_version >= '3.11' and python_version < '3.12'" }, + { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl", markers = "sys_platform == 'darwin' and python_version >= '3.12' and python_version < '3.13'" } ] pandas = "2.2.2" From e43d4ba7a7747e96f1bcaf53a9421b522377b860 Mon Sep 17 00:00:00 2001 From: polischuks Date: Wed, 14 May 2025 14:43:48 +0300 Subject: [PATCH 50/57] Update dependencies: bump psutil to 7.0.0, mypy to 1.13.0, pandas to 2.2.3, scipy to 1.15.3, and ruff to 0.7.3 --- poetry.lock | 447 ++++++++++++++++--------------------------- pyproject.toml | 23 +-- requirements-dev.txt | 18 -- requirements.txt | 18 +- setup.py | 26 --- 5 files changed, 174 insertions(+), 358 deletions(-) delete mode 100644 requirements-dev.txt delete mode 100644 setup.py diff --git a/poetry.lock b/poetry.lock index 0f6817b6..699e3883 100755 --- a/poetry.lock +++ b/poetry.lock @@ -7,7 +7,7 @@ description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["dev"] -markers = "sys_platform == \"win32\" and (python_version <= \"3.11\" or python_version >= \"3.12\")" +markers = "python_version <= \"3.11\" and sys_platform == \"win32\" or python_version >= \"3.12\" and sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -361,49 +361,55 @@ dev = ["meson-python (>=0.13.1,<0.17.0)", "pybind11 (>=2.13.2,!=2.13.3)", "setup [[package]] name = "mypy" -version = "1.10.1" +version = "1.13.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" groups = ["dev"] markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"}, - {file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"}, - {file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"}, - {file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"}, - {file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"}, - {file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"}, - {file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"}, - {file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"}, - {file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"}, - {file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"}, - {file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"}, - {file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"}, - {file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"}, - {file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"}, - {file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"}, - {file = "mypy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:604282c886497645ffb87b8f35a57ec773a4a2721161e709a4422c1636ddde5c"}, - {file = "mypy-1.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37fd87cab83f09842653f08de066ee68f1182b9b5282e4634cdb4b407266bade"}, - {file = "mypy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8addf6313777dbb92e9564c5d32ec122bf2c6c39d683ea64de6a1fd98b90fe37"}, - {file = "mypy-1.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cc3ca0a244eb9a5249c7c583ad9a7e881aa5d7b73c35652296ddcdb33b2b9c7"}, - {file = "mypy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:1b3a2ffce52cc4dbaeee4df762f20a2905aa171ef157b82192f2e2f368eec05d"}, - {file = "mypy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe85ed6836165d52ae8b88f99527d3d1b2362e0cb90b005409b8bed90e9059b3"}, - {file = "mypy-1.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2ae450d60d7d020d67ab440c6e3fae375809988119817214440033f26ddf7bf"}, - {file = "mypy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be84c06e6abd72f960ba9a71561c14137a583093ffcf9bbfaf5e613d63fa531"}, - {file = "mypy-1.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2189ff1e39db399f08205e22a797383613ce1cb0cb3b13d8bcf0170e45b96cc3"}, - {file = "mypy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:97a131ee36ac37ce9581f4220311247ab6cba896b4395b9c87af0675a13a755f"}, - {file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"}, - {file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, + {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, + {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, + {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" +typing-extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] install-types = ["pip"] mypyc = ["setuptools (>=50)"] reports = ["lxml"] @@ -502,42 +508,55 @@ files = [ [[package]] name = "pandas" -version = "2.2.2" +version = "2.2.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" groups = ["main"] markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, - {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, - {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, - {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, - {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, - {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, - {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, - {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, - {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, - {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, - {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, - {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, - {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, - {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, - {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, - {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, - {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, + {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, + {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, + {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, + {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, + {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, + {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, ] [package.dependencies] @@ -684,174 +703,28 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "sys_platform == \"linux\" and python_version < \"3.11\"" -files = [ - {file = "psutil-5.8.0-cp310-cp310-linux_x86_64.whl", hash = "sha256:4dd2052113d323b3fe9fd93e9915ab364bdb9ea3bd4995d7a0bf62c492713167"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-linux_x86_64.whl" - -[[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "sys_platform == \"darwin\" and python_version < \"3.11\"" -files = [ - {file = "psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b611197383b14311c87919eaa8fbeebf15eab5fdeb22d4c7b67f03772a79716"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl" - -[[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." +version = "7.0.0" +description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7." optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" groups = ["main"] -markers = "sys_platform == \"win32\" and python_version < \"3.11\"" -files = [ - {file = "psutil-5.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:07fd5212435883f325b925480b326bb5a2537e085e5552acd9877b91da4566ec"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-win_amd64.whl" - -[[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "sys_platform == \"linux\" and python_version == \"3.11\"" -files = [ - {file = "psutil-5.8.0-cp311-cp311-linux_x86_64.whl", hash = "sha256:b3e5c23e33e96c582e5e3a99e2d2e432621784880e229c4a4e81655932ebd25f"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-linux_x86_64.whl" - -[[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "sys_platform == \"darwin\" and python_version == \"3.11\"" -files = [ - {file = "psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f126690bbb74f08cccaaf5defe9d9d4e0b629855e8eb24e07ba6f6087337edf8"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl" - -[[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "sys_platform == \"win32\" and python_version == \"3.11\"" -files = [ - {file = "psutil-5.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:b175a13518b8b3c6671f0e3e626706e2eff936a2519c38ca0a5f73138736099a"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-win_amd64.whl" - -[[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "sys_platform == \"linux\" and python_version >= \"3.12\" and python_version < \"3.13\"" -files = [ - {file = "psutil-5.8.0-cp312-cp312-linux_x86_64.whl", hash = "sha256:adf09ab4d9caaf3ac048a2d9486e14eba84e6b231df24b46ce7d495372672f05"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-linux_x86_64.whl" - -[[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "sys_platform == \"darwin\" and python_version >= \"3.12\" and python_version < \"3.13\"" -files = [ - {file = "psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f7d30e6a6ca11ea83f50039b9dfefc9a1ef2c66028cc0db9071ac1edd9d9bbb9"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl" - -[[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["main"] -markers = "sys_platform == \"win32\" and python_version >= \"3.12\" and python_version < \"3.13\"" +markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "psutil-5.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:2b478fe4266e25e7b0caea5eeec6df836b2a37308cb24efbce30f915b1f03cfe"}, + {file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"}, + {file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993"}, + {file = "psutil-7.0.0-cp36-cp36m-win32.whl", hash = "sha256:84df4eb63e16849689f76b1ffcb36db7b8de703d1bc1fe41773db487621b6c17"}, + {file = "psutil-7.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1e744154a6580bc968a0195fd25e80432d3afec619daf145b9e5ba16cc1d688e"}, + {file = "psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99"}, + {file = "psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553"}, + {file = "psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456"}, ] [package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] - -[package.source] -type = "url" -url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-win_amd64.whl" +dev = ["abi3audit", "black (==24.10.0)", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest", "pytest-cov", "pytest-xdist", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"] +test = ["pytest", "pytest-xdist", "setuptools"] [[package]] name = "pyparsing" @@ -924,82 +797,88 @@ files = [ [[package]] name = "ruff" -version = "0.6.0" +version = "0.7.3" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["dev"] markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "ruff-0.6.0-py3-none-linux_armv6l.whl", hash = "sha256:92dcce923e5df265781e5fc76f9a1edad52201a7aafe56e586b90988d5239013"}, - {file = "ruff-0.6.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:31b90ff9dc79ed476c04e957ba7e2b95c3fceb76148f2079d0d68a908d2cfae7"}, - {file = "ruff-0.6.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d834a9ec9f8287dd6c3297058b3a265ed6b59233db22593379ee38ebc4b9768"}, - {file = "ruff-0.6.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2089267692696aba342179471831a085043f218706e642564812145df8b8d0d"}, - {file = "ruff-0.6.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aa62b423ee4bbd8765f2c1dbe8f6aac203e0583993a91453dc0a449d465c84da"}, - {file = "ruff-0.6.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7344e1a964b16b1137ea361d6516ce4ee61a0403fa94252a1913ecc1311adcae"}, - {file = "ruff-0.6.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:487f3a35c3f33bf82be212ce15dc6278ea854e35573a3f809442f73bec8b2760"}, - {file = "ruff-0.6.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75db409984077a793cf344d499165298a6f65449e905747ac65983b12e3e64b1"}, - {file = "ruff-0.6.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84908bd603533ecf1db456d8fc2665d1f4335d722e84bc871d3bbd2d1116c272"}, - {file = "ruff-0.6.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f1749a0aef3ec41ed91a0e2127a6ae97d2e2853af16dbd4f3c00d7a3af726c5"}, - {file = "ruff-0.6.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:016fea751e2bcfbbd2f8cb19b97b37b3fd33148e4df45b526e87096f4e17354f"}, - {file = "ruff-0.6.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6ae80f141b53b2e36e230017e64f5ea2def18fac14334ffceaae1b780d70c4f7"}, - {file = "ruff-0.6.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eaaaf33ea4b3f63fd264d6a6f4a73fa224bbfda4b438ffea59a5340f4afa2bb5"}, - {file = "ruff-0.6.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7667ddd1fc688150a7ca4137140867584c63309695a30016880caf20831503a0"}, - {file = "ruff-0.6.0-py3-none-win32.whl", hash = "sha256:ae48365aae60d40865a412356f8c6f2c0be1c928591168111eaf07eaefa6bea3"}, - {file = "ruff-0.6.0-py3-none-win_amd64.whl", hash = "sha256:774032b507c96f0c803c8237ce7d2ef3934df208a09c40fa809c2931f957fe5e"}, - {file = "ruff-0.6.0-py3-none-win_arm64.whl", hash = "sha256:a5366e8c3ae6b2dc32821749b532606c42e609a99b0ae1472cf601da931a048c"}, - {file = "ruff-0.6.0.tar.gz", hash = "sha256:272a81830f68f9bd19d49eaf7fa01a5545c5a2e86f32a9935bb0e4bb9a1db5b8"}, + {file = "ruff-0.7.3-py3-none-linux_armv6l.whl", hash = "sha256:34f2339dc22687ec7e7002792d1f50712bf84a13d5152e75712ac08be565d344"}, + {file = "ruff-0.7.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:fb397332a1879b9764a3455a0bb1087bda876c2db8aca3a3cbb67b3dbce8cda0"}, + {file = "ruff-0.7.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:37d0b619546103274e7f62643d14e1adcbccb242efda4e4bdb9544d7764782e9"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d59f0c3ee4d1a6787614e7135b72e21024875266101142a09a61439cb6e38a5"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:44eb93c2499a169d49fafd07bc62ac89b1bc800b197e50ff4633aed212569299"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d0242ce53f3a576c35ee32d907475a8d569944c0407f91d207c8af5be5dae4e"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6b6224af8b5e09772c2ecb8dc9f3f344c1aa48201c7f07e7315367f6dd90ac29"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c50f95a82b94421c964fae4c27c0242890a20fe67d203d127e84fbb8013855f5"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f3eff9961b5d2644bcf1616c606e93baa2d6b349e8aa8b035f654df252c8c67"}, + {file = "ruff-0.7.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8963cab06d130c4df2fd52c84e9f10d297826d2e8169ae0c798b6221be1d1d2"}, + {file = "ruff-0.7.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:61b46049d6edc0e4317fb14b33bd693245281a3007288b68a3f5b74a22a0746d"}, + {file = "ruff-0.7.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:10ebce7696afe4644e8c1a23b3cf8c0f2193a310c18387c06e583ae9ef284de2"}, + {file = "ruff-0.7.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3f36d56326b3aef8eeee150b700e519880d1aab92f471eefdef656fd57492aa2"}, + {file = "ruff-0.7.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5d024301109a0007b78d57ab0ba190087b43dce852e552734ebf0b0b85e4fb16"}, + {file = "ruff-0.7.3-py3-none-win32.whl", hash = "sha256:4ba81a5f0c5478aa61674c5a2194de8b02652f17addf8dfc40c8937e6e7d79fc"}, + {file = "ruff-0.7.3-py3-none-win_amd64.whl", hash = "sha256:588a9ff2fecf01025ed065fe28809cd5a53b43505f48b69a1ac7707b1b7e4088"}, + {file = "ruff-0.7.3-py3-none-win_arm64.whl", hash = "sha256:1713e2c5545863cdbfe2cbce21f69ffaf37b813bfd1fb3b90dc9a6f1963f5a8c"}, + {file = "ruff-0.7.3.tar.gz", hash = "sha256:e1d1ba2e40b6e71a61b063354d04be669ab0d39c352461f3d789cac68b54a313"}, ] [[package]] name = "scipy" -version = "1.15.1" +version = "1.15.3" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.10" groups = ["main"] markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ - {file = "scipy-1.15.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:c64ded12dcab08afff9e805a67ff4480f5e69993310e093434b10e85dc9d43e1"}, - {file = "scipy-1.15.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5b190b935e7db569960b48840e5bef71dc513314cc4e79a1b7d14664f57fd4ff"}, - {file = "scipy-1.15.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:4b17d4220df99bacb63065c76b0d1126d82bbf00167d1730019d2a30d6ae01ea"}, - {file = "scipy-1.15.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:63b9b6cd0333d0eb1a49de6f834e8aeaefe438df8f6372352084535ad095219e"}, - {file = "scipy-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f151e9fb60fbf8e52426132f473221a49362091ce7a5e72f8aa41f8e0da4f25"}, - {file = "scipy-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21e10b1dd56ce92fba3e786007322542361984f8463c6d37f6f25935a5a6ef52"}, - {file = "scipy-1.15.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5dff14e75cdbcf07cdaa1c7707db6017d130f0af9ac41f6ce443a93318d6c6e0"}, - {file = "scipy-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:f82fcf4e5b377f819542fbc8541f7b5fbcf1c0017d0df0bc22c781bf60abc4d8"}, - {file = "scipy-1.15.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:5bd8d27d44e2c13d0c1124e6a556454f52cd3f704742985f6b09e75e163d20d2"}, - {file = "scipy-1.15.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:be3deeb32844c27599347faa077b359584ba96664c5c79d71a354b80a0ad0ce0"}, - {file = "scipy-1.15.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:5eb0ca35d4b08e95da99a9f9c400dc9f6c21c424298a0ba876fdc69c7afacedf"}, - {file = "scipy-1.15.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:74bb864ff7640dea310a1377d8567dc2cb7599c26a79ca852fc184cc851954ac"}, - {file = "scipy-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:667f950bf8b7c3a23b4199db24cb9bf7512e27e86d0e3813f015b74ec2c6e3df"}, - {file = "scipy-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395be70220d1189756068b3173853029a013d8c8dd5fd3d1361d505b2aa58fa7"}, - {file = "scipy-1.15.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ce3a000cd28b4430426db2ca44d96636f701ed12e2b3ca1f2b1dd7abdd84b39a"}, - {file = "scipy-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:3fe1d95944f9cf6ba77aa28b82dd6bb2a5b52f2026beb39ecf05304b8392864b"}, - {file = "scipy-1.15.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c09aa9d90f3500ea4c9b393ee96f96b0ccb27f2f350d09a47f533293c78ea776"}, - {file = "scipy-1.15.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:0ac102ce99934b162914b1e4a6b94ca7da0f4058b6d6fd65b0cef330c0f3346f"}, - {file = "scipy-1.15.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:09c52320c42d7f5c7748b69e9f0389266fd4f82cf34c38485c14ee976cb8cb04"}, - {file = "scipy-1.15.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:cdde8414154054763b42b74fe8ce89d7f3d17a7ac5dd77204f0e142cdc9239e9"}, - {file = "scipy-1.15.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c9d8fc81d6a3b6844235e6fd175ee1d4c060163905a2becce8e74cb0d7554ce"}, - {file = "scipy-1.15.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fb57b30f0017d4afa5fe5f5b150b8f807618819287c21cbe51130de7ccdaed2"}, - {file = "scipy-1.15.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:491d57fe89927fa1aafbe260f4cfa5ffa20ab9f1435025045a5315006a91b8f5"}, - {file = "scipy-1.15.1-cp312-cp312-win_amd64.whl", hash = "sha256:900f3fa3db87257510f011c292a5779eb627043dd89731b9c461cd16ef76ab3d"}, - {file = "scipy-1.15.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:100193bb72fbff37dbd0bf14322314fc7cbe08b7ff3137f11a34d06dc0ee6b85"}, - {file = "scipy-1.15.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:2114a08daec64980e4b4cbdf5bee90935af66d750146b1d2feb0d3ac30613692"}, - {file = "scipy-1.15.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6b3e71893c6687fc5e29208d518900c24ea372a862854c9888368c0b267387ab"}, - {file = "scipy-1.15.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:837299eec3d19b7e042923448d17d95a86e43941104d33f00da7e31a0f715d3c"}, - {file = "scipy-1.15.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82add84e8a9fb12af5c2c1a3a3f1cb51849d27a580cb9e6bd66226195142be6e"}, - {file = "scipy-1.15.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:070d10654f0cb6abd295bc96c12656f948e623ec5f9a4eab0ddb1466c000716e"}, - {file = "scipy-1.15.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:55cc79ce4085c702ac31e49b1e69b27ef41111f22beafb9b49fea67142b696c4"}, - {file = "scipy-1.15.1-cp313-cp313-win_amd64.whl", hash = "sha256:c352c1b6d7cac452534517e022f8f7b8d139cd9f27e6fbd9f3cbd0bfd39f5bef"}, - {file = "scipy-1.15.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0458839c9f873062db69a03de9a9765ae2e694352c76a16be44f93ea45c28d2b"}, - {file = "scipy-1.15.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:af0b61c1de46d0565b4b39c6417373304c1d4f5220004058bdad3061c9fa8a95"}, - {file = "scipy-1.15.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:71ba9a76c2390eca6e359be81a3e879614af3a71dfdabb96d1d7ab33da6f2364"}, - {file = "scipy-1.15.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14eaa373c89eaf553be73c3affb11ec6c37493b7eaaf31cf9ac5dffae700c2e0"}, - {file = "scipy-1.15.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f735bc41bd1c792c96bc426dece66c8723283695f02df61dcc4d0a707a42fc54"}, - {file = "scipy-1.15.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2722a021a7929d21168830790202a75dbb20b468a8133c74a2c0230c72626b6c"}, - {file = "scipy-1.15.1-cp313-cp313t-win_amd64.whl", hash = "sha256:bc7136626261ac1ed988dca56cfc4ab5180f75e0ee52e58f1e6aa74b5f3eacd5"}, - {file = "scipy-1.15.1.tar.gz", hash = "sha256:033a75ddad1463970c96a88063a1df87ccfddd526437136b6ee81ff0312ebdf6"}, + {file = "scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c"}, + {file = "scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253"}, + {file = "scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f"}, + {file = "scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92"}, + {file = "scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82"}, + {file = "scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40"}, + {file = "scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e"}, + {file = "scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c"}, + {file = "scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13"}, + {file = "scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b"}, + {file = "scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba"}, + {file = "scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65"}, + {file = "scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1"}, + {file = "scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889"}, + {file = "scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982"}, + {file = "scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9"}, + {file = "scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594"}, + {file = "scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb"}, + {file = "scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019"}, + {file = "scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6"}, + {file = "scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477"}, + {file = "scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c"}, + {file = "scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45"}, + {file = "scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49"}, + {file = "scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e"}, + {file = "scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539"}, + {file = "scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed"}, + {file = "scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759"}, + {file = "scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62"}, + {file = "scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb"}, + {file = "scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730"}, + {file = "scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825"}, + {file = "scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7"}, + {file = "scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11"}, + {file = "scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126"}, + {file = "scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163"}, + {file = "scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8"}, + {file = "scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5"}, + {file = "scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e"}, + {file = "scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb"}, + {file = "scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723"}, + {file = "scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb"}, + {file = "scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4"}, + {file = "scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5"}, + {file = "scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca"}, + {file = "scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf"}, ] [package.dependencies] @@ -1007,7 +886,7 @@ numpy = ">=1.23.5,<2.5" [package.extras] dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] -doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.16.5)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.0.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"] +doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.19.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.0.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"] test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] [[package]] @@ -1118,4 +997,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "104c9363ce679a986c71e1cb3e686696eddd22abcbae318e251ff44e4121bf29" +content-hash = "caddf5b2fa311dc7112a4da8bd42fc71cdf88507c10839441225082b343df16c" diff --git a/pyproject.toml b/pyproject.toml index 8cceb948..a4af8fb4 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "hs-test-python" -version = "0.1.0" +version = "11.0.27" description = "" authors = ["Hyperskill Team"] readme = "README.md" @@ -12,26 +12,15 @@ packages = [ python = "^3.10" # psutil with platform and Python version markers -psutil = [ - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-win_amd64.whl", markers = "sys_platform == 'win32' and python_version >= '3.10' and python_version < '3.11'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-win_amd64.whl", markers = "sys_platform == 'win32' and python_version >= '3.11' and python_version < '3.12'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-win_amd64.whl", markers = "sys_platform == 'win32' and python_version >= '3.12' and python_version < '3.13'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-linux_x86_64.whl", markers = "sys_platform == 'linux' and python_version >= '3.10' and python_version < '3.11'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-linux_x86_64.whl", markers = "sys_platform == 'linux' and python_version >= '3.11' and python_version < '3.12'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-linux_x86_64.whl", markers = "sys_platform == 'linux' and python_version >= '3.12' and python_version < '3.13'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl", markers = "sys_platform == 'darwin' and python_version >= '3.10' and python_version < '3.11'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl", markers = "sys_platform == 'darwin' and python_version >= '3.11' and python_version < '3.12'" }, - { url = "https://github.com/hyperskill/hs-test-python/releases/download/v11.0.26/psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl", markers = "sys_platform == 'darwin' and python_version >= '3.12' and python_version < '3.13'" } -] - -pandas = "2.2.2" +psutil = "7.0.0" +pandas = "2.2.3" seaborn = "0.13.2" -scipy = "^1.12.0" +scipy = "1.15.3" matplotlib = "^3.9.2" [tool.poetry.group.dev.dependencies] -mypy = "1.10.1" -ruff = "0.6.0" +mypy = "1.13.0" +ruff = "0.7.3" pytest = "8.3.4" [build-system] diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 4b85c157..00000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,18 +0,0 @@ -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp310-cp310-win_amd64.whl ; sys_platform == 'win32' and python_version == '3.10' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp311-cp311-win_amd64.whl ; sys_platform == 'win32' and python_version == '3.11' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp312-cp312-win_amd64.whl ; sys_platform == 'win32' and python_version == '3.12' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp310-cp310-linux_x86_64.whl ; sys_platform == 'linux' and python_version == '3.10' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp311-cp311-linux_x86_64.whl ; sys_platform == 'linux' and python_version == '3.11' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp312-cp312-linux_x86_64.whl ; sys_platform == 'linux' and python_version == '3.12' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl ; sys_platform == 'darwin' and python_version == '3.10' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl ; sys_platform == 'darwin' and python_version == '3.11' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl ; sys_platform == 'darwin' and python_version == '3.12' -matplotlib -seaborn -pandas -scipy -flake8 -isort -mypy -ruff -pytest \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 5b1fe2f0..d524b1db 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,15 +1,7 @@ -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp310-cp310-win_amd64.whl ; sys_platform == 'win32' and python_version == '3.10' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp311-cp311-win_amd64.whl ; sys_platform == 'win32' and python_version == '3.11' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp312-cp312-win_amd64.whl ; sys_platform == 'win32' and python_version == '3.12' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp310-cp310-linux_x86_64.whl ; sys_platform == 'linux' and python_version == '3.10' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp311-cp311-linux_x86_64.whl ; sys_platform == 'linux' and python_version == '3.11' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp312-cp312-linux_x86_64.whl ; sys_platform == 'linux' and python_version == '3.12' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp310-cp310-macosx_10_9_universal2.whl ; sys_platform == 'darwin' and python_version == '3.10' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp311-cp311-macosx_10_9_universal2.whl ; sys_platform == 'darwin' and python_version == '3.11' -psutil @ https://github.com/hyperskill/hs-test-python/releases/latest/download/psutil-5.8.0-cp312-cp312-macosx_10_13_universal2.whl ; sys_platform == 'darwin' and python_version == '3.12' -mypy==1.10.1 -pandas==2.2.2 -ruff==0.6.0 +psutil==7.0.0 +mypy==1.13.0 +pandas==2.2.3 +ruff==0.7.3 matplotlib==3.9.2 seaborn==0.13.2 -scipy==1.12.0 \ No newline at end of file +scipy==1.15.3 \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index aeff0f26..00000000 --- a/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import annotations - -from setuptools import find_namespace_packages, setup - -with open("README.md", encoding="utf-8") as readme_file: - readme = readme_file.read() - -setup( - name="hs-test-python", - version="11.0.0", - author="Vladimir Turov", - author_email="vladimir.turov@stepik.org", - description=( - "A framework that simplifies testing educational projects for https://hyperskill.org/." - ), - long_description=readme, - long_description_content_type="text/markdown", - url="https://github.com/hyperskill/hs-test-python", - packages=find_namespace_packages(exclude=["tests", "package.json", "requirements-dev.txt"]), - python_requires=">=3.6", - install_requires=[ - "psutil-wheels ; python_version >= '3.10'", - "psutil ; python_version < '3.10'", - ], - classifiers=["Programming Language :: Python :: 3.6"], -) From 178851854ba711069180aad7211d5c1234e55a43 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 11:44:58 +0000 Subject: [PATCH 51/57] Backend: Auto format --- hstest/dynamic/input/dynamic_input_handler.py | 3 +-- hstest/stage/django_test.py | 3 +-- hstest/stage/flask_test.py | 3 +-- hstest/testing/execution/searcher/base_searcher.py | 3 +-- hstest/testing/plotting/seaborn_handler.py | 4 ++-- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/hstest/dynamic/input/dynamic_input_handler.py b/hstest/dynamic/input/dynamic_input_handler.py index 5226a317..f2b7b790 100644 --- a/hstest/dynamic/input/dynamic_input_handler.py +++ b/hstest/dynamic/input/dynamic_input_handler.py @@ -34,7 +34,6 @@ def _eject_next_input(self) -> None: new_input = clean_text(new_input) - if new_input.endswith("\n"): - new_input = new_input[:-1] + new_input = new_input.removesuffix("\n") self._input_lines += new_input.split("\n") diff --git a/hstest/stage/django_test.py b/hstest/stage/django_test.py index 82f460a0..071fb04e 100644 --- a/hstest/stage/django_test.py +++ b/hstest/stage/django_test.py @@ -28,8 +28,7 @@ def read_page(self, link: str) -> str: return clean_text(urlopen(link).read().decode()) def get_url(self, link: str = "") -> str: - if link.startswith("/"): - link = link[1:] + link = link.removeprefix("/") return f"http://localhost:{self.attach.port}/{link}" def get(self, link: str) -> str: diff --git a/hstest/stage/flask_test.py b/hstest/stage/flask_test.py index 3cc4b568..c7b88b53 100644 --- a/hstest/stage/flask_test.py +++ b/hstest/stage/flask_test.py @@ -36,8 +36,7 @@ def __init__(self, args="", *, source: str = "") -> None: self.attach.sources += [item] def get_url(self, link: str = "", *, source: str | None = None): - if link.startswith("/"): - link = link[1:] + link = link.removeprefix("/") def create_url(port: int) -> str: return f"http://localhost:{port}/{link}" diff --git a/hstest/testing/execution/searcher/base_searcher.py b/hstest/testing/execution/searcher/base_searcher.py index 4fe59cc4..a72e96c5 100644 --- a/hstest/testing/execution/searcher/base_searcher.py +++ b/hstest/testing/execution/searcher/base_searcher.py @@ -253,8 +253,7 @@ def _parse_source(self, source: str) -> tuple[Folder, File, Module]: source_module = source[: -len(ext)].replace(os.sep, ".") elif os.sep in source: - if source.endswith(os.sep): - source = source[: -len(os.sep)] + source = source.removesuffix(os.sep) source_folder = source source_file = None diff --git a/hstest/testing/plotting/seaborn_handler.py b/hstest/testing/plotting/seaborn_handler.py index ed181955..ad0c5951 100644 --- a/hstest/testing/plotting/seaborn_handler.py +++ b/hstest/testing/plotting/seaborn_handler.py @@ -47,8 +47,8 @@ def replace_plots(drawings: DrawingsStorage) -> None: return def displot(data=None, **kwargs) -> None: - x = kwargs.get("x", None) - y = kwargs.get("y", None) + x = kwargs.get("x") + y = kwargs.get("y") if data is None: curr_data = {"x": np.array(x, dtype=object), "y": np.array(y, dtype=object)} From 2b8bdc9ffe2f9ba1acbac512c76ba41fe46e539a Mon Sep 17 00:00:00 2001 From: polischuks Date: Wed, 14 May 2025 15:19:46 +0300 Subject: [PATCH 52/57] Add .go-version and .node-version files; update action.yml to dynamically set Node.js and Go versions --- .github/workflows/actions/prepare/action.yml | 27 +++++++++++++++++--- .go-version | 0 .node-version | 1 + 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 .go-version create mode 100644 .node-version diff --git a/.github/workflows/actions/prepare/action.yml b/.github/workflows/actions/prepare/action.yml index 4b6f8e72..4ca83ab0 100755 --- a/.github/workflows/actions/prepare/action.yml +++ b/.github/workflows/actions/prepare/action.yml @@ -25,7 +25,7 @@ runs: if [ -f .poetry-version ]; then echo "version=$(head -n 1 .poetry-version)" >> $GITHUB_OUTPUT else - echo "version=1.7.1" >> $GITHUB_OUTPUT + echo "version=2.0.1" >> $GITHUB_OUTPUT fi shell: bash @@ -65,9 +65,18 @@ runs: shell: pwsh # Setup Node.js for JavaScript tests + - name: Get Node.js version + id: node-version + run: | + if [ -f .node-version ]; then + echo "version=$(head -n 1 .node-version)" >> $GITHUB_OUTPUT + else + echo "version=20" >> $GITHUB_OUTPUT + fi + shell: bash - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: ${{ steps.node-version.outputs.version }} # Install Node.js dependencies - name: Install Node.js dependencies @@ -75,7 +84,17 @@ runs: shell: bash # Setup Go for Go language tests + - name: Get Go version + id: go-version + run: | + if [ -f .go-version ]; then + echo "version=$(head -n 1 .go-version)" >> $GITHUB_OUTPUT + else + echo "version=1.21" >> $GITHUB_OUTPUT + fi + shell: bash + - uses: actions/setup-go@v5 with: - go-version: '1.21' - cache: false + go-version: ${{ steps.go-version.outputs.version }} + cache: 'false' diff --git a/.go-version b/.go-version new file mode 100644 index 00000000..e69de29b diff --git a/.node-version b/.node-version new file mode 100644 index 00000000..2edeafb0 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +20 \ No newline at end of file From 1f044aa43e585ef1f9d9422256adc976286d7b83 Mon Sep 17 00:00:00 2001 From: polischuks Date: Wed, 14 May 2025 15:24:03 +0300 Subject: [PATCH 53/57] Add support for Python 3.13 in CI workflow --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 493b529c..0fdcccf8 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,6 +67,8 @@ jobs: python-version: "3.11" - os: ubuntu-latest python-version: "3.12" + - os: ubuntu-latest + python-version: "3.13" # Windows - os: windows-latest @@ -75,6 +77,8 @@ jobs: python-version: "3.11" - os: windows-latest python-version: "3.12" + - os: windows-latest + python-version: "3.13" # macOS (arm64) - os: macos-14 @@ -83,6 +87,8 @@ jobs: python-version: "3.11" - os: macos-14 python-version: "3.12" + - os: macos-14 + python-version: "3.13" steps: - uses: actions/checkout@v4 - uses: ./.github/workflows/actions/prepare From c74351915e3aa090f09cb6d4f8ed46e306368929 Mon Sep 17 00:00:00 2001 From: polischuks Date: Wed, 14 May 2025 15:25:22 +0300 Subject: [PATCH 54/57] Add Go version 1.21 to .go-version file --- .go-version | 1 + 1 file changed, 1 insertion(+) diff --git a/.go-version b/.go-version index e69de29b..0f6abf48 100644 --- a/.go-version +++ b/.go-version @@ -0,0 +1 @@ +1.21 \ No newline at end of file From 9251d39202d53f979c524bc266ca4b2d935784a2 Mon Sep 17 00:00:00 2001 From: polischuks Date: Wed, 14 May 2025 17:32:07 +0300 Subject: [PATCH 55/57] Fix: dynamically import PlottingTest and update __all__ list --- hstest/__init__.py | 6 +++++- hstest/stage/__init__.py | 6 +++++- tests/test_testcase.py | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/hstest/__init__.py b/hstest/__init__.py index 772deb31..29813316 100644 --- a/hstest/__init__.py +++ b/hstest/__init__.py @@ -1,10 +1,10 @@ from __future__ import annotations +from typing import Any, Union __all__ = [ "CheckResult", "DjangoTest", "FlaskTest", - "PlottingTest", "SQLTest", "SimpleTestCase", "StageTest", @@ -23,7 +23,11 @@ from hstest.test_case import CheckResult, correct, SimpleTestCase, TestCase, wrong from hstest.testing.tested_program import TestedProgram +# Define PlottingTest as Any before trying to import it +PlottingTest: Any + try: from hstest.stage import PlottingTest + __all__.append("PlottingTest") except ImportError: PlottingTest = None diff --git a/hstest/stage/__init__.py b/hstest/stage/__init__.py index 6cc14d38..97999ab7 100644 --- a/hstest/stage/__init__.py +++ b/hstest/stage/__init__.py @@ -1,9 +1,9 @@ from __future__ import annotations +from typing import Any, Union __all__ = [ "DjangoTest", "FlaskTest", - "PlottingTest", "SQLTest", "StageTest", ] @@ -13,7 +13,11 @@ from hstest.stage.sql_test import SQLTest from hstest.stage.stage_test import StageTest +# Define PlottingTest as Any before trying to import it +PlottingTest: Any + try: from hstest.stage.plotting_test import PlottingTest + __all__.append("PlottingTest") except ImportError: PlottingTest = None diff --git a/tests/test_testcase.py b/tests/test_testcase.py index df068572..74482104 100644 --- a/tests/test_testcase.py +++ b/tests/test_testcase.py @@ -68,4 +68,4 @@ def test_from_stepik_mixed(self) -> None: def test_from_stepik_bad_data(self) -> None: with self.assertRaises(UnexpectedError): - TestCase.from_stepik([("mixed1", 234567), 234345, ("mixed345", 456234), "567"]) + TestCase.from_stepik([("mixed1", 234567), 234345, ("mixed345", 456234), "567"]) # type: ignore[list-item] From 27b43c8f02f6fb52e16ab412f1ebeb2a7d638453 Mon Sep 17 00:00:00 2001 From: polischuks Date: Wed, 14 May 2025 19:35:24 +0300 Subject: [PATCH 56/57] Fix: dynamically import PlottingTest and update __all__ list --- hstest/testing/unittest/expected_fail_test.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hstest/testing/unittest/expected_fail_test.py b/hstest/testing/unittest/expected_fail_test.py index 50f5baa9..864c2384 100644 --- a/hstest/testing/unittest/expected_fail_test.py +++ b/hstest/testing/unittest/expected_fail_test.py @@ -31,13 +31,13 @@ def test_run_unittest(self) -> None: self.assertEqual(result, -1) if type(self._base_contain) != list: - self._base_contain = [self._base_contain] + self._base_contain = [self._base_contain] # type: ignore[list-item] if type(self._base_not_contain) != list: - self._base_not_contain = [self._base_not_contain] + self._base_not_contain = [self._base_not_contain] # type: ignore[list-item] if type(self.contain) != list: - self.contain = [self.contain] + self.contain = [self.contain] # type: ignore[list-item] if type(self.not_contain) != list: - self.not_contain = [self.not_contain] + self.not_contain = [self.not_contain] # type: ignore[list-item] should_contain = self._base_contain + self.contain should_not_contain = self._base_not_contain + self.not_contain From fb0514de8422ea7db87075dec735007d622d875a Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 16:38:34 +0000 Subject: [PATCH 57/57] Backend: Auto format --- hstest/__init__.py | 4 +++- hstest/stage/__init__.py | 4 +++- hstest/testing/unittest/expected_fail_test.py | 8 ++++---- tests/test_testcase.py | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/hstest/__init__.py b/hstest/__init__.py index 29813316..8346bc80 100644 --- a/hstest/__init__.py +++ b/hstest/__init__.py @@ -1,5 +1,6 @@ from __future__ import annotations -from typing import Any, Union + +from typing import Any __all__ = [ "CheckResult", @@ -28,6 +29,7 @@ try: from hstest.stage import PlottingTest + __all__.append("PlottingTest") except ImportError: PlottingTest = None diff --git a/hstest/stage/__init__.py b/hstest/stage/__init__.py index 97999ab7..f6bf6f3c 100644 --- a/hstest/stage/__init__.py +++ b/hstest/stage/__init__.py @@ -1,5 +1,6 @@ from __future__ import annotations -from typing import Any, Union + +from typing import Any __all__ = [ "DjangoTest", @@ -18,6 +19,7 @@ try: from hstest.stage.plotting_test import PlottingTest + __all__.append("PlottingTest") except ImportError: PlottingTest = None diff --git a/hstest/testing/unittest/expected_fail_test.py b/hstest/testing/unittest/expected_fail_test.py index 864c2384..5d641ee8 100644 --- a/hstest/testing/unittest/expected_fail_test.py +++ b/hstest/testing/unittest/expected_fail_test.py @@ -31,13 +31,13 @@ def test_run_unittest(self) -> None: self.assertEqual(result, -1) if type(self._base_contain) != list: - self._base_contain = [self._base_contain] # type: ignore[list-item] + self._base_contain = [self._base_contain] # type: ignore[list-item] if type(self._base_not_contain) != list: - self._base_not_contain = [self._base_not_contain] # type: ignore[list-item] + self._base_not_contain = [self._base_not_contain] # type: ignore[list-item] if type(self.contain) != list: - self.contain = [self.contain] # type: ignore[list-item] + self.contain = [self.contain] # type: ignore[list-item] if type(self.not_contain) != list: - self.not_contain = [self.not_contain] # type: ignore[list-item] + self.not_contain = [self.not_contain] # type: ignore[list-item] should_contain = self._base_contain + self.contain should_not_contain = self._base_not_contain + self.not_contain diff --git a/tests/test_testcase.py b/tests/test_testcase.py index 74482104..21c6a067 100644 --- a/tests/test_testcase.py +++ b/tests/test_testcase.py @@ -68,4 +68,4 @@ def test_from_stepik_mixed(self) -> None: def test_from_stepik_bad_data(self) -> None: with self.assertRaises(UnexpectedError): - TestCase.from_stepik([("mixed1", 234567), 234345, ("mixed345", 456234), "567"]) # type: ignore[list-item] + TestCase.from_stepik([("mixed1", 234567), 234345, ("mixed345", 456234), "567"]) # type: ignore[list-item]