From cba6a190db280713eb1a0de1321842563f55c74a Mon Sep 17 00:00:00 2001 From: Greg Pauloski <18683347+gpauloski@users.noreply.github.com> Date: Tue, 11 Nov 2025 12:26:45 -0800 Subject: [PATCH] Bump python versions --- .github/workflows/tests.yml | 6 +++--- docs/guides/executor.md | 12 ++---------- pyproject.toml | 9 ++++----- taps/apps/cholesky.py | 7 +------ taps/apps/configs/failures.py | 3 +-- taps/apps/configs/fedlearn.py | 3 +-- taps/apps/configs/mapreduce.py | 3 +-- taps/apps/configs/physics.py | 3 +-- taps/apps/configs/synthetic.py | 3 +-- taps/apps/failures/app.py | 7 +------ taps/apps/fedlearn/client.py | 3 +-- taps/apps/fedlearn/types.py | 11 ++--------- taps/apps/moldesign/tasks.py | 6 +----- taps/apps/montage.py | 7 ++++++- taps/apps/physics.py | 22 ++++++++++++++++------ taps/engine/_config.py | 7 +++---- taps/engine/_engine.py | 11 +++++------ taps/engine/task.py | 19 ++++++------------- taps/executor/dask.py | 18 +++++++----------- taps/executor/parsl.py | 34 +++++++++++++++------------------- taps/executor/python.py | 5 +---- taps/executor/ray.py | 12 +++--------- taps/executor/taskvine.py | 14 ++++++-------- taps/executor/utils.py | 10 ++++------ taps/filter/_object.py | 4 +--- taps/plugins.py | 7 +------ taps/record.py | 9 ++------- taps/run/config.py | 15 ++++++--------- taps/run/env.py | 5 ++--- taps/transformer/_proxy.py | 4 +--- testing/globus.py | 7 +------ tests/engine/task_test.py | 6 +----- tox.ini | 2 +- 33 files changed, 108 insertions(+), 186 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3a36ca5c..1e8d0265 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,9 +14,6 @@ jobs: strategy: matrix: include: - - os: ubuntu-latest - python: 3.9 - toxenv: py39 - os: ubuntu-latest python: '3.10' toxenv: py310 @@ -26,6 +23,9 @@ jobs: - os: ubuntu-latest python: '3.12' toxenv: py312 + - os: ubuntu-latest + python: '3.13' + toxenv: py313 runs-on: ${{ matrix.os }} steps: diff --git a/docs/guides/executor.md b/docs/guides/executor.md index 73d8a247..63dde062 100644 --- a/docs/guides/executor.md +++ b/docs/guides/executor.md @@ -29,13 +29,9 @@ from concurrent.futures import Future from typing import Callable from typing import Iterable from typing import Iterator +from typing import ParamSpec from typing import TypeVar -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import ParamSpec -else: # pragma: <3.10 cover - from typing_extensions import ParamSpec - P = ParamSpec('P') T = TypeVar('T') @@ -133,13 +129,9 @@ from typing import Callable from typing import Iterable from typing import Iterator from typing import Literal +from typing import ParamSpec from typing import TypeVar -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import ParamSpec -else: # pragma: <3.10 cover - from typing_extensions import ParamSpec - from pydantic import Field from taps.executor import ExecutorConfig diff --git a/pyproject.toml b/pyproject.toml index c2405080..8313ed62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ authors = [ ] description = "Task Performance Suite for benchmarking parallel execution frameworks." readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" license = {text = "MIT"} classifiers = [ "License :: OSI Approved :: MIT License", @@ -25,8 +25,7 @@ dependencies = [ "parsl>=2025.07.07", "proxystore>=0.7.0", "psutil", - "pydantic>=2,<2.10 ; python_version<'3.9'", - "pydantic>=2 ; python_version>='3.9'", + "pydantic>=2", "pydantic-settings>=2.3.0", "ray[client]", "tomli-w", @@ -98,7 +97,7 @@ plugins = [ "proxystore.mypy_plugin", "pydantic.mypy", ] -python_version = "3.12" +python_version = "3.14" check_untyped_defs = true disallow_any_generics = true disallow_incomplete_defs = true @@ -120,7 +119,7 @@ allow_untyped_defs = true [tool.ruff] line-length = 79 -target-version = "py39" +target-version = "py310" [tool.ruff.format] indent-style = "space" diff --git a/taps/apps/cholesky.py b/taps/apps/cholesky.py index 724eb940..fedbf6b9 100644 --- a/taps/apps/cholesky.py +++ b/taps/apps/cholesky.py @@ -4,12 +4,7 @@ import logging import pathlib -import sys - -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import TypeAlias -else: # pragma: <3.10 cover - from typing_extensions import TypeAlias +from typing import TypeAlias import numpy from numpy.typing import NDArray diff --git a/taps/apps/configs/failures.py b/taps/apps/configs/failures.py index 54ac5ce2..69fcc168 100644 --- a/taps/apps/configs/failures.py +++ b/taps/apps/configs/failures.py @@ -2,7 +2,6 @@ import sys from typing import Any -from typing import Dict from typing import Literal if sys.version_info >= (3, 11): # pragma: >=3.11 cover @@ -29,7 +28,7 @@ class FailureInjectionConfig(AppConfig, use_enum_values=True): description='Application name.', ) base: str = Field(description='Base app to inject failures into.') - config: Dict[str, Any] = Field( # noqa: UP006 + config: dict[str, Any] = Field( default_factory=dict, description='Base app configuration.', ) diff --git a/taps/apps/configs/fedlearn.py b/taps/apps/configs/fedlearn.py index 1f35a83e..83165470 100644 --- a/taps/apps/configs/fedlearn.py +++ b/taps/apps/configs/fedlearn.py @@ -3,7 +3,6 @@ import pathlib from typing import Any from typing import Literal -from typing import Optional from pydantic import Field from pydantic import field_validator @@ -64,7 +63,7 @@ class FedlearnConfig(AppConfig, use_enum_values=True): True, description='Evaluate the global model on test data after each round.', ) - seed: Optional[int] = Field(None, description='Random seed.') # noqa: UP045 + seed: int | None = Field(None, description='Random seed.') @field_validator('dataset', mode='before') @classmethod diff --git a/taps/apps/configs/mapreduce.py b/taps/apps/configs/mapreduce.py index 6bca097d..bcd31d66 100644 --- a/taps/apps/configs/mapreduce.py +++ b/taps/apps/configs/mapreduce.py @@ -2,7 +2,6 @@ import pathlib from typing import Literal -from typing import Optional from pydantic import Field @@ -20,7 +19,7 @@ class MapreduceConfig(AppConfig): description='Application name.', ) data_dir: pathlib.Path = Field(description='Text file directory.') - map_tasks: Optional[int] = Field( # noqa: UP045 + map_tasks: int | None = Field( 32, description=( 'Maximum number of map tasks (`None` uses one ' diff --git a/taps/apps/configs/physics.py b/taps/apps/configs/physics.py index 07e7e3a2..f649dd2f 100644 --- a/taps/apps/configs/physics.py +++ b/taps/apps/configs/physics.py @@ -1,7 +1,6 @@ from __future__ import annotations from typing import Literal -from typing import Optional from pydantic import Field @@ -27,7 +26,7 @@ class PhysicsConfig(AppConfig): tick_rate: int = Field(240, description='Simulation steps per seconds.') total_time: int = Field(10, description='Simulation runtime in seconds.') real_time: bool = Field(True, description='Simulate at real time.') - seed: Optional[int] = Field(None, description='Random seed.') # noqa: UP045 + seed: int | None = Field(None, description='Random seed.') terrain_width: int = Field( 20, description='Terrain width/length in meters.', diff --git a/taps/apps/configs/synthetic.py b/taps/apps/configs/synthetic.py index 3c632f05..dfa88d2a 100644 --- a/taps/apps/configs/synthetic.py +++ b/taps/apps/configs/synthetic.py @@ -4,7 +4,6 @@ import sys from typing import Any from typing import Literal -from typing import Optional if sys.version_info >= (3, 11): # pragma: >=3.11 cover from typing import Self @@ -41,7 +40,7 @@ class SyntheticConfig(AppConfig, use_enum_values=True): task_count: int = Field(description='Number of tasks in the workflow.') task_data_bytes: int = Field(0, description='Intermediate task data size.') task_sleep: float = Field(0, description='Minimum duration of each task.') - bag_max_running: Optional[int] = Field( # noqa: UP045 + bag_max_running: int | None = Field( None, description='Max running tasks in bag workflow.', ) diff --git a/taps/apps/failures/app.py b/taps/apps/failures/app.py index c5474157..ee806ed0 100644 --- a/taps/apps/failures/app.py +++ b/taps/apps/failures/app.py @@ -4,17 +4,12 @@ import logging import pathlib import random -import sys from typing import Any from typing import Callable from typing import cast +from typing import ParamSpec from typing import TypeVar -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import ParamSpec -else: # pragma: <3.10 cover - from typing_extensions import ParamSpec - from taps.apps import AppConfig from taps.apps.failures.types import FAILURE_FUNCTIONS from taps.apps.failures.types import FailureType diff --git a/taps/apps/fedlearn/client.py b/taps/apps/fedlearn/client.py index f2f794f7..37bc3f2b 100644 --- a/taps/apps/fedlearn/client.py +++ b/taps/apps/fedlearn/client.py @@ -1,7 +1,6 @@ from __future__ import annotations from collections import OrderedDict -from typing import Optional import torch from numpy.random import Generator @@ -22,7 +21,7 @@ class Client(BaseModel): idx: int = Field(description='Client ID.') model: torch.nn.Module = Field(description='Client local model.') - data: Optional[Subset] = Field( # noqa: UP045 + data: Subset | None = Field( description='Subset of data this client will train on.', ) diff --git a/taps/apps/fedlearn/types.py b/taps/apps/fedlearn/types.py index d8762de3..02416e5d 100644 --- a/taps/apps/fedlearn/types.py +++ b/taps/apps/fedlearn/types.py @@ -1,17 +1,10 @@ from __future__ import annotations import enum -import sys from typing import Any -from typing import Dict +from typing import TypeAlias -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import TypeAlias -else: # pragma: <3.10 cover - from typing_extensions import TypeAlias - - -Result: TypeAlias = Dict[str, Any] # noqa: UP006 +Result: TypeAlias = dict[str, Any] """Result type for each FL epoch, round, and task.""" diff --git a/taps/apps/moldesign/tasks.py b/taps/apps/moldesign/tasks.py index fd631a0d..e9ffeba9 100644 --- a/taps/apps/moldesign/tasks.py +++ b/taps/apps/moldesign/tasks.py @@ -7,13 +7,9 @@ from io import StringIO from typing import Any from typing import Callable +from typing import ParamSpec from typing import TypeVar -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import ParamSpec -else: # pragma: <3.10 cover - from typing_extensions import ParamSpec - if sys.version_info >= (3, 11): # pragma: >=3.11 cover from typing import Self else: # pragma: <3.11 cover diff --git a/taps/apps/montage.py b/taps/apps/montage.py index 012edfd5..c689983b 100644 --- a/taps/apps/montage.py +++ b/taps/apps/montage.py @@ -342,7 +342,12 @@ def run(self, engine: Engine, run_dir: pathlib.Path) -> None: # noqa: PLR0915 mdiff_futures = [] logger.log(APP_LOG_LEVEL, 'Starting difference computations') - for image1, image2, output in zip(images1, images2, outputs): + for image1, image2, output in zip( + images1, + images2, + outputs, + strict=False, + ): future = engine.submit( mdiff, image_1=projections_dir / image1, diff --git a/taps/apps/physics.py b/taps/apps/physics.py index d721b9ea..0eb54e8d 100644 --- a/taps/apps/physics.py +++ b/taps/apps/physics.py @@ -88,7 +88,11 @@ def create_contour_plot( x = numpy.linspace(0, config.width, num=config.width * config.resolution) y = numpy.linspace(0, config.width, num=config.width * config.resolution) - for ax, positions in zip(axs, (initial_positions, final_positions)): + for ax, positions in zip( + axs, + (initial_positions, final_positions), + strict=False, + ): handle = ax.contour(x, y, heightmap, levels=10) plt.clabel(handle, inline=True) px, py = ( @@ -99,7 +103,9 @@ def create_contour_plot( ) ax.scatter(px, py, s=16, c='#FFFFFF', zorder=100) - for i, (ax, title) in enumerate(zip(axs, ('Initial', 'Final'))): + for i, (ax, title) in enumerate( + zip(axs, ('Initial', 'Final'), strict=False), + ): ax.set_title(title) ax.set_xlim(0, config.width) ax.set_ylim(0, config.width) @@ -141,13 +147,17 @@ def create_terrain_plot( y = numpy.arange(0, config.width, 1 / config.resolution) x, y = numpy.meshgrid(x, y) - for ax, positions in zip(axs, (initial_positions, final_positions)): - px, py, _ = zip(*positions) + for ax, positions in zip( + axs, + (initial_positions, final_positions), + strict=False, + ): + px, py, _ = zip(*positions, strict=False) xy = numpy.vstack([px, py]) kde = gaussian_kde(xy)(xy) kde_grid = numpy.zeros_like(heightmap) - for i, (xi, yi) in enumerate(zip(px, py)): + for i, (xi, yi) in enumerate(zip(px, py, strict=False)): # Clamp positions to be in [0, config.width]. If a ball rolled # off the edge, it's position would be outside the mesh map. max_index = (config.width * config.resolution) - 1 @@ -166,7 +176,7 @@ def create_terrain_plot( alpha=0.9, ) - for ax, title in zip(axs, ('Initial', 'Final')): + for ax, title in zip(axs, ('Initial', 'Final'), strict=False): ax.set_title(title, pad=-20) ax.set_xlim(0, config.width) ax.set_ylim(0, config.width) diff --git a/taps/engine/_config.py b/taps/engine/_config.py index 2abd6c45..c20fae36 100644 --- a/taps/engine/_config.py +++ b/taps/engine/_config.py @@ -1,7 +1,6 @@ from __future__ import annotations from typing import Any -from typing import Optional from pydantic import BaseModel from pydantic import ConfigDict @@ -22,15 +21,15 @@ class EngineConfig(BaseModel): default_factory=ProcessPoolConfig, description='Executor configuration.', ) - filter: Optional[FilterConfig] = Field( # noqa: UP045 + filter: FilterConfig | None = Field( default=None, description='Filter configuration.', ) - transformer: Optional[TransformerConfig] = Field( # noqa: UP045 + transformer: TransformerConfig | None = Field( default=None, description='Transformer configuration.', ) - task_record_file_name: Optional[str] = Field( # noqa: UP045 + task_record_file_name: str | None = Field( 'tasks.jsonl', description='Name of line-delimted JSON file to log task records to.', ) diff --git a/taps/engine/_engine.py b/taps/engine/_engine.py index c7950aeb..0be7835a 100644 --- a/taps/engine/_engine.py +++ b/taps/engine/_engine.py @@ -18,14 +18,10 @@ from typing import Generic from typing import Iterable from typing import Iterator +from typing import ParamSpec from typing import Sequence from typing import TypeVar -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import ParamSpec -else: # pragma: <3.10 cover - from typing_extensions import ParamSpec - if sys.version_info >= (3, 11): # pragma: >=3.11 cover from typing import Self else: # pragma: <3.11 cover @@ -336,7 +332,10 @@ def map( if timeout is not None: end_time = timeout + time.monotonic() - tasks = [self.submit(function, *args) for args in zip(*iterables)] + tasks = [ + self.submit(function, *args) + for args in zip(*iterables, strict=False) + ] # Yield must be hidden in closure so that the futures are submitted # before the first iterator value is required. diff --git a/taps/engine/task.py b/taps/engine/task.py index d569ef99..69d0408a 100644 --- a/taps/engine/task.py +++ b/taps/engine/task.py @@ -3,24 +3,17 @@ import dataclasses import functools import socket -import sys import time from dataclasses import field from typing import Any from typing import Callable from typing import Generic -from typing import List -from typing import Optional from typing import overload +from typing import ParamSpec from typing import Protocol from typing import runtime_checkable from typing import TypeVar -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import ParamSpec -else: # pragma: <3.10 cover - from typing_extensions import ParamSpec - from taps.engine.transform import TaskTransformer P = ParamSpec('P') @@ -147,7 +140,7 @@ class TaskInfo: ), }, ) - parent_task_ids: List[str] = field( # noqa: UP006 + parent_task_ids: list[str] = field( metadata={ 'description': ( 'UUIDs of parent tasks. A task is a child task if its ' @@ -163,7 +156,7 @@ class TaskInfo: ), }, ) - received_time: Optional[float] = field( # noqa: UP045 + received_time: float | None = field( default=None, metadata={ 'description': ( @@ -174,7 +167,7 @@ class TaskInfo: ), }, ) - success: Optional[bool] = field( # noqa: UP045 + success: bool | None = field( default=None, metadata={ 'description': ( @@ -183,11 +176,11 @@ class TaskInfo: ), }, ) - exception: Optional[ExceptionInfo] = field( # noqa: UP045 + exception: ExceptionInfo | None = field( default=None, metadata={'description': 'Task exception information.'}, ) - execution: Optional[ExecutionInfo] = field( # noqa: UP045 + execution: ExecutionInfo | None = field( default=None, metadata={'description': 'Task execution information.'}, ) diff --git a/taps/executor/dask.py b/taps/executor/dask.py index 4dc81f57..77d08d5b 100644 --- a/taps/executor/dask.py +++ b/taps/executor/dask.py @@ -1,7 +1,6 @@ from __future__ import annotations import logging -import sys from concurrent.futures import Executor from concurrent.futures import Future from typing import Any @@ -10,14 +9,9 @@ from typing import Iterable from typing import Iterator from typing import Literal -from typing import Optional +from typing import ParamSpec from typing import TypeVar -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import ParamSpec -else: # pragma: <3.10 cover - from typing_extensions import ParamSpec - import dask from dask.distributed import Client from dask.distributed import Future as DaskFuture @@ -95,6 +89,7 @@ def map( *iterables: Iterable[Any], timeout: float | None = None, chunksize: int = 1, + buffersize: int | None = None, ) -> Iterator[T]: """Map a function onto iterables of arguments. @@ -105,6 +100,7 @@ def map( timeout: The maximum number of seconds to wait. If None, then there is no limit on the wait time. chunksize: Sets the Dask batch size. + buffersize: Ignored. Returns: An iterator equivalent to: `map(func, *iterables)` but the calls \ @@ -150,7 +146,7 @@ class DaskDistributedConfig(ExecutorConfig): """[`DaskDistributedExecutor`][taps.executor.dask.DaskDistributedExecutor] plugin configuration.""" # noqa: E501 name: Literal['dask'] = Field('dask', description='Executor name.') - scheduler: Optional[str] = Field( # noqa: UP045 + scheduler: str | None = Field( None, description='Dask scheduler address.', ) @@ -158,7 +154,7 @@ class DaskDistributedConfig(ExecutorConfig): False, description='Use threads instead of processes for dask workers.', ) - workers: Optional[int] = Field( # noqa: UP045 + workers: int | None = Field( None, description='Maximum number of dask workers.', ) @@ -166,14 +162,14 @@ class DaskDistributedConfig(ExecutorConfig): True, description='Configure if workers are daemon.', ) - wait_for_workers: Optional[int] = Field( # noqa: UP045 + wait_for_workers: int | None = Field( None, description=( 'Wait for N workers to connect before starting. ' 'Useful when connecting to a remote scheduler.' ), ) - wait_for_workers_timeout: Optional[float] = Field( # noqa: UP045 + wait_for_workers_timeout: float | None = Field( None, description='Timeout (seconds) for waiting for workers to connect.', ) diff --git a/taps/executor/parsl.py b/taps/executor/parsl.py index 0a4950b0..059085e8 100644 --- a/taps/executor/parsl.py +++ b/taps/executor/parsl.py @@ -1,12 +1,8 @@ from __future__ import annotations import multiprocessing -from typing import Dict from typing import Literal -from typing import Optional -from typing import Tuple from typing import TYPE_CHECKING -from typing import Union import parsl from parsl.addresses import address_by_hostname @@ -45,7 +41,7 @@ class ParslLocalConfig(TapsExecutorConfig): 'parsl-local', description='Executor name.', ) - workers: Optional[int] = Field( # noqa: UP045 + workers: int | None = Field( None, description='Maximum number of parsl workers.', ) @@ -96,10 +92,10 @@ class ParslHTExConfig(TapsExecutorConfig): 'parsl-htex', description='Executor name.', ) - htex: Union[HTExConfig, Dict[str, HTExConfig]] = Field( # noqa: UP006,UP007 + htex: HTExConfig | dict[str, HTExConfig] = Field( description='HTEx configuration.', ) - app_cache: Optional[bool] = Field( # noqa: UP045 + app_cache: bool | None = Field( None, description='Enable app caching.', ) @@ -107,15 +103,15 @@ class ParslHTExConfig(TapsExecutorConfig): 0, description='Number of task retries in case of task failure.', ) - strategy: Optional[str] = Field( # noqa: UP045 + strategy: str | None = Field( None, description='Block scaling strategy.', ) - max_idletime: Optional[float] = Field( # noqa: UP045 + max_idletime: float | None = Field( None, description='Idle time before strategy can shutdown unused blocks.', ) - monitoring: Optional[MonitoringConfig] = Field( # noqa: UP045 + monitoring: MonitoringConfig | None = Field( None, description='Database monitoring configuration.', ) @@ -211,30 +207,30 @@ class HTExConfig(BaseModel): model_config = ConfigDict(extra='allow') label: str = Field('taps-htex', description='Executor label.') - provider: Optional[ProviderConfig] = Field( # noqa: UP045 + provider: ProviderConfig | None = Field( None, description='Configuration for the compute resource provider.', ) - address: Optional[Union[str, AddressConfig]] = Field( # noqa: UP007,UP045 + address: str | AddressConfig | None = Field( None, description='Address to connect to the main Parsl process.', ) - manager_selector: Optional[ManagerSelectorConfig] = Field( # noqa: UP045 + manager_selector: ManagerSelectorConfig | None = Field( None, description=( 'Configuration for the manager selector (available in ' 'Parsl v2024.8.5 and later).' ), ) - worker_port: Optional[int] = Field( # noqa: UP045 + worker_port: int | None = Field( None, description='Port used by workers to connect to Parsl', ) - worker_port_range: Optional[Tuple[int, int]] = Field( # noqa: UP006,UP045 + worker_port_range: tuple[int, int] | None = Field( None, description='Range of ports to choose worker ports from.', ) - interchange_port_range: Optional[Tuple[int, int]] = Field( # noqa: UP006,UP045 + interchange_port_range: tuple[int, int] | None = Field( None, description='Ports used by Parsl to connect to interchange.', ) @@ -347,7 +343,7 @@ class ProviderConfig(BaseModel): model_config = ConfigDict(extra='allow') kind: str = Field(description='Execution provider class name') - launcher: Optional[LauncherConfig] = Field( # noqa: UP045 + launcher: LauncherConfig | None = Field( None, description='Launcher configuration.', ) @@ -522,11 +518,11 @@ class MonitoringConfig(BaseModel): model_config = ConfigDict(extra='allow') - hub_address: Optional[Union[str, AddressConfig]] = Field( # noqa: UP007,UP045 + hub_address: str | AddressConfig | None = Field( None, description='Address to connect to the monitoring hub.', ) - hub_port_range: Optional[Tuple[int, int]] = Field( # noqa: UP006,UP045 + hub_port_range: tuple[int, int] | None = Field( None, description='Port range for a ZMQ channel from executor process.', ) diff --git a/taps/executor/python.py b/taps/executor/python.py index ba474325..2eb819ed 100644 --- a/taps/executor/python.py +++ b/taps/executor/python.py @@ -4,7 +4,6 @@ from concurrent.futures import ProcessPoolExecutor from concurrent.futures import ThreadPoolExecutor from typing import Literal -from typing import Optional from pydantic import Field @@ -25,9 +24,7 @@ class ProcessPoolConfig(ExecutorConfig): multiprocessing.cpu_count(), description='Maximum number of processes.', ) - context: Optional[ # noqa: UP045 - Literal['fork', 'spawn', 'forkserver'] - ] = Field( + context: Literal['fork', 'spawn', 'forkserver'] | None = Field( None, description=( 'Multiprocessing start method (one of fork, spawn, or forkserver).' diff --git a/taps/executor/ray.py b/taps/executor/ray.py index 3085c14b..6ec84768 100644 --- a/taps/executor/ray.py +++ b/taps/executor/ray.py @@ -1,19 +1,13 @@ from __future__ import annotations -import sys from concurrent.futures import Executor from concurrent.futures import Future from typing import Any from typing import Callable from typing import Literal -from typing import Optional +from typing import ParamSpec from typing import TypeVar -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import ParamSpec -else: # pragma: <3.10 cover - from typing_extensions import ParamSpec - try: import ray @@ -140,11 +134,11 @@ class RayConfig(ExecutorConfig): """[`RayExecutor`][taps.executor.ray.RayExecutor] plugin configuration.""" name: Literal['ray'] = Field('ray', description='Executor name.') - address: Optional[str] = Field( # noqa: UP045 + address: str | None = Field( 'local', description='Ray scheduler address (default spawns local cluster).', ) - num_cpus: Optional[int] = Field( # noqa: UP045, + num_cpus: int | None = Field( None, description='Maximum number of CPUs that ray will use.', ) diff --git a/taps/executor/taskvine.py b/taps/executor/taskvine.py index 8e150062..ad64f1ec 100644 --- a/taps/executor/taskvine.py +++ b/taps/executor/taskvine.py @@ -10,14 +10,9 @@ from typing import Iterable from typing import Iterator from typing import Literal -from typing import Optional +from typing import ParamSpec from typing import TypeVar -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import ParamSpec -else: # pragma: <3.10 cover - from typing_extensions import ParamSpec - from pydantic import Field try: # pragma: no cover @@ -204,7 +199,10 @@ def map( if timeout is not None: end_time = timeout + time.monotonic() - futures = [self.submit(function, *args) for args in zip(*iterables)] + futures = [ + self.submit(function, *args) + for args in zip(*iterables, strict=False) + ] def _result_iterator() -> Generator[R, None, None]: futures.reverse() @@ -226,7 +224,7 @@ class TaskVineConfig(ExecutorConfig): 1, description='Number of cores per task.', ) - cores_per_worker: Optional[int] = Field( # noqa: UP045 + cores_per_worker: int | None = Field( None, description='Number of cores per worker.', ) diff --git a/taps/executor/utils.py b/taps/executor/utils.py index be5fd0c3..b9f083c0 100644 --- a/taps/executor/utils.py +++ b/taps/executor/utils.py @@ -17,17 +17,13 @@ from typing import Iterable from typing import Iterator from typing import Mapping +from typing import ParamSpec from typing import Sequence from typing import TypeVar from taps.future import FutureProtocol from taps.logging import get_repr -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import ParamSpec -else: # pragma: <3.10 cover - from typing_extensions import ParamSpec - if sys.version_info >= (3, 11): # pragma: >=3.11 cover from typing import Self else: # pragma: <3.11 cover @@ -43,7 +39,7 @@ def _get_chunks( *iterables: Iterable[T], chunksize: int, ) -> Generator[tuple[tuple[T, ...], ...], None, None]: - it = zip(*iterables) + it = zip(*iterables, strict=False) while True: chunk = tuple(itertools.islice(it, chunksize)) if not chunk: @@ -207,6 +203,7 @@ def map( *iterables: Iterable[Any], timeout: float | None = None, chunksize: int = 1, + buffersize: int | None = None, ) -> Iterator[T]: """Map a function onto iterables of arguments. @@ -219,6 +216,7 @@ def map( chunksize: If greater than one, the iterables will be chopped into chunks of size chunksize and submitted to the executor. If set to one, the items in the list will be sent one at a time. + buffersize: Ignored. Returns: An iterator equivalent to: `map(func, *iterables)` but the calls \ diff --git a/taps/filter/_object.py b/taps/filter/_object.py index e5edec60..1fc160bf 100644 --- a/taps/filter/_object.py +++ b/taps/filter/_object.py @@ -5,9 +5,7 @@ import re import sys from typing import Any -from typing import List from typing import Literal -from typing import Optional from typing import Sequence from pydantic import Field @@ -150,7 +148,7 @@ class ObjectTypeFilterConfig(FilterConfig): 'object-type', description='Filter name.', ) - patterns: Optional[List[str]] = Field( # noqa: UP006,UP045 + patterns: list[str] | None = Field( None, description='List of patterns to match against type names.', ) diff --git a/taps/plugins.py b/taps/plugins.py index 7e16bad8..03dcf391 100644 --- a/taps/plugins.py +++ b/taps/plugins.py @@ -1,16 +1,11 @@ from __future__ import annotations -import sys from typing import Callable from typing import Literal from typing import TYPE_CHECKING +from typing import TypeAlias from typing import TypeVar -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import TypeAlias -else: # pragma: <3.10 cover - from typing_extensions import TypeAlias - from pydantic import BaseModel from taps.apps import AppConfig diff --git a/taps/record.py b/taps/record.py index 3c346e80..6fcfa83a 100644 --- a/taps/record.py +++ b/taps/record.py @@ -5,13 +5,8 @@ import sys from types import TracebackType from typing import Any -from typing import Dict from typing import Protocol - -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import TypeAlias -else: # pragma: <3.10 cover - from typing_extensions import TypeAlias +from typing import TypeAlias if sys.version_info >= (3, 11): # pragma: >=3.11 cover from typing import Self @@ -19,7 +14,7 @@ from typing_extensions import Self -Record: TypeAlias = Dict[str, Any] # noqa: UP006 +Record: TypeAlias = dict[str, Any] """Record type.""" diff --git a/taps/run/config.py b/taps/run/config.py index 5b76a983..a7301f3c 100644 --- a/taps/run/config.py +++ b/taps/run/config.py @@ -4,9 +4,6 @@ import sys from datetime import datetime from typing import Any -from typing import Dict -from typing import Optional -from typing import Union if sys.version_info >= (3, 11): # pragma: >=3.11 cover import tomllib @@ -35,15 +32,15 @@ class LoggingConfig(BaseModel): """Logging configuration.""" - level: Union[int, str] = Field( # noqa: UP007 + level: int | str = Field( 'INFO', description='Minimum logging level for stdout.', ) - file_level: Optional[Union[int, str]] = Field( # noqa: UP007,UP045 + file_level: int | str | None = Field( None, description='Override logging level for the log file.', ) - file_name: Optional[str] = Field( # noqa: UP045 + file_name: str | None = Field( 'log.txt', description='Logging file name.', ) @@ -59,7 +56,7 @@ class RunConfig(BaseModel): '"{executor}" for formatting).' ), ) - env_vars: Optional[Dict[str, str]] = Field( # noqa: UP006,UP045 + env_vars: dict[str, str] | None = Field( None, description='Environment variables to set during benchmarking.', ) @@ -163,7 +160,7 @@ def _make_config_cls(options: dict[str, Any]) -> type[Config]: description=f'Filter configuration (selected: {filter_name}).', ) else: - filter_cls = Optional[FilterConfig] # type: ignore[assignment] + filter_cls = FilterConfig | None # type: ignore[assignment] filter_field = Field( # type: ignore[assignment] None, description='Filter configuration (selected: none).', @@ -179,7 +176,7 @@ def _make_config_cls(options: dict[str, Any]) -> type[Config]: ), ) else: - transformer_cls = Optional[TransformerConfig] # type: ignore[assignment] + transformer_cls = TransformerConfig | None # type: ignore[assignment] transformer_field = Field( # type: ignore[assignment] None, description='Transformer configuration (selected: none).', diff --git a/taps/run/env.py b/taps/run/env.py index 61ce4dc1..037770a1 100644 --- a/taps/run/env.py +++ b/taps/run/env.py @@ -9,7 +9,6 @@ import sys from dataclasses import field from typing import Any -from typing import Optional if sys.version_info >= (3, 11): # pragma: >=3.11 cover from typing import Self @@ -38,10 +37,10 @@ class Hardware: """Hardware information.""" architecture: str = field(metadata={'description': 'CPU architecture.'}) - physical_cores: Optional[int] = field( # noqa: UP045 + physical_cores: int | None = field( metadata={'description': 'CPU physical core count.'}, ) - logical_cores: Optional[int] = field( # noqa: UP045 + logical_cores: int | None = field( metadata={'description': 'CPU logical core count.'}, ) memory_capacity: float = field( diff --git a/taps/transformer/_proxy.py b/taps/transformer/_proxy.py index 998cf03e..a0fe0541 100644 --- a/taps/transformer/_proxy.py +++ b/taps/transformer/_proxy.py @@ -6,10 +6,8 @@ import sys from typing import Any from typing import cast -from typing import Dict from typing import Literal from typing import TypeVar -from typing import Union if sys.version_info >= (3, 11): # pragma: >=3.11 cover from typing import Self @@ -30,7 +28,7 @@ from taps.transformer._protocol import TransformerConfig T = TypeVar('T') -JSON = Union[int, float, str, Dict[str, 'JSON']] # noqa: UP006 +JSON = int | float | str | dict[str, 'JSON'] _PROXYSTORE_DIR = 'proxystore' _PROXYSTORE_AGGREGATE_FILE = 'aggregated.json' _PROXYSTORE_STATS_FILE = 'stats.jsonl' diff --git a/testing/globus.py b/testing/globus.py index e5374fa0..4a772c62 100644 --- a/testing/globus.py +++ b/testing/globus.py @@ -1,19 +1,14 @@ from __future__ import annotations import contextlib -import sys from concurrent.futures import Future from typing import Any from typing import Callable from typing import Generator +from typing import ParamSpec from typing import TypeVar from unittest import mock -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import ParamSpec -else: # pragma: <3.10 cover - from typing_extensions import ParamSpec - import globus_compute_sdk P = ParamSpec('P') diff --git a/tests/engine/task_test.py b/tests/engine/task_test.py index ab79e3ab..06bd001c 100644 --- a/tests/engine/task_test.py +++ b/tests/engine/task_test.py @@ -3,13 +3,9 @@ import pickle import sys from typing import Callable +from typing import ParamSpec from typing import TypeVar -if sys.version_info >= (3, 10): # pragma: >=3.10 cover - from typing import ParamSpec -else: # pragma: <3.10 cover - from typing_extensions import ParamSpec - if sys.version_info >= (3, 11): # pragma: >=3.11 cover from typing import assert_type else: # pragma: <3.11 cover diff --git a/tox.ini b/tox.ini index af2ab210..b6bff979 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py39, py310, py311, py312, pre-commit, docs +envlist = py{310,311,312,313}, pre-commit, docs [testenv] extras = dev