diff --git a/.github/workflows/tests-e2e.yml b/.github/workflows/tests-e2e.yml index baff453a..43d09a05 100644 --- a/.github/workflows/tests-e2e.yml +++ b/.github/workflows/tests-e2e.yml @@ -17,7 +17,7 @@ jobs: runs-on: arc-runner-set strategy: matrix: - python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12", "3.13" ] + python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ] steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows/unit-in-pull-request.yml b/.github/workflows/unit-in-pull-request.yml index 3020d4ba..3b982bfd 100644 --- a/.github/workflows/unit-in-pull-request.yml +++ b/.github/workflows/unit-in-pull-request.yml @@ -12,8 +12,8 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu, windows, macos] - python-version: ["3.8"] + os: [ ubuntu, windows, macos ] + python-version: [ "3.9" ] name: 'test (${{ matrix.os }} - py${{ matrix.python-version }})' runs-on: ${{ matrix.os }}-latest steps: diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 65f19d65..a09cd4b7 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -15,8 +15,8 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] - os: [ubuntu, windows, macos] + python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ] + os: [ ubuntu, windows, macos ] name: 'test (${{ matrix.os }} - py${{ matrix.python-version }})' runs-on: ${{ matrix.os }}-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index f506879f..556c1cf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added function `neptune_scale.list_projects()` to list all projects the current user has access to ([#97](https://github.com/neptune-ai/neptune-client-scale/pull/97)) ### Changed +- Removed support for Python 3.8 - Neptune will now skip non-finite metric values by default, instead of raising an error. This can be configured using the new `NEPTUNE_SKIP_NON_FINITE_METRICS` environment variable ([#85](https://github.com/neptune-ai/neptune-client-scale/pull/85)) - Made default error callback logs more informative ([#78](https://github.com/neptune-ai/neptune-client-scale/pull/78)) diff --git a/pyproject.toml b/pyproject.toml index 99a0f8bc..0a9cc87a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ style = "semver" pattern = "default-unprefixed" [tool.poetry.dependencies] -python = "^3.8" +python = "^3.9" neptune-api = "^0.8.0" more-itertools = "^10.0.0" @@ -54,7 +54,7 @@ keywords = [ [tool.black] line-length = 120 -target_version = ['py38', 'py39', 'py310', 'py311', 'py312'] +target_version = ['py39', 'py310', 'py311', 'py312', 'py313'] include = '\.pyi?$,\_pb2\.py$' exclude = ''' /( @@ -74,7 +74,7 @@ force_grid_wrap = 2 [tool.ruff] line-length = 120 -target-version = "py38" +target-version = "py39" [tool.ruff.lint] select = ["F", "UP"] diff --git a/src/neptune_scale/api/attribute.py b/src/neptune_scale/api/attribute.py index b5517b69..fb9b7c8a 100644 --- a/src/neptune_scale/api/attribute.py +++ b/src/neptune_scale/api/attribute.py @@ -1,18 +1,16 @@ import functools import itertools import warnings +from collections.abc import ( + Collection, + Iterator, +) from datetime import datetime from typing import ( TYPE_CHECKING, Any, Callable, - Collection, - Dict, - Iterator, - List, Optional, - Set, - Tuple, Union, cast, ) @@ -52,7 +50,7 @@ def wrapper(*args, **kwargs): # type: ignore class AttributeStore: def __init__(self, run: "Run") -> None: self._run = run - self._attributes: Dict[str, Attribute] = {} + self._attributes: dict[str, Attribute] = {} def __getitem__(self, path: str) -> "Attribute": path = cleanup_path(path) @@ -72,10 +70,10 @@ def log( self, step: Optional[Union[float, int]] = None, timestamp: Optional[Union[datetime, float]] = None, - configs: Optional[Dict[str, Union[float, bool, int, str, datetime, list, set, tuple]]] = None, - metrics: Optional[Dict[str, Union[float, int]]] = None, - tags_add: Optional[Dict[str, Union[List[str], Set[str], Tuple[str]]]] = None, - tags_remove: Optional[Dict[str, Union[List[str], Set[str], Tuple[str]]]] = None, + configs: Optional[dict[str, Union[float, bool, int, str, datetime, list, set, tuple]]] = None, + metrics: Optional[dict[str, Union[float, int]]] = None, + tags_add: Optional[dict[str, Union[list[str], set[str], tuple[str]]]] = None, + tags_remove: Optional[dict[str, Union[list[str], set[str], tuple[str]]]] = None, ) -> None: # TODO: This should not call Run.log, but do the actual work. Reverse the current dependency so that this # class handles all the logging @@ -117,7 +115,7 @@ def assign(self, value: Any, *, wait: bool = False) -> None: @warn_unsupported_params def append( self, - value: Union[Dict[str, Any], float], + value: Union[dict[str, Any], float], *, step: Union[float, int], timestamp: Optional[Union[float, datetime]] = None, @@ -130,7 +128,7 @@ def append( @warn_unsupported_params # TODO: this should be Iterable in Run as well # def add(self, values: Union[str, Iterable[str]], *, wait: bool = False) -> None: - def add(self, values: Union[str, Union[List[str], Set[str], Tuple[str]]], *, wait: bool = False) -> None: + def add(self, values: Union[str, Union[list[str], set[str], tuple[str]]], *, wait: bool = False) -> None: if isinstance(values, str): values = (values,) self._store.log(tags_add={self._path: values}) @@ -138,7 +136,7 @@ def add(self, values: Union[str, Union[List[str], Set[str], Tuple[str]]], *, wai @warn_unsupported_params # TODO: this should be Iterable in Run as well # def remove(self, values: Union[str, Iterable[str]], *, wait: bool = False) -> None: - def remove(self, values: Union[str, Union[List[str], Set[str], Tuple[str]]], *, wait: bool = False) -> None: + def remove(self, values: Union[str, Union[list[str], set[str], tuple[str]]], *, wait: bool = False) -> None: if isinstance(values, str): values = (values,) self._store.log(tags_remove={self._path: values}) @@ -168,7 +166,7 @@ def extend( # TODO: change Run API to typehint timestamp as Union[datetime, float] -def iter_nested(dict_: Dict[str, ValueType], path: str) -> Iterator[Tuple[Tuple[str, ...], ValueType]]: +def iter_nested(dict_: dict[str, ValueType], path: str) -> Iterator[tuple[tuple[str, ...], ValueType]]: """Iterate a nested dictionary, yielding a tuple of path components and value. >>> list(iter_nested({"foo": 1, "bar": {"baz": 2}}, "base")) @@ -187,7 +185,7 @@ def iter_nested(dict_: Dict[str, ValueType], path: str) -> Iterator[Tuple[Tuple[ yield from _iter_nested(dict_, parts) -def _iter_nested(dict_: Dict[str, ValueType], path_acc: Tuple[str, ...]) -> Iterator[Tuple[Tuple[str, ...], ValueType]]: +def _iter_nested(dict_: dict[str, ValueType], path_acc: tuple[str, ...]) -> Iterator[tuple[tuple[str, ...], ValueType]]: if not dict_: raise ValueError("The dictionary cannot be empty or contain empty nested dictionaries.") @@ -228,7 +226,7 @@ def cleanup_path(path: str) -> str: return path -def accumulate_dict_values(value: Union[ValueType, Dict[str, ValueType]], path_or_base: str) -> Dict: +def accumulate_dict_values(value: Union[ValueType, dict[str, ValueType]], path_or_base: str) -> dict: """ >>> accumulate_dict_values(1, "foo") {'foo': 1} diff --git a/src/neptune_scale/api/run.py b/src/neptune_scale/api/run.py index cb3878b0..74302bb0 100644 --- a/src/neptune_scale/api/run.py +++ b/src/neptune_scale/api/run.py @@ -10,17 +10,13 @@ import os import threading import time +from collections.abc import Callable from contextlib import AbstractContextManager from datetime import datetime from typing import ( Any, - Callable, - Dict, - List, Literal, Optional, - Set, - Tuple, Union, ) @@ -401,7 +397,7 @@ def __setitem__(self, key: str, value: Any) -> None: def log_metrics( self, - data: Dict[str, Union[float, int]], + data: dict[str, Union[float, int]], step: Union[float, int], *, timestamp: Optional[datetime] = None, @@ -442,7 +438,7 @@ def log_metrics( self.log(step=step, timestamp=timestamp, metrics=data) def log_configs( - self, data: Optional[Dict[str, Union[float, bool, int, str, datetime, list, set, tuple]]] = None + self, data: Optional[dict[str, Union[float, bool, int, str, datetime, list, set, tuple]]] = None ) -> None: """ Logs the specified metadata to a Neptune run. @@ -477,7 +473,7 @@ def log_configs( """ self.log(configs=data) - def add_tags(self, tags: Union[List[str], Set[str], Tuple[str]], group_tags: bool = False) -> None: + def add_tags(self, tags: Union[list[str], set[str], tuple[str]], group_tags: bool = False) -> None: """ Adds the list of tags to the run. @@ -496,7 +492,7 @@ def add_tags(self, tags: Union[List[str], Set[str], Tuple[str]], group_tags: boo name = "sys/tags" if not group_tags else "sys/group_tags" self.log(tags_add={name: tags}) - def remove_tags(self, tags: Union[List[str], Set[str], Tuple[str]], group_tags: bool = False) -> None: + def remove_tags(self, tags: Union[list[str], set[str], tuple[str]], group_tags: bool = False) -> None: """ Removes the specified tags from the run. @@ -519,10 +515,10 @@ def log( self, step: Optional[Union[float, int]] = None, timestamp: Optional[datetime] = None, - configs: Optional[Dict[str, Union[float, bool, int, str, datetime, list, set, tuple]]] = None, - metrics: Optional[Dict[str, Union[float, int]]] = None, - tags_add: Optional[Dict[str, Union[List[str], Set[str], Tuple[str]]]] = None, - tags_remove: Optional[Dict[str, Union[List[str], Set[str], Tuple[str]]]] = None, + configs: Optional[dict[str, Union[float, bool, int, str, datetime, list, set, tuple]]] = None, + metrics: Optional[dict[str, Union[float, int]]] = None, + tags_add: Optional[dict[str, Union[list[str], set[str], tuple[str]]]] = None, + tags_remove: Optional[dict[str, Union[list[str], set[str], tuple[str]]]] = None, ) -> None: """ See one of the following instead: diff --git a/src/neptune_scale/api/validation.py b/src/neptune_scale/api/validation.py index 578bb9b8..7142b1b0 100644 --- a/src/neptune_scale/api/validation.py +++ b/src/neptune_scale/api/validation.py @@ -10,16 +10,15 @@ from typing import ( Any, - Type, Union, ) -def get_type_name(var_type: Union[Type, tuple]) -> str: +def get_type_name(var_type: Union[type, tuple]) -> str: return var_type.__name__ if hasattr(var_type, "__name__") else str(var_type) -def verify_type(var_name: str, var: Any, expected_type: Union[Type, tuple]) -> None: +def verify_type(var_name: str, var: Any, expected_type: Union[type, tuple]) -> None: try: if isinstance(expected_type, tuple): type_name = " or ".join(get_type_name(t) for t in expected_type) diff --git a/src/neptune_scale/net/api_client.py b/src/neptune_scale/net/api_client.py index 75ddb12b..a6cf90c2 100644 --- a/src/neptune_scale/net/api_client.py +++ b/src/neptune_scale/net/api_client.py @@ -21,11 +21,11 @@ import functools import os import uuid +from collections.abc import Callable from dataclasses import dataclass from http import HTTPStatus from typing import ( Any, - Callable, Literal, ) diff --git a/src/neptune_scale/net/projects.py b/src/neptune_scale/net/projects.py index df5d12b9..ce698ada 100644 --- a/src/neptune_scale/net/projects.py +++ b/src/neptune_scale/net/projects.py @@ -4,8 +4,6 @@ from json import JSONDecodeError from typing import ( Any, - Dict, - List, Optional, cast, ) @@ -93,7 +91,7 @@ def _safe_json(response: httpx.Response) -> Any: return {} -def get_project_list(*, api_token: Optional[str] = None) -> List[Dict]: +def get_project_list(*, api_token: Optional[str] = None) -> list[dict]: client = HostedApiClient(api_token=_get_api_token(api_token)) params = { @@ -102,4 +100,4 @@ def get_project_list(*, api_token: Optional[str] = None) -> List[Dict]: } response = client.backend.get_httpx_client().request("get", PROJECTS_PATH_BASE, params=params) - return cast(List[Dict], response.json()["entries"]) + return cast(list[dict], response.json()["entries"]) diff --git a/src/neptune_scale/net/serialization.py b/src/neptune_scale/net/serialization.py index af73f1dc..741c48a3 100644 --- a/src/neptune_scale/net/serialization.py +++ b/src/neptune_scale/net/serialization.py @@ -8,11 +8,7 @@ ) from datetime import datetime -from typing import ( - List, - Set, - Union, -) +from typing import Union from google.protobuf.timestamp_pb2 import Timestamp from neptune_api.proto.neptune_pb.ingest.v1.common_pb2 import ( @@ -22,7 +18,7 @@ ) -def make_value(value: Union[Value, float, str, int, bool, datetime, List[str], Set[str]]) -> Value: +def make_value(value: Union[Value, float, str, int, bool, datetime, list[str], set[str]]) -> Value: if isinstance(value, Value): return value if isinstance(value, float): diff --git a/src/neptune_scale/projects.py b/src/neptune_scale/projects.py index 2579d234..10f1968e 100644 --- a/src/neptune_scale/projects.py +++ b/src/neptune_scale/projects.py @@ -1,8 +1,6 @@ import re from typing import ( - List, Optional, - Tuple, cast, ) @@ -66,7 +64,7 @@ def create_project( return normalize_project_name(name, workspace) -def extract_workspace_and_project(name: str, workspace: Optional[str] = None) -> Tuple[str, str]: +def extract_workspace_and_project(name: str, workspace: Optional[str] = None) -> tuple[str, str]: """Return a tuple of (workspace name, project name) from the provided fully qualified project name, or a name + workspace @@ -118,7 +116,7 @@ def normalize_project_name(name: str, workspace: Optional[str] = None) -> str: return f"{extracted_workspace_name}/{extracted_project_name}" -def list_projects(*, api_token: Optional[str] = None) -> List[str]: +def list_projects(*, api_token: Optional[str] = None) -> list[str]: """Lists projects that the account has access to. Args: diff --git a/src/neptune_scale/sync/errors_tracking.py b/src/neptune_scale/sync/errors_tracking.py index 2b93d2bc..fbf48b88 100644 --- a/src/neptune_scale/sync/errors_tracking.py +++ b/src/neptune_scale/sync/errors_tracking.py @@ -5,11 +5,8 @@ import multiprocessing import queue import time -from typing import ( - Callable, - Optional, - Type, -) +from collections.abc import Callable +from typing import Optional from neptune_scale.exceptions import ( NeptuneAsyncLagThresholdExceeded, @@ -90,7 +87,7 @@ def __init__( on_warning_callback or default_warning_callback ) - self._last_raised_timestamps: dict[Type[BaseException], float] = {} + self._last_raised_timestamps: dict[type[BaseException], float] = {} def get_next(self) -> Optional[BaseException]: try: diff --git a/src/neptune_scale/sync/lag_tracking.py b/src/neptune_scale/sync/lag_tracking.py index dd00ea32..76c2bb08 100644 --- a/src/neptune_scale/sync/lag_tracking.py +++ b/src/neptune_scale/sync/lag_tracking.py @@ -2,8 +2,8 @@ __all__ = ("LagTracker",) +from collections.abc import Callable from time import monotonic -from typing import Callable from neptune_scale.sync.errors_tracking import ErrorsQueue from neptune_scale.sync.operations_queue import OperationsQueue diff --git a/src/neptune_scale/sync/metadata_splitter.py b/src/neptune_scale/sync/metadata_splitter.py index d30b5418..01743495 100644 --- a/src/neptune_scale/sync/metadata_splitter.py +++ b/src/neptune_scale/sync/metadata_splitter.py @@ -4,16 +4,14 @@ import math import warnings +from collections.abc import ( + Callable, + Iterator, +) from datetime import datetime from typing import ( Any, - Callable, - Dict, - Iterator, - List, Optional, - Set, - Tuple, TypeVar, Union, ) @@ -46,7 +44,7 @@ T = TypeVar("T", bound=Any) -class MetadataSplitter(Iterator[Tuple[RunOperation, int]]): +class MetadataSplitter(Iterator[tuple[RunOperation, int]]): def __init__( self, *, @@ -54,10 +52,10 @@ def __init__( run_id: str, step: Optional[Union[int, float]], timestamp: datetime, - configs: Dict[str, Union[float, bool, int, str, datetime, list, set, tuple]], - metrics: Dict[str, float], - add_tags: Dict[str, Union[List[str], Set[str], Tuple[str]]], - remove_tags: Dict[str, Union[List[str], Set[str], Tuple[str]]], + configs: dict[str, Union[float, bool, int, str, datetime, list, set, tuple]], + metrics: dict[str, float], + add_tags: dict[str, Union[list[str], set[str], tuple[str]]], + remove_tags: dict[str, Union[list[str], set[str], tuple[str]]], max_message_bytes_size: int = 1024 * 1024, ): self._step = None if step is None else make_step(number=step) @@ -85,7 +83,7 @@ def __iter__(self) -> MetadataSplitter: self._has_returned = False return self - def __next__(self) -> Tuple[RunOperation, int]: + def __next__(self) -> tuple[RunOperation, int]: update = UpdateRunSnapshot( step=self._step, timestamp=self._timestamp, @@ -179,7 +177,7 @@ def populate_tags( return size - def _skip_non_finite(self, step: Optional[float | int], metrics: Dict[str, float]) -> Iterator[Tuple[str, float]]: + def _skip_non_finite(self, step: Optional[float | int], metrics: dict[str, float]) -> Iterator[tuple[str, float]]: """Yields (metric, value) pairs, skipping non-finite values depending on the env setting.""" for k, v in metrics.items(): diff --git a/src/neptune_scale/sync/sync_process.py b/src/neptune_scale/sync/sync_process.py index ed76337a..2a08f916 100644 --- a/src/neptune_scale/sync/sync_process.py +++ b/src/neptune_scale/sync/sync_process.py @@ -12,13 +12,10 @@ ) from types import FrameType from typing import ( - Dict, Generic, - List, Literal, NamedTuple, Optional, - Type, TypeVar, ) @@ -99,7 +96,7 @@ logger = get_logger() -CODE_TO_ERROR: Dict[IngestCode.ValueType, Optional[Type[Exception]]] = { +CODE_TO_ERROR: dict[IngestCode.ValueType, Optional[type[Exception]]] = { IngestCode.OK: None, IngestCode.PROJECT_NOT_FOUND: NeptuneProjectNotFound, IngestCode.PROJECT_INVALID_NAME: NeptuneProjectInvalidName, @@ -130,7 +127,7 @@ class StatusTrackingElement(NamedTuple): request_id: str -def code_to_exception(code: IngestCode.ValueType) -> Optional[Type[Exception]]: +def code_to_exception(code: IngestCode.ValueType) -> Optional[type[Exception]]: if code in CODE_TO_ERROR: return CODE_TO_ERROR[code] return NeptuneUnexpectedError @@ -145,7 +142,7 @@ def put(self, element: T) -> None: with self._lock: self._queue.put(element) - def peek(self, max_size: int) -> Optional[List[T]]: + def peek(self, max_size: int) -> Optional[list[T]]: with self._lock: size = self._queue.qsize() if size == 0: @@ -485,7 +482,7 @@ def resources(self) -> tuple[Resource, ...]: return (self._backend,) return () - def get_next(self) -> Optional[List[StatusTrackingElement]]: + def get_next(self) -> Optional[list[StatusTrackingElement]]: try: return self._status_tracking_queue.peek(max_size=MAX_REQUESTS_STATUS_BATCH_SIZE) except queue.Empty: @@ -493,7 +490,7 @@ def get_next(self) -> Optional[List[StatusTrackingElement]]: @backoff.on_exception(backoff.expo, NeptuneConnectionLostError, max_time=MAX_REQUEST_RETRY_SECONDS) @with_api_errors_handling - def check_batch(self, *, request_ids: List[str]) -> Optional[BulkRequestStatus]: + def check_batch(self, *, request_ids: list[str]) -> Optional[BulkRequestStatus]: if self._backend is None: self._backend = backend_factory(api_token=self._api_token, mode=self._mode) diff --git a/src/neptune_scale/util/abstract.py b/src/neptune_scale/util/abstract.py index 637ade59..80538c36 100644 --- a/src/neptune_scale/util/abstract.py +++ b/src/neptune_scale/util/abstract.py @@ -5,10 +5,7 @@ abstractmethod, ) from types import TracebackType -from typing import ( - Optional, - Type, -) +from typing import Optional class AutoCloseable(ABC): @@ -20,7 +17,7 @@ def close(self) -> None: ... def __exit__( self, - exc_type: Optional[Type[BaseException]], + exc_type: Optional[type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[TracebackType], ) -> None: diff --git a/src/neptune_scale/util/process_killer.py b/src/neptune_scale/util/process_killer.py index 0cc27e71..8e373bd8 100644 --- a/src/neptune_scale/util/process_killer.py +++ b/src/neptune_scale/util/process_killer.py @@ -1,7 +1,6 @@ __all__ = ["kill_me"] import os -from typing import List import psutil @@ -41,7 +40,7 @@ def _kill(process: psutil.Process) -> None: pass -def _get_process_children(process: psutil.Process) -> List[psutil.Process]: +def _get_process_children(process: psutil.Process) -> list[psutil.Process]: try: return process.children(recursive=True) except psutil.NoSuchProcess: diff --git a/src/neptune_scale/util/process_link.py b/src/neptune_scale/util/process_link.py index 733fc283..dff7febc 100644 --- a/src/neptune_scale/util/process_link.py +++ b/src/neptune_scale/util/process_link.py @@ -2,11 +2,11 @@ import os import queue import threading +from collections.abc import Callable from functools import partial from multiprocessing.connection import Connection from typing import ( Any, - Callable, Optional, ) diff --git a/src/neptune_scale/util/shared_var.py b/src/neptune_scale/util/shared_var.py index ba6df3bd..84f48c28 100644 --- a/src/neptune_scale/util/shared_var.py +++ b/src/neptune_scale/util/shared_var.py @@ -1,10 +1,9 @@ import multiprocessing +from collections.abc import Callable from types import TracebackType from typing import ( - Callable, Generic, Optional, - Type, TypeVar, cast, ) @@ -66,7 +65,7 @@ def __enter__(self) -> "SharedVar[T]": def __exit__( self, - exc_type: Optional[Type[BaseException]], + exc_type: Optional[type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[TracebackType], ) -> None: diff --git a/src/neptune_scale/util/styles.py b/src/neptune_scale/util/styles.py index e5f1c6db..d537deab 100644 --- a/src/neptune_scale/util/styles.py +++ b/src/neptune_scale/util/styles.py @@ -2,7 +2,6 @@ import os import platform -from typing import Dict from neptune_scale.util.envs import DISABLE_COLORS @@ -49,7 +48,7 @@ } -STYLES: Dict[str, str] = {} +STYLES: dict[str, str] = {} def ensure_style_detected() -> None: diff --git a/tests/unit/test_metadata_splitter.py b/tests/unit/test_metadata_splitter.py index fc79f5a8..69a3e647 100644 --- a/tests/unit/test_metadata_splitter.py +++ b/tests/unit/test_metadata_splitter.py @@ -262,7 +262,7 @@ def test_split_large_tags(): assert all(op.update.timestamp == Timestamp(seconds=1722341532, nanos=21934) for op, _ in result) # Check if all StringSet values are split correctly - assert set([key for op, _ in result for key in op.update.modify_sets.keys()]) == set( + assert {key for op, _ in result for key in op.update.modify_sets.keys()} == set( list(add_tags.keys()) + list(remove_tags.keys()) ) diff --git a/tests/unit/test_sync_process.py b/tests/unit/test_sync_process.py index 7f62aa46..c1140b18 100644 --- a/tests/unit/test_sync_process.py +++ b/tests/unit/test_sync_process.py @@ -1,6 +1,5 @@ import queue import time -from typing import List from unittest.mock import Mock import pytest @@ -20,7 +19,7 @@ from neptune_scale.util.shared_var import SharedInt -def response(request_ids: List[str], status_code: int = 200): +def response(request_ids: list[str], status_code: int = 200): body = SubmitResponse(request_ids=request_ids, request_id=request_ids[-1] if request_ids else None) content = body.SerializeToString() return Mock(status_code=status_code, content=content, parsed=body)