Skip to content

Commit 3e87bcc

Browse files
committed
Properly populate the origin type for members
1 parent 04b554d commit 3e87bcc

3 files changed

Lines changed: 17 additions & 20 deletions

File tree

tests/test_type_dir.py

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import typing
33
from typing import Literal, Never, TypeVar, Union
44

5-
import pytest
65

76
from typemap.type_eval import eval_typing
87
from typemap.typing import (
@@ -313,9 +312,6 @@ def _get_member(members, name):
313312
)
314313

315314

316-
@pytest.mark.xfail(
317-
reason="Members does not actually report the correct origin class!"
318-
)
319315
def test_type_members_attr_():
320316
d = eval_typing(Members[Final])
321317
member = _get_member(d, "ordinary")
@@ -328,31 +324,21 @@ def test_type_members_func_1a():
328324
d = eval_typing(Members[Final])
329325
member = _get_member(d, "foo")
330326
assert typing.get_origin(member) is Member
331-
name, typ, quals, _origin = typing.get_args(member)
327+
name, typ, quals, origin = typing.get_args(member)
332328
assert name == typing.Literal["foo"]
333329
assert quals == typing.Literal["ClassVar"]
334330

335331
assert (
336332
str(typ)
337333
== "\
338334
typing.Callable[[\
339-
typemap.typing.Param[typing.Literal['self'], tests.test_type_dir.Final, typing.Never], \
335+
typemap.typing.Param[typing.Literal['self'], tests.test_type_dir.Base[int], typing.Never], \
340336
typemap.typing.Param[typing.Literal['a'], int | None, typing.Never], \
341337
typemap.typing.Param[typing.Literal['b'], int, typing.Literal['keyword', \
342338
'default']]], \
343339
dict[str, int]]"
344340
)
345341

346-
347-
@pytest.mark.xfail(
348-
reason="Members does not actually report the correct origin class!"
349-
)
350-
def test_type_members_func_1b():
351-
# This should be merged up with 1a once it is fixed
352-
d = eval_typing(Members[Final])
353-
member = _get_member(d, "foo")
354-
assert typing.get_origin(member) is Member
355-
_, _, _, origin = typing.get_args(member)
356342
assert origin.__name__ == "Base[int]"
357343

358344

@@ -367,7 +353,7 @@ def test_type_members_func_2():
367353
assert (
368354
str(typ)
369355
== "\
370-
classmethod[tests.test_type_dir.Final, tuple[typemap.typing.Param[typing.Literal['a'], int | None, typing.Never], typemap.typing.Param[typing.Literal['b'], ~K, typing.Never]], dict[str, int]]"
356+
classmethod[tests.test_type_dir.Base[int], tuple[typemap.typing.Param[typing.Literal['a'], int | None, typing.Never], typemap.typing.Param[typing.Literal['b'], ~K, typing.Never]], dict[str, int]]"
371357
)
372358

373359

typemap/type_eval/_apply_generic.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,8 @@ def apply(
273273
# before we evaluate the contents?
274274

275275
# FIXME: right now we flatten out all the attributes... but should we??
276+
# XXX: Yeah, a lot of work is put into copying everything into every
277+
# class and it is not worth it, at all.
276278

277279
new = {}
278280

@@ -302,6 +304,7 @@ def apply(
302304

303305
annos: dict[str, Any] = {}
304306
dct: dict[str, Any] = {}
307+
sources: dict[str, Any] = {}
305308

306309
cboxed.__local_annotations__, cboxed.__local_defns__ = _get_local_defns(
307310
boxed
@@ -310,9 +313,12 @@ def apply(
310313
cbase = new[base]
311314
annos.update(cbase.__local_annotations__)
312315
dct.update(cbase.__local_defns__) # uh.
316+
for k in [*cbase.__local_annotations__, *cbase.__local_defns__]:
317+
sources[k] = cbase
313318

314319
cboxed.__defn_names__ = set(dct)
315320
cboxed.__annotations__ = annos
321+
cboxed.__defn_sources__ = sources
316322
cboxed.__generalized_mro__ = [new[b] for b in boxed.mro]
317323

318324
for k, v in dct.items():

typemap/type_eval/_eval_operators.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ def get_annotated_type_hints(cls, **kwargs):
5454
for acls in cls.__mro__:
5555
if not hasattr(acls, "__annotations__"):
5656
continue
57+
# XXX: This is super janky; we should just use the real mro
58+
# and not flatten things
59+
sources = getattr(acls, "__defn_sources__", {})
5760
for k in acls.__annotations__:
5861
if k not in hints:
5962
quals = set()
@@ -73,7 +76,7 @@ def get_annotated_type_hints(cls, **kwargs):
7376
else:
7477
break
7578

76-
hints[k] = ty, tuple(sorted(quals)), acls
79+
hints[k] = ty, tuple(sorted(quals)), sources.get(k, acls)
7780

7881
# Stop early if we are done.
7982
if len(hints) == len(ohints):
@@ -84,6 +87,7 @@ def get_annotated_type_hints(cls, **kwargs):
8487
def get_annotated_method_hints(tp):
8588
hints = {}
8689
for ptp in reversed(tp.mro()):
90+
sources = getattr(ptp, "__defn_sources__", {})
8791
for name, attr in ptp.__dict__.items():
8892
if isinstance(
8993
attr,
@@ -97,10 +101,11 @@ def get_annotated_method_hints(tp):
97101
if attr is typing._no_init_or_replace_init:
98102
continue
99103

104+
rtp = sources.get(name, ptp)
100105
hints[name] = (
101-
_function_type(attr, receiver_type=ptp),
106+
_function_type(attr, receiver_type=rtp),
102107
("ClassVar",),
103-
ptp,
108+
rtp,
104109
)
105110

106111
return hints

0 commit comments

Comments
 (0)