diff --git a/pyproject.toml b/pyproject.toml index 8876efe..9ecbdec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -208,6 +208,9 @@ ignore-variadic-names = true "S101", # assert statements in tests "PLR2004", # magic values in assertions in tests ] +"src/jsonlt/_exceptions.py" = [ + "TC001", # direct imports from _types.py to break circular import cycle +] [tool.uv.build-backend] module-name = "jsonlt" diff --git a/src/jsonlt/_exceptions.py b/src/jsonlt/_exceptions.py index ecf310e..aaf17c2 100644 --- a/src/jsonlt/_exceptions.py +++ b/src/jsonlt/_exceptions.py @@ -4,14 +4,9 @@ following the specification's error categories. """ -# pyright: reportImportCycles=false - -from typing import TYPE_CHECKING from typing_extensions import override -if TYPE_CHECKING: - from ._json import JSONObject - from ._keys import Key +from ._types import JSONObject, Key class JSONLTError(Exception): diff --git a/src/jsonlt/_json.py b/src/jsonlt/_json.py index 5e54a65..24ea67b 100644 --- a/src/jsonlt/_json.py +++ b/src/jsonlt/_json.py @@ -9,20 +9,17 @@ import json from json import JSONDecodeError -from typing import TYPE_CHECKING, TypeAlias, cast +from typing import TYPE_CHECKING, cast from ._constants import MIN_NESTING_DEPTH from ._exceptions import LimitError, ParseError +from ._types import JSONArray, JSONObject, JSONPrimitive, JSONValue if TYPE_CHECKING: from collections.abc import Mapping, Sequence -# JSON type definitions per RFC 8259 -# Using string annotations for forward references to avoid runtime | issues -JSONPrimitive: TypeAlias = "str | int | float | bool | None" -JSONArray: TypeAlias = "list[JSONValue]" -JSONObject: TypeAlias = "dict[str, JSONValue]" -JSONValue: TypeAlias = "JSONPrimitive | JSONArray | JSONObject" +# Re-export type aliases for backwards compatibility +__all__ = ["JSONArray", "JSONObject", "JSONPrimitive", "JSONValue"] def json_nesting_depth(value: object) -> int: diff --git a/src/jsonlt/_keys.py b/src/jsonlt/_keys.py index 534bd54..74a5959 100644 --- a/src/jsonlt/_keys.py +++ b/src/jsonlt/_keys.py @@ -6,7 +6,7 @@ import json from collections.abc import Sequence -from typing import TYPE_CHECKING, TypeAlias +from typing import TYPE_CHECKING from ._constants import ( MAX_INTEGER_KEY, @@ -16,30 +16,14 @@ ) from ._exceptions import InvalidKeyError, LimitError from ._json import utf8_byte_length +from ._types import Key, KeyElement, KeySpecifier if TYPE_CHECKING: from typing import TypeGuard from typing_extensions import TypeIs -KeyElement: TypeAlias = "str | int" -"""A key element is a string or integer that may appear in a tuple key.""" - -Key: TypeAlias = "str | int | tuple[str | int, ...]" -"""A key identifies a record within a table. - -A key is one of: -- A string -- An integer in the range [-(2^53)+1, (2^53)-1] -- A tuple of key elements (non-empty, max 16 elements) -""" - -KeySpecifier: TypeAlias = "str | tuple[str, ...]" -"""A key specifier defines how to extract a key from a record. - -A key specifier is one of: -- A string naming a single field -- A tuple of strings naming multiple fields (for compound keys) -""" +# Re-export type aliases for backwards compatibility +__all__ = ["Key", "KeyElement", "KeySpecifier"] def is_valid_key_element(value: object) -> "TypeIs[str | int]": diff --git a/src/jsonlt/_types.py b/src/jsonlt/_types.py new file mode 100644 index 0000000..4bfb064 --- /dev/null +++ b/src/jsonlt/_types.py @@ -0,0 +1,48 @@ +"""Type aliases for JSONLT. + +This module contains ONLY TypeAlias definitions for JSON and key types. +It exists to break circular imports between _exceptions.py, _json.py, and _keys.py: +- _exceptions.py needs JSONObject and Key for ConflictError +- _json.py needs ParseError, LimitError from _exceptions.py +- _keys.py needs InvalidKeyError, LimitError from _exceptions.py + +By placing the type aliases here with no dependencies on other JSONLT modules, +all modules can safely import from _types.py. +""" + +from typing import TypeAlias + +# JSON type definitions per RFC 8259 +# Using string annotations for forward references to avoid runtime | issues +JSONPrimitive: TypeAlias = "str | int | float | bool | None" +"""A JSON primitive value: string, number, boolean, or null.""" + +JSONArray: TypeAlias = "list[JSONValue]" +"""A JSON array containing any JSON values.""" + +JSONObject: TypeAlias = "dict[str, JSONValue]" +"""A JSON object mapping string keys to JSON values.""" + +JSONValue: TypeAlias = "JSONPrimitive | JSONArray | JSONObject" +"""Any JSON value: primitive, array, or object.""" + +# Key type definitions per JSONLT specification +KeyElement: TypeAlias = "str | int" +"""A key element is a string or integer that may appear in a tuple key.""" + +Key: TypeAlias = "str | int | tuple[str | int, ...]" +"""A key identifies a record within a table. + +A key is one of: +- A string +- An integer in the range [-(2^53)+1, (2^53)-1] +- A tuple of key elements (non-empty, max 16 elements) +""" + +KeySpecifier: TypeAlias = "str | tuple[str, ...]" +"""A key specifier defines how to extract a key from a record. + +A key specifier is one of: +- A string naming a single field +- A tuple of strings naming multiple fields (for compound keys) +"""