Skip to content
Merged
Show file tree
Hide file tree
Changes from 143 commits
Commits
Show all changes
148 commits
Select commit Hold shift + click to select a range
d2ac198
Update yscope-dev-utils
sitaowang1998 Jul 15, 2025
cc8a097
Use boost install task
sitaowang1998 Jul 15, 2025
7f68bdc
Update install task variable names
sitaowang1998 Jul 15, 2025
2394649
Set CMP0074 to NEW to find boost
sitaowang1998 Jul 15, 2025
6ff3e33
Add uv to install script
sitaowang1998 Jul 15, 2025
7ae8a78
Fix cpp-lint root paths
sitaowang1998 Jul 15, 2025
56502f9
Fix clang-tidy file pattern
sitaowang1998 Jul 15, 2025
6f6becf
Limit build parallelism
sitaowang1998 Jul 17, 2025
a74523d
Bug fix
sitaowang1998 Jul 17, 2025
2345d22
Merge branch 'dep-concurrency' into yscope-dev-utils
sitaowang1998 Jul 17, 2025
77ee494
Bug fix
sitaowang1998 Jul 17, 2025
38b86c8
Merge branch 'dep-concurrency' into yscope-dev-utils
sitaowang1998 Jul 17, 2025
3698339
Bug fix
sitaowang1998 Jul 17, 2025
369e9f1
Rename variables to mirror CLP core
sitaowang1998 Aug 4, 2025
90aa5a2
Rename variables to mirror clp core
sitaowang1998 Aug 4, 2025
1769c95
Merge branch 'dep-concurrency' of github.com:sitaowang1998/spider int…
sitaowang1998 Aug 4, 2025
edaa834
Merge branch 'dep-concurrency' into yscope-dev-utils
sitaowang1998 Aug 4, 2025
7faac8f
Revert "Merge branch 'dep-concurrency' of github.com:sitaowang1998/sp…
sitaowang1998 Aug 4, 2025
f494a90
Add comment for deps parallelism default value
sitaowang1998 Aug 4, 2025
eb01bb2
Merge branch 'dep-concurrency' into yscope-dev-utils
sitaowang1998 Aug 4, 2025
ff2fe1c
Update yscope-dev-utils
sitaowang1998 Aug 4, 2025
d476e42
Merge branch 'main' into yscope-dev-utils
sitaowang1998 Aug 4, 2025
850126d
Merge branch 'yscope-dev-utils' into python_lint
sitaowang1998 Aug 4, 2025
65841a0
Add latest python lint config files
sitaowang1998 Aug 4, 2025
8564fc2
Update ruff lint tasks
sitaowang1998 Aug 4, 2025
a6e7d29
Fix ruff lint
sitaowang1998 Aug 4, 2025
573b448
Fix ruff lint
sitaowang1998 Aug 4, 2025
6ad72c3
Bug fix
sitaowang1998 Aug 4, 2025
ba7c6e5
Fix ruff
sitaowang1998 Aug 4, 2025
209acb1
Fix ruff
sitaowang1998 Aug 4, 2025
406b514
Reformat files
sitaowang1998 Aug 4, 2025
66892f5
Remove .inc from cpp linting
sitaowang1998 Aug 4, 2025
80f0a10
Merge branch 'yscope-dev-utils' into python_lint
sitaowang1998 Aug 4, 2025
3186a42
Add mypy and merge lint and test requirements.txt
sitaowang1998 Aug 4, 2025
0925938
Fix mysql connection type
sitaowang1998 Aug 4, 2025
6936a9b
Fix socket name type
sitaowang1998 Aug 4, 2025
7407090
Fix return type from db cursor
sitaowang1998 Aug 4, 2025
6a95b24
Fix db cursor return type
sitaowang1998 Aug 4, 2025
f29dcba
Fix mypy import untyped
sitaowang1998 Aug 4, 2025
453f209
Fix mypy and Popen
sitaowang1998 Aug 5, 2025
d1011c7
Fix generator type hint
sitaowang1998 Aug 5, 2025
a7dc642
Fix mypy
sitaowang1998 Aug 5, 2025
a7d92c2
Fix ruff
sitaowang1998 Aug 5, 2025
1c6213b
Simply socket return types.
sitaowang1998 Aug 5, 2025
0f13d07
Merge branch 'main' into python_lint
sitaowang1998 Aug 5, 2025
14d6326
Merge branch 'python_lint' into mypy_lint
sitaowang1998 Aug 5, 2025
55a727a
Add tombi lint tasks
sitaowang1998 Aug 5, 2025
c98fea1
Rename cpp build tasks
sitaowang1998 Aug 5, 2025
ef64454
Merge branch 'main' into mypy_lint
sitaowang1998 Aug 5, 2025
09ba8ad
Merge branch 'main' into tombi
sitaowang1998 Aug 5, 2025
4e7237e
Add basic python structure
sitaowang1998 Aug 5, 2025
1f944d0
Fix code structure
sitaowang1998 Aug 5, 2025
68992fc
Fix ruff lint
sitaowang1998 Aug 5, 2025
1127b22
Extend lint tasks to python directory
sitaowang1998 Aug 5, 2025
4c51c91
Add python build tasks
sitaowang1998 Aug 5, 2025
56878b2
Merge branch 'main' into mypy_lint
sitaowang1998 Aug 5, 2025
bb428c9
Merge branch 'mypy_lint' into tombi
sitaowang1998 Aug 5, 2025
944bd5e
Merge branch 'tombi' into build-task
sitaowang1998 Aug 5, 2025
d75bc15
Merge branch 'build-task' into python_structure
sitaowang1998 Aug 5, 2025
4b7eca2
Merge branch 'main' into tombi
sitaowang1998 Aug 5, 2025
8ddbda7
Merge branch 'tombi' into build-task
sitaowang1998 Aug 5, 2025
0d998ec
Merge branch 'build-task' into python_structure
sitaowang1998 Aug 5, 2025
6004d64
Fix yaml lint error
sitaowang1998 Aug 5, 2025
bcab5db
Fix tombi lint
sitaowang1998 Aug 5, 2025
a50d25a
Remove wrong mypy config
sitaowang1998 Aug 5, 2025
fbebb96
Fix typo and format file
sitaowang1998 Aug 5, 2025
0322bd5
Use typed msgpack and remove mypy config for msgpack from pyproject
sitaowang1998 Aug 6, 2025
2a08155
Update uv lock
sitaowang1998 Aug 6, 2025
0b62f73
Merge branch 'main' into tombi
sitaowang1998 Aug 6, 2025
7384158
Merge branch 'main' into build-task
sitaowang1998 Aug 6, 2025
70ef90a
Merge branch 'main' into python_structure
sitaowang1998 Aug 6, 2025
b09515c
Increase min version of tombi
sitaowang1998 Aug 6, 2025
bad9675
Merge branch 'tombi' into build-task
sitaowang1998 Aug 6, 2025
e009ff4
Merge branch 'tombi' into python_structure
sitaowang1998 Aug 6, 2025
a0407a7
Merge branch 'main' into tombi
sitaowang1998 Aug 6, 2025
aa9a8de
Merge branch 'tombi' into build-task
sitaowang1998 Aug 6, 2025
3c7e794
Merge branch 'tombi' into python_structure
sitaowang1998 Aug 6, 2025
b4d6576
Add uv in README
sitaowang1998 Aug 6, 2025
23f31cd
Merge branch 'main' into build-task
sitaowang1998 Aug 6, 2025
0167ac0
Merge branch 'build-task' into python_structure
sitaowang1998 Aug 6, 2025
f447614
Fix redenduncy caused by merge
sitaowang1998 Aug 6, 2025
a84de83
Merge branch 'build-task' into python_structure
sitaowang1998 Aug 6, 2025
2d1b6fc
Merge branch 'main' into python_structure
sitaowang1998 Aug 7, 2025
061d101
Merge branch 'main' into python_structure
sitaowang1998 Aug 7, 2025
de149ee
Add integral types
sitaowang1998 Aug 7, 2025
d332dbe
Revert "Add integral types"
sitaowang1998 Aug 7, 2025
80b6772
Add package export control for type
sitaowang1998 Aug 7, 2025
f2ebaa6
Add floating point type
sitaowang1998 Aug 8, 2025
8c27b16
Restructure under src/spider
sitaowang1998 Aug 8, 2025
b7f1884
Merge branch 'python_structure' into python_type
sitaowang1998 Aug 8, 2025
a7aa6b0
Re-export spider.type under spider
sitaowang1998 Aug 8, 2025
95b98c8
Merge branch 'main' into python_structure
sitaowang1998 Aug 8, 2025
5765acf
Add tdl types; Allow no docstring for override function
sitaowang1998 Aug 8, 2025
427dd6b
Merge branch 'main' into python_structure
sitaowang1998 Aug 8, 2025
213d2fc
Add type conversion to tdl type
sitaowang1998 Aug 8, 2025
a1a86f3
Add to_tdl_str
sitaowang1998 Aug 8, 2025
97af71b
Add to_tdl_type_str to type package export
sitaowang1998 Aug 8, 2025
74f31e2
Add pytest and basic test structure
sitaowang1998 Aug 8, 2025
7d95706
Add python test tasks
sitaowang1998 Aug 8, 2025
96b5324
Rename some cpp tests and add python tests to GH workflow and doc
sitaowang1998 Aug 8, 2025
14ea6fb
Don't create __pycache__ when running pytest
sitaowang1998 Aug 8, 2025
854cd11
Fix missing link
sitaowang1998 Aug 8, 2025
5565c9e
Merge branch 'python_structure' of github.com:sitaowang1998/spider in…
sitaowang1998 Aug 8, 2025
2504429
Merge branch 'python_structure' into python_test_setup
sitaowang1998 Aug 8, 2025
7ea4928
Use task env
sitaowang1998 Aug 8, 2025
01dd0c6
Fix task executor path
sitaowang1998 Aug 8, 2025
4cec09b
Merge branch 'python_structure' into python_test_setup
sitaowang1998 Aug 8, 2025
e022404
Merge branch 'main' into python_structure
sitaowang1998 Aug 8, 2025
8894b90
Merge branch 'main' into python_test_setup
sitaowang1998 Aug 8, 2025
7753153
Merge branch 'main' into python_structure
sitaowang1998 Aug 9, 2025
dd59915
Merge branch 'main' into python_test_setup
sitaowang1998 Aug 9, 2025
ac1d4ee
Merge branch 'python_structure' into python_type
sitaowang1998 Aug 9, 2025
0d38c43
Merge branch 'python_test_setup' into python_type
sitaowang1998 Aug 9, 2025
438f6e7
Remove unnecessary __init__.py files
sitaowang1998 Aug 9, 2025
2caad5d
Merge branch 'python_structure' into python_test_setup
sitaowang1998 Aug 9, 2025
c6815ce
Merge branch 'python_test_setup' into python_type
sitaowang1998 Aug 9, 2025
61e0651
Fix bugs
sitaowang1998 Aug 9, 2025
8b13abd
Add some type convertion tests
sitaowang1998 Aug 9, 2025
50b2b25
Bug fix
sitaowang1998 Aug 9, 2025
519dcd8
Merge branch 'python_test_setup' into python_type
sitaowang1998 Aug 9, 2025
8e2786b
Add more tests
sitaowang1998 Aug 9, 2025
41d5de2
Add parsing of tdl type string
sitaowang1998 Aug 9, 2025
0a377f5
Add tests and fix parser
sitaowang1998 Aug 9, 2025
a8c8641
Fix pytest
sitaowang1998 Aug 9, 2025
9fabc44
Merge branch 'python_test_setup' into python_type
sitaowang1998 Aug 9, 2025
ade589b
Merge branch 'python_type' into python_type_parse
sitaowang1998 Aug 9, 2025
0b18170
Satisfy linter
sitaowang1998 Aug 9, 2025
0bfbdb5
Fix comment
sitaowang1998 Aug 9, 2025
e558f67
Improve code according to coderabbit
sitaowang1998 Aug 9, 2025
73e8f0c
Merge branch 'python_type' into python_type_parse
sitaowang1998 Aug 9, 2025
6b58ff0
Move functions to util
sitaowang1998 Aug 9, 2025
90ab334
Unwarp the type node to get TdlType
sitaowang1998 Aug 10, 2025
49acaee
Catch and rethrow lark error
sitaowang1998 Aug 10, 2025
d3f3eb1
Export tdl parse function
sitaowang1998 Aug 14, 2025
43343aa
Refactor spider-py structure to perpare for merge
sitaowang1998 Aug 23, 2025
2f81cd9
Merge branch 'main' into python_type_parse
sitaowang1998 Aug 23, 2025
ff60b0f
Fix merge
sitaowang1998 Aug 23, 2025
8223792
Fix the docstring
sitaowang1998 Aug 23, 2025
311e041
Fix merge
sitaowang1998 Aug 23, 2025
3e75f87
Fix ruff
sitaowang1998 Aug 23, 2025
de28844
Improve style based on code review
sitaowang1998 Aug 23, 2025
346a45a
Use v_args for base_type
sitaowang1998 Aug 23, 2025
d348ed0
Use static lark transformer
sitaowang1998 Aug 23, 2025
84aea87
Apply suggestions from code review
sitaowang1998 Aug 23, 2025
1f02daf
Improve docstring
sitaowang1998 Aug 23, 2025
138ef8d
Fix style
sitaowang1998 Aug 23, 2025
9d2d70e
Rename private functions
sitaowang1998 Aug 23, 2025
af5dd2c
Avoid using Yoda condition
sitaowang1998 Aug 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions python/spider-py/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ authors = [
{ name = "Zhihao Lin", email = "lin.zhihao@yscope.com" },
]
dependencies = [
"lark>=1.2.2",
"mariadb>=1.1.13",
"msgpack-types>=0.5.0",
]
Comment on lines +12 to 15

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Add missing runtime dependency for typing_extensions and consider constraining Lark’s major version.

  • This repo imports typing_extensions.override (see python/spider-py/src/spider_py/type/tdl_type.py Line 6). On Python 3.10, typing_extensions is not in the stdlib, so the package must declare it as a dependency, otherwise imports will fail in clean environments.
  • Optional: Since Lark follows semver and 2.x may include breaking changes, favour pinning an upper bound to avoid unexpected resolver picks.

Apply this diff to add typing-extensions and (optionally) cap Lark to <2:

 dependencies = [
-  "lark>=1.2.2",
+  "lark>=1.2.2,<2",
   "mariadb>=1.1.13",
   "msgpack-types>=0.5.0",
+  "typing-extensions>=4.8",
 ]

Run this to confirm usage of typing_extensions across the repo and catch any other missing deps:


🏁 Script executed:

#!/bin/bash
rg -n --glob 'python/**' -C2 -e '\btyping_extensions\b'

Length of output: 1312


Action Required: Add typing-extensions as a runtime dependency and constrain Lark’s major version

The code imports override from typing_extensions (see python/spider-py/src/spider_py/type/tdl_type.py:6), but typing-extensions isn’t declared in pyproject.toml. In a clean Python 3.10 environment this import will fail. Additionally, since Lark follows semver and a 2.x release may introduce breaking changes, it’s safer to pin an upper bound on its major version.

Please update python/spider-py/pyproject.toml accordingly:

 dependencies = [
-  "lark>=1.2.2",
+  "lark>=1.2.2,<2",
   "mariadb>=1.1.13",
   "msgpack-types>=0.5.0",
+  "typing-extensions>=4.8",
 ]
  • Confirm that any CI or packaging tooling picks up the new dependency.
  • Optional: adjust the minimum version of typing-extensions if you need features introduced after 4.8.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"lark>=1.2.2",
"mariadb>=1.1.13",
"msgpack-types>=0.5.0",
]
# python/spider-py/pyproject.toml
dependencies = [
"lark>=1.2.2,<2",
"mariadb>=1.1.13",
"msgpack-types>=0.5.0",
"typing-extensions>=4.8",
]
🤖 Prompt for AI Agents
In python/spider-py/pyproject.toml around lines 12–15, add typing-extensions as
a runtime dependency and constrain Lark’s major version to avoid accidental 2.x
breakage; specifically add an entry for "typing-extensions" (optionally with a
minimum like ">=4.8" if you rely on newer features) to the dependencies list and
change the Lark entry to include an upper bound such as "lark>=1.2.2,<2.0.0";
after editing, ensure CI/packaging picks up the updated pyproject (e.g., by
re-running dependency install steps or updating lock files) so the new
dependency is propagated.

Expand Down
12 changes: 11 additions & 1 deletion python/spider-py/src/spider_py/type/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
"""Spider type package."""

from spider_py.type.tdl_convert import to_tdl_type_str
from spider_py.type.tdl_parse import parse_tdl_type
from spider_py.type.type import Double, Float, Int8, Int16, Int32, Int64

__all__ = ["Double", "Float", "Int8", "Int16", "Int32", "Int64", "to_tdl_type_str"]
__all__ = [
"Double",
"Float",
"Int8",
"Int16",
"Int32",
"Int64",
"parse_tdl_type",
"to_tdl_type_str",
]
102 changes: 102 additions & 0 deletions python/spider-py/src/spider_py/type/tdl_parse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""Parse TDL type string."""

from lark import Lark, LarkError, Token, Transformer, v_args

from spider_py.type.tdl_type import (
BoolType,
ClassType,
DoubleType,
FloatType,
Int8Type,
Int16Type,
Int32Type,
Int64Type,
ListType,
MapType,
TdlType,
)

grammar = r"""
type: map_type | list_type | base_type

map_type: "Map" "<" type "," type ">"
list_type: "List" "<" type ">"
base_type: ID

ID: /[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*/

%import common.WS
%ignore WS
"""

PrimitiveTypeDict = {
"bool": BoolType(),
"double": DoubleType(),
"float": FloatType(),
"int8": Int8Type(),
"int16": Int16Type(),
"int32": Int32Type(),
"int64": Int64Type(),
}


class TypeTransformer(Transformer[Token, TdlType]):
"""Transforms Lark parse tree into TDL type."""

@v_args(inline=True)
def type(self, value_type: TdlType) -> TdlType:
"""
Unwraps the type node to return the TdlType.
:param value_type: The type node to unwrap.
:return: The unwrapped TdlType.
"""
return value_type

@v_args(inline=True)
def map_type(self, key_type: TdlType, value_type: TdlType) -> TdlType:
"""
Transforms map node into Map type.
:param key_type: The key type of the map.
:param value_type: The value type of the map.
:return: The MapType with the given `key_type` and `value_type`.
"""
return MapType(key_type, value_type)

@v_args(inline=True)
def list_type(self, element_type: TdlType) -> TdlType:
"""
Transforms list node into List type.
:param element_type: The element type of the list.
:return: The ListType with the given `element_type`.
"""
return ListType(element_type)

@v_args(inline=True)
def base_type(self, token_id: str) -> TdlType:
"""
Transforms primitive node into primitive type.
:param token_id: The token id of the primitive type.
:return: The corresponding TdlType of the `token_id`.
"""
if token_id in PrimitiveTypeDict:
return PrimitiveTypeDict[token_id]
return ClassType(token_id)


_parser = Lark(grammar, start="type", parser="lalr")
_transformer = TypeTransformer(visit_tokens=False)


def parse_tdl_type(type_string: str) -> TdlType:
"""
Parses TDL type string into TDL type.
:param type_string: TDL type string.
:return: Parsed TDL type.
:raises TypeError: If string is not a valid TDL type.
"""
try:
tree = _parser.parse(type_string)
return _transformer.transform(tree)
except LarkError as e:
msg = f"Cannot parse TDL type `{type_string}`"
raise TypeError(msg) from e
52 changes: 52 additions & 0 deletions python/spider-py/src/spider_py/type/tdl_type.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
"""Spider TDL types."""

from abc import ABC, abstractmethod
from types import GenericAlias

from typing_extensions import override

from spider_py.type.type import Double, Float, Int8, Int16, Int32, Int64
from spider_py.type.utils import get_class_by_name


class TdlType(ABC):
"""Abstract base class for all TDL types."""
Expand All @@ -12,6 +16,10 @@ class TdlType(ABC):
def type_str(self) -> str:
""":return: String representation of the TDL type."""

@abstractmethod
def native_type(self) -> type | GenericAlias:
""":return: Native Python type of the TDL type."""


class DoubleType(TdlType):
"""TDL double type."""
Expand All @@ -20,6 +28,10 @@ class DoubleType(TdlType):
def type_str(self) -> str:
return "double"

@override
def native_type(self) -> type | GenericAlias:
return Double


class FloatType(TdlType):
"""TDL float type."""
Expand All @@ -28,6 +40,10 @@ class FloatType(TdlType):
def type_str(self) -> str:
return "float"

@override
def native_type(self) -> type | GenericAlias:
return Float


class Int8Type(TdlType):
"""TDL int8 type."""
Expand All @@ -36,6 +52,10 @@ class Int8Type(TdlType):
def type_str(self) -> str:
return "int8"

@override
def native_type(self) -> type | GenericAlias:
return Int8


class Int16Type(TdlType):
"""TDL int16 type."""
Expand All @@ -44,6 +64,10 @@ class Int16Type(TdlType):
def type_str(self) -> str:
return "int16"

@override
def native_type(self) -> type | GenericAlias:
return Int16


class Int32Type(TdlType):
"""TDL int32 type."""
Expand All @@ -52,6 +76,10 @@ class Int32Type(TdlType):
def type_str(self) -> str:
return "int32"

@override
def native_type(self) -> type | GenericAlias:
return Int32


class Int64Type(TdlType):
"""TDL int64 type."""
Expand All @@ -60,6 +88,10 @@ class Int64Type(TdlType):
def type_str(self) -> str:
return "int64"

@override
def native_type(self) -> type | GenericAlias:
return Int64


class BoolType(TdlType):
"""TDL bool type."""
Expand All @@ -68,6 +100,10 @@ class BoolType(TdlType):
def type_str(self) -> str:
return "bool"

@override
def native_type(self) -> type | GenericAlias:
return bool


class ClassType(TdlType):
"""TDL Custom class type."""
Expand All @@ -83,6 +119,14 @@ def __init__(self, name: str) -> None:
def type_str(self) -> str:
return self._name

@override
def native_type(self) -> type | GenericAlias:
"""
:return: Native Python type of the class.
:raise: TypeError if `class_name` is not a valid class.
"""
return get_class_by_name(self._name)


class ListType(TdlType):
"""TDL List type."""
Expand All @@ -98,6 +142,10 @@ def __init__(self, element_type: TdlType) -> None:
def type_str(self) -> str:
return f"List<{self.element_type.type_str()}>"

@override
def native_type(self) -> type | GenericAlias:
return list[self.element_type.native_type()] # type: ignore[misc]


def is_integral(tdl_type: TdlType) -> bool:
"""
Expand Down Expand Up @@ -142,3 +190,7 @@ def __init__(self, key_type: TdlType, value_type: TdlType) -> None:
@override
def type_str(self) -> str:
return f"Map<{self.key_type.type_str()},{self.value_type.type_str()}>"

@override
def native_type(self) -> type | GenericAlias:
return dict[self.key_type.native_type(), self.value_type.native_type()] # type: ignore[misc]
21 changes: 21 additions & 0 deletions python/spider-py/src/spider_py/type/utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
"""Utility for TDL types."""

from importlib import import_module
from typing import cast


def get_class_name(cls: type) -> str:
"""
:param cls:
:return: Full name of `cls`.
"""
return f"{cls.__module__}.{cls.__qualname__}"


def get_class_by_name(name: str) -> type:
"""
Gets class by name.
:param name:
:return:
Comment thread
sitaowang1998 marked this conversation as resolved.
Resolved
:raise: TypeError if `class_name` is not a valid class.
"""
parts = name.split(".")
module_name = ".".join(parts[:-1])
class_name = parts[-1]
Comment thread
sitaowang1998 marked this conversation as resolved.
Resolved
try:
module = import_module(module_name)
return cast("type", getattr(module, class_name))
except (ValueError, ModuleNotFoundError, AttributeError) as exc:
Comment thread
sitaowang1998 marked this conversation as resolved.
Resolved
msg = f"{name} is not a valid TDL type."
raise TypeError(msg) from exc

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Make class resolution robust to nested classes and improve diagnostics.

Current logic splits on the last dot and getattr’s a single attribute from the module. This fails for nested classes (e.g., pkg.mod.Outer.Inner) and for objects not actually being classes. It also conflates module import failures with attribute lookups in error messaging.

Refactor to resolve the longest importable module prefix, then getattr-chain the remaining components. Also validate the resolved object is a class and surface clearer messages:

 def get_class_by_name(name: str) -> type:
     """
     Gets class by name.
     :param name:
     :return:
     :raise: TypeError if `class_name` is not a valid class.
     """
-    parts = name.split(".")
-    module_name = ".".join(parts[:-1])
-    class_name = parts[-1]
-    try:
-        module = import_module(module_name)
-        return cast("type", getattr(module, class_name))
-    except (ValueError, ModuleNotFoundError, AttributeError) as exc:
-        msg = f"{name} is not a valid TDL type."
-        raise TypeError(msg) from exc
+    parts = name.split(".")
+    if len(parts) < 2:
+        raise TypeError(f"{name} is not a fully-qualified class path.")
+
+    last_exc: Exception | None = None
+    # Try longest module prefix first, then shorten until import succeeds.
+    for i in range(len(parts) - 1, 0, -1):
+        module_name = ".".join(parts[:i])
+        attrs = parts[i:]
+        try:
+            module = import_module(module_name)
+        except (ValueError, ModuleNotFoundError) as exc:
+            last_exc = exc
+            continue
+        obj: object = module
+        try:
+            for attr in attrs:
+                obj = getattr(obj, attr)
+        except AttributeError as exc:
+            last_exc = exc
+            continue
+        if not isinstance(obj, type):
+            raise TypeError(f"{name} resolved to {type(obj).__name__}, not a class.")
+        return cast("type", obj)
+
+    raise TypeError(f"{name} is not a valid class path.") from last_exc

Consider aligning the error text with the utility’s purpose, e.g., “valid class path” instead of “valid TDL type,” to reduce confusion.

If you’d like, I can add unit tests covering nested classes (Outer.Inner) resolution.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def get_class_by_name(name: str) -> type:
"""
Gets class by name.
:param name:
:return:
:raise: TypeError if `class_name` is not a valid class.
"""
parts = name.split(".")
module_name = ".".join(parts[:-1])
class_name = parts[-1]
try:
module = import_module(module_name)
return cast("type", getattr(module, class_name))
except (ValueError, ModuleNotFoundError, AttributeError) as exc:
msg = f"{name} is not a valid TDL type."
raise TypeError(msg) from exc
def get_class_by_name(name: str) -> type:
"""
Gets class by name.
:param name:
:return:
:raise: TypeError if `class_name` is not a valid class.
"""
parts = name.split(".")
if len(parts) < 2:
raise TypeError(f"{name} is not a fully-qualified class path.")
last_exc: Exception | None = None
# Try longest module prefix first, then shorten until import succeeds.
for i in range(len(parts) - 1, 0, -1):
module_name = ".".join(parts[:i])
attrs = parts[i:]
try:
module = import_module(module_name)
except (ValueError, ModuleNotFoundError) as exc:
last_exc = exc
continue
obj: object = module
try:
for attr in attrs:
obj = getattr(obj, attr)
except AttributeError as exc:
last_exc = exc
continue
if not isinstance(obj, type):
raise TypeError(f"{name} resolved to {type(obj).__name__}, not a class.")
return cast("type", obj)
raise TypeError(f"{name} is not a valid class path.") from last_exc

54 changes: 54 additions & 0 deletions python/spider-py/tests/type/test_to_native.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""Test converting TDL type to native type."""

from types import GenericAlias

import pytest

import spider_py
from spider_py.type.tdl_parse import parse_tdl_type
from spider_py.type.utils import get_class_name


def string_to_native(s: str) -> type | GenericAlias:
Comment thread
sitaowang1998 marked this conversation as resolved.
Resolved
"""
Converts TDL string to native type.
:param s: TDL string to convert.
:return: The converted native type.
Comment thread
sitaowang1998 marked this conversation as resolved.
Resolved
:raises: TypeError forwards from `parse_tdl_type` and `native_type`.
Comment thread
sitaowang1998 marked this conversation as resolved.
Resolved
"""
return parse_tdl_type(s).native_type()


class TestToNativeType:
"""Test converting TDL type to native type."""

def test_to_primitive_native_type(self) -> None:
"""Test converting primitive TDL type to native type."""
assert string_to_native("bool") is bool
assert string_to_native("double") is spider_py.Double
assert string_to_native("float") is spider_py.Float
assert string_to_native("int8") is spider_py.Int8
assert string_to_native("int16") is spider_py.Int16
assert string_to_native("int32") is spider_py.Int32
assert string_to_native("int64") is spider_py.Int64

def test_to_class_type(self) -> None:
"""Test converting class TDL type to native type."""
assert string_to_native(get_class_name(TestToNativeType)) == TestToNativeType
with pytest.raises(TypeError):
string_to_native("NonExistType")
with pytest.raises(TypeError):
string_to_native("NonExistModule.NonExistType")

def test_to_list_type(self) -> None:
"""Test converting list TDL type to native type."""
assert string_to_native("List<int8>") == list[spider_py.Int8]
assert string_to_native("List<List<int8>>") == list[list[spider_py.Int8]]

def test_to_map_type(self) -> None:
"""Test converting map TDL type to native type."""
assert string_to_native("Map<int8,int8>") == dict[spider_py.Int8, spider_py.Int8]
assert (
string_to_native("Map<List<int8>,Map<int8,double>>")
== dict[list[spider_py.Int8], dict[spider_py.Int8, spider_py.Double]]
)
Loading