Skip to content

Commit b55c1bf

Browse files
committed
Drop support for Python < 3.10 and MySQL < 8.0
- Update pyproject.toml to require Python >=3.10 (was >=3.9) - Update ruff target-version to py310 - Update pixi Python version constraint - Modernize type hints to use Python 3.10+ union syntax (X | Y instead of Union[X, Y], X | None instead of Optional[X]) - Use built-in dict, list, tuple for generics instead of typing imports - Update MySQL documentation reference from 5.7 to 8.0
1 parent 69ed63e commit b55c1bf

File tree

5 files changed

+29
-31
lines changed

5 files changed

+29
-31
lines changed

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ dependencies = [
2323
"setuptools",
2424
"pydantic-settings>=2.0.0",
2525
]
26-
requires-python = ">=3.9,<3.14"
26+
requires-python = ">=3.10,<3.14"
2727
authors = [
2828
{name = "Dimitri Yatsenko", email = "[email protected]"},
2929
{name = "Thinh Nguyen", email = "[email protected]"},
@@ -102,7 +102,7 @@ dev = [
102102
[tool.ruff]
103103
# Equivalent to flake8 configuration
104104
line-length = 127
105-
target-version = "py39"
105+
target-version = "py310"
106106

107107
[tool.ruff.lint]
108108
# Enable specific rule sets equivalent to flake8 configuration
@@ -176,7 +176,7 @@ test = { features = ["test"], solve-group = "default" }
176176
[tool.pixi.tasks]
177177

178178
[tool.pixi.dependencies]
179-
python = ">=3.9,<3.14"
179+
python = ">=3.10,<3.14"
180180
graphviz = ">=13.1.2,<14"
181181

182182
[tool.pixi.activation]

src/datajoint/condition.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import re
99
import uuid
1010
from dataclasses import dataclass
11-
from typing import List, Union
1211

1312
import numpy
1413
import pandas
@@ -67,8 +66,8 @@ class Top:
6766
In SQL, this corresponds to ORDER BY ... LIMIT ... OFFSET
6867
"""
6968

70-
limit: Union[int, None] = 1
71-
order_by: Union[str, List[str]] = "KEY"
69+
limit: int | None = 1
70+
order_by: str | list[str] = "KEY"
7271
offset: int = 0
7372

7473
def __post_init__(self):

src/datajoint/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def conn(host=None, user=None, password=None, *, init_fun=None, reset=False, use
8686
:param reset: whether the connection should be reset or not
8787
:param use_tls: TLS encryption option. Valid options are: True (required), False
8888
(required no TLS), None (TLS preferred, default), dict (Manually specify values per
89-
https://dev.mysql.com/doc/refman/5.7/en/connection-options.html#encrypted-connection-options).
89+
https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#encrypted-connection-options).
9090
"""
9191
if not hasattr(conn, "connection") or reset:
9292
host = host if host is not None else config["database.host"]

src/datajoint/settings.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from copy import deepcopy
3434
from enum import Enum
3535
from pathlib import Path
36-
from typing import Any, Dict, Iterator, Literal, Optional, Tuple, Union
36+
from typing import Any, Iterator, Literal
3737

3838
from pydantic import Field, SecretStr, field_validator
3939
from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -58,7 +58,7 @@
5858
logger = logging.getLogger(__name__.split(".")[0])
5959

6060

61-
def find_config_file(start: Optional[Path] = None) -> Optional[Path]:
61+
def find_config_file(start: Path | None = None) -> Path | None:
6262
"""
6363
Search for datajoint.json in current and parent directories.
6464
@@ -89,7 +89,7 @@ def find_config_file(start: Optional[Path] = None) -> Optional[Path]:
8989
current = current.parent
9090

9191

92-
def find_secrets_dir(config_path: Optional[Path] = None) -> Optional[Path]:
92+
def find_secrets_dir(config_path: Path | None = None) -> Path | None:
9393
"""
9494
Find the secrets directory.
9595
@@ -116,7 +116,7 @@ def find_secrets_dir(config_path: Optional[Path] = None) -> Optional[Path]:
116116
return None
117117

118118

119-
def read_secret_file(secrets_dir: Optional[Path], name: str) -> Optional[str]:
119+
def read_secret_file(secrets_dir: Path | None, name: str) -> str | None:
120120
"""
121121
Read a secret value from a file in the secrets directory.
122122
@@ -148,19 +148,19 @@ class DatabaseSettings(BaseSettings):
148148
)
149149

150150
host: str = Field(default="localhost", validation_alias="DJ_HOST")
151-
user: Optional[str] = Field(default=None, validation_alias="DJ_USER")
152-
password: Optional[SecretStr] = Field(default=None, validation_alias="DJ_PASS")
151+
user: str | None = Field(default=None, validation_alias="DJ_USER")
152+
password: SecretStr | None = Field(default=None, validation_alias="DJ_PASS")
153153
port: int = Field(default=3306, validation_alias="DJ_PORT")
154154
reconnect: bool = True
155-
use_tls: Optional[bool] = None
155+
use_tls: bool | None = None
156156

157157

158158
class ConnectionSettings(BaseSettings):
159159
"""Connection behavior settings."""
160160

161161
model_config = SettingsConfigDict(extra="forbid", validate_assignment=True)
162162

163-
init_function: Optional[str] = None
163+
init_function: str | None = None
164164
charset: str = "" # pymysql uses '' as default
165165

166166

@@ -184,8 +184,8 @@ class ExternalSettings(BaseSettings):
184184
validate_assignment=True,
185185
)
186186

187-
aws_access_key_id: Optional[str] = Field(default=None, validation_alias="DJ_AWS_ACCESS_KEY_ID")
188-
aws_secret_access_key: Optional[SecretStr] = Field(default=None, validation_alias="DJ_AWS_SECRET_ACCESS_KEY")
187+
aws_access_key_id: str | None = Field(default=None, validation_alias="DJ_AWS_ACCESS_KEY_ID")
188+
aws_secret_access_key: SecretStr | None = Field(default=None, validation_alias="DJ_AWS_SECRET_ACCESS_KEY")
189189

190190

191191
class Config(BaseSettings):
@@ -226,18 +226,18 @@ class Config(BaseSettings):
226226
fetch_format: Literal["array", "frame"] = "array"
227227
enable_python_native_blobs: bool = True
228228
add_hidden_timestamp: bool = False
229-
filepath_checksum_size_limit: Optional[int] = None
229+
filepath_checksum_size_limit: int | None = None
230230

231231
# External stores configuration
232-
stores: Dict[str, Dict[str, Any]] = Field(default_factory=dict)
232+
stores: dict[str, dict[str, Any]] = Field(default_factory=dict)
233233

234234
# Cache paths
235-
cache: Optional[Path] = None
236-
query_cache: Optional[Path] = None
235+
cache: Path | None = None
236+
query_cache: Path | None = None
237237

238238
# Internal: track where config was loaded from
239-
_config_path: Optional[Path] = None
240-
_secrets_dir: Optional[Path] = None
239+
_config_path: Path | None = None
240+
_secrets_dir: Path | None = None
241241

242242
@field_validator("loglevel", mode="after")
243243
@classmethod
@@ -248,13 +248,13 @@ def set_logger_level(cls, v: str) -> str:
248248

249249
@field_validator("cache", "query_cache", mode="before")
250250
@classmethod
251-
def convert_path(cls, v: Any) -> Optional[Path]:
251+
def convert_path(cls, v: Any) -> Path | None:
252252
"""Convert string paths to Path objects."""
253253
if v is None:
254254
return None
255255
return Path(v) if not isinstance(v, Path) else v
256256

257-
def get_store_spec(self, store: str) -> Dict[str, Any]:
257+
def get_store_spec(self, store: str) -> dict[str, Any]:
258258
"""
259259
Get configuration for an external store.
260260
@@ -279,11 +279,11 @@ def get_store_spec(self, store: str) -> Dict[str, Any]:
279279
raise DataJointError(f'Missing or invalid protocol in config.stores["{store}"]')
280280

281281
# Define required and allowed keys by protocol
282-
required_keys: Dict[str, Tuple[str, ...]] = {
282+
required_keys: dict[str, tuple[str, ...]] = {
283283
"file": ("protocol", "location"),
284284
"s3": ("protocol", "endpoint", "bucket", "access_key", "secret_key", "location"),
285285
}
286-
allowed_keys: Dict[str, Tuple[str, ...]] = {
286+
allowed_keys: dict[str, tuple[str, ...]] = {
287287
"file": ("protocol", "location", "subfolding", "stage"),
288288
"s3": (
289289
"protocol",
@@ -311,7 +311,7 @@ def get_store_spec(self, store: str) -> Dict[str, Any]:
311311

312312
return spec
313313

314-
def load(self, filename: Union[str, Path]) -> None:
314+
def load(self, filename: str | Path) -> None:
315315
"""
316316
Load settings from a JSON file.
317317
@@ -330,7 +330,7 @@ def load(self, filename: Union[str, Path]) -> None:
330330
self._update_from_flat_dict(data)
331331
self._config_path = filepath
332332

333-
def _update_from_flat_dict(self, data: Dict[str, Any]) -> None:
333+
def _update_from_flat_dict(self, data: dict[str, Any]) -> None:
334334
"""Update settings from a flat dict with dot notation keys."""
335335
for key, value in data.items():
336336
parts = key.split(".")

src/datajoint/table.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import re
99
import uuid
1010
from pathlib import Path
11-
from typing import Union
1211

1312
import numpy as np
1413
import pandas
@@ -430,7 +429,7 @@ def delete_quick(self, get_count=False):
430429
def delete(
431430
self,
432431
transaction: bool = True,
433-
safemode: Union[bool, None] = None,
432+
safemode: bool | None = None,
434433
force_parts: bool = False,
435434
force_masters: bool = False,
436435
) -> int:

0 commit comments

Comments
 (0)