Mypy zwraca błąd, którego nie rozumiem (ani nie mogę odtworzyć w zmniejszonej formie). Googlowanie błędu jest trudne (biorąc pod uwagę znak zapytania).

Czy ktoś wie, co oznacza ten błąd? Co konkretnie oznacza znak zapytania?

Unsupported type Type[typeBasePage?]

Kod, o którym mowa:

typeBasePage = TypeVar("typeBasePage", bound="BasePage")  # any instance subclass of BasePage
typeNavCls = Type[typeBasePage] # error occurs here - trying to make an alias

class Nav:
    def __init__(self, cls: typeNavCls):
        self.cls: typeNavCls = cls

class BasePage(Base):
    ...
    # redacted because it's huge

Ponownie, jeśli spróbuję odtworzyć powyższy kod w bardzo zredukowanej formie, mypy nie zawiera błędów.

typeB = TypeVar('typeB', bound='B')
tAlias = Type[typeB]

class Nav:
    def __init__(self, cls: tAlias):
        self.cls: tAlias = cls

class A: pass
class B(A): pass
1
Marcel Wilson 19 listopad 2019, 22:14
Czy to typeBaseTab czy typeBasePage? To, co zamieściłeś, nie wygląda na wewnętrznie spójne i nie jest jasne, czy jest to również problem w Twoim rzeczywistym kodzie.
 – 
user2357112 supports Monica
19 listopad 2019, 22:40
Przepraszam... literówka podczas próbowania różnych odmian. Poprawiłem kod. Powinno być typeBasePage.
 – 
Marcel Wilson
19 listopad 2019, 22:55

2 odpowiedzi

Obawiam się, że bez pełnej repliki będzie trochę trudno określić, na czym polega problem. Myślę, że jest to najprawdopodobniej błąd mypy: mypy jest prawdopodobnie zdezorientowany przez jakąś cykliczność w twoim kodzie. Może cykl importu, trochę dziwności związanych z importowaniem modułów i podmodułów, twoja pamięć podręczna mypy jest w jakiś sposób uszkodzona...

Inną możliwością jest to, że twój kod w jakiś inny sposób nie jest bezpieczny pod względem typu, a ten błąd jest po prostu następstwem tego, że mypy i tak próbuje walczyć do przodu.

Aby zawęzić problem, zalecamy ponowne uruchomienie mypy po upewnieniu się, że używasz najnowszej wersji i po usunięciu pliku .mypy_cache. Jeśli problem nadal występuje, warto spróbować stopniowo usuwać niepowiązane części bazy kodu, aby spróbować odtworzyć kopię.


To powiedziawszy, może być również warte przestawienia się na nieużywanie aliasów typu tutaj: w rzeczywistości nie czerpiesz żadnych korzyści z używania aliasów/zmiennej typu w twoim przykładzie.

Krótko mówiąc, jeśli używasz dowolnych zmiennych typu w aliasach typów, te zmienne typu są w rzeczywistości zawsze wolne: musisz jawnie powiązać je z jakimś typem, gdy zaczniesz używać aliasu. Więc zamiast robić def __init__(self, cls: typeNavCls), będziesz chciał zrobić def __init__(self, cls: typeNavCls[SomeTypeHere]).

W przeciwnym razie mypy potraktuje Twój podpis jako dokładnie to samo co def __init__(self, cls: typeNavCls[Any]) w ten sam sposób, w jaki podpis def foo(x: List) jest traktowany jako def foo(x: List[Any]).

Możesz skonfigurować mypy, aby ostrzegał Cię o tym problemie, uruchamiając go ze znacznikiem --strict lub flagami --disallow-any-generics: poprzednia flaga automatycznie włącza drugą.

Zamiast tego prawdopodobnie powinieneś wykonać jedną z następujących czynności:

# Approach 1: If you don't want Nav to be inherently generic
class Nav:
    def __init__(self, cls: Type[BasePage]):
        self.cls = cls

# Approach 2: If you want Nav to be inherently generic
_TBasePage = TypeVar('_TBasePage', bound='BasePage')
class Nav(Generic[_TBasePage]):
    def __init__(self, cls: Type[_TBasePage]):
        self.cls = cls

# Approach 3: If you want to ensure two or more types are the same,
# but nothing else
class Nav:
    def __init__(self, cls: Type[_TBasePage], instance: _TBasePage):
        # ...but you won't be able to save 'cls' as a field here: your
        # Nav class needs to be generic with respect to _TBasePage if you
        # want to "preserve" whatever type that's bound to as a field.

# Approach 4: If your alias is complex enough where you want to
# force a relationship between two or more types within the alias
MyAlias = Tuple[Type[_TBasePage], _TBasePage]
class Nav:
    def __init__(self, info: MyAlias[BasePageSubtype]):
        self.info = info

# Approach 5: ...or if you want Nav to also be generic:
MyAlias = Tuple[Type[_TBasePage], _TBasePage]
class Nav(Generic[_TBasePage]):
    def __init__(self, info: MyAlias[_TBasePage]):
        self.info = info

# Important note: the previous example is actually exactly
# equivalent to doing:

_T1 = TypeVar('_T1', bound=BasePage)
MyAlias = Tuple[Type[_T1], T1]

_T2 = TypeVar('_T2', bound=BasePage)
class Nav(Generic[_T2]):
    def __init__(self, info: MyAlias[_T2]):
        self.info = info

Podejścia 1, 2 i 3 powinny ominąć pierwotny problem, który miałeś. Jeśli te podejścia są podobne do tego, co faktycznie chcesz zrobić, po prostu prostsze/bardziej zwięzłe jest w ogóle nie używać aliasów. A jeśli nie używasz aliasów, nie natkniesz się na błąd.

Ale jeśli twój rzeczywisty kod jest bardziej podobny do podejścia 4 lub 5, niestety konieczne będzie głębsze zbadanie.


Ostatnia uwaga dotycząca Twojej odpowiedzi samodzielnej tutaj: ta sekcja dokumentów odnosi się do używania znaków zapytania tylko w kontekście typów literału. Ale nie używasz typów literałów, więc ta sekcja dokumentów nie jest tutaj istotna.

Jeśli pytanie pojawia się po zwykłym wystąpieniu, oznacza to, że typ jest w rzeczywistości „typem niezwiązanym”. Jest to specjalny rodzaj typu tylko do użytku wewnętrznego, którego mypy używa jako symbolu zastępczego dla rzeczy, które powinny być typami, ale nie mogą znaleźć odpowiadającej definicji.

Oznacza to, że albo jest błąd w mypy, który powoduje, że typ niezwiązany pojawia się w miejscach, w których nie powinien być, albo że istnieje jakiś inny, prawdopodobny, uzasadniony błąd, który zanieczyszcza błędy niższego rzędu.

2
Michael0x2a 20 listopad 2019, 02:26

Wydaje mi się, że znalazłem znak zapytania, ale nie jestem bliżej wyjaśnienia, dlaczego błąd występuje w mypy.

https://mypy.readthedocs.io/en/latest/literal_types.html

Jeśli nie podasz jawnego typu w Final, typ c staje się zależny od kontekstu: mypy zasadniczo spróbuje „zastąpić” oryginalna przypisana wartość, gdy jest używana przed wykonaniem typu kontrola. Dlatego ujawnionym typem c jest Literal[19]?: znak zapytania na końcu odzwierciedla ten kontekstowy charakter.

0
Marcel Wilson 19 listopad 2019, 22:43