Skip to content

Commit 1f1dcc9

Browse files
committed
an initial attempt
1 parent 152d380 commit 1f1dcc9

File tree

8 files changed

+135
-3
lines changed

8 files changed

+135
-3
lines changed

mypy/nodes.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ def __str__(self) -> str:
379379

380380

381381
FUNCBASE_FLAGS = [
382-
'is_property', 'is_class', 'is_static', 'is_final'
382+
'is_property', 'is_class', 'is_static', 'is_final', 'plugin_generated'
383383
] # type: Final
384384

385385

@@ -394,6 +394,7 @@ class FuncBase(Node):
394394
'is_static', # Uses "@staticmethod"
395395
'is_final', # Uses "@final"
396396
'_fullname',
397+
'plugin_generated',
397398
)
398399

399400
def __init__(self) -> None:
@@ -410,6 +411,7 @@ def __init__(self) -> None:
410411
self.is_class = False
411412
self.is_static = False
412413
self.is_final = False
414+
self.plugin_generated = False
413415
# Name with module prefix
414416
# TODO: Type should be Optional[str]
415417
self._fullname = cast(Bogus[str], None)
@@ -2711,13 +2713,15 @@ def __init__(self,
27112713
implicit: bool = False,
27122714
module_hidden: bool = False,
27132715
*,
2716+
plugin_generated: bool = False,
27142717
no_serialize: bool = False) -> None:
27152718
self.kind = kind
27162719
self.node = node
27172720
self.module_public = module_public
27182721
self.implicit = implicit
27192722
self.module_hidden = module_hidden
27202723
self.cross_ref = None # type: Optional[str]
2724+
# self.plugin_generated = plugin_generated
27212725
self.no_serialize = no_serialize
27222726

27232727
@property
@@ -2774,6 +2778,8 @@ def serialize(self, prefix: str, name: str) -> JsonDict:
27742778
data['module_public'] = False
27752779
if self.implicit:
27762780
data['implicit'] = True
2781+
# if self.plugin_generated:
2782+
# data['plugin_generated'] = True
27772783
if self.kind == MODULE_REF:
27782784
assert self.node is not None, "Missing module cross ref in %s for %s" % (prefix, name)
27792785
data['cross_ref'] = self.node.fullname()
@@ -2807,6 +2813,8 @@ def deserialize(cls, data: JsonDict) -> 'SymbolTableNode':
28072813
stnode.module_public = data['module_public']
28082814
if 'implicit' in data:
28092815
stnode.implicit = data['implicit']
2816+
# if 'plugin_generated' in data:
2817+
# stnode.plugin_generated = data['plugin_generated']
28102818
return stnode
28112819

28122820

mypy/plugin.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ def lookup_qualified(self, name: str, ctx: Context,
113113
suppress_errors: bool = False) -> Optional[SymbolTableNode]:
114114
raise NotImplementedError
115115

116+
@abstractmethod
117+
def add_symbol_to_type(self, info: TypeInfo, name: str, node: SymbolTableNode) -> None:
118+
raise NotImplementedError
119+
116120

117121
# A context for a function hook that infers the return type of a function with
118122
# a special signature.

mypy/plugins/common.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def _add_method(
109109
func.type = set_callable_name(signature, func)
110110
func._fullname = info.fullname() + '.' + name
111111
func.line = info.line
112+
func.plugin_generated = True
112113

113114
info.names[name] = SymbolTableNode(MDEF, func)
114115
info.defn.defs.body.append(func)

mypy/semanal.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3375,6 +3375,9 @@ def create_getattr_var(self, getattr_defn: SymbolTableNode,
33753375
return SymbolTableNode(GDEF, v)
33763376
return None
33773377

3378+
def add_symbol_to_type(self, info: TypeInfo, name: str, node: SymbolTableNode) -> None:
3379+
pass
3380+
33783381
def rebind_symbol_table_node(self, n: SymbolTableNode) -> Optional[SymbolTableNode]:
33793382
"""If node refers to old version of module, return reference to new version.
33803383
@@ -3567,6 +3570,7 @@ def name_already_defined(self, name: str, ctx: Context,
35673570
node = original_ctx.node
35683571
elif isinstance(original_ctx, SymbolNode):
35693572
node = original_ctx
3573+
# import pdb; pdb.set_trace()
35703574

35713575
if isinstance(original_ctx, SymbolTableNode) and original_ctx.kind == MODULE_REF:
35723576
# Since this is an import, original_ctx.node points to the module definition.

mypy/server/aststrip.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
Node, FuncDef, NameExpr, MemberExpr, RefExpr, MypyFile, FuncItem, ClassDef, AssignmentStmt,
4545
ImportFrom, Import, TypeInfo, SymbolTable, Var, CallExpr, Decorator, OverloadedFuncDef,
4646
SuperExpr, UNBOUND_IMPORTED, GDEF, MDEF, IndexExpr, SymbolTableNode, ImportAll, TupleExpr,
47-
ListExpr, ForStmt, Block
47+
ListExpr, ForStmt, Block, FuncBase,
4848
)
4949
from mypy.semanal_shared import create_indirect_imported_name
5050
from mypy.traverser import TraverserVisitor
@@ -94,10 +94,17 @@ def visit_class_def(self, node: ClassDef) -> None:
9494
node.type_vars = []
9595
node.base_type_exprs.extend(node.removed_base_type_exprs)
9696
node.removed_base_type_exprs = []
97+
print(node.defs.body)
98+
node.defs.body = [s for s in node.defs.body
99+
if not (isinstance(s, FuncBase) and s.plugin_generated)]
100+
print(node.defs.body)
101+
97102
with self.enter_class(node.info):
98103
super().visit_class_def(node)
99104

100105
def strip_type_info(self, info: TypeInfo) -> None:
106+
print("stripping", info.fullname())
107+
101108
info.type_vars = []
102109
info.bases = []
103110
info.is_abstract = False
@@ -111,6 +118,13 @@ def strip_type_info(self, info: TypeInfo) -> None:
111118
info.declared_metaclass = None
112119
info.metaclass_type = None
113120

121+
to_delete = [k for k, v in info.names.items()
122+
if isinstance(v.node, FuncBase) and v.node.plugin_generated]
123+
# print("YO HOW BOUT THIS", to_delete, info.names.items())
124+
for k in to_delete:
125+
del info.names[k]
126+
# import pdb; pdb.set_trace()
127+
114128
def visit_func_def(self, node: FuncDef) -> None:
115129
if not self.recurse_into_functions:
116130
return

mypy/server/update.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@
126126
from mypy.checker import FineGrainedDeferredNode
127127
from mypy.errors import CompileError
128128
from mypy.nodes import (
129-
MypyFile, FuncDef, TypeInfo, SymbolNode, Decorator,
129+
MypyFile, FuncDef, FuncBase, TypeInfo, SymbolNode, Decorator,
130130
OverloadedFuncDef, SymbolTable, LambdaExpr
131131
)
132132
from mypy.options import Options
@@ -846,6 +846,12 @@ def reprocess_nodes(manager: BuildManager,
846846
module_id)
847847
return set()
848848

849+
nodeset_ = set(s for s in nodeset
850+
if not (isinstance(s.node, FuncBase) and s.node.plugin_generated))
851+
print(nodeset_ - nodeset)
852+
nodeset = nodeset_
853+
print(nodeset)
854+
849855
file_node = manager.modules[module_id]
850856
old_symbols = find_symbol_tables_recursive(file_node.fullname(), file_node.names)
851857
old_symbols = {name: names.copy() for name, names in old_symbols.items()}
@@ -885,6 +891,7 @@ def key(node: FineGrainedDeferredNode) -> int:
885891
fnam=file_node.path,
886892
options=options,
887893
active_type=deferred.active_typeinfo):
894+
# import pdb; pdb.set_trace()
888895
manager.semantic_analyzer.refresh_partial(deferred.node, patches)
889896

890897
# Third pass of semantic analysis.

test-data/unit/deps.test

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,3 +1346,32 @@ class D(C):
13461346
<m.C.mm> -> m.D.mm
13471347
<m.C> -> m, m.C, m.D
13481348
<m.D> -> m.D
1349+
1350+
[case testDataclassDeps]
1351+
# flags: --python-version 3.6
1352+
from dataclasses import dataclass
1353+
1354+
Z = int
1355+
1356+
@dataclass
1357+
class A:
1358+
x: Z
1359+
1360+
@dataclass
1361+
class B(A):
1362+
y: int
1363+
[builtins fixtures/list.pyi]
1364+
1365+
[out]
1366+
<m.A.(abstract)> -> <m.B.__init__>, m
1367+
<m.A.__eq__> -> <m.B.__eq__>
1368+
<m.A.__init__> -> <m.B.__init__>, m.B.__init__
1369+
<m.A.__ne__> -> <m.B.__ne__>
1370+
<m.A.__new__> -> <m.B.__new__>
1371+
<m.A.x> -> <m.B.x>
1372+
<m.A.y> -> <m.B.y>
1373+
<m.A> -> m, m.A, m.B
1374+
<m.B.y> -> m
1375+
<m.B> -> m.B
1376+
<m.Z> -> m
1377+
<dataclasses.dataclass> -> m

test-data/unit/fine-grained.test

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,71 @@ class M(type):
629629
==
630630
a.py:4: error: Incompatible types in assignment (expression has type "str", variable has type "int")
631631

632+
[case testDataclassUpdate1]
633+
# flags: --python-version 3.6
634+
[file a.py]
635+
from dataclasses import dataclass
636+
637+
@dataclass
638+
class A:
639+
x: int
640+
641+
[file b.py]
642+
from dataclasses import dataclass
643+
644+
from a import A
645+
@dataclass
646+
class B(A):
647+
y: int
648+
649+
B(1, 2)
650+
651+
[file a.py.2]
652+
from dataclasses import dataclass
653+
654+
@dataclass
655+
class A:
656+
x: str
657+
658+
[out]
659+
==
660+
b.py:8: error: Argument 1 to "B" has incompatible type "int"; expected "str"
661+
[builtins fixtures/list.pyi]
662+
663+
[case testDataclassUpdate2]
664+
# flags: --python-version 3.6
665+
[file c.py]
666+
Foo = int
667+
668+
[file c.py.2]
669+
Foo = str
670+
671+
[file a.py]
672+
from dataclasses import dataclass
673+
from c import Foo
674+
675+
@dataclass
676+
class A:
677+
x: Foo
678+
679+
[file b.py]
680+
from dataclasses import dataclass
681+
682+
from a import A
683+
@dataclass
684+
class B(A):
685+
y: int
686+
687+
B(1, 2)
688+
689+
[out]
690+
==
691+
b.py:8: error: Argument 1 to "B" has incompatible type "int"; expected "str"
692+
[builtins fixtures/list.pyi]
693+
694+
695+
696+
632697
[case testAddBaseClassMethodCausingInvalidOverride]
633698
import m
634699
class B(m.A):

0 commit comments

Comments
 (0)