Skip to content

Experiment: Override __class__ for Builtin types to shadow isinstance #347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions src/codegen/sdk/core/expressions/boolean.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@ def __bool__(self):
@override
def _compute_dependencies(self, usage_type: UsageKind, dest: HasName | None = None) -> None:
pass

@property
def __class__(self):
return bool
4 changes: 4 additions & 0 deletions src/codegen/sdk/core/expressions/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ class Number(Expression[Parent], Builtin, Generic[Parent]):
@override
def _compute_dependencies(self, usage_type: UsageKind, dest: HasName | None = None) -> None:
pass

@property
def __class__(self):
return int
4 changes: 4 additions & 0 deletions src/codegen/sdk/core/expressions/string.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,7 @@ def _compute_dependencies(self, usage_type: UsageKind | None = None, dest: HasNa
# If the string is a template string, we need to compute the dependencies of the string content
for expression in self.expressions:
expression._compute_dependencies(usage_type, dest)

@property
def __class__(self):
return str
4 changes: 2 additions & 2 deletions src/codegen/sdk/core/interfaces/editable.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,10 @@
def __eq__(self, other: object):
if other is None:
return False
if isinstance(other, str):
return self.source == other
if isinstance(other, Editable):
return self.filepath == other.filepath and self.ts_node.kind_id == other.ts_node.kind_id and self.range == other.range
if isinstance(other, str):
return self.source == other
return False

@reader
Expand Down Expand Up @@ -404,7 +404,7 @@
"""
matches: list[Editable[Self]] = []
for node in self.extended_nodes:
matches.extend(node._find_string_literals(strings_to_match, fuzzy_match))

Check failure on line 407 in src/codegen/sdk/core/interfaces/editable.py

View workflow job for this annotation

GitHub Actions / mypy

error: Argument 1 to "extend" of "list" has incompatible type "Sequence[Editable[Editable[Any]]]"; expected "Iterable[Editable[Self]]" [arg-type]
return matches

@noapidoc
Expand Down Expand Up @@ -507,7 +507,7 @@
# Use search to find string
search_results = itertools.chain.from_iterable(map(self._search, map(re.escape, strings_to_match)))
if exact:
search_results = filter(lambda result: result.source in strings_to_match, search_results)

Check failure on line 510 in src/codegen/sdk/core/interfaces/editable.py

View workflow job for this annotation

GitHub Actions / mypy

error: Incompatible types in assignment (expression has type "filter[Editable[Any]]", variable has type "chain[Editable[Any]]") [assignment]

# Combine and deduplicate results
return list(search_results)
Expand Down Expand Up @@ -897,9 +897,9 @@
if arguments is not None and any(identifier == arg.child_by_field_name("left") for arg in arguments.named_children):
continue

usages.append(self._parse_expression(identifier))

Check failure on line 900 in src/codegen/sdk/core/interfaces/editable.py

View workflow job for this annotation

GitHub Actions / mypy

error: "Sequence[Editable[Self]]" has no attribute "append" [attr-defined]

return usages

Check failure on line 902 in src/codegen/sdk/core/interfaces/editable.py

View workflow job for this annotation

GitHub Actions / mypy

error: Incompatible return value type (got "Sequence[Editable[Self]]", expected "list[Editable[Any]]") [return-value]

@reader
def get_variable_usages(self, var_name: str, fuzzy_match: bool = False) -> Sequence[Editable[Self]]:
Expand Down Expand Up @@ -954,14 +954,14 @@

@commiter
@noapidoc
def _add_symbol_usages(self: HasName, identifiers: list[TSNode], usage_type: UsageKind, dest: HasName | None = None) -> None:

Check failure on line 957 in src/codegen/sdk/core/interfaces/editable.py

View workflow job for this annotation

GitHub Actions / mypy

error: The erased type of self "codegen.sdk.core.interfaces.has_name.HasName" is not a supertype of its class "codegen.sdk.core.interfaces.editable.Editable[Parent`1]" [misc]
from codegen.sdk.core.expressions import Name
from codegen.sdk.core.interfaces.resolvable import Resolvable

if dest is None:
dest = self
for x in identifiers:
if dep := self._parse_expression(x, default=Name):

Check failure on line 964 in src/codegen/sdk/core/interfaces/editable.py

View workflow job for this annotation

GitHub Actions / mypy

error: "HasName" has no attribute "_parse_expression" [attr-defined]
assert isinstance(dep, Resolvable)
dep._compute_dependencies(usage_type, dest)

Expand All @@ -971,7 +971,7 @@
id_types = self.G.node_classes.resolvables
# Skip identifiers that are part of a property
identifiers = find_all_descendants(self.ts_node, id_types, nested=False)
return self._add_symbol_usages(identifiers, usage_type, dest)

Check failure on line 974 in src/codegen/sdk/core/interfaces/editable.py

View workflow job for this annotation

GitHub Actions / mypy

error: Invalid self argument "Editable[Parent]" to attribute function "_add_symbol_usages" with type "Callable[[HasName, list[Node], UsageKind, HasName | None], None]" [misc]

@commiter
@noapidoc
Expand All @@ -980,7 +980,7 @@
id_types = self.G.node_classes.resolvables
# Skip identifiers that are part of a property
identifiers = find_all_descendants(child, id_types, nested=False)
return self._add_symbol_usages(identifiers, usage_type, dest)

Check failure on line 983 in src/codegen/sdk/core/interfaces/editable.py

View workflow job for this annotation

GitHub Actions / mypy

error: Invalid self argument "Editable[Parent]" to attribute function "_add_symbol_usages" with type "Callable[[HasName, list[Node], UsageKind, HasName | None], None]" [misc]

@noapidoc
def _log_parse(self, msg: str, *args, **kwargs):
Expand All @@ -1006,7 +1006,7 @@

@cached_property
@noapidoc
def github_url(self) -> str | None:

Check failure on line 1009 in src/codegen/sdk/core/interfaces/editable.py

View workflow job for this annotation

GitHub Actions / mypy

error: Missing return statement [return]
if self.file.github_url:
return self.file.github_url + f"#L{self.start_point[0] + 1}-L{self.end_point[0] + 1}"

Expand Down Expand Up @@ -1067,7 +1067,7 @@
dest = self
while dest and not isinstance(dest, Importable):
dest = dest.parent
return dest

Check failure on line 1070 in src/codegen/sdk/core/interfaces/editable.py

View workflow job for this annotation

GitHub Actions / mypy

error: Incompatible return value type (got "Editable[Parent] | Importable[Any]", expected "Importable[Any]") [return-value]

@cached_property
@noapidoc
Expand Down
4 changes: 2 additions & 2 deletions src/codegen/sdk/core/symbol_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ def __hash__(self):
def __eq__(self, other: object) -> bool:
if other is None:
return False
if isinstance(other, list):
return self.symbols == other
if isinstance(other, SymbolGroup):
return self.symbols == other.symbols
if isinstance(other, list):
return self.symbols == other
return super().__eq__(other)

@property
Expand Down
4 changes: 4 additions & 0 deletions src/codegen/sdk/core/symbol_groups/dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,7 @@ def descendant_symbols(self) -> list["Importable"]:
if child.value:
ret.extend(child.value.descendant_symbols)
return ret

@property
def __class__(self):
return dict
4 changes: 4 additions & 0 deletions src/codegen/sdk/core/symbol_groups/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ class List(Collection["Expression[Self, None]", Parent], Expression[Parent], Bui
def __init__(self, ts_node: TSNode, file_node_id: NodeId, G: "CodebaseGraph", parent: Parent) -> None:
super().__init__(ts_node, file_node_id, G, parent)
self._init_children([self._parse_expression(child) for child in ts_node.named_children if child.type])

@property
def __class__(self):
return list
4 changes: 4 additions & 0 deletions src/codegen/sdk/core/symbol_groups/tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ class Tuple(Collection["Expression[Self, None]", Parent], Expression[Parent], Bu
def __init__(self, ts_node: TSNode, file_node_id: NodeId, G: "CodebaseGraph", parent: Parent) -> None:
super().__init__(ts_node, file_node_id, G, parent)
self._init_children([self._parse_expression(child) for child in ts_node.named_children if child.type])

@property
def __class__(self):
return tuple
51 changes: 51 additions & 0 deletions tests/unit/codegen/sdk/python/expressions/test_builtin_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from codegen.sdk.codebase.factory.get_session import get_codebase_session
from codegen.sdk.core.expressions.boolean import Boolean
from codegen.sdk.core.expressions.number import Number
from codegen.sdk.core.expressions.string import String
from codegen.sdk.core.symbol_groups.dict import Dict
from codegen.sdk.core.symbol_groups.list import List
from codegen.sdk.core.symbol_groups.tuple import Tuple
from codegen.sdk.enums import ProgrammingLanguage


def test_builtin_types(tmpdir):
# language=python
content = """
a = 1
b = "hello"
c = True
d = [1, 2, 3]
e = {"a": 1, "b": 2}
f = (1, 2, 3)
"""
with get_codebase_session(tmpdir=tmpdir, files={"test.py": content}, programming_language=ProgrammingLanguage.PYTHON) as codebase:
file = codebase.get_file("test.py")
# Test Number
a = file.get_global_var("a")
assert isinstance(a.value, Number)
assert isinstance(a.value, int)

# Test String
b = file.get_global_var("b")
assert isinstance(b.value, String)
assert isinstance(b.value, str)

# Test Boolean
c = file.get_global_var("c")
assert isinstance(c.value, Boolean)
assert isinstance(c.value, bool)

# Test List
d = file.get_global_var("d")
assert isinstance(d.value, List)
assert isinstance(d.value, list)

# Test Dict
e = file.get_global_var("e")
assert isinstance(e.value, Dict)
assert isinstance(e.value, dict)

# Test Tuple
f = file.get_global_var("f")
assert isinstance(f.value, Tuple)
assert isinstance(f.value, tuple)
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from codegen.sdk.codebase.factory.get_session import get_codebase_session
from codegen.sdk.core.expressions.boolean import Boolean
from codegen.sdk.core.expressions.number import Number
from codegen.sdk.core.expressions.string import String
from codegen.sdk.core.symbol_groups.dict import Dict
from codegen.sdk.core.symbol_groups.list import List
from codegen.sdk.enums import ProgrammingLanguage


def test_builtin_types(tmpdir):
# language=typescript
content = """
let a = 1;
let b = "hello";
let c = true;
let d = [1, 2, 3];
let e = {"a": 1, "b": 2};
"""
with get_codebase_session(tmpdir=tmpdir, files={"test.ts": content}, programming_language=ProgrammingLanguage.TYPESCRIPT) as codebase:
file = codebase.get_file("test.ts")
# Test Number
a = file.get_global_var("a")
assert isinstance(a.value, Number)
assert isinstance(a.value, int)

# Test String
b = file.get_global_var("b")
assert isinstance(b.value, String)
assert isinstance(b.value, str)

# Test Boolean
c = file.get_global_var("c")
assert isinstance(c.value, Boolean)
assert isinstance(c.value, bool)

# Test List/Array
d = file.get_global_var("d")
assert isinstance(d.value, List)
assert isinstance(d.value, list)

# Test Dict/Object
e = file.get_global_var("e")
assert isinstance(e.value, Dict)
assert isinstance(e.value, dict)