From c55eba2077f74d7b6e36ea4159984979d420b76e Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 6 Jun 2026 22:29:16 +0100 Subject: [PATCH] Add max deps and bump old ones --- great_ai/deploy/great_ai.py | 2 +- great_ai/deploy/routes/bootstrap_dashboard.py | 7 +- .../routes/dashboard/get_description.py | 6 +- .../large_file/large_file/large_file_base.py | 4 +- great_ai/models/use_model.py | 15 +++- great_ai/tracing/add_ground_truth.py | 4 +- great_ai/tracing/tracing_context.py | 8 +- great_ai/utilities/config_file/config_file.py | 6 +- .../parallel_map/manage_communication.py | 6 +- .../utilities/parallel_map/parallel_map.py | 2 +- .../parallel_map/threaded_parallel_map.py | 3 +- great_ai/views/operators.py | 4 +- .../views/outputs/sequence_labeling_output.py | 4 +- great_ai/views/sort_by.py | 3 +- great_ai/views/trace.py | 2 +- pyproject.toml | 78 ++++++++++--------- scripts/check-python.sh | 4 +- tests/utilities/test_config_file.py | 15 +++- 18 files changed, 98 insertions(+), 75 deletions(-) diff --git a/great_ai/deploy/great_ai.py b/great_ai/deploy/great_ai.py index a6ea818..ac14590 100644 --- a/great_ai/deploy/great_ai.py +++ b/great_ai/deploy/great_ai.py @@ -6,6 +6,7 @@ from typing import ( Callable, Generic, List, + Literal, Optional, Sequence, Tuple, @@ -17,7 +18,6 @@ from typing import ( from fastapi import FastAPI from tqdm import tqdm -from typing_extensions import Literal # <= Python 3.7 from ..constants import DASHBOARD_PATH from ..context import get_context diff --git a/great_ai/deploy/routes/bootstrap_dashboard.py b/great_ai/deploy/routes/bootstrap_dashboard.py index 2989c95..6bafc5f 100644 --- a/great_ai/deploy/routes/bootstrap_dashboard.py +++ b/great_ai/deploy/routes/bootstrap_dashboard.py @@ -1,7 +1,7 @@ from pathlib import Path +from a2wsgi import WSGIMiddleware from fastapi import FastAPI -from fastapi.middleware.wsgi import WSGIMiddleware from fastapi.responses import FileResponse, RedirectResponse from fastapi.staticfiles import StaticFiles @@ -19,7 +19,10 @@ def bootstrap_dashboard(app: FastAPI, function_name: str, documentation: str) -> def get_favicon() -> FileResponse: return FileResponse(PATH / "dashboard/assets/favicon.ico") - app.mount(DASHBOARD_PATH, WSGIMiddleware(dash_app)) + # a2wsgi types its WSGI input (Flask) and ASGI output more strictly than + # Starlette's loose WSGIApp/ASGIApp aliases, so mypy flags the mount despite + # this being the canonical, runtime-correct way to bridge the Dash app. + app.mount(DASHBOARD_PATH, WSGIMiddleware(dash_app)) # type: ignore[arg-type] @app.get("/", include_in_schema=False) def redirect_to_entrypoint() -> RedirectResponse: diff --git a/great_ai/deploy/routes/dashboard/get_description.py b/great_ai/deploy/routes/dashboard/get_description.py index 74d82c8..8982f24 100644 --- a/great_ai/deploy/routes/dashboard/get_description.py +++ b/great_ai/deploy/routes/dashboard/get_description.py @@ -16,7 +16,8 @@ def get_description( style={"color": accent_color}, ), dcc.Markdown( - strip_lines(f""" + strip_lines( + f""" > View the live data of your deployment here. ## Using the API @@ -26,7 +27,8 @@ def get_description( ## Details {function_docs} - """), + """ + ), className="description", ), ] diff --git a/great_ai/large_file/large_file/large_file_base.py b/great_ai/large_file/large_file/large_file_base.py index 64138c5..92fa5d9 100644 --- a/great_ai/large_file/large_file/large_file_base.py +++ b/great_ai/large_file/large_file/large_file_base.py @@ -7,9 +7,7 @@ from abc import ABC, abstractmethod from functools import lru_cache from pathlib import Path from types import TracebackType -from typing import IO, Any, List, Optional, Type, Union, cast - -from typing_extensions import Literal # <= Python 3.7 +from typing import IO, Any, List, Literal, Optional, Type, Union, cast from great_ai.utilities import ConfigFile, get_logger diff --git a/great_ai/models/use_model.py b/great_ai/models/use_model.py index 5fcca36..e18eff8 100644 --- a/great_ai/models/use_model.py +++ b/great_ai/models/use_model.py @@ -1,8 +1,19 @@ from functools import wraps -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, TypeVar, Union, cast +from typing import ( + Any, + Callable, + Dict, + List, + Literal, + Optional, + Set, + Tuple, + TypeVar, + Union, + cast, +) from dill import load -from typing_extensions import Literal # <= Python 3.7 from ..context import get_context from ..helper import get_function_metadata_store diff --git a/great_ai/tracing/add_ground_truth.py b/great_ai/tracing/add_ground_truth.py index aa7f7b5..15d87a9 100644 --- a/great_ai/tracing/add_ground_truth.py +++ b/great_ai/tracing/add_ground_truth.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timezone from math import ceil from random import shuffle from typing import Any, Iterable, List, TypeVar, Union, cast @@ -94,7 +94,7 @@ def add_ground_truth( ) shuffle(split_tags) - created = datetime.utcnow().isoformat() + created = datetime.now(timezone.utc).replace(tzinfo=None).isoformat() traces = [ cast( Trace[T], diff --git a/great_ai/tracing/tracing_context.py b/great_ai/tracing/tracing_context.py index 7ff0cc8..9180061 100644 --- a/great_ai/tracing/tracing_context.py +++ b/great_ai/tracing/tracing_context.py @@ -1,12 +1,10 @@ from contextvars import ContextVar -from datetime import datetime +from datetime import datetime, timezone from time import perf_counter from types import TracebackType -from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, cast +from typing import Any, Dict, Generic, List, Literal, Optional, Type, TypeVar, cast from uuid import uuid4 -from typing_extensions import Literal # <= Python 3.7 - from ..constants import DEVELOPMENT_TAG_NAME, ONLINE_TAG_NAME, PRODUCTION_TAG_NAME from ..context import get_context from ..views import Model, Trace @@ -25,7 +23,7 @@ class TracingContext(Generic[T]): self._models: List[Model] = [] self._values: Dict[str, Any] = {} self._trace: Optional[Trace[T]] = None - self._start_datetime = datetime.utcnow() + self._start_datetime = datetime.now(timezone.utc).replace(tzinfo=None) self._start_time = perf_counter() self._name = function_name diff --git a/great_ai/utilities/config_file/config_file.py b/great_ai/utilities/config_file/config_file.py index 33e14b7..1116833 100644 --- a/great_ai/utilities/config_file/config_file.py +++ b/great_ai/utilities/config_file/config_file.py @@ -94,9 +94,11 @@ class ConfigFile(Mapping[str, str]): try: value = next(v for v in values if v) except StopIteration: - raise ParseError(f"""Cannot parse config file ({ + raise ParseError( + f"""Cannot parse config file ({ self._path.absolute() - }), error at key `{key}`""") + }), error at key `{key}`""" + ) already_exists = key in self._key_values if already_exists and not value.startswith( diff --git a/great_ai/utilities/parallel_map/manage_communication.py b/great_ai/utilities/parallel_map/manage_communication.py index 8476e4c..4d53d2b 100644 --- a/great_ai/utilities/parallel_map/manage_communication.py +++ b/great_ai/utilities/parallel_map/manage_communication.py @@ -39,9 +39,11 @@ def manage_communication( is_iteration_over = True except Exception as e: if ignore_exceptions: - logger.error(f"""Exception {e} encountered in input, traceback:\n{ + logger.error( + f"""Exception {e} encountered in input, traceback:\n{ traceback.format_exc() - }""") + }""" + ) else: raise diff --git a/great_ai/utilities/parallel_map/parallel_map.py b/great_ai/utilities/parallel_map/parallel_map.py index b2a36d6..f8c8e1f 100644 --- a/great_ai/utilities/parallel_map/parallel_map.py +++ b/great_ai/utilities/parallel_map/parallel_map.py @@ -3,6 +3,7 @@ from typing import ( Awaitable, Callable, Iterable, + Literal, Optional, Sequence, TypeVar, @@ -11,7 +12,6 @@ from typing import ( ) import dill -from typing_extensions import Literal # <= Python 3.7 from .get_config import get_config from .manage_communication import manage_communication diff --git a/great_ai/utilities/parallel_map/threaded_parallel_map.py b/great_ai/utilities/parallel_map/threaded_parallel_map.py index 262d000..21d80e5 100644 --- a/great_ai/utilities/parallel_map/threaded_parallel_map.py +++ b/great_ai/utilities/parallel_map/threaded_parallel_map.py @@ -4,6 +4,7 @@ from typing import ( Awaitable, Callable, Iterable, + Literal, Optional, Sequence, TypeVar, @@ -11,8 +12,6 @@ from typing import ( overload, ) -from typing_extensions import Literal # <= Python 3.7 - from .get_config import get_config from .manage_communication import manage_communication from .mapper_function import mapper_function diff --git a/great_ai/views/operators.py b/great_ai/views/operators.py index 3bd2624..071e266 100644 --- a/great_ai/views/operators.py +++ b/great_ai/views/operators.py @@ -1,6 +1,4 @@ -from typing import List - -from typing_extensions import Literal # <= Python 3.7 +from typing import List, Literal Operator = Literal[">=", "<=", "<", ">", "!=", "=", "contains"] diff --git a/great_ai/views/outputs/sequence_labeling_output.py b/great_ai/views/outputs/sequence_labeling_output.py index 1c54e2b..7dceb8c 100644 --- a/great_ai/views/outputs/sequence_labeling_output.py +++ b/great_ai/views/outputs/sequence_labeling_output.py @@ -1,6 +1,4 @@ -from typing import Any, List, Optional - -from typing_extensions import Literal # <= Python 3.7 +from typing import Any, List, Literal, Optional from ..hashable_base_model import HashableBaseModel diff --git a/great_ai/views/sort_by.py b/great_ai/views/sort_by.py index aa92fcc..d0f8eff 100644 --- a/great_ai/views/sort_by.py +++ b/great_ai/views/sort_by.py @@ -1,5 +1,6 @@ +from typing import Literal + from pydantic import BaseModel -from typing_extensions import Literal # <= Python 3.7 class SortBy(BaseModel): diff --git a/great_ai/views/trace.py b/great_ai/views/trace.py index 1cefbb4..f7c6328 100644 --- a/great_ai/views/trace.py +++ b/great_ai/views/trace.py @@ -9,7 +9,7 @@ from .model import Model T = TypeVar("T") -class Trace(Generic[T], HashableBaseModel): +class Trace(HashableBaseModel, Generic[T]): """Universal structure for storing prediction traces and training data. Attributes: diff --git a/pyproject.toml b/pyproject.toml index 454863f..a02460d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,49 +21,51 @@ classifiers = [ "Natural Language :: English", ] keywords = ["SE4ML", "MLOps", "AI engineering", "general", "robust", "end-to-end", "automated", "trustworthy", "ai", "deployment"] -requires-python = ">= 3.7" +requires-python = ">= 3.10" dependencies = [ - "scikit-learn", - "matplotlib", - "numpy", - "nbconvert", - "ipython", - "unidecode >= 1.3.0", - "syntok >= 1.4.0", - "langcodes[data] >= 3.3.0", - "langdetect >= 1.0.9", - "tinydb >= 4.7.0", - "boto3 >= 1.23.0", - "plotly >= 5.8.0", - "pandas", - "dash >= 2.4.0", - "fastapi >= 0.70.0", - "uvicorn[standard] >= 0.18.0", - "watchdog >= 2.1.0", - "typeguard >= 2.10.0", - "pymongo >= 4.0.0", - "dill >= 0.3.5.0", - "tqdm", - "httpx >= 0.20.0", + "scikit-learn >= 1.3, < 2", + "matplotlib >= 3.7, < 4", + "numpy >= 1.24, < 3", + "nbconvert >= 7.0, < 8", + "ipython >= 8.0, < 10", + "unidecode >= 1.3.0, < 2", + "syntok >= 1.4.0, < 2", + "langcodes[data] >= 3.3.0, < 4", + "langdetect >= 1.0.9, < 2", + "tinydb >= 4.7.0, < 5", + "boto3 >= 1.34, < 2", + "plotly >= 5.20, < 7", + "pandas >= 2.0, < 4", + "dash >= 2.16, < 5", + "fastapi >= 0.110, < 1", + "uvicorn[standard] >= 0.27, < 1", + "a2wsgi >= 1.9, < 2", + "watchdog >= 3.0, < 7", + "typeguard >= 4.0, < 5", + "pydantic >= 2.5, < 3", + "pymongo >= 4.6, < 5", + "dill >= 0.3.6, < 1", + "tqdm >= 4.64, < 5", + "httpx >= 0.24, < 1", ] [project.optional-dependencies] dev = [ - "flit", - "mkdocs", - "mkdocstrings[python]", - "mkdocs-material", - "mkdocs-jupyter", - "mkdocs-git-revision-date-localized-plugin", - "autoflake", - "isort", - "black[jupyter]", - "mypy", - "flake8", - "tox", - "pytest", - "pytest-cov", - "pytest-asyncio", + "flit >= 3.9, < 4", + "mkdocs >= 1.5, < 2", + "mkdocstrings[python] >= 0.24, < 1", + "mkdocs-material >= 9.5, < 10", + "mkdocs-jupyter >= 0.24, < 1", + "mkdocs-git-revision-date-localized-plugin >= 1.2, < 2", + "autoflake >= 2.0, < 3", + "isort >= 5.12, < 7", + "black[jupyter] >= 25.1, < 26", + "mypy >= 1.8, < 2", + "flake8 >= 7.0, < 8", + "tox >= 4.0, < 5", + "pytest >= 7.4, < 9", + "pytest-cov >= 4.1, < 8", + "pytest-asyncio >= 0.23, < 2", ] [tool.pytest.ini_options] diff --git a/scripts/check-python.sh b/scripts/check-python.sh index 1c2c2e5..1b36198 100755 --- a/scripts/check-python.sh +++ b/scripts/check-python.sh @@ -3,7 +3,7 @@ set -e echo "Installing dependencies if necessary" -python3 -m pip install --upgrade autoflake isort black[jupyter] mypy flake8 +python3 -m pip install autoflake isort black[jupyter] mypy flake8 for dir in "$@"; do echo "Checking $dir" @@ -19,7 +19,7 @@ for dir in "$@"; do python3 -m mypy --namespace-packages --ignore-missing-imports --install-types --non-interactive --disallow-untyped-defs --disallow-incomplete-defs --follow-imports=silent --exclude=external/ --exclude=/build/ --exclude='(^|/)__main__\.py$' --pretty . fi - python3 -m flake8 . --count --show-source --statistics --exclude=__init__.py,.env,external --ignore=E501,E402,F821,W503,E722,E203,E704 + python3 -m flake8 . --count --show-source --statistics --exclude=__init__.py,.env,external --extend-ignore=E501,E402,F821,W503,E722,E203,E704 cd - done diff --git a/tests/utilities/test_config_file.py b/tests/utilities/test_config_file.py index b3476c1..df6a48b 100644 --- a/tests/utilities/test_config_file.py +++ b/tests/utilities/test_config_file.py @@ -14,11 +14,14 @@ def test_simple() -> None: assert c.first_key == "AndrĂ¡s" assert c.second_key == "test 2" assert c.third_key == "test= 2==" - assert c.fourth_key == """ + assert ( + c.fourth_key + == """ this# is multiline """ + ) assert c.whitespace == "hardly matters" with pytest.raises(KeyError): c.this @@ -31,11 +34,14 @@ def test_simple_dict() -> None: assert c["my_hashtag"] == "#great_ai" assert c["second_key"] == "test 2" assert c["third_key"] == "test= 2==" - assert c["fourth_key"] == """ + assert ( + c["fourth_key"] + == """ this# is multiline """ + ) assert c["whitespace"] == "hardly matters" with pytest.raises(KeyError): c["#this"] @@ -49,11 +55,14 @@ def test_string_path() -> None: assert c.second_key == "test 2" assert c.third_key == "test= 2==" - assert c.fourth_key == """ + assert ( + c.fourth_key + == """ this# is multiline """ + ) assert c.whitespace == "hardly matters"