Skip to content

Commit c1b9142

Browse files
committed
Disallow attribute access in Record and subclasses
1 parent 853740e commit c1b9142

File tree

3 files changed

+8
-24
lines changed

3 files changed

+8
-24
lines changed

asyncpg/mypy/__init__.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ def get_attribute_hook(self, fullname: str) \
2828
class_name, _, _ = fullname.rpartition('.')
2929
symbol = self.lookup_fully_qualified(class_name)
3030

31-
if symbol and isinstance(symbol.node, mypy.nodes.TypeInfo) and \
32-
symbol.node.has_base(common.RECORD_NAME):
31+
if symbol is not None and \
32+
isinstance(symbol.node, mypy.nodes.TypeInfo) and \
33+
utils.is_record(symbol.node):
3334
return hooks.record_attribute
3435

3536
return None
@@ -38,12 +39,6 @@ def get_customize_class_mro_hook(self, fullname: str) \
3839
-> typing.Optional[common.ClassDefHook]:
3940
return hooks.mark_record
4041

41-
def get_base_class_hook(self, fullname: str) \
42-
-> typing.Optional[common.ClassDefHook]:
43-
if fullname == common.RECORD_NAME:
44-
return hooks.record_final_attributes
45-
return None
46-
4742

4843
def plugin(version: str) -> typing.Type[mypy.plugin.Plugin]:
4944
return AsyncpgPlugin

asyncpg/mypy/hooks.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,17 @@ def mark_record(ctx: mypy.plugin.ClassDefContext) -> None:
1515
utils.mark_record(ctx.cls.info)
1616

1717

18-
def record_final_attributes(ctx: mypy.plugin.ClassDefContext) -> None:
19-
if utils.is_record(ctx.cls.info):
20-
for name, value in ctx.cls.info.names.items():
21-
# set all properties as Final so they can't be set
22-
if isinstance(value.node, mypy.nodes.Var):
23-
value.node.is_final = True
24-
value.node.final_set_in_init = True
25-
value.node.final_unset_in_class = True
26-
27-
2818
def record_attribute(ctx: mypy.plugin.AttributeContext) \
2919
-> mypy.types.Type:
3020
if isinstance(ctx.type, mypy.types.Instance) and \
3121
utils.is_record(ctx.type.type):
3222
assert isinstance(ctx.context, mypy.nodes.MemberExpr)
33-
symbol = ctx.type.type.get(ctx.context.name)
3423

35-
if symbol is None:
36-
ctx.api.fail('"{}" has no attribute "{}"'
37-
.format(ctx.type.type.name, ctx.context.name),
38-
ctx.context)
24+
# raise an error when users try to access the keys defined
25+
# using attribute notation
26+
ctx.api.fail('"{}" has no attribute "{}"'
27+
.format(ctx.type.type.name, ctx.context.name),
28+
ctx.context)
3929

4030
return ctx.default_attr_type
4131

asyncpg/protocol/protocol.pyi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,6 @@ class Record:
216216
def items(self) -> Iterator[Tuple[str, Any]]: ...
217217
def keys(self) -> Iterator[str]: ...
218218
def values(self) -> Iterator[Any]: ...
219-
def __getattribute__(self, attr: str) -> Any: ...
220219
@overload
221220
def __getitem__(self, index: str) -> Any: ...
222221
@overload

0 commit comments

Comments
 (0)