#type-hinting

1 messages · Page 1 of 1 (latest)

proven fog
#

How can I fix this mypy error:

with supertype "object"  [override]```

It is complaining about: `def __dict__(self) -> dict[str, Any]:`
soft matrix
#

are you implementing dict as a method?

rare scarab
#

__dict__ should be a dict, not a function returning dict

acoustic thicket
#

might accept @property pithink

#

why are you overriding it anyway

ruby ember
#

I'm not sure what the following mypy error means :

There are no .py[i] files in directory '.'

I'm aware that there are no py files in the directory - but i'm not sure why it's not recursing into the directories and picking up the py files that are in there ?

proven fog
#

I guess I misunderstood what __dict__ is

#

Does dict call it or no?

rare scarab
#

no, it would be called by vars()

proven fog
#

ok

rare scarab
#

and that's not even considering if the class uses __slots__ instead

proven fog
#

so I should just use a property if I want a to get a dict repr?

rare scarab
#

What do you mean?

proven fog
#

I just want a dict representation of my class instance

rare scarab
#

you should make a method like asdict()

proven fog
#

yeah I was thinking that

#

ok thanks👍

devout barn
#

quick question, what are the 3 generics for collections.abc.Coroutine?
like suppose Coroutine[A, B, C] what do these A, B, and C stand for?

soft matrix
#

yield type send type and return type respectively

devout barn
#

thanks!

soft matrix
#

most people use a type alias cause the first 2 are useless for 99% of cases

grave fjord
#

I've never been able to use them

#

I thought I would be able to though

soft matrix
#

would also be nice if in the documentation for typing they just named the TypeVars YieldType, SendType, ReturnType in class typing.Coroutine(Awaitable[V_co], Generic[T_co, T_contra, V_co]) (same for Generator)

grave fjord
#

So like Callable[P, Coro[T_yield, T_send, R]]

ruby ember
#

note - the reason for this (#type-hinting message) is that there were ignores in mypy.ini as:

[mypy]
exclude = [ "tests", 'build']

I'm wondering what's typically done here 🤔

When I push up to gitlab build is excluded, so mypy fails on CI, maybe i should use an include instead of exclude ?

grave fjord
#

ReturnType is wrong because R is idiomatic, and it looks like a TypeAlias

#

According to pep8 T_yield is wrong because it's named based on how it's used and not how is behaves, but I don't care :p

devout barn
#

doesn't standard lib things like inspect.iscoroutinefunction properly narrow types or do we have to implement our own type guards?
I got into this situation :

def benchmark(anycallable_or_coro: AnyFunc | Coro):
    if iscoroutinefunction(anycallable_or_coro):
        # AnyFunc is aliased to unions of Callable (see definition)
        # Coro is aliased to Coroutine
        # At this point, `anycallable_or_coro` should be narrowed to CoroFunc
        # but pyright thinks :
        # Argument of type "AnyFunc | Coro[Unknown]" cannot be assigned to parameter "corofunc" of type 
        # "CoroFunc[Unknown]" in function "_corofunc_wrapper"
        return _corofunc_wrapper(anycallable_or_coro)

Definitions :

Coro: TypeAlias = Coroutine[None, None, Result]
Func: TypeAlias = Callable[Params, Result]
CoroFunc: TypeAlias = Callable[Params, Coro]
AnyFunc: TypeAlias = Func | CoroFunc
rare scarab
#

What does reveal_type() say?

grave fjord
#

A Coro isn't a CoroFunc

rare scarab
#

mhm. The function returns a coroutine.

#

i.e. Callable[..., Coro]

grave fjord
#

There's no return type

devout barn
#

includes Coro as well

grave fjord
#

I think you want Func | CoroFunc | Coro

devout barn
rare scarab
#

just AnyFunc should do

grave fjord
#

What happens if you add a return type?

devout barn
#

I hope that is what is happening right now pithink

devout barn
#

the function def of benchmark ?

grave fjord
#

Yeah

#

And what does reaveal_type say?

#

And I think you do need a type guard

rough sluiceBOT
#

stdlib/inspect.pyi line 191

def iscoroutinefunction(obj: Callable[_P, object]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, Any]]]: ...```
grave fjord
#

Also the TypeGuard is different in >= 3.8

devout barn
#

I was implementing a typeguard and ended up overloading the definitions. And that seems to work too pithink

#

god I love typing

#

I did it like this :

@overload
def benchmark(anycallable_or_coro: Func) -> Benchmarked:
    ...


@overload
def benchmark(anycallable_or_coro: CoroFunc) -> Callable[Params, Benchmarked]:
    ...


@overload
def benchmark(anycallable_or_coro: Coro) -> Benchmarked:
    ...


def benchmark(anycallable_or_coro):
    if iscoroutinefunction(anycallable_or_coro):
        reveal_type(anycallable_or_coro)
        return _corofunc_wrapper(anycallable_or_coro)
    if iscoroutine(anycallable_or_coro) or callable(anycallable_or_coro):
        return Benchmarked(anycallable_or_coro)
    msg = f"Expected callable or coroutine, got {anycallable_or_coro!r} instead"
    raise TypeError(msg)
grave fjord
#

Just only allow async functions

devout barn
#

and the reveal type in this situation said Type of "anycallable_or_coro" is "Unknown"

#

is it possible to skip the fields of a generic?

#

like say I have a class Foo which implements two generics
then can I do something like Foo[None, int]
then let first field be inferred?

edit: Apparently this can be done by filling out the fields with an ellipsis ...

rare scarab
#

you could use Awaitable[Result]

devout barn
#

is it valid to annotate to the instance self with class generics for overloading?
for example :

    @overload
    def function(
        self: Benchmarked[Params, Awaitable[Result]]
    ) -> Callable[Params, Awaitable[Result]]:
        ...

    @overload
    def function(self: Benchmarked[Params, Result]) -> Callable[Params, Result]:
        ...

    @property
    def function(self):
        if self.__original_corofunc is None:
            return self.__func # Non-async func
        return self.__original_corofunc # async function

because reveal_type shows the the return type as Unknown and assignments are inferred as Unknown as well pithink

#

to make this minimally reproducible I did :

from __future__ import annotations
from typing import Generic, TypeVar, overload

T = TypeVar("T", int, str, float)

lol = {
    int: str,
    str: float,
    float: dict,
}


class Foo(Generic[T]):
    def __init__(self, bar: T) -> None:
        self._bar: T = bar

    @overload
    def bar_it(self: Foo[int]) -> str:
        ...

    @overload
    def bar_it(self: Foo[str]) -> float:
        ...

    @overload
    def bar_it(self: Foo[float]) -> dict:
        ...

    def bar_it(self):
        return lol[type(self._bar)]()


f = Foo("Hi")
print(f.bar_it())
#

But why does making it a property make it Unknown

from __future__ import annotations
from typing import Awaitable, Generic, TypeVar, overload, ParamSpec
from typing_extensions import reveal_type

T = TypeVar("T")
P = ParamSpec("P")


class Foo(Generic[P, T]):
    def __init__(self, fn) -> None:
        self.fn = fn

    @overload
    def function(self: Foo[P, Awaitable[T]]):
        ...

    @overload
    def function(self: Foo[P, T]):
        ...

    @property
    def function(self):
        return self.fn


@Foo
async def a():
    ...


@Foo
def b():
    ...


reveal_type(a.function()) # Type of "a.function()" is "Unknown"
reveal_type(b.function()) # Type of "b.function()" is "Unknown"
rare scarab
#

There isn't enough information to infer the generics.

#

Add them explicitly to the Foo call

#

@Foo[str, str]

brisk hedge
#

generic properties have been denied before, for technical reasons I believe

devout barn
#

How to mention that I am expecting an invariant list of float and not a covariant one?

def foo(x: list[float]): ...
acoustic thicket
#

variance is a property of the generic type itself, not a property of individual list objects

#

specifically list[T] is invariant in T

brisk hedge
#

Yeah, list is already invariant over its argument so there isn't a "covariant one"

devout barn
#

huh pithink

#

I think I got my definition wrong

#

if list is invariant

#

then how is this possible :

def foo(x: list[float]) -> None:
    print(x)


foo([1, 2, 3]) # This is accepted
               # so isn't list covariant?
               # Because int was considered as an subtype of float?
oblique urchin
#

it's called type context or bidirectional inference

#

the type checker infers [1, 2, 3] to be of type list[float]

#

generally if you put it in a variable l = [1, 2, 3] first it will reject it

devout barn
#

oh

acoustic thicket
#

interesting

oblique urchin
#

invariance doesn't mean the list can't contain subtypes of float, just that the static type must be list[float]

devout barn
#

that rejection after being assigned to a variable is so strange

brisk hedge
#

specifically, the inference goes inside the list itself, interpreting each individual int as a float

#

which bypasses the concern for variance

devout barn
#

so is there a way to tell I am accepting a covariant list now?

class Parent:
    pass


class Child:
    pass


def foo(x: list[Parent]) -> None:
    print(x)


foo([Child()]) # Since lists are invariant, this is rejected
               # How to mention that we accept covariant list of Parent classes?
#

(excuse my stupid questions, just learning variance now pithink )

oblique urchin
#

for the same reason as we talked about with int/float

#

there is no way to actually say "covariant list", but a close equivalent is to use Sequence

brisk hedge
#

depends on what you use it for
Sequence[T] is covariant

devout barn
#

So I can't fully wrap my head around how list isn't covariant when Child classes are accepted when the function signature mentions about accepting parent types

brisk hedge
#

it's because the child object is interpreted as a parent object in that specific expression
not in general

copper geode
#
foo: (int, int)

this is not a valid replacement for

foo: Tuple(int, int)

right?

acoustic thicket
#

right

soft matrix
#

(Neither are valid)

acoustic thicket
#

im blind

oblique urchin
#

right, should be Tuple[int, int] (or tuple[int, int] in 3.9+)

rustic gull
#

Is it possible to define a type variable/alias that has an unbound variable to it? I'm probably getting the terminology wrong, but something like:
myalias[T] = Mapping[T, T]
CC @ornate thunder

oblique urchin
rustic gull
#

no because then I can't pass in a T

soft matrix
#

But you can

rustic gull
#

myalias[int]

soft matrix
#

Try it

brittle socket
#

How can I fix this?
https://mypy-play.net/?mypy=latest&python=3.10&gist=62d1dd91437fc1edffd0bd5a298858b8

import re

markers = {
    "span_markers": (["abc"], ["abc"])
}

span_markers: tuple[list[re.Pattern[str]], list[re.Pattern[str]]]

span_markers = tuple(
    list(re.compile(m) for m in span)
    for span in markers["span_markers"])
Incompatible types in assignment (expression has type "Tuple[List[Pattern[str]], ...]", variable has type "Tuple[List[Pattern[str]], List[Pattern[str]]]")
#

It's like it doesn't like that it somehow can't see exactly how many elements in the tuple there are

soft matrix
#

Try casting markers as a Final[Mapping]

#

Or just giving it an explicit type

brittle socket
#

Same error when I give it an explicit type markers: dict[str, tuple[list[str], list[str]]]

#

I'm not familiar with Final[Mapping], what would that do

past pumice
#

Final indicates that the variable under the given name does not get reassigned

#

It does not make mutable types immutable (in terms of typing)

#

So I wouldn't think it'd help here

brittle socket
#

Right, ok

past pumice
#

I had completely honed in on the Final and missed the Mapping portion, which would indicate that it's immutable, but I don't think it'll work. I don't think specifying the number of arguments would work like this no matter how simple the expression inside is

#

span_markers: tuple[str, str] = tuple(["a", "b"]) this would still produce the same error

brittle socket
#

Yeah, that should be span_markers: tuple[list[str]] in your case

past pumice
#

Not quite, no

#

!e print(tuple(["a", "b"]))

rough sluiceBOT
#

@past pumice :white_check_mark: Your 3.11 eval job has completed with return code 0.

('a', 'b')
brittle socket
#

...

#

I hate that

#

Ok, it's constructing a tuple from the list

#

I still hate it

past pumice
#

It's in the specification for the tuple function lol

#

But yeah I can see why you'd think it'd work that way

#

Not sure which would be more intuitive

#

Well, I can't really come up with a good solution

#

It doesn't seem to be possible to do this with the tuple function

brittle socket
#

I don't see how else to construct a tuple 😕

#

Not like there are tuple comprehensions

past pumice
#

Yeah so what I've come to is just using the comma format and duplicate the generation

#

Something like ```py
= list(re.compile(m) for m in markers["span"][0]), list(re.compile(m) for m in markers["span"][1])

#

Hardly pythonic though

#

The other options are to embrace the variable-length or disable type checking for that line

brittle socket
#

Expressing that it's a fixed-length pair of lists is exactly why I'd went with tuple of lists instead of list of lists 😕

#

Seems like the choice is between expressing the correct semantics, or compromising on them to appease the type checker

#

I think it's fair to say this is an example where the type checker is an obstacle

past pumice
#

Perhaps a set?

brittle socket
#

I'd wonder "what are they trying to unique here" if I saw a set there, wouldn't you?

past pumice
#

Fair

brittle socket
#

I'll go with the disabling-for-this-line option 🤷‍♂️

#

Thank you 🙂

devout barn
#

Doesn't subclassing with a new variant typevar make the derived class use the new typevar?
I did this :

from typing import TypeVar

T_contra = TypeVar("T_contra", contravariant=True)

class Parent: pass
class Child(Parent): pass

class ContraTuple(tuple[T_contra]):
    def first_element(self: "ContraTuple[T_contra]"):
        return self[0]

c = ContraTuple[Child]([Parent()]) # Error during typecheck

and pyright reported this :

Argument of type "list[Parent]" cannot be assigned to parameter "__iterable" of type "Iterable[Child]" in function "__new__"
  TypeVar "_T_co@Iterable" is covariant
    "Parent" is incompatible with "Child"

Why is ContraTuple using the old covariant typevar when it should be using the contravariant one? why does this happen?

#

TypeVar "_T_co@Iterable" is covariant
why isn't this contravariant by using the new T_contra@ContraTuple?

brisk hedge
#

you can't make tuple contravariant by force

#

it's covariant over its arguments

#

in general custom variance is only accepted by Generic[T]

devout barn
#

Oh I see, thanks!

devout barn
#
def _corofunc_wrapper(
    corofunc: Callable[Params, Awaitable[Result]]
) -> Callable[Params, Benchmarked[Params, Result]]:

    def benchmark_wrapper(
        *args: Params.args, **kwargs: Params.kwargs
    ) -> Benchmarked[Params, Result]:
        coro: Awaitable[Result] = corofunc(*args, **kwargs)
        return Benchmarked[Params, Result](routine=coro, original_callable=corofunc)

    return benchmark_wrapper


# Sync Function
@overload
def benchmark(anyfunc: Callable[Params, Result]) -> Benchmarked[Params, Result]:
    ...

# Async Coroutine Function
@overload
def benchmark(
    anyfunc: Callable[Params, Coroutine[Any, Any, Result]]
) -> Callable[Params, Benchmarked[Params, Result]]:
    ...

def benchmark(anyfunc: Callable):
    if iscoroutinefunction(anyfunc):
        return _corofunc_wrapper(anyfunc)
    if callable(anyfunc):
        return Benchmarked(routine=anyfunc, original_callable=anyfunc)
    msg = f"Expected callable, got {anyfunc!r} instead"
    raise TypeError(msg)

So I have the following signatures as mentioned above.
If a corofuncttion is fed into benchmark then a wrapper function is returned which when called returns an instance of Benchmarked class
if a sync function is fed then an instance of Benchmarked is directly returned.
However, this is not getting inferred.
For example :

@benchmark
async def foo():
    return 1


x = foo()
reveal_type(x)
# Getting inferred as Type of "x" is "Coroutine[Any, Any, Literal[1]]"
# When in reality it's Benchmarked[(), Literal[1]]
# How can I get this fixed?

I have looked at the overloads for quite some time, but they seem to be correct. How can I fix this?

#

It's almost like the typechecker is one step ahead in each type change...

@benchmark
async def foo():
    return 1


print(foo, type(foo)) # <function _corofunc_wrapper.<locals>.benchmark_wrapper at 0x000002BED19EE5F0> <class 'function'>
reveal_type(foo) # Type of "foo" is "Benchmarked[(), Coroutine[Any, Any, Literal[1]]]"

x = foo()

print(x, type(x)) # foo: not measured <class 'tools.benchmark.Benchmarked'>
reveal_type(x) # Type of "x" is "Coroutine[Any, Any, Literal[1]]"
devout barn
#

I simplified / refactored the above code to this :

class CoroutineWrapper(Generic[Params, Result]):
    def __init__(self, corofunc: Callable[Params, Awaitable[Result]]) -> None:
        self.cf = corofunc

    def __call__(
        self, *args: Params.args, **kwargs: Params.kwargs
    ) -> Benchmarked[Params, Result]:
        coro: Awaitable[Result] = self.cf(*args, **kwargs)
        return Benchmarked[Params, Result](coro, self.cf)


def benchmark(
    anyfunc: Callable[Params, Result | Awaitable[Result]]
) -> CoroutineWrapper[Params, Result] | Benchmarked[Params, Result]:
    if iscoroutinefunction(anyfunc):
        return CoroutineWrapper[Params, Result](anyfunc)
    if callable(anyfunc):
        return Benchmarked[Params, Result](routine=anyfunc, original_callable=anyfunc)
    msg = f"Expected callable, got {anyfunc!r} instead"
    raise TypeError(msg)

Still doesn't typecheck but looks better

@benchmark
async def foo():
    return 1


reveal_type(foo)    # Type of "foo" is "CoroutineWrapper[(), Literal[1]] | Benchmarked[(), Literal[1]]"
                    # Actually it's CoroutineWrapper[(), Literal[1]]

reveal_type(foo())  # Type of "foo()" is "Benchmarked[(), Literal[1]] | Literal[1]"
                    # Actually it's Benchmarked[(), Literal[1]]
# So how to get proper inference?
grave fjord
#

Don't worry about coroutine objects they are an implementation detail of your async framework

ornate thunder
#

Hey, how can I typehint a type that's common in the whole class?
Example:

class cls:
  def foo(self, ...) -> T:
    ...
  def bar(self, e : T) -> int:
    ...

In both functions the same type should be passed in

rough sluiceBOT
#

class typing.Generic```
Abstract base class for generic types.

A generic type is typically declared by inheriting from an instantiation of this class with one or more type variables. For example, a generic mapping type might be defined as:

```py
class Mapping(Generic[KT, VT]):
    def __getitem__(self, key: KT) -> VT:
        ...
        # Etc.
```  This class can then be used as follows...
void panther
#

doing class cls(Generic[T]) will have the T in the whole body of the class

ornate thunder
#

And I guess I still need to declare a T = TypeVar('T') right?

void panther
#

yes

ornate thunder
#

okay, thanks
it works

brittle socket
devout barn
# grave fjord Just make your code only work for async functions

Oh yeah! Making two separate decorators for async and sync function makes everything work perfectly (typechecks as well) but I wanted a single decorated which handled both cases, whilst that works during runtime it doesn't typecheck well. So do i have to separate this into two decorators?

grave fjord
#

the type system also doesn't know if something is a function that returns an object or a function that returns an Awaitable[object]

#

Because Awaitable[object] is also an object @devout barn

#

also inspect.iscoroutinefunction often doesn't match what is or isn't usable as a coroutine function

brittle socket
#

https://mypy-play.net/?mypy=latest&python=3.10&gist=b752d2b28cfa9c2345513760a8fbb575

from typing import TypeAlias, Literal, Tuple

MarkerKey: TypeAlias = Literal["start_marker", "na_span_markers"]
StrMarkers: TypeAlias = str | Tuple[list[str], list[str]]

def fn(markers: dict[MarkerKey, StrMarkers]):
    pass

my_markers = {
    "start_marker": "intro",
    "na_span_markers": (["abc"], ["def", "ghi"])
}

fn(my_markers)
Argument 1 to "fn" has incompatible type "Dict[str, Sequence[Sequence[str]]]"; expected "Dict[Literal['start_marker', 'na_span_markers'], Union[str, Tuple[List[str], List[str]]]]"

Looks to me like my_markers obeys dict[MarkerKey, StrMarkers]. Am I missing something?

soft matrix
#

it looks like you should be using a typed dict but i cant see whats wrong with this

#

!d typing.TypedDict

rough sluiceBOT
#

class typing.TypedDict(dict)```
Special construct to add type hints to a dictionary. At runtime it is a plain [`dict`](https://docs.python.org/3/library/stdtypes.html#dict "dict").

`TypedDict` declares a dictionary type that expects all of its instances to have a certain set of keys, where each key is associated with a value of a consistent type. This expectation is not checked at runtime but is only enforced by type checkers. Usage...
brittle socket
#

Don't typed dicts need to have homogeneous types in their values?

soft matrix
#

no?

brittle socket
#

Right:

each key is associated with a value of a consistent type
My values can be str or tuple[list[str], list[str]]

soft matrix
#

fwiw my_markers: dict[MarkerKey, StrMarkers] = { fixes this

soft matrix
brittle socket
soft matrix
#

if you dont want the user to typehint neither will work

brittle socket
#
from typing import TypedDict

class Markers(TypedDict):
    start_marker: str
    na_span_markers: str | tuple[list[str], list[str]]

def fn(markers: Markers):
    pass

my_markers = {
    "start_marker": "intro",
    "na_span_markers": (["abc"], ["def", "ghi"])
}

fn(my_markers)
Argument 1 to "fn" has incompatible type "Dict[str, Sequence[Sequence[str]]]"; expected "Markers"

https://mypy-play.net/?mypy=latest&python=3.10&gist=dd750274f42c0164eddc598cb99eaa13
Sigh

soft matrix
#

also to solve this issue you can just pass the arg straight to the function

#
fn({
    "start_marker": "intro",
    "na_span_markers": (["abc"], ["def", "ghi"])
})```should work
brittle socket
#

Yeah I think I'll go with this approach, along with the TypedDict. Thank you 🙂

rustic gull
#

I have this BaseChannel TyedDict which looks like this: ```py
class BaseChannel(TypedDict):
type: Literal[0, 1, 2]

Now for every value of type, there will be another child class with more variables.. Like ```py
class Type1Channel(BaseChannel):
    type: 0
    foo: str

But it raises an error that both classes have different types.. How do I fix that? Should I just remove the type variable from BaseChannel?

rare scarab
#

type it as Literal[0]

rustic gull
#

Oh sorry I actually did that just forgot to write it here.. it's still same though

#

Just in case if this helps

trim tangle
#

This function is legal, and it won't work correctly if you pass in a TextChannel

#

Should I just remove the type variable from BaseChannel?
Yes, probably.

#

You can also use a union type:

Channel = Union[Channel1, Channel2, Channel3, ...]
rustic gull
brittle socket
#
WARNING: there is a new pyright version available (v1.1.262 -> v1.1.263).
Please install the new version or set PYRIGHT_PYTHON_FORCE_VERSION to `latest`

Any way to get rid of this warning? I run a conda environment and 262 is the latest on conda channels

rare scarab
#

how did you install pyright?

brittle socket
#

conda install pyright=1.1.262 -c conda-forge -y

rare scarab
#

remove the = part

#

or set the env var PYRIGHT_PYTHON_FORCE_VERSION=latest

brittle socket
#
conda install pyright -c conda-forge -y
Collecting package metadata (current_repodata.json): done
Solving environment: done

# All requested packages already installed.
#

I don't think it matters so long as I'm in a conda environment

#

Their channels are just not updated with the latest pyright yet

#
conda search pyright | tail
pyright                      1.1.260  py38h0a891b7_0  conda-forge
pyright                      1.1.260  py38h50598f1_0  conda-forge
pyright                      1.1.260  py39h4d8b378_0  conda-forge
pyright                      1.1.260  py39hb9d737c_0  conda-forge
pyright                      1.1.262 py310h5764c6d_0  conda-forge
pyright                      1.1.262  py37h540881e_0  conda-forge
pyright                      1.1.262  py38h0a891b7_0  conda-forge
pyright                      1.1.262  py38h50598f1_0  conda-forge
pyright                      1.1.262  py39h4d8b378_0  conda-forge
pyright                      1.1.262  py39hb9d737c_0  conda-forge
#

Oh. Setting the env var worked

#

Weird. There was no installing done. But the warning doesn't complain anymore. 🤷‍♂️

rare scarab
#

it overrides the version pyright thinks it is

spare flame
#

What format is this time string in?

'2022-07-22T17:58:09+0000'

buoyant swift
#

iso8601?

rare scarab
#

what's that have to do with types?

#

just call datetime.fromisostringwhatever()

blazing cobalt
brittle socket
#

261 it is. Until 263 is available on conda

rare scarab
#

Why not install pyright from npm?

#

npm i -g pyright

#

it uses node anyway. The pip package is just a wrapper

errant plover
#

why is pyright made in node wtf

covert dagger
#

it is easier to do vscode extensions with typescript+node, and node is faster than python so that is another advantage

brisk hedge
ornate thunder
#

Hey, if I have a dict, that contains classes with a common base classes, can I just do this: dict[CommonBase0, CommonBase1]? Or I have to use 2 bound TypeVar's?

#
class CommonBase0:
  pass
class CommonBase1:
  pass
my_dict : dict[CommonBase0, CommonBase1] = dict()
soft matrix
#

Trivially this looks fine

#

But what does the dict actually look like?

#

Wait no this is definitely running into variance issues

#

It's probably better to use a union because you'll get errors if you assign a subclass of common base 1 to the value

ornate thunder
#

Union of all classes that might set as the value/key?

#
class SubClass0_0(CommmonBase0):
  pass
class SubClass1_0(CommmonBase1):
  pass
my_dict[SubClass0_0] = SubClass1_0() # Edited

And so on.

soft matrix
#

Perhaps

#

What does my_dict look like?

ornate thunder
#

If that helps anything, a value of the same class never appears

#

So the type of both the key and value are unique

soft matrix
#

can you show me the definition of my_dict

ornate thunder
# soft matrix can you show me the definition of my_dict

Sorry for the late reply, had some stuff to do.
well I mean

self.my_dict = {
   # Key is always a class type (Common base class of all keys is `CommonBase0`), value is always a class instance, and the common base type for all of them is `CommonBase1`
   # CommonBase0: CommonBase1()
   SubClass0_0:  SubClass1_0(),
   SubClass0_1:  SubClass1_1(),
   SubClass0_n:  SubClass1_n(),
}
void panther
#

is there any way to mark a class variable required to be defined in subclasses?

soft matrix
#

abstractmethod + classmethod + property?

#

Except don't use classmethod + property

void panther
#

Didn't really work when I tried it. Guess I'll just do a normal abstract property to at least partially get it

soft matrix
#

Wdym by didn't really work?

void panther
#

pyright is complaining about assigning to the property type, and mypy didn't care if I overrode it with a method with the same signature as the property

soft matrix
#

oh well a variable is incompatible with a property

void panther
#

how would I get something like this to work?

class BaseDict(typing.TypedDict):
    a: int

class DerivedDict(BaseDict):
    b: int

class Super:
    def from_dict(self, arg: BaseDict) -> None:
        ...

class Child(Super):
    def from_dict(self, arg: DerivedDict) -> None:
        ...

I understand why it doesn't type check, but I'm not sure how to get the behaviour

rustic gull
#

is something like this pythonic? i dont see why not

typing.ClassVar[list[int]]
soft matrix
#

that isnt checked is it?

rustic gull
soft matrix
#

Like it doesn't enforce that a subclass has to override it

rustic gull
#

sorry i dont understand what you mean😅

blazing nest
blazing nest
brittle socket
#
T = TypeVar('T')
def f(iterable: Iterator[T]) -> Iterator[T]:
    for e in iterable:
        yield e

Is this the most correct way I can typehint such a function?

#

I figured I needed Iterator instead of Iterable since that for loop requires a __next__

buoyant swift
#

for loops automatically use iter

#

!e

L = [1, 2, 3]
for x in L:
  print(x)

next(L)
rough sluiceBOT
#

@buoyant swift :x: Your 3.11 eval job has completed with return code 1.

001 | 1
002 | 2
003 | 3
004 | Traceback (most recent call last):
005 |   File "<string>", line 5, in <module>
006 | TypeError: 'list' object is not an iterator
brittle socket
#

Uuh I get why your code broke but I'm not sure what you're suggesting I do about mine

buoyant swift
#

so you don't need Iterator, you can just use Iterable

brittle socket
#

Ohh, you meant "for loops use iter", not "for loops, I should use iter"

#

So __next__ has nothing to do with it, gocha

#

What if iterable is not a list but an iterator, still holds?

buoyant swift
#

iterators are iterable, so yes

brittle socket
#

Oh yeah ofc, iterators are iterables

#

But the return type is more correct as Iterator

buoyant swift
#

yep

brittle socket
#

Thank you

oblique urchin
#

PEP 695 is your answer

brisk heart
#

With PEP695 I'm surprised more keywords aren't made soft

#

Wonder if there are any that can't be soft, kinda doubt it

oblique urchin
rare scarab
#

!pep 695

rough sluiceBOT
#
**PEP 695 - Type Parameter Syntax**
Status

Draft

Python-Version

3.12

Created

15-Jun-2022

Type

Standards Track

acoustic thicket
#

pretty excited about this

blazing nest
#

I don't understand this: ```python
class ClassC[V]:
# The use of M and K are not allowed for "method2". A type checker
# should generate an error in this case because this method uses the
# new syntax for type parameters, and all type parameters associated
# with the method must be explicitly declared. In this case, K
# is not declared by "method2", nor is it supplied defined an outer
# scope.
def method2[M](self, a: M, b: K) -> M | K: ...

#

If I want to type my method like that, what would be the fix?

acoustic thicket
#

def method2[M, K]?

cedar sundial
#

What would M and K refer to here? The input types or return types?

acoustic thicket
#

they're type variables, they're allowed in both input types and return types

cedar sundial
#

I was just thinking why repeat itself if the input annotations are already there; I guess the input annotations are necessary to identify each parameter as a specific type

blazing nest
blazing nest
soft matrix
#

How do you lie about covariance or similar with this?

brittle socket
#

What's the right way to type hint a pred parameter so as to also work with a regex match as pred?
Currently I'm just marking it as Callable but I'm trying to be more specific if possible

def fn(pred: Callable, iterable: Iterable[T]) -> Iterator[T]:
   for e in iterable:
       if pred(e):
           yield e
fn(compiled_re_pattern.match, my_list)
#

My first reflex was Callable[[T], bool] but that didn't work. Part of it was that re.match doesn't return a bool

soft matrix
#

Callable[[T], bool] (Callable[[T], Any] is technically more correct but i think bool is better)

brittle socket
#

This is what I get with Callable[[T], bool]

incompatible type "Callable[[str, int, int], Optional[Match[str]]]"; expected "Callable[[str], bool]
soft matrix
#

ok this is a case where Any makes sense as the last argument

#

but idk whats up with the param list here

brittle socket
#

That's re.Pattern.match's function signature Pattern.match(string[, pos[, endpos]]) (Callable[[str, int, int], ...)

#

Instead of Any, is there a way to say "convertible to bool"?

soft matrix
#

no because everything implements __bool__

#

the default is just True

brittle socket
#

Right

#

Oh well, guess Callable[[T], Any] is the best I can do here

glossy thunder
#

[

blazing nest
#

May want to move forward with posting to the thread about some use-cases for lying about covariance

brittle socket
#

What return type do you put for a function that could return T or raise exceptions?

buoyant swift
#

there's no way to annotate that a function might raise, like in java, so just T

brittle socket
#

Alright, T it is, thanks

#

There is a Exception type in the code snippets in the typehinting docs, but it's apparently only for "documentational" purposes because Guido didn't want it in

brittle socket
#
def async_query(on_success: Callable[[int], None],
                on_error: Callable[[int, Exception], None]) -> None:
    # Body
oblique urchin
#

that just means the callback takes an Exception object as an argument

brittle socket
#

Yeah. Exception is neither defined nor mentioned anywhere before

oblique urchin
#

it's a builtin

brittle socket
#

Oh. That I didn't know lol

#

https://stackoverflow.com/questions/44282268/python-type-hinting-with-exceptions

Exceptions
No syntax for listing explicitly raised exceptions is proposed. Currently the only known use case for this feature is documentational, in which case the recommendation is to put this information in a docstring

Guido van Rossum has strongly opposed adding exceptions to the type hinting spec, as he doesn't want to end up in a situation where exceptions need to be checked (handled in calling code) or declared explicitly at each level.

oblique urchin
pastel egret
brittle socket
#

Took me a minute to grok that, but it's an interesting notion. Since I'm just using the thing's __call__ method, that's considered a base functionality and hence object fits better iiuc

pastel egret
#

Not call, that's not base object behaviour. But yes to bool.

brittle socket
#

Oh right __call__ is obv not base. By bool you mean __bool__ which is a base method?

pastel egret
#

Yep, because all objects are convertable to bool, unless they explicitly raise an exception. By defautl they're just always true.

#

Other examples are __str__()/__repr__(), __eq__(), __ne__()...

brittle socket
#

Nice. Thank you! I'll try it out later

trim tangle
soft matrix
#

Is bool None?

rare scarab
#

!e ```py
import numpy as np

array = np.array(10)
print(bool(array))

rough sluiceBOT
#

@rare scarab :white_check_mark: Your 3.11 eval job has completed with return code 0.

True
rare scarab
#

??

acoustic thicket
#

!e

import numpy as np

array = np.array([10, 11])
print(bool(array))
rough sluiceBOT
#

@acoustic thicket :x: Your 3.11 eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 4, in <module>
003 | ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
buoyant swift
#

that's a 0-dim array. for arrays of multiple- ^

devout barn
#

Is every regular type annotation like

class DerivedInt(int):
    pass


def f(x: int):
    return "Some stuff", x


f(DerivedInt())

covariant by default? pithink

soft matrix
#

function parameters are covariant yeah

#

callable is not

trim tangle
#

whereas list[T] is invariant with respect to T

foggy thicket
#

Does

cannot be further subscribed.
(https://github.com/python/cpython/blob/main/Lib/typing.py#L623)

mean that ClassVar cannot be aliased?
Because creating aliases for typing.ClassVar does not depict the same behavior as the non-aliased ones.
Example :

class Foo:
    var: ClassVar[str] = "Some"
    def __init__(self) -> None:
        self.var = "Should fail" # Fails as expected (fine)

but,

Aliased = ClassVar[str]

class Bar:
    var: Aliased = "Some"
    def __init__(self) -> None:
        self.var = "Should fail but doesn't"  # Typechecks

Could this be a bug?
I am using pyright 1.1.263 (latest)

rough sluiceBOT
#

Lib/typing.py line 623

ClassVar accepts only types and cannot be further subscribed.```
brisk hedge
#

classvar cannot be generic (ClassVar[list[T]])

#

that's what the error is supposed to be for

brittle socket
#

I'm trying not to pollute the code file with # type: ignores so I'm doing it from mypy.ini, but isn't there a simpler/more compact way of doing this?

[mypy]

[mypy-funcy.*]
ignore_missing_imports = True
[mypy-more-itertools.*]
ignore_missing_imports = True
[mypy-docx.*]
ignore_missing_imports = True
[mypy-simplify_docx.*]
ignore_missing_imports = True
#

Like somehow applying ignore_missing_imports to a list of specific modules?

soft matrix
#

I have

class Foo:
    x: tuple[int, int] | tuple[str]
    def __init__(self, *args: ???):
        self.x = x
```what should go in ??? to make this actually work?
#

I've tried *tuple[int, int] and *tuple[str] and they both work individually

#

but not in a union

#

(I changed it to Unpack cause its a syntax error with the union)

fervent sierra
#

isn't it just the type of the individual element? so tuple[int, int] | tuple[str]

soft matrix
#

no

#

cause thatd be a tuple of tuple[int, int] | tuple[str]s

oblique urchin
#

*(tuple[int, int] | tuple[str]) would make sense but I don't think the runtime allows that

soft matrix
#

can't unpack unions

#

Expected TypeVarTuple or Tuple as type argument for Unpack Pylance reportGeneralTypeIssues

fervent sierra
#

but then what's the idea? if you want to assign to self.x, why even have the *args as opposed to a single, non-variadic arg?

soft matrix
#

because i shouldnt have to pander to the type system for something so trivial

#

also i dont like the way passing a tuple literal looks

fervent sierra
soft matrix
#

just having the args being 2 different types

#

i can probably just use an overload

fervent sierra
#

i guess...
I still don't get the reason for the variadic args... did you want the type checker to infer that 2 arguments should be tuple[int, int] and a single arg should be a tuple[str] ?

soft matrix
#

yes

fervent sierra
#

aha
Well, i kinda think it's good that this is not allowed. Otherwise whoever is reading the signature for __init__ would have to also read the type of self.x to figure out that it's not really a param that takes an arbitrary number of arguments, but rather a shorthand for a context-dependent type

soft matrix
#

im the only person using this

fervent sierra
#

Your future self in 3 months is essentially a different person ^^

tranquil turtle
#

you already mentioned it, sorry for ping

pastel egret
#

Defining an overload would be best, specifying 1 or 2 positional-only args.

dull lance
#

How would I write a type stub for a class to indicate that the class should not be instantiated directly (via cls())?

#

I tried def __new__(cls) -> NoReturn: ... but it didn't work, so right now I am including an argument in the constructor which type cannot be accessed externally, like

class _Dummy:
    pass

class MyClass:
    def __init__(self, internal_only: _Dummy) -> None: ...

Wondering if there is a better way to do this

acoustic thicket
#

make it an abc.ABC?

#

hm instantiating ABCs doesnt set off typecheckers

#

interesting

brisk heart
#

it should complain only if there's still an abstract method so makes sense I suppose

fervent sierra
celest ore
#

I also get the error: TypeError: non-default argument 'foo' follows default argument when defining it, which doesn't make sense to me because I am giving a default argument to that field - does default here mean "not a primitive type" ?

blazing nest
#

I don't remember whether this was possible... how do I type that two attributes are of the same type out of a union?

#

Making the class generic is not a choice for me. I want to make type checkers be able to infer the type of one of the attributes if it knows the other

brazen jolt
#

isn't that just a typevar bound to more values?

#

could you give some example on where you need this?

soft matrix
#

do you want a tagged union?

blazing nest
brazen jolt
#

and why can't you make the class generic again?

#

the type-checker will infer what the type should be if it knows one of those types

blazing nest
blazing nest
brazen jolt
#

how are these attributes getting set?

blazing nest
#

It's based off of runtime data I get from the network. I cannot know in advance what the types of these are

brazen jolt
#

well then what better thing would you want to do, the type checker won't run on runtime to check those things

blazing nest
#

I don't want to make separate classes for this. If I can't figure this out I'll just leave it as-is with unions

brazen jolt
#

just make the class generic

#

yeah, the type-checker will infer it as the union without being able to narrow it. but that's fine

blazing nest
#

Well, I am hoping to.. if I can 😅

brazen jolt
#

this is your best option probably: ```py
from typing import TypeVar, Union, Generic

T = TypeVar('T', bound=Union[str, int])

class Test(Generic[T]):
attr: T
var: T

brazen jolt
soft matrix
#

i think they are

brazen jolt
#

at least with pyright, it doesn't seem like it can handle it

blazing nest
#

Yeah, unfortunate

brazen jolt
#

but type-wise making the class generic does make sense for what you want

#

and this might get added into pyright eventually

blazing nest
#

Hmm yeah

spiral fjord
#

@blazing nest

from typing import TypeVar, Union, Generic
from typing_extensions import reveal_type


T = TypeVar("T", str, int)


class Test(Generic[T]):
    attr: T
    var: T


a = Test()

if isinstance(a.attr, str):
    reveal_type(a.var)

mypy tells met the revealed type is str here

blazing nest
#

Ooh! Fancy haha

spiral fjord
#

@blazing nest or if you want both pyright and mypy there's type guards

#
from typing import TypeVar, Generic, TypeGuard
from typing_extensions import reveal_type


T = TypeVar("T", str, int)


class Test(Generic[T]):
    attr: T
    var: T


def is_string_test(t: Test[T]) -> TypeGuard[Test[str]]:
    return isinstance(a.attr, str)


a = Test()

if is_string_test(a):
    reveal_type(a.var)
brazen jolt
#

yeah, typeguards would solve this

#

you could even make one to test out the specific type from the union dynamically

#
def is_test_of(test: Test, of: type[T]) -> TypeGuard[Test[T]]:
    return isinstance(a.attr, of)
rustic lagoon
#

How does one type hint fixture request type?
I have a fixture whose only purpose is monkey patching a aiosmtp smtp client since i need to test methods that use them internally. I would also like to parametrise the fixture to represent different states the mail server could be in. However I'm not sure how to type hint the request parameter of the fixture

@pytest.fixture(params=["hello", "pytest"])
def patch_smtp(monkeypatch: pytest.MonkeyPatch, request: pytest.FixtureRequest) -> None:
    def mock_init(self, *args: Any, **kwargs: Any) -> None:
        self.is_mock = True
        self.message = request.param

    monkeypatch.setattr(SMTP, "__init__", mock_init)

however using request.param later is treated as an error by mypy error: "FixtureRequest" has no attribute "param"

rustic lagoon
#

oh

#

ok

#

-.-

chrome dust
#

How do you do type a forwarding function? E.g.:

from typing import Optional


def foo(a: str, b: str) -> None:
    pass


def bar(*args, c: Optional[str] = None) -> None:
    foo(*args)

How do you type *args without copying the signature from foo manually?

acoustic thicket
#

paramspec

fervent sierra
chrome dust
chrome dust
acoustic thicket
#

nvm you can't use paramspec for this, my bad

fervent sierra
soft matrix
#

You need to make a decorator that copies the type

brisk hedge
#

from __future__ import annotations

from typing import Callable, Generic, TypeVar
from typing_extensions import reveal_type

A = TypeVar("A")
B = TypeVar("B")
C = TypeVar("C")

class Compose(Generic[A, B]):
    def __init__(self, f: Callable[[A], B]):
        self.f = f
    
    def __or__(self, g: Callable[[B], C]) -> Compose[A, C]:
        return Compose(lambda a: g(self.f(a)))
    
    def __call__(self, a: A) -> B:
        return self.f(a)

def generic(a: A) -> A:
    return a

def concrete(a: int) -> int:
    return a

reveal_type(Compose(concrete) | generic)  # "Compose[int, int]"
reveal_type(Compose(concrete) | concrete) # "Compose[int, int]"
reveal_type(Compose(generic) | generic)   # "Compose[A@generic, A@generic]"
reveal_type(Compose(generic) | concrete)  # Expected "Compose[int, int]"
# Operator "|" not supported for types "Compose[A@generic, A@generic]" and "(x: int) -> str"

Bad inference or just incompatible types?

brisk hedge
#

It might be that the bidirectional inference isn't strong enough for generic types

#

If so, there's probably a closed issue somewhere

spark flare
#

Hi, how is it going? Can I ask what's your opinion about pydantic? Is this a right place to ask it ?

trim tangle
cunning plover
cunning plover
soft matrix
#

Pydantic is literally in the channel description

trim tangle
# cunning plover Fixed

I mean, I thought the person wanted to ask people's general opinion about pydantic. Whether it's good, bad, where it is applicable etc.

cunning plover
trim tangle
cunning plover
trim tangle
cunning plover
# spark flare Hi, how is it going? Can I ask what's your opinion about pydantic? Is this a rig...

Awesome since it provides really easy to use data validation, but its main feature for me auto generating openapi schema for automated interactive documentation generation in FastAPI(redoc and etc)

I am in general a bit sceptical of typing validation on run time. I would prefer to see typing validation on compiling and runtime at the same time, and therefore not really seeing only runtime data validation as worthy goal to pursue. (Having good unit / integration testing and code architecture is first goal for me)
I still do typing anyway, but just because I wish getting IDE syntax colorations.

Perhaps together with mypy it should shine, but it is a bit of pain in the ass to setup. May be I am wrong though, but why Django mypy required additional library of stubs in order to work, will it be same difficult in fastAPI or more? pithink

Regardless of pydantic or not, but regular dataclasses I use as often as wishing. Just for better self documentation and variable grouping / cleaner code / better code structure. I haven't explored everything pydantic can offer though

hallow flint
brisk heart
#

I feel like pydantic just nearly misses what it'd do best

#

Rather than just data validation I'd expect it to do actual full data parsing of often unpredictable fields

#

I used pydantic for my models whenever I encountered epsecially pesky APIs that were just horribly unstandardized and it helped a bit but far from what I'd expect

copper geode
#

can you type hint the elements that you'll put into a queue.Queue?

trim tangle
#

Queue[int]

#

(I think)

copper geode
spark flare
#

Ok, thanks. It seems a good starting point, I will check the link you shared. About my experience with pydantic atm I can say, it is not always so elementary to use it when you have crazy dict to parse and validate (for example keys that are file names and you want a different class for every file). Documentation something is not enough and you need to search in the source code.

#

Anyway, one of my big "?" are the 500 open issue on github. A lot of stars, a big community of users, great sponsors, but I have some doubts (this is just my nature, I feel safe only when using modules in the core of Py lemon_happy ).. but I think atm using it just for runtime validation for example is ok. What do you think?

mortal fractal
#
def num(val: str) -> float:
    basenum = 20.
    prefix = 0
    power = 1024
    return basenum * power**prefix
#

mypy strict mode doesn't like it lol

soft matrix
#

why are you doing power^0 in the first place?

#

but also try annotating prefix as Final

desert delta
#

any ideas why mypy hates this?

def returns_dict(arg: Optional[Dict[str, Any]]) -> Dict[str, Any]:
    if arg is None:
        return {'foo': 'bar'}
    arg = cast(Dict[str, Any], arg)
    if arg['baz'] == {'foo' : 'bar'}:
        arg = arg['baz']
    
    return arg

error: Incompatible return value type (got "Optional[Dict[str, Any]]", expected "Dict[str, Any]")

#

ok, I think the error is a little misleading here -- if I move the cast right before the return statement, mypy is fine with it

#

so probably mypy doesn't know that arg['baz'] is Dict[str. Any] , but I'm not sure why that would trigger this particular error message instead of mypy inferring that arg is being assigned to something with type Unknown

trim tangle
#

huh, pyright has the same issue

hallow flint
#

Probably no type narrowing applied to arg[‘baz’]

#

So type checkers think it could be any value compatible with the original type of arg, which includes None

hallow flint
#

yeah, computer confirms:

from typing import *

def returns_dict(arg: Optional[Dict[str, Any]]) -> Dict[str, Any]:
    if arg is None:
        return {'foo': 'bar'}
    if arg['baz'] == {'foo': 'bar'}:
        arg = cast(Dict[str, Any], arg['baz'])
    return arg
blazing nest
blazing nest
#

Anyways you should be able to do ```python
from typing import Any, Dict, Optional, cast

def returns_dict(arg: Optional[Dict[str, Any]]) -> Dict[str, Any]:
if arg is None:
return {'foo': 'bar'}

if arg['baz'] == {'foo' : 'bar'}:
    return arg['baz']

return arg
dim flume
trim tangle
cedar sundial
#

Argument of type "Literal[1]" cannot be assigned to parameter "__s" of type "slice" in function "__setitem__" "Literal[1]" is incompatible with "slice".

What? I'm using the update operator on a TypedDict object. I don't get the error

#

The Literal[1] is a list index and I am trying to update the 2nd element in the list, which is the TypedDict object

spark flare
spark flare
#

ah ok..I've seen that cattr is similar to pydantic and has something like root

runic sleet
#

is this a valid way to annotate a generic subclassed generator

import typing as t
from collections.abc import Generator

T_yield = t.TypeVar("T_yield")
T_send = t.TypeVar("T_send")
T_return = t.TypeVar("T_return")

class WrapGenerator(Generator[T_yield, T_send, T_return]):
    def __init__(self, gen: Generator[T_yield, T_send, T_return]):
        self.gen = gen

    def send(self, value: T_send) -> T_yield:
        return self.gen.send(value)

    def throw(self, *args, **kwargs) -> T_yield:
        return self.gen.throw(*args, **kwargs)
#

I'm not sure what types the generic .throw() method is supposed to be

trim tangle
runic sleet
#

Could I just copy it? What about the ... defaults?

trim tangle
#

ah it's complicated

runic sleet
#

pithink

error: Signature of "throw" incompatible with supertype "Generator"
note:      Superclass:
note:          @overload
note:          def throw(self, Type[BaseException], object = ..., Optional[TracebackType] = ...) -> T_yield
note:          @overload
note:          def throw(self, BaseException, None = ..., Optional[TracebackType] = ...) -> T_yield
note:      Subclass:
note:          def throw(self, Type[BaseException], Union[BaseException, object] = ..., Optional[TracebackType] = ...) -> T_yield
brittle socket
#
def foo(raisable=None):
  if raisable:  
    raise raisable

What's the right way to type raisable? Exception | None?

leaden oak
#

not sure if in Python 3.10, you can use the built in type instead of typing.Type or not ...

brittle socket
#

3.10.5. So...I could use type[Exception] | Exception | None?

#

Oh, that worked, neat. Thank you 🙂

#

I'll look into this type generic, never used it before

leaden oak
#

it means the class object itself

#

Exception | None accepts None and also any class instance of the Exception class (or its subclasses)

trim tangle
#

An exception could in theory be falsey...

#

so maybe if raisable is None:

leaden oak
#

Type[Exception] | None accepts None and the Exception class and any subclasses it may have

brittle socket
leaden oak
#

uh, the type annotation is its own separate thing from the default, you'll want to do this instead

def foo(raisable: type[Exception] | Exception | None = None):
    try:
        # ...
    except StopIteration as exc:
        raise raisable or ValueError("...") from exc
brittle socket
#

Yeah, I edited since 😅

leaden oak
#

ah, poor timing then on my side

brittle socket
#

iiuc, type[Exception] won't accept instances right? so Exception is still needed

leaden oak
#

yup.

brittle socket
#

Thank you btw!

trim tangle
#

if someone wants to raise ValueError, well, too bad, they'll have to call the class

brittle socket
#

Interesting. I think the type annotation wouldn't even be necessary in that form

trim tangle
#

pyright will infer the annotation, but I wouldn't rely on it

#

mypy will probably treat it as Any

brittle socket
#
raise raisable or ValueError(f"Expected ... but got {value!r}")

In this case, I can't use ValueError as a default parameter since it depends on local value

runic sleet
#

what's the annotation for a functools.wraps wrapped function pithink

brittle socket
#
UtException: TypeAlias = type[Exception] | Exception | None

Value of type "Type[type]" is not indexable mypy mypy always lagging behind. type: ignore-ing it

brittle socket
#
import re
from collections.abc import Callable, Iterator
from functools import partial

class B:
    re_compile = partial(re.compile, flags=re.UNICODE)
    
    @staticmethod
    def join_bisections() -> Callable[[str], Iterator[str]]:

        bisection = B.re_compile(r"(\w+)-\s(\w+)") # This is the line that errors

        return lambda p: map(partial(bisection.sub, r"\1\2"), p)
main.py:11: error: Access to generic instance variables via class is ambiguous

https://mypy-play.net/?mypy=latest&python=3.10&gist=55f5abe49dbe4115ef08f4ab8b9ab4ef
Why is this a problem?

rare scarab
#

Try this. ```py
import re
from typing import Callable, Iterator, Pattern, ClassVar
from functools import partial

class B:
re_compile: ClassVar[Callable[[str], Pattern]] = partial(re.compile, flags=re.UNICODE)

@staticmethod
def join_bisections() -> Callable[[str], Iterator[str]]:

    bisection = B.re_compile(r"(\w+)-\s(\w+)")

    return lambda p: map(partial(bisection.sub, r"\1\2"), p)
#

@brittle socket ?

brittle socket
#

That worked, thank you 🙂

#

Apparently just re_compile: ClassVar is enough too

brittle socket
#

https://pyright-playground.decorator-factory.su/

from collections.abc import Callable, Iterator
from typing import ClassVar
from functools import partial
import re

class B:
    re_compile_unicode: ClassVar = partial(re.compile, flags=re.UNICODE)

    @staticmethod
    def join_bisections() -> Callable[[str], Iterator[str]]:
        
        bisection = B.re_compile_unicode(r"(\w+)-\s(\w+)")
    
        return lambda paragraph: map(
            partial(bisection.sub, r"\1\2"), paragraph) # line that errors
Could not bind method "sub" because "Pattern[AnyStr@compile]" is not assignable to parameter "self"
    TypeVar "AnyStr@Pattern" is invariant
      Type "AnyStr@compile" cannot be assigned to type "str" (reportGeneralTypeIssues)
#

iiuc, the call to re.sub tries to assign something to a self parameter and pyright doesn't like it? 😕

rare scarab
#

it works if you fully type the ClassVar

#

my example from earlier still works

spiral fjord
#

The problem seems to be unrelated to the class

brittle socket
#

Indeed

#

ClassVar[Callable] worked 🙂

rustic gull
#

@manic wolf what your T should have been is Union[TypeA, TypeB, TypeC], but I can't quite put my finger on why your TypeVar wasn't allowed, perhaps because it wasn't related to a second use of T?

#

(context:

TypeA = NewType("TypeA", str)
TypeB = NewType("TypeB", str)
TypeC = NewType("TypeC", str)


T = TypeVar("T", TypeA, TypeB, TypeC)


def f(s: str) -> Union[T, Literal["error!"]]:
    if s == "a":
        return TypeA("hello A")
    if s == "b":
        return TypeB("hello B")
    if s == "c":
        return TypeC("hello C")
    return "error!"
#

the docs for TypeVar don't seem so great to me :x

acoustic thicket
#

i think you're looking for a type alias not a typevar

#

T = Union[TypeA, TypeB, TypeC]

#

typevars are for generic functions and types

rustic gull
#

I'm guessing that T can't be determined what it is statically, though perhaps there's a better way to phrase that

#

a union would be allowed to dynamically pick any of those, but a TypeVar would for a single call be a statically selected type... I think

#

but I don't think that's the whole reason :x
... or even necessarily correct

#

to simplify:

from typing import TypeVar

T = TypeVar("T", bound=int)

def f() -> T:
    return 0
acoustic thicket
#

a typevar is meant to link the argument types and the return type, just one occurrence of T in the signature is not meaningful

rustic gull
#

but in haskell you get to do:

f :: a
f = undefined
#

though if I try to give a concrete value instead of undefined I run into trouble.

#

can't say it's any and then return an Int

#

though that is something python will let me do, with Any

#

oh well. I sort of kinda almost get it. But I wouldn't be able to explain it.

brisk hedge
#

Any is special

#

it's not a traditional bottom type but a mix of top and bottom

#

Assignable to everything, as well as the reverse

brisk hedge
#

in practice this is a type checker warning

uneven ginkgo
#

If I use

@contextmanager
def temporary_accounts(
    apih: ApiHandle,
    obj_payload: List[Dict[str, Any]],
) -> Iterator[List[Account]]:

and I want to yield a list of the class Account

should it yield Iterator[List[Account]]
or just yield List[Account]

pastel egret
#

It should be a Generator actually, contextmanager requires one.

#

The type hint isn't correct at the moment, it's unresolved whether it's a good idea to correct that hint.

ornate thunder
#

perhaps not the perfect channel to ask, but is there any library for that is similar to dataclasses, but allows me to have a static class variable?
I have a little event dispatching system, and event_name is defined inside each Event class:

@datacalss(frozen=True)
class Event:
  event_name : str

@datacalss(frozen=True)
class MyAwesomeEvent(Event):
  event_name = "my_awesome_event" # Shouldn't be initializable when constructing the class
  event_specific_arg : int # This one should be though

Perhaps, my approach isn't the best either, so what do you guys think?
I looked at using attrs too.

soft matrix
#

i think this already works

ornate thunder
#

It doesn't, as it requires event_name to be passed to __init__

soft matrix
#

oh

#

because its defined in the superclass as a field

ornate thunder
#

I'm looking at attrs, and seems like this:

@attrs.frozen()
class Event:
  event_name : str = attrs.field(default="", init=False)

@attrs.frozen()
class MyAwesomeEvent(Event):
   event_name : str = attrs.field(default="my_awesome_event", init=False)

   event_specific_arg : int # Not sure if i need attrs.field here?
#

may work?

pastel egret
#

Define it as a ClassVar[str], then it'll be ignored.

ornate thunder
#

hm

#
from dataclasses import dataclass
from typing import ClassVar

@dataclass(frozen=True)
class Event:
    event_name : ClassVar[str]
    
    
@dataclass(frozen=True)
class MyEvent(Event):
    event_name : ClassVar[str] = "my_event"
    arg : int
    
print(MyEvent(arg=1))

works

pastel egret
#

That's how you specify class attributes not defined on the instance themselves. Both decotators detect and skip processing them.

ornate thunder
#

It also seems like I don't need to typehint in the derived class

#

which is nice

#

neat, just what I needed

winter delta
#

How to type hint Class type self inside of a class.

@dataclasses.dataclass(frozen=True, eq=True)
class Point:
x: float
y: float

def to_tuple(self):
    return self.x, self.y

@ancient thistle
def pointlist_points_to_tuples(pointlist: list[Point]):
    return [point.to_tuple() for point in pointlist]

Like here in the static method, what to replace list[Point] with

#

Tried self and pycharm said that was wrong as well

acoustic thicket
#

typing_extensions.Self

#

just "Point" (as a string) would work too, but its not as inheritance friendly

winter delta
#

Sweet that works thanks

soft matrix
#

shouldnt work in staticmethods

#

you should just use Point cause you cant actually check the current type inside a staticmethod

winter delta
#

Hrm you think? Pycharm stopped complaining but just ran it and didn't realize typing_extensions was not' builtin.

#

Point causes NameError on runtime as well

winter delta
#

Ok yeah that works correctly without any bs

knotty wren
#

is there a way i can fix this linter error?

@dataclass
class Thing:
    something: Optional[Something]

class SomeClass:
    def do_something(self):
        thing = self.__get_thing_somehow()
        self.__validate_thing(thing)
        # Getting an error here because linter thinks
        # thing.something could be None, when it cannot
        self.__handle_something(thing.something)

    def __validate_thing(self, thing: Thing):
        if not thing.something:
            raise ValueError("Something is missing here.")

    def __handle_something(self, something: Something):
        ...
soft matrix
#

have the function return thing.something as Something not Optional[Something]

#

and then just pass that

analog orbit
#

Need some help with this type alias. I'm trying to denote a callback that takes no arguments and either returns None or returns a coroutine that returns None ```py
Callback = Callable[[], Coroutine[None]|None]

class PubSub:
def init(self):
self.subscribers = defaultdict(set)

def subscribe(self, trigger: Hashable, callback: Callback):
    self.subscribers[trigger].add(callback)
#

pleae @ me on reply!

hallow flint
#

@analog orbit

analog orbit
hallow flint
rustic lagoon
#

this code:

from typing import Callable, Generic, TypeAlias, TypeVar

T = TypeVar("T")
A = TypeVar("A")
B = TypeVar("B")


class SealedA(Generic[A, B]):
    def __init__(self, val: A) -> None:
        self._inner = val

    def show(self) -> str:
        return f"SealedA: {str(self._inner)}"


class SealedB(Generic[A, B]):
    def __init__(self, val: B) -> None:
        self._inner = val

    def show(self) -> str:
        return f"SealedB: {str(self._inner)}"


PseudoSealed: TypeAlias = SealedA[A, B] | SealedB[A, B]


def a_not_none(func: Callable[[A], T]) -> Callable[[A | None], PseudoSealed[T, None]]:
    return lambda val: SealedB(val) if val is None else SealedA(func(val))


@a_not_none
def identity(x: T) -> T:
    return x


def main() -> None:
    n: int | None = 5
    not_none_a = identity(n)
    print(not_none_a.show())


if __name__ == "__main__":
    main()

has these mypy errors:
error: Argument 1 to "identity" has incompatible type "Optional[int]"; expected "Optional[T]"

#

that feels like a bug

#

or is there a way to type hint this to be considered valid by mypy?

trim tangle
rustic lagoon
#

basically if you have a higher order function that takes a function with generic argument types, and returns a function that takes generic types too, applying it to a function that takes generic results in the genetics being treated as explicit types

trim tangle
#

Hmmm actually

#

Yeah you'll have to make some callable protocols

rustic lagoon
#

I rewrote it to this using protocols

from functools import wraps
from typing import Generic, Protocol, TypeAlias, TypeVar

T = TypeVar("T")
A = TypeVar("A")
B = TypeVar("B")

CO = TypeVar("CO", covariant=True)
CT = TypeVar("CT", contravariant=True)


class SealedA(Generic[A, B]):
    def __init__(self, val: A) -> None:
        self._inner = val

    def show(self) -> str:
        return f"SealedA: {str(self._inner)}"


class SealedB(Generic[A, B]):
    def __init__(self, val: B) -> None:
        self._inner = val

    def show(self) -> str:
        return f"SealedB: {str(self._inner)}"


PseudoSealed: TypeAlias = SealedA[A, B] | SealedB[A, B]


class UnaryFunction(Protocol[CT, CO]):
    def __call__(self, val: CT) -> CO:
        ...


def a_not_none(
    func: UnaryFunction[A, T]
) -> UnaryFunction[A | None, PseudoSealed[T, None]]:
    @wraps(func)
    def inner(val: A | None) -> PseudoSealed[T, None]:
        return SealedB(None) if val is None else SealedA(func(val))

    return inner


class Iden:
    def __call__(self, val: A) -> A:
        return val


identity = a_not_none(Iden())


def main() -> None:
    n: int | None = 5
    not_none_a = identity(n)
    print(not_none_a.show())


if __name__ == "__main__":
    main()
#

and it still throws errors

#

error: Argument 1 to "__call__" of "UnaryFunction" has incompatible type "Optional[int]"; expected "Optional[A]"

trim tangle
#

No that's not quite what I meant

#

Actually, maybe it's impossible to express this

brisk hedge
brittle socket
#

I have Callable[[T], object] and I want to call it as Pred[T]. What should I make it as? A generic?

soft matrix
#

!d typing.TypeAlias

rough sluiceBOT
#

typing.TypeAlias```
Special annotation for explicitly declaring a [type alias](https://docs.python.org/3/library/typing.html#type-aliases). For example:

```py
from typing import TypeAlias

Factors: TypeAlias = list[int]
```  See [**PEP 613**](https://www.python.org/dev/peps/pep-0613) for more details about explicit type aliases.

New in version 3.10.
brittle socket
#

Yeah but I wouldn't be able to pass a T to a TypeAlias

soft matrix
#

you can

#

assuming you havent made it a string

brittle socket
#
Pred: TypeAlias = Callable[[T], object]

How can I use this as Pred[T]?

soft matrix
#

like that

brittle socket
#

and if I call it as Pred[U], will it resolve to Callable[[U], object]?

soft matrix
#

try it and see

brittle socket
#

On phone, sec

soft matrix
#

ok then yes

brittle socket
#
from typing import TypeAlias, TypeVar

T = TypeVar('T')
Pred: TypeAlias = Callable[[T], object]

def f(pred1: Pred[T], pred2: Pred[U]) -> tuple[T, U]:
  pass

So this should work

#

Weird...I can't intuit how Pred is subscriptable without being a generic, nor how it could take any type as a parameter and correctly resolve it as Callable's input

soft matrix
#

at runtime the subscription will go to CallableGenericAlias' getitem and that will perform the subscription if the generic alias has __parameters__ that can be substituted into

#

but i wouldnt worry about it that much

#

the annotation TypeAlias does nothing to the actual type of Pred

brittle socket
#

Alright, thank you 🙂

oblique urchin
#

use named functions with annotations

brittle socket
#

Regardless of mypy, that's considered better practice?

oblique urchin
#

very complicated lambdas tend to be frowned upon

#

I assume your real use case doesn't just return 42

brittle socket
#

It returns a function call which resolves to Iterator[T]

#

f here is actually just a currying function

#

Alright, named functions it is, thanks

#

Which I'm now realizing I'm getting wrong 🤦‍♂️, it just needs to be return lambda iterable: g(pred, iterable)

brittle socket
#

# pyright: reportPrivateImportUsage=false
I'm disabling this warning like this in my code, but does pyright have a flag or a config file where I can specify it instead of in my code?

trim tangle
#

I think there's something about it in the README/docs on github

brittle socket
#

Thanks 🙏

brittle socket
#

Ended up specifying it in a pyproject.toml instead 🙂

#

What does pyright mean by No configuration file found.? What is it looking for?

#

I'm assuming it's looking for pyrightconfig.json but I'm handling that directly in pyproject.toml's [tool.pyright]

slender timber
#

How good is pyright compared to mypy

#

I mean pyright is integrated into vscode but it doesn't seem to do shit

rustic lagoon
#

by default it has no type checking enabled

slender timber
#

Mypy was such a pita, imagine how dumb it is to type hint every function and variable

slender timber
rustic lagoon
#

you need to set it to either basic or strict in the config

slender timber
#

Yea i think i could do that on a workspace basis

rustic lagoon
#

you can do that on a workspace basis or globally

slender timber
#

Globally would be better

rustic lagoon
#

you can also set the rules you want enforced in the pyrightconfig.json

slender timber
#

I didn't know I could do this

#

Kinda bummer that vscode doesn't have it enabled by default

rustic lagoon
#

that's cause type hinting is optional in python, and a lot of people treat it as an extra opt in feature

slender timber
#

It must be cpu heavy though, in vscode once I go above 1k loc in a single file, even highlighting gets messy and wrong

slender timber
rustic lagoon
#

i don't think so? it usually takes some time to setup when opening a project, but then it's caching the changes

slender timber
#

Generally its quick

#

But above 1k lines or so, it gets extremely slow

rustic lagoon
#

i've used it for much larger projects and it's fine

slender timber
#

That's interesting

#

Maybe I used too much type hints and that caused it probably?

rustic lagoon
#

i use it in strict mode and add type hints everywhere

slender timber
#

wow ok what cpu do you have

rustic lagoon
#

if you were using mypy before, and used the default vscode mypy integration, then i think it was not really optimised

slender timber
#

Vscode doesn't use mypy anymore

#

Its not included in pylance

#

It was replaced with pyright

#

mypy with its daemon is quite fast btw

rustic lagoon
#

i know, but mypy is available as a linter in vscode

slender timber
#

Oh

brittle socket
#

HPC languages are statically typed and used in projects with millions of LOC. Typing was never really a performance hit.

slender timber
#

Typing gets weird with decorators amd stuff

#

Why do you do it compulsorily when it can make the code look unnecessarily verbose

brittle socket
#

Decorators have nothing to do with typing

slender timber
rustic lagoon
#

was there a question?

slender timber
#

Decorators accept callables and stuff, the typing for that is quite big

#

And it imo provides no added value

rustic lagoon
#

you can just not do it then

#

and even though i'd agree type hinting higher order functions can get a little unreadable fast in python, it does have a lot of value

slender timber
#

Is it a good idea to import the annotations and use shorter syntax?

#

Like the | for union types

brittle socket
#

Type aliasing can help when types get complicated

#

I just wrote a UnaryPred: TypeAlias = Callable[[T], object] and been using it a lot

slender timber
#

I got a library which I need to keep compatible with 3.6+, I am rewriting it so it will be a major change so I guess I can use annotations and make it 3.7+ compatible

#

I noticed that importing annotations breaks some stuff like inspect.signature, but I won't be using that it kinda seems hacky

rustic lagoon
#

it's doable, definitely, but when comparing it to something like haskell, the type annotations look a little verbose

flip :: (a -> b -> c) -> (b -> a -> c)
flip f = \x y -> f y x
def flip(func: Callable[[T1, T2], T3]) -> Callable[[T2, T1], T3]:
    return lambda x, y: func(y, x)
rustic lagoon
#

heh, got another one

rustic lagoon
#

there's a tool for modernising python i think

slender timber
#

pyupgrade ik

#

I don't need it because I am rewriting the library entirely

rustic lagoon
#

i don't actually remember what's in and not in python 3.7. I've hard switched to 3.10 as soon as it came out for structural pattern matching

slender timber
#

3.7 has dataclasses, lazy type annotations

slender timber
#

What does this error TypeVar "_MT" appears only once in generic function signature mean?

slender timber
slender timber
acoustic thicket
slender timber
#

It felt wrong to me too

frigid grove
#

Hi, why is there a whole channel for type-hinting?

#

I wanted to ask:
Do you use both -> str and :return: result_blocks: list or choose one?

soft matrix
#

Yep

#

Although you might be able to get away with just the first if you use Sphinx

#

It should add things like that itself

frigid grove
#

Thank you

fierce ridge
frigid grove
#

I love it

fierce ridge
#

personally i love it too, but it's still very clunky to use and can be severely limiting in some codebases

#

it's great for things like backend web servers and cli tools

#

it's a lot more complicated for libraries like pandas that have complicated, highly-polymorphic interfaces

#

the sooner python devs re-discover design-by-contract, the better imo

#

i've had mixed experiences so far w/ deal and icontract, because i think they made some questionable interface design choices. but i've come around to just putting assert statements for any properties that are difficult to test with static types. it's annoying that i lose integration w/ hypothesis, but it's so far been a good combination of "developer ux" and "runtime safety"

frigid grove
#

I meant that I love the existence of the channel. As for so much additional text for documentation, I'm still on the fence

fierce ridge
#

meh, the basic use case of f(x: float, y: float) -> float | None: is great and definitely beats a docstring to me

frigid grove
#

right, I meant the docstring actually, as having too much text. My bad.

#

So you skip the docstring?

fierce ridge
#

i usually try to write one as long as it's not totally redundant with the function name

#

e.g. i tend to skip it for smaller "internal-only" functions

twin lantern
#

guys

#

i was looking at godbolt and apparently type annotations don't just disappear and are actually of use for the runtime

#

so 2 questions

#
  1. what does it do with that info
#
  1. does this incur overhead that i might avoid by leaving types to be inferred where it's correct
#

because it's not really consistent for people who might not have inlay in their editors

trim tangle
# twin lantern 1. what does it do with that info
  1. Libraries like pydantic or dataclass-factory can inspect annotations to do some deserialization. The original rationale for function annotations (PEP3107) included the ability to introspect annotations at runtime.
  2. Yes but it's pretty insignificant, only occuring at startup time (when you import the module)
twin lantern
#

deserialisation of what

#

i just google pydantic and this seems great

#

im gonna use it

trim tangle
#

Like, decoding a JSON object into a dataclass according to type annotations in the class

#

Similar stuff is implemented in typer but for command-line interfaces

twin lantern
#

do you don't have to construct the primitive classes manually

slender timber
#

Apparently pyright complains that I cannot subclass my enum class

class _EventEnumMeta(enum.EnumMeta):
    def __contains__(cls, id: int):
        try:
            cls(id)
        except ValueError:
            return False
        else:
            return True

class EventEnum(int, enum.Enum, metaclass=_EventEnumMeta):
    def __new__(cls, id: int, type: Optional[Type[Any]] = None):
        obj = int.__new__(cls, id)
        obj._value_ = id
        setattr(obj, "type", type)
        return obj

class EventID(EventEnum):
     ...
rare scarab
#

You don't need to subclass Enum.

slender timber
#

I need to

#

Because I am using the EventEnum at quite a few places in separate enums

#

I need the __new__ method logic

rare scarab
#

isn't the enum class just this? ```py
class Enum(metaclass=EnumMeta):
pass

slender timber
#

I didn't get your question.

rare scarab
#

I'm talking about the standard enum.

boreal ingot
#

its py class Enum(metaclass=EnumMeta): @_magic_enum_attr def name(self) -> str: ... @_magic_enum_attr def value(self) -> Any: ... _name_: str _value_: Any _ignore_: str | list[str] _order_: str __order__: str @classmethod def _missing_(cls, value: object) -> Any: ... @staticmethod def _generate_next_value_(name: str, start: int, count: int, last_values: list[Any]) -> Any: ... # It's not true that `__new__` will accept any argument type, # so ideally we'd use `Any` to indicate that the argument type is inexpressible. # However, using `Any` causes too many false-positives for those using mypy's `--disallow-any-expr` # (see #7752, #2539, mypy/#5788), # and in practice using `object` here has the same effect as using `Any`. def __new__(cls: type[Self], value: object) -> Self: ... def __dir__(self) -> list[str]: ... def __format__(self, format_spec: str) -> str: ... def __reduce_ex__(self, proto: object) -> tuple[Any, ...]: ...

rare scarab
#

hm... then I have no idea what I'm talking about 😂

slender timber
#

but why does pyright complain?

boreal ingot
#

what exactly is the error you are getting?

slender timber
#

Also the __contains__ method it is a perfectly working solution from SO, yet pyright complains of missing args

slender timber
boreal ingot
slender timber
#

I haven't used the final decorator either

#

Example subclass:

class ArrangementID(EventEnum):
    New = (WORD + 35, U16Event)
    Name = TEXT + 49
    Playlist = (DATA + 25, PlaylistEvent)
rare scarab
#

maybe you have to apply the metaclass on each enum?

slender timber
#

Infact, IntEnum is declared as class IntEnum(int, Enum)

rare scarab
#

What does mypy say about the class?

slender timber
#

The metaclass is applied to EventEnum, it should get applied to all subclasses of EventEnum

#

Python enums are a really fucked up mess

oblique urchin
#

enum is highly magical in its implementation, and type checkers need to special case it heavily as a result. Non-standard use of enums is unlikely to be understood by type checkers

slender timber
#

Non standard how? This example is literally in Python docs

rare scarab
#

Maybe it's just one of those things where maybe a typechecker implies the enum is abstract if it has no atrributes

slender timber
#

An example like this which inherits from bytes and Enum

boreal ingot
slender timber
#

How do I check that?

slender timber
#

I am using the one bundled with vscode

#

and using strict mode btw

boreal ingot
slender timber
#

it won't be complaining in basic mode ig

#

v2022.8.10

boreal ingot
slender timber
boreal ingot
#

cant hurt to make sure

slender timber
#

I am using a 3.10 venv btw and pyright has some venv config going on but that shouldn't be a problem

slender timber
boreal ingot
#

hmmm, strange

slender timber
#

Anyways yet another messed up typechecker

#

How do I do an ignore for such cases

#

I don't wanna add # type: ignore everywhere

#

Also I don't understand this error, I mean TimeMarker is clearly a subclass of MultiEventModel.

slender timber
#

Anyways how to make a List contravariant?

slender timber
slender timber
#

no?

buoyant swift
#

you can't really "make" things have variance, they just are

slender timber
#

Well how can i return a list containing objects of different subclasses of the same parent class?

#

Make a TypeVar?

#

I guess then

oblique urchin
#

why not just return list[Parent]?

slender timber
#

I am bored of creating a thousand TypeVars

slender timber
oblique urchin
#

return a Sequence, or make _collect_events also return list[Parent]

slender timber
#

Basically it return a list of objects of a type specified by the type param

oblique urchin
#

I see, that is a good case for a TypeVar then

slender timber
#

meh

#

okay then

#

MEMT_co = TypeVar("MEMT_co", bound=MultiEventModel, contravariant=True) that would be I guess

#

Oh nice so the typevar has to appear in one of the args as well as he return type, fantastic

oblique urchin
#

type: Type[MEMT_co]

brittle socket
#

If you're returning an object of type C with a __call__, should the return type be C or Callable? (The object is called right after)

soft matrix
#

C is more correct neither is wrong though

brittle socket
#

That's what I'm doing but pylint and pyright complain about the object not being callable when I call with it

soft matrix
#

well if pyright says its wrong its probably wrong

#

whats your code?

brittle socket
#

Working on isolating an example

#

Ah, here we go, getting something with mypy here:

from collections.abc import Callable

class B():
    def __call__(self, text: str, check=True):
        pass

class M(B):
    def __call__(self, text: str, check=True) -> str:
        return 'M'        

class C(B):
    def __call__(self, text: str, check=True) -> str:
        return 'C'
        
class Other():
    def __call__(
        self, text: str, n: int, pred: Callable[[str], object] = lambda _: True
    ) -> str:
        
        return "Other"

def factory(name: str) -> B | Other:
    if name == "M":
        return M()
    elif name == "C":
        return C()
    elif name == "O":
        return Other()
    else:
        raise NotImplementedError
        
s = factory("M")
s("text")
Missing positional argument "n" in call to "__call__" of "Other"

https://mypy-play.net/?mypy=latest&python=3.10&gist=50b7fd5b4dbefc97420c84d6a4cc3f60
How can I fix this?

soft matrix
#

overloads?

#

factory("B") should return Never

#

so it shouldnt let you assign s

brittle socket
#

Oh shit, that's a typo, my bad

hallow flint
#

(or if it makes sense, add a default argument to n in Other.__call__ )

brittle socket
#

should be M

#

Fixed. (Same error)

soft matrix
#

yeah ok still i think the answer is overloads

#
@overload
def factory(name: Literal["M"]) -> M: ...
@overload
def factory(name: Literal["C"]) -> C: ...
@overload
def factory(name: Literal["O"]) -> Other: ...
@overload
def factory(name: Any) -> Never: ...
```not sure if name should be Any or object
#

probably Any as its an input type

brittle socket
#

Interesting...working on it

#

Which one would have the actual implementation? def factory(name: Any)?

#

And I can't find Never in typehint docs

soft matrix
#

personally id do (name: Literal["M", "C", "O"]) -> M | C | Other | Never cause then you get narrowing inside the function

soft matrix
#

Never is effectively an alias for NoReturn

brittle socket
#

Didn't work in my actual code...working on translating what I did to an example

soft matrix
#

well feel free to send that

brittle socket
#
from typing import overload, Literal, Any
from typing_extensions import Never
from collections.abc import Callable

class B():
    def __call__(self, text: str, check=True):
        pass

class M(B):
    def __call__(self, text: str, check=True) -> str:
        return 'M'        

class C(B):
    def __call__(self, text: str, check=True) -> str:
        return 'C'
        
class Other():
    def __call__(
        self, text: str, n: int, pred: Callable = lambda _: True
    ) -> str:
        return "Other"

@overload
def factory(
    name: Literal["Other"],
    encoder: Any
) -> Other: ...

@overload
def factory(
    name: Literal["C", "M"]
) -> B: ...

def factory(
    name: Literal["C", "M", "Other"],
    encoder: Any | None = None
) -> B | Other | Never:

    if name == "M":
        return M()
    elif name == "C":
        return C()
    elif name == "O":
        return Other()
    else:
        raise NotImplementedError
        
s = factory("M")
s("text")
#

Is this what you meant?

soft matrix
#

yeah

#

that seems to work fine

brittle socket
#

Sigh, yeah it does in the example, but not in my code 🤦‍♂️

#

And it's a bit complex to paste here

#

Can I show you with a screenshare?

soft matrix
#

!paste

rough sluiceBOT
#

Pasting large amounts of code

If your code is too long to fit in a codeblock in Discord, you can paste your code here:
https://paste.pythondiscord.com/

After pasting your code, save it by clicking the floppy disk icon in the top right, or by typing ctrl + S. After doing that, the URL should change. Copy the URL and post it here so others can see it.

soft matrix
#

theres fine

#

well id hazard a guess and say that the parameter is missing

#

but since FrenchTextRank isnt actually included here i cant say for sure

brittle socket
#

It executes fine so it can't be the parameter 😕 I'm working on reducing the example to what's necessary

#

I'll work on factoring out the conditional

#

Nope, didn't work. Sigh

brittle socket
#

Ended up only returning B from that factory function, and handling Other elsewhere. Now pyright is content

#

Pylint is not, but for some reason re-annotating the variable at the call-site (with the type already declared as the function's return) silences it

sacred flower
#

Wow how awesome the exact channel for this exists here 🙂

I'm trying to create some really general base class that can accept a standardized API SDK class that we have as an initialization param. This then gets stored in a class attribute; however, one thing that's really painful is not being able to (to my knowledge) declare the type annotation of which SDK class was used for initialization.

This is primarily to facilitate auto-completion of these SDK methods. It's quite painful not to have access to all methods via ".".

Any ideas how to dynamically assign the class type on initialization?

brittle socket
sacred flower
#

@brittle socket the number is quite large... At the moment there are 6 but this will continue to grow. It would be a little annoying but possible to list them out but then would the editor be able to identify which one it actually is when recommending methods/attributes? Thanks for the help

#

Generally what I'm aiming for is this (pseudocode). Seems pretty straightforward but I guess the biggest issue is that it doesn't know the type until runtime which doesn't help Pycharm properly recommend attributes/methods.

class Foo:
  def __init__(self, sdk):
    self.sdk: type(sdk) = sdk

Foo.sdk.{Pycharm recommends attributes/methods properly}
twin lantern
#

because if there is you can make a generic with that as its bound

#

you just want the least constrained unique thing that they all have in common

sacred flower
#

@twin lantern that's interesting. Would having some base class that they all inherit help? They each have a lot of custom methods though so I assume those wouldn't be pulled in. These custom methods are typically what we want to call. There's some repeatable stuff under the hood that could be inherited but usually these are just tools that we use to build out the later methods we want to call. Not sure if I answerd your question fully

twin lantern
#

you can make a marker BaseSdk class or something like that

#

and have sdk = TypeVar["SDK", bound=BaseSdk] or something

#

idk what the string is for in that ill google

#

there's also a thing called Generic

#

in typing

#

if someone could provide insight

#

because this is quite confusing and messy

sacred flower
#

Interesting, I'll try to look into this pattern a bit more. It feels kind of crazy that this isn't easier 😛

twin lantern
#

nails and plywood

#

even im confused

brittle socket
#

I don't know if python typehinting has something akin to defining a type by the methods it must have. That feels like what you need here

rough sluiceBOT
#

class typing.Protocol(Generic)```
Base class for protocol classes. Protocol classes are defined like this:

```py
class Proto(Protocol):
    def meth(self) -> int:
        ...
```  Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing), for example...
brittle socket
#

Nice

#

@sacred flower

class SDK(Protocol):
  # list signatures of methods all SDKs have in common
class Foo:
  def __init__(self, sdk: SDK):
    self.sdk = sdk
brisk hedge
#

keep in mind only instance attributes work easily like that, there's a strange limitation with ClassVar that doesn't allow generic types as class attributes

#

This way you get can exact autocomplete for your SDK-specific methods, which I assume was a goal 😄

brittle socket
#

Interesting

#

It assumes the SDK type is known at instantiation. If that is the case, I think it's the most correct solution

sacred flower
#

Thanks a ton guys, sorry I'm a bit slow to understand all of this but I will be playing with it until I do. Really appreciate the help, this is quite a new area of Python for me 🙂

#

Having some troubles with Pycharm recognizing "credentials" as an attribute of sdk. It's printing them correctly, just Pycharm being a pain. Going to try with the Protocol approach.

#

Maybe I misunderstood and the approach screenshotted above will only recognize attributes of the BaseSDK class in the editor, not the child SDK?

fierce ridge
# sacred flower Having some troubles with Pycharm recognizing "credentials" as an attribute of s...

pycharm's type checker might be unwilling or unable to infer that self.credentials means that there will always be a credentials attribute. also you didn't give it a type in __init__ so it would be Any anyway.

try:

class MySdk(BaseSdk):
    credentials: YourTypeHere
    def __init__(self, credentials):
        self.credentials = credentials

or:

class MySdk(BaseSdk):
    def __init__(self, credentials: YourTypeHere):
        self.credentials = credentials
twin lantern
#

someone pls explain the difference between generic and TypeVar

fierce ridge
#
A = TypeVar('A')

class MyThing:
    x: A
   
    def __init__(self, x: A):
        self.x = x

    def get_x(self) -> A:
        return self.x

in this example, you don't know what A specifically is, but you know that A must always be the same type wherever it appears in this class definition

#

(note that "getters" like this are considered bad style in python, but it makes for an easy demonstration)

twin lantern
#

ok and would generics be able to do the same thing

#

or no

#

because like

#

i see classes inherit from generic

#

what does that do

fierce ridge
#

inheriting from Generic makes the class a generic

#

you can make MyThing a generic

A = TypeVar('A')

class MyThing(Generic[A]):
    x: A
   
    def __init__(self, x: A):
        self.x = x

    def get_x(self) -> A:
        return self.x

now MyThing is a generic class. e.g. you can write int_thing: MyThing[int], and inside int_thing the A placeholder will always be given the specific value int.

twin lantern
#

so like list would inherit from it

fierce ridge
#

yes, at least conceptually

twin lantern
#

ok ok

#

and what's the string TypeVar takes for

fierce ridge
#

e.g. if you did B = TypeVar('A') you'd get an error when running the type checker

twin lantern
#

oh

#

that sucks

fierce ridge
#

i agree. other programming languages with compilers and type systems that are built into the language don't have limitations like this. it's just something we have to live with in python.

twin lantern
#

im only using it temporarily anyway so i can go back to the lands of extensive static analysis but i want to bring as much as i can with me at least for now

fierce ridge
#

i think it's good to explore using it in order to see how much of it you're willing to stomach

#

some codebases are more amenable to thorough strict typing than others. it depends a lot on what libraries you're using and who your intended end user is.

twin lantern
#

also does pydantic do any other checking than just making dataclasses or whatever

#

because that's the most.msrketed thing

#

because ideally it runtime checks everything

#

if im having the overhead im going all the way

fierce ridge
soft matrix
#

I only found this out recently

tranquil turtle
#

!e

from typing import Generic, TypeVar
T = TypeVar('T')

class X(Generic[T]): ...

x = X[int]()
print(x.__dict__)

x = X()
print(x.__dict__)
rough sluiceBOT
#

@tranquil turtle :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | {'__orig_class__': __main__.X[int]}
002 | {}
tranquil turtle
#

X and X[int] isnt the same at runtime

soft matrix
#

Yep

tranquil turtle
hallow pollen
#

Is there a tool that automatically migrates all the List and Dict type annotations to lowercase and deletes the import?

acoustic thicket
#

fix's time to shine

trim tangle
#

!pypi flake8-pep585

rough sluiceBOT
trim tangle
#

There's also pyupgrade which actually does the updates but it doesn't catch all of them

hallow pollen
#

I'll just have to make a regular expression to do it.

trim tangle
#

that's a big 🍑 regex 👀

boreal ingot
#

hey, so I have a recursive json type, for some reason I am getting a error from pyright when passing a variable, but not a literal: ```py
JSON_DATA = str | int | dict[str, "JSON_DATA"]

def foo(data: JSON_DATA) -> None: ...

bar = {"a": "b", "c": 1}
foo(bar) # this gives an error
foo({"a": "b", "c": 1}) # but not this?
/home/vivax/coding/jerde/minimal.py:6:5 - error: Argument of type "dict[str, int | str]" cannot be assigned to parameter "data" of type "JSON_DATA" in function "foo"
  Type "dict[str, int | str]" cannot be assigned to type "JSON_DATA"
    "dict[str, int | str]" is incompatible with "str"
    "dict[str, int | str]" is incompatible with "int"
    TypeVar "_VT@dict" is invariant
      Type "int | str" cannot be assigned to type "JSON_DATA"
        Type "int" cannot be assigned to type "JSON_DATA"
          "int" is incompatible with "str" (reportGeneralTypeIssues)

it only happens with strict mode on, so I am assuming that `bar` is being narrowed to some other type than with "basic" mode.
but I dont understand where the issue is?

I am feeling like this a pyright bug? (I am on the latest version, `1.1.265`)
soft matrix
#

I think the issue is that variance is just screwing you

boreal ingot
#

but the line

      Type "int" cannot be assigned to type "JSON_DATA"

is clearly wrong, right?

#

or ...

#

wait I see

#

okay yhe that does make sense when I think about it

soft matrix
#

You need to use Mapping I'm pretty sure

boreal ingot
#

yhe gonna try that

#

yhe that works perfectly, thanks!

soft matrix
#

I think pyright can do joining or meeting (I forget which) in the direct call but not in the variable

boreal ingot
#

that makes sense

soft matrix
#

Mypy does the same for some things

boreal ingot
#

I realized it wouldn't let me with dict because according to the type hint I could in theory add a value that was a dict in the foo function.
which would make the (inferred) type hint wrong in the outer scope

rustic gull
#

Can someone give an example of regex

#

A basic example?

#

Ping me if you do please.

soft matrix
#

@trim tangle pyright playgrounds generate permalink feature is 502ing

trim tangle
#

Well that sucks

#

I'll take a look in a few hours. I hope you don't base anything life-critical on it

soft matrix
#

maybe i do lemon_wink

#

i was just using it for a pyright issue, ill generate the link myself

#

oh wait it doesnt work either way

frigid jolt
#

hi, I was wondering what I should do in these cases

from typing import Optional, List

def foo(a: Optional[List[str]] = None) -> None:
	if a == None:
		pass
		
	if isinstance(a, list):
		for i in a:
			pass
			

# mypy error: 
# Value of type Optional[List[str]] is not indexable, use SupportIndex
soft matrix
#

update your mypy cause that should work

#

oh wait thats not ever gonna produce that error

frigid jolt
#

K, maybe I'll repost my code later, I used only one example

rare scarab
#

Do you want return instead of pass in the if block?

#

hm...

leaden oak
#

@oblique urchin is there a forum or something similar for mypyc? I know of the mypy gitter, but it seems rather dead 😦

frigid jolt
#
def write(self, string: Optional[str] = None, delay: float = 0.05):
    for character in string:
      stdout.write(character)
      stdout.flush()
      sleep(delay)
    print("")
error: Item "None" of "Optional[str]" has no attribute "__iter__" (not iterable)
soft matrix
#

You need to do what you did before where you check if string is not None before iterating over it

trim tangle
#

yep the code won't work with None

soft matrix
#

tbh id just remove the default of None

frigid jolt
#

it's not for internal use

uneven hemlock
oblique urchin
dim trail
#

actually if you pass, it won't narrow

#

actually you have a isinstance there.... confusing

#

generally you need to ensure that the value you want to narrow is the only thing able to reach that branch. branches can be stopped with a exception, return or continue (in loops)

frigid jolt
#

btw thx for your tips

indigo locust
#

For a class-based decorator

#

(As opposed to a function bases decorator)

#
from typing import Callable
from typing import Generic
from typing import ParamSpec
from typing import TypeVar

T_Params = ParamSpec('T_Params')
T_Return = TypeVar('T_Return')

class Wrapper(Generic[T_Params, T_Return]):

    def __init__(self, wrapped: Callable[T_Params, T_Return]) -> None:
        self.function = wrapped

    def __call__(self, *args: T_Params.args, **kwargs: T_Params.kwargs) -> T_Return:
        return self.function(*args, **kwargs)
#

Is this how I'm supposed to do it?

soft matrix
#

Looks like it

frigid jolt
frigid jolt
#

😕

indigo locust
#

Not meaning to be rude, its just

#

I super duper don't care, and I don't really have patience for pointless adherence standard

#

Pointful adherence to standard I do care about. But whether or not I import my types one at a time isn't something I lose sleep over

frigid jolt
#

ok

indigo locust
# frigid jolt ok

Sorry, I was kinda rude. Python has been getting on my nerves a bit lately

hallow flint
austere flower
#

if there anyway to type hint a class from a different file without importing the class?

pastel egret
#

Yes, you can use if typing.TYPE_CHECKING: to add code only the type checker reads to do the import.

#

If you've got an import cycle that can help. However, you might want to consider if you can rearrange your code to make it less cyclical, it might improve the conceptual layout.

fierce ridge
#

you can also write your type annotations in a separate "type stub" file

rare scarab
#

^ that makes it work with py<3.7

granite gulch
fierce ridge
granite gulch
#

Isn't it what templates are? Haven't done C in a decade

trim tangle
#

templates are kinda like bad generics

trim tangle
granite gulch
#

I just remember there was such thing as templates that were kind of like an interface

leaden oak
fluid ice
#

I posted a type hinting question in #help-avocado if anyone feels like taking a look

narrow sigil
#

how should functions that either do nothing or raise an error be type hinted?

#

talking about something like this ```py
def check_os():
if platform.architecture()[0] != '64bit':
raise OSError('This script is only compatible with 64bit systems.')

if platform.system() not in ('Linux', 'Windows'):
    raise OSError(f'Unsupported OS "{platform.system()}"')
oblique urchin
narrow sigil
#

thanks!

brittle socket
#

pylint is emitting unpacking-non-sequence for an assignment from a function typehinted as returning a tuple. 😕

#
def factory(name: str) -> tuple[A | B, Callable]:
    if some_cond:
        return A(), lambda x: x*2
    else:
        return B(), lambda x: x*2

    raise NotImplementedError

a, b = factory(name) # pylint unpacking-non-sequence
#

There is no pylint playground to replicate it unfortunately

oblique urchin
brittle socket
#

My bad, just corrected it

soft matrix
#

looks like a bug if thats still the case

brittle socket
#

Meh, I'll just disable it on that line then

tawny ember
#

Hi, I need help with std libraries. I'm using two libs: functolls and typing. I'm trying to make my code look cleaner with singledispatchmethod and got this problem:

pipenv run dev
Traceback (most recent call last):
  File "/Users/mark/Git/diam-core/app.py", line 3, in <module>
    from diamcore import *
  File "/Users/mark/Git/diam-core/diamcore/__init__.py", line 5, in <module>
    from .datamodels import *
  File "/Users/mark/Git/diam-core/diamcore/datamodels.py", line 63, in <module>
    class Integer(BaseDataModel):
  File "/Users/mark/Git/diam-core/diamcore/datamodels.py", line 106, in Integer
    def _(self, v: IntegerType):
  File "/opt/homebrew/Cellar/python@3.9/3.9.13_2/Frameworks/Python.framework/Versions/3.9/lib/python3.9/functools.py", line 933, in register
    return self.dispatcher.register(cls, func=method)
  File "/opt/homebrew/Cellar/python@3.9/3.9.13_2/Frameworks/Python.framework/Versions/3.9/lib/python3.9/functools.py", line 872, in register
    raise TypeError(
TypeError: Invalid annotation for 'v'. ~IntegerType is not a class.

And here is the code:

from dataclasses import dataclass as struct
from dataclasses import field
from functools import singledispatchmethod
from typing import Union, TypeVar

IntegerType = TypeVar('IntegerType', bound='Integer')

@struct
class BaseDataModel:
    def __post_init__(self):
        ...

class Integer(BaseDataModel):
    value: Union[int, float]

    @singledispatchmethod
    def __init__(self, v):
        raise TypeError(f'Incorrect provided type! Getted {type(v)} is acceptable here!')

    @__init__.register
    def _(self, v: int):
        self.value = v

    @__init__.register
    def _(self, v: float):
        self.value = v

    @__init__.register
    def _(self, v: bool):
        self.value = int(v)

    @__init__.register
    def _(self, v: str):
        self.value = float(v)

    @__init__.register
    def _(self, v: IntegerType):
        self.value = v.value
soft matrix
#

you shouldnt be using Integer here as a type var bound

#

you need to use Integer just in quotes

tawny ember
#

Just left the IntegerType = TypeVar('IntegerType'?

soft matrix
#

or enable __future__.annotations

#

no

#

theres no need here for a type var

tawny ember
#

Enabling the __future__.annotations doesn't helps

oblique urchin
#

Also, I wouldn't call the code with singledispatchmethod cleaner

#

It's rather more obfuscated

tawny ember
#

Just "Integer" doen't works either with singledispatchmethod

#

@oblique urchin, what You would recomend?