Skip to content

Commit

Permalink
Fix slots and deleted attributes in incremental mode (#12645)
Browse files Browse the repository at this point in the history
They weren't serialized before. Manually written serialization
code is a pain to keep up-to-date.
  • Loading branch information
JukkaL committed Apr 21, 2022
1 parent a16c414 commit 00a9815
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 1 deletion.
4 changes: 4 additions & 0 deletions mypy/nodes.py
Expand Up @@ -2907,6 +2907,8 @@ def serialize(self) -> JsonDict:
None if self.typeddict_type is None else self.typeddict_type.serialize(),
'flags': get_flags(self, TypeInfo.FLAGS),
'metadata': self.metadata,
'slots': list(sorted(self.slots)) if self.slots is not None else None,
'deletable_attributes': self.deletable_attributes,
}
return data

Expand Down Expand Up @@ -2944,6 +2946,8 @@ def deserialize(cls, data: JsonDict) -> 'TypeInfo':
ti.typeddict_type = (None if data['typeddict_type'] is None
else mypy.types.TypedDictType.deserialize(data['typeddict_type']))
ti.metadata = data['metadata']
ti.slots = set(data['slots']) if data['slots'] is not None else None
ti.deletable_attributes = data['deletable_attributes']
set_flags(ti, data['flags'])
return ti

Expand Down
2 changes: 2 additions & 0 deletions mypyc/ir/class_ir.py
Expand Up @@ -325,6 +325,7 @@ def serialize(self) -> JsonDict:
'children': [
cir.fullname for cir in self.children
] if self.children is not None else None,
'deletable': self.deletable,
}

@classmethod
Expand Down Expand Up @@ -373,6 +374,7 @@ def deserialize(cls, data: JsonDict, ctx: 'DeserMaps') -> 'ClassIR':
ir.mro = [ctx.classes[s] for s in data['mro']]
ir.base_mro = [ctx.classes[s] for s in data['base_mro']]
ir.children = data['children'] and [ctx.classes[s] for s in data['children']]
ir.deletable = data['deletable']

return ir

Expand Down
2 changes: 1 addition & 1 deletion mypyc/test-data/fixtures/ir.py
Expand Up @@ -294,7 +294,7 @@ def next(i: Iterator[T]) -> T: pass
def next(i: Iterator[T], default: T) -> T: pass
def hash(o: object) -> int: ...
def globals() -> Dict[str, Any]: ...
def getattr(obj: object, name: str) -> Any: ...
def getattr(obj: object, name: str, default: Any = None) -> Any: ...
def setattr(obj: object, name: str, value: Any) -> None: ...
def enumerate(x: Iterable[T]) -> Iterator[Tuple[int, T]]: ...
@overload
Expand Down
22 changes: 22 additions & 0 deletions mypyc/test-data/run-multimodule.test
Expand Up @@ -798,3 +798,25 @@ def foo() -> int: return 10
import native

[rechecked native, other_a]

[case testIncrementalCompilationWithDeletable]
import other_a
[file other_a.py]
from other_b import C
[file other_a.py.2]
from other_b import C
c = C()
print(getattr(c, 'x', None))
del c.x
print(getattr(c, 'x', None))
[file other_b.py]
class C:
__deletable__ = ['x']
def __init__(self) -> None:
self.x = 0
[file driver.py]
import native
[out]
[out2]
0
None
25 changes: 25 additions & 0 deletions test-data/unit/check-incremental.test
Expand Up @@ -5633,3 +5633,28 @@ main:5: error: Cannot override final attribute "x" (previously declared in base
main:3: error: Cannot override writable attribute "x" with a final one
main:4: error: Cannot extend enum with existing members: "FinalEnum"
main:5: error: Cannot override final attribute "x" (previously declared in base class "FinalEnum")

[case testSlotsSerialization]
import a
[file a.py]
from b import C

class D(C):
pass
[file b.py]
class C:
__slots__ = ('x',)
[file a.py.2]
from b import C

class D(C):
__slots__ = ('y',)

def __init__(self) -> None:
self.x = 1
self.y = 2
self.z = 3
[builtins fixtures/tuple.pyi]
[out]
[out2]
tmp/a.py:9: error: Trying to assign name "z" that is not in "__slots__" of type "a.D"

0 comments on commit 00a9815

Please sign in to comment.