Skip to content

Commit 3453e5d

Browse files
authored
refactor: extract type aliases to _types.py to fix import cycle (#13)
Move JSONObject, JSONValue, JSONArray, JSONPrimitive, Key, KeyElement, and KeySpecifier type aliases to a new _types.py module. This breaks the circular import between _exceptions.py, _json.py, and _keys.py. The cycle existed because _exceptions.py needed type annotations from _json.py and _keys.py, but those modules import exceptions.
1 parent 48ed64a commit 3453e5d

File tree

5 files changed

+60
-33
lines changed

5 files changed

+60
-33
lines changed

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ ignore-variadic-names = true
208208
"S101", # assert statements in tests
209209
"PLR2004", # magic values in assertions in tests
210210
]
211+
"src/jsonlt/_exceptions.py" = [
212+
"TC001", # direct imports from _types.py to break circular import cycle
213+
]
211214

212215
[tool.uv.build-backend]
213216
module-name = "jsonlt"

src/jsonlt/_exceptions.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,9 @@
44
following the specification's error categories.
55
"""
66

7-
# pyright: reportImportCycles=false
8-
9-
from typing import TYPE_CHECKING
107
from typing_extensions import override
118

12-
if TYPE_CHECKING:
13-
from ._json import JSONObject
14-
from ._keys import Key
9+
from ._types import JSONObject, Key
1510

1611

1712
class JSONLTError(Exception):

src/jsonlt/_json.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,17 @@
99

1010
import json
1111
from json import JSONDecodeError
12-
from typing import TYPE_CHECKING, TypeAlias, cast
12+
from typing import TYPE_CHECKING, cast
1313

1414
from ._constants import MIN_NESTING_DEPTH
1515
from ._exceptions import LimitError, ParseError
16+
from ._types import JSONArray, JSONObject, JSONPrimitive, JSONValue
1617

1718
if TYPE_CHECKING:
1819
from collections.abc import Mapping, Sequence
1920

20-
# JSON type definitions per RFC 8259
21-
# Using string annotations for forward references to avoid runtime | issues
22-
JSONPrimitive: TypeAlias = "str | int | float | bool | None"
23-
JSONArray: TypeAlias = "list[JSONValue]"
24-
JSONObject: TypeAlias = "dict[str, JSONValue]"
25-
JSONValue: TypeAlias = "JSONPrimitive | JSONArray | JSONObject"
21+
# Re-export type aliases for backwards compatibility
22+
__all__ = ["JSONArray", "JSONObject", "JSONPrimitive", "JSONValue"]
2623

2724

2825
def json_nesting_depth(value: object) -> int:

src/jsonlt/_keys.py

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import json
88
from collections.abc import Sequence
9-
from typing import TYPE_CHECKING, TypeAlias
9+
from typing import TYPE_CHECKING
1010

1111
from ._constants import (
1212
MAX_INTEGER_KEY,
@@ -16,30 +16,14 @@
1616
)
1717
from ._exceptions import InvalidKeyError, LimitError
1818
from ._json import utf8_byte_length
19+
from ._types import Key, KeyElement, KeySpecifier
1920

2021
if TYPE_CHECKING:
2122
from typing import TypeGuard
2223
from typing_extensions import TypeIs
2324

24-
KeyElement: TypeAlias = "str | int"
25-
"""A key element is a string or integer that may appear in a tuple key."""
26-
27-
Key: TypeAlias = "str | int | tuple[str | int, ...]"
28-
"""A key identifies a record within a table.
29-
30-
A key is one of:
31-
- A string
32-
- An integer in the range [-(2^53)+1, (2^53)-1]
33-
- A tuple of key elements (non-empty, max 16 elements)
34-
"""
35-
36-
KeySpecifier: TypeAlias = "str | tuple[str, ...]"
37-
"""A key specifier defines how to extract a key from a record.
38-
39-
A key specifier is one of:
40-
- A string naming a single field
41-
- A tuple of strings naming multiple fields (for compound keys)
42-
"""
25+
# Re-export type aliases for backwards compatibility
26+
__all__ = ["Key", "KeyElement", "KeySpecifier"]
4327

4428

4529
def is_valid_key_element(value: object) -> "TypeIs[str | int]":

src/jsonlt/_types.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""Type aliases for JSONLT.
2+
3+
This module contains ONLY TypeAlias definitions for JSON and key types.
4+
It exists to break circular imports between _exceptions.py, _json.py, and _keys.py:
5+
- _exceptions.py needs JSONObject and Key for ConflictError
6+
- _json.py needs ParseError, LimitError from _exceptions.py
7+
- _keys.py needs InvalidKeyError, LimitError from _exceptions.py
8+
9+
By placing the type aliases here with no dependencies on other JSONLT modules,
10+
all modules can safely import from _types.py.
11+
"""
12+
13+
from typing import TypeAlias
14+
15+
# JSON type definitions per RFC 8259
16+
# Using string annotations for forward references to avoid runtime | issues
17+
JSONPrimitive: TypeAlias = "str | int | float | bool | None"
18+
"""A JSON primitive value: string, number, boolean, or null."""
19+
20+
JSONArray: TypeAlias = "list[JSONValue]"
21+
"""A JSON array containing any JSON values."""
22+
23+
JSONObject: TypeAlias = "dict[str, JSONValue]"
24+
"""A JSON object mapping string keys to JSON values."""
25+
26+
JSONValue: TypeAlias = "JSONPrimitive | JSONArray | JSONObject"
27+
"""Any JSON value: primitive, array, or object."""
28+
29+
# Key type definitions per JSONLT specification
30+
KeyElement: TypeAlias = "str | int"
31+
"""A key element is a string or integer that may appear in a tuple key."""
32+
33+
Key: TypeAlias = "str | int | tuple[str | int, ...]"
34+
"""A key identifies a record within a table.
35+
36+
A key is one of:
37+
- A string
38+
- An integer in the range [-(2^53)+1, (2^53)-1]
39+
- A tuple of key elements (non-empty, max 16 elements)
40+
"""
41+
42+
KeySpecifier: TypeAlias = "str | tuple[str, ...]"
43+
"""A key specifier defines how to extract a key from a record.
44+
45+
A key specifier is one of:
46+
- A string naming a single field
47+
- A tuple of strings naming multiple fields (for compound keys)
48+
"""

0 commit comments

Comments
 (0)