#type-hinting

1 messages Β· Page 56 of 1

little hare
#

er, that is actually a different story 😳

rustic gull
little hare
#

it doesn't do anything unless the program wants to look at it

trim tangle
#

yep, that's sometimes useful

rustic gull
#

and when does the program want to look at it?

little hare
#

when there's code that wants to do that, and afaik there's no stdlib that does look at typehinting

#

wait---

#

yes there is

#

!d dataclasses.dataclass

rough sluiceBOT
#

@dataclasses.dataclass(*, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False)```
This function is a [decorator](https://docs.python.org/3/glossary.html#term-decorator) that is used to add generated [special method](https://docs.python.org/3/glossary.html#term-special-method)s to classes, as described below.

The [`dataclass()`](https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass "dataclasses.dataclass") decorator examines the class to find `field`s. A `field` is defined as a class variable that has a [type annotation](https://docs.python.org/3/glossary.html#term-variable-annotation). With two exceptions described below, nothing in [`dataclass()`](https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass "dataclasses.dataclass") examines the type specified in the variable annotation.

The order of the fields in all of the generated methods is the order in which they appear in the class definition.
trim tangle
#

well

#

dataclass doesn't inspect annotations

#

it just ignores them

rustic gull
#

okay now we're way beyond me knowledge in python xD

trim tangle
#

is your question "who wants to inspect type annotations?" @rustic gull

oblique urchin
little hare
trim tangle
#

A type checker just looks at your code, considers the annotations (statically, without running anything), and then tell you about the errors you've got. Or shows you what type something is. Kind of like a code reviewer.

trim tangle
rustic gull
#

Alr thanks

#

Last question, might not be considered in this topic but idk what it is, maybe it is a type 🀷 but i've seen people use yield, what is that?

#

is there a docs command i can run for that object?

little hare
#

!d yield

rough sluiceBOT
#

7.7. The yield statement


yield_stmt ::=  yield_expression
``` A [`yield`](https://docs.python.org/3/reference/simple_stmts.html#yield) statement is semantically equivalent to a [yield expression](https://docs.python.org/3/reference/expressions.html#yieldexpr). The yield statement can be used to omit the parentheses that would otherwise be required in the equivalent yield expression statement. For example, the yield statements

```py
yield <expr>
yield from <expr>
```  are equivalent to the yield expression statements...
rustic gull
#

okay nvm i wont understand that xD, thanks guys!

little hare
#

its for generators

#

you'll probably get to them sometime πŸ˜„

trim tangle
rustic gull
#

For sure πŸ™‚

little hare
#

once you get into testing... and perhaps use pytest

little hare
#

I actually need to get better at generators

rustic gull
#

I know how generators works

#

and iterators, iterables etc...

#

never crossed the field with yields though

little hare
rustic gull
#

I doubt that xD πŸ˜‚

little hare
#

er well

#

I made a class to implement a function I wrote to be a generator as __next__

#

then I learned that iter() exists

#

!d iter

rough sluiceBOT
#

iter(object[, sentinel])```
Return an [iterator](https://docs.python.org/3/glossary.html#term-iterator) object. The first argument is interpreted very differently depending on the presence of the second argument. Without a second argument, *object* must be a collection object which supports the [iterable](https://docs.python.org/3/glossary.html#term-iterable) protocol (the `__iter__()` method), or it must support the sequence protocol (the `__getitem__()` method with integer arguments starting at `0`). If it does not support either of those protocols, [`TypeError`](https://docs.python.org/3/library/exceptions.html#TypeError "TypeError") is raised. If the second argument, *sentinel*, is given, then *object* must be a callable object. The iterator created in this case will call *object* with no arguments for each call to its [`__next__()`](https://docs.python.org/3/library/stdtypes.html#iterator.__next__ "iterator.__next__") method; if the value returned is equal to *sentinel*, [`StopIteration`](https://docs.python.org/3/library/exceptions.html#StopIteration "StopIteration") will be raised, otherwise the value will be returned.
rustic gull
#

the only thing i know @little hare about these are:

#
iterators are iterable
iterable can make iterators
list comp is a expression that leaves you with a iterable
generators is a expression that leaves you with a generator
a generator is a iterator, which is iterable
and the list is iterable, which means it can make a iterator
#

if that makes sense for you, idk xD I usually note stuff

little hare
#

ah lol

rustic gull
#

so basically: ```py

NOTES

#iterators are iterable
#iterables can make iterators

listcomp = [i for i in range(10)] #the expression gives you a iterable
#since it's iterable we can do:
for i in listcomp:
print(i)
#since we can make iterables a iterator we can do:
iter(listcomp)
print(next(listcomp))

generator = (i for i in range(10)) #the expression gives you a iterator, which is iterable
#since it's a iterator we can do:
print(next(generator))

#since iterators are also iterable we can do:
for i in generator:
print(i)

This is what I understood how generators works so 🀷@little hare
#

Maybe it isn't 100% accurate but

buoyant swift
#

list comps give you lists

#

and you can't do next(listcomp) since it's still a list, iter doesn't modify it

little hare
#

!e ```py
listcomp = [i for i in range(10)] #the expression gives you a iterable
#since we can make iterables a iterator we can do:
iter(listcomp)
print(next(listcomp))

rough sluiceBOT
#

@little hare :x: Your eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 4, in <module>
003 | TypeError: 'list' object is not an iterator
little hare
#

!e ```py
listcomp = [i for i in range(10)] #the expression gives you a iterable
#since we can make iterables a iterator we can do:

print(next(iter(listcomp)))

rough sluiceBOT
#

@little hare :white_check_mark: Your eval job has completed with return code 0.

0
little hare
#

@rustic gull ^

rustic gull
#

Ah yeah sorry my bad

trim tangle
buoyant swift
#

and their subclases, of course
😩

trim tangle
#

sorry

#

I suck at orthography

#

and grammar

granite plover
#

so, shouldnt intellisense pick up that the type of records here is List[Record] or am i thinking that intellisense is smarter than it actually is

def create_records(raw_source: List[str]) -> List[Record]:
    records = []
    for line in raw_source:
        date, name = line.split("\t")
        records.append((date, name))
    return records
trim tangle
#

I don't think any type checker does that

#

yet

#

you have to do records: List[Record]

buoyant swift
granite plover
#

ok yes

#

intellisense not so intelligent after all

trim tangle
#

yeah

#

petition to rename to stupidsense πŸ‘

granite plover
#

seconded

buoyant swift
#

thirded

trim tangle
#

TypeScript doesn't do that either

#

πŸ˜”

granite plover
#

small nitpick, why is this lowercased

trim tangle
#

🀷

granite plover
#

but this one isnt

buoyant swift
#

stupidsense

trim tangle
#

what IDE is that?

granite plover
#

vscode

trim tangle
#

ah, it does do that

granite plover
#

goshdarn stupidsense

trim tangle
#

maybe it's another microoptimization πŸ™‚

#

don't want to waste those precious CPU cycles on unifying the names

little hare
granite plover
#

drowning in them microoptimizations πŸ₯΅

little hare
#

so in one it is confirmed to be type list whereas the other one is just a typehint of List

trim tangle
#

it is cursed

granite plover
#

its bothering me

#

one or the other, doesnt matter

trim tangle
#

file a bug on the repo

little hare
#

i am actually surprised I was right

trim tangle
#

Speaking of cursed...

def create_records(raw_source: List[str]) -> List[Record]:
    def _gen():
        for line in raw_source:
            date, name = line.split("\t")
            yield date, name
    return list(_gen())

this should typecheck alright πŸ™‚

granite plover
#

this is 3.9.7

trim tangle
#

then use list[str]

little hare
granite plover
#

i could i guess, no one in the office is using <3.9

little hare
#

if you use list[] then its not a problem

granite plover
#

alright i'll do just that then

trim tangle
#

it has a terrible editor

#

and its markdown interprets single line breaks as actual line breaks 🀦

#

which is cursed because I was first writing the markdown in VSCode

undone carbon
#

i m in py3.10 but mypy keeps givin me tiz error: "tuple" is not subscriptable
here's the code: alias = tuple[int, int]

buoyant swift
#

isn't that supposed to be fixed by now

undone carbon
#

ya gg

#

lemme check it's version

#

oh, i used the wrong mypy

#

wat does tiz mypy error mean then Type application has too many types (1 expected)?
code: alias = MyClass | tuple[Optional[int], Optional[int]] using py3.10 btw

oblique urchin
#

the latest release of mypy is still pretty buggy with lowercase tuple

undone carbon
#

bruhh

#

ok nvm

#

as long as ma code works πŸ™ƒ

grave fjord
#

It's MyClass | tuple[int | None, int | None]

undone carbon
#

oh okayy

undone carbon
#

how should i type hint any class?

trim tangle
undone carbon
#

like

#

u can pass in any defined classes into the func?

trim tangle
#

Like your_function(str)?

undone carbon
#

huh

#

like

trim tangle
#

or ```py
class Foo:
pass

your_function(Foo)

undone carbon
#

ya kinda like tat

trim tangle
#

well, str is a class as well

#

The type of a type is type

#

so you'd do ```py
def your_function(cls: type):
...

undone carbon
#

oh

#

thx a lot

pastel egret
#

If you want to require only classes that are subclasses of some other class (or something like Mapping[int, str]), wrap it in typing.Type[MyClass].

undone carbon
#

oh

#

okayy thx

wicked scarab
#
Traceback (most recent call last):
  File "C:/Users/drago/PycharmProjects/playground2/main.py", line 73, in <module>
    a: list[int] = [1]
TypeError: 'type' object is not subscriptable
```umm.... I got 3.9 tho?
solid light
#

What output does this give?py import sys print(sys.version)

wicked scarab
#

oh..

#

wait what

#

I have 3.9 installed

#

tho

wicked scarab
#

3.8.10

#

but

solid light
#

Then you're using 3.8.10 :p

#

You just need to change what you're running

#

You need to configure PyCharm to use 3.9 instead of 3.8

wicked scarab
#

oh

#

Im dumb

#

hmm

#

but this thing still appears

solid light
#

You've still got it configured to 3.8

#

It says so at the bottom of that image

#

< Python 3.8 (playground2) >

wicked scarab
#

3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)]

#

maybe restart

#

wait

#

this is weird

solid light
#

Look at the bottom right of pycharm

#

It's still 3.8

wicked scarab
#

wait, isn't configuring the interpreter enough?

#

or...

#

I need to do something else

solid light
#

You have to configure the interpreter and then actually select it

solid light
# solid light

Click the thing the arrow is pointing to and select the 3.9 interpreter @wicked scarab

wicked scarab
solid light
#

That's the one for the DiscordBot project

wicked scarab
#

huh? Can't I use the same interpreter for different project?

solid light
#

@wicked scarab

#

That's what I get when I click it

#

Just click it and select 3.9

wicked scarab
solid light
#

Then you've not added the interpreter

wicked scarab
#

ah

#

it worked

#

sorry for the trouble πŸ˜”

solid light
#

No worries lol

#

PyCharm version control is very confusing to people who aren't familiar with it

wicked scarab
#

yeah indeed

solid light
#

It took me ages to get used to it (and I still struggle sometimes)

wicked scarab
#

CH_ThumbsUpSmile thanks anyway

solid light
wicked scarab
#

yeah

solid light
#

Nice πŸ‘

wicked scarab
#

no error, no warning, nothing

#

ty

twilit badge
#

How should I type-hint a variable that can hold any instance of any class

#

Also, is it correct if I type-hint a variable that can hold any class (but not the instance) as owner_cls: type?

void panther
twilit badge
#

well, I'm trying to type-hint a descriptor class

#

so specifically, these methods:

def __set_name__(self, owner_cls: type, name: str) -> None:
    ...
def __get__(self, instance, owner_cls) -> Any:
    ...
def __set__(self, instance, value) -> None:
    ...
def __delete__(self, instance) -> None:
    ...
upbeat wadi
#

You can make a protocol that defines those methods

#

!d typing.Protocol

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...
twilit badge
#

why? I mean, what's the advantage of having a protocol like that if I can just define them in my class directly?

twilit badge
upbeat wadi
twilit badge
#
class Descriptor:
    def __set_name__(self, owner_cls: type, name: str) -> None:
        self.name = name
        self.cls = owner_cls

    def __get__(self, instance: Any, owner_cls: type) -> Any:
        if instance is None:
            return self
        return instance.__dict__.get(self.name)

    def __set__(self, instance: Any, value: Any) -> None:
        instance.__dict__[self.name] = value

    def __delete__(self, instance: Any) -> None:
        del instance.__dict__[self.name]
#

I can just do this, why would I want a protocol that defines which methods should be present in a descriptor?

#

all my descriptors will inherit from this class

upbeat wadi
#

You would have the typehint as Descriptor

twilit badge
#

?

#

I can already do ```py
x: Descriptor = Descriptor()

twilit badge
#

I just can't see what's the advantage of a protocol

upbeat wadi
#

I wasn't aware that all descriptors would be a subtype of a specific class

upbeat wadi
twilit badge
#

isn't protocol only really useful if you need to do something like Protocol[int] which is a type-variable that is shared across multiple methods

#

but I don't think I need something like that for descriptors

twilit badge
#

but I don't see how overloads in the main descriptor class would make sense here

#

though I could do this py def __get__(self, instance: T, owner_cls: type[T]) -> Any: ... since the __get__ is invoked just by doing: __get__(a, type(a)) (which makes me wonder why is it even there but I suppose it's just for legacy reasons to ensure backwards compatibility)

upbeat wadi
twilit badge
#

hmm, now that I'm thinking about it it may actually make sense to make it into a generic though

#
from typing import Any, TypeVar, Generic

T = TypeVar("T")

class Descriptor(Generic[T]):
    def __set_name__(self, owner_cls: type[T], name: str) -> None:
        self.name = name
        self.cls = owner_cls

    def __get__(self, instance: T, owner_cls: type[T]) -> Any:
        if instance is None:
            return self
        return instance.__dict__.get(self.name)

    def __set__(self, instance: T, value: Any) -> None:
        instance.__dict__[self.name] = value

    def __delete__(self, instance: T) -> None:
        del instance.__dict__[self.name]
#

since when a descriptor is created in a Foo class, I could then do this ```py
class Foo:
x: Descriptor["Foo"] = Descriptor()

upbeat wadi
#

If you aren't using it as a return type for __get__ it wouldn't have an effect

twilit badge
#

?

#

return type for get is any

#

it's what the user puts into the descriptor, I can't control that

upbeat wadi
#

Yes so having instance be Any and owner_cls be type has the same effect; it doesn't need to be generic

twilit badge
#
f = Foo()
f.x = "s"  # now ret type for __get__ is str
f.x = 2  # now it's int
...
twilit badge
#

it will mean that whenever the instance passed into something like __set__ will be of certain type, we can always know that the instance passed to __get__ will also be of that type, same for delete

#

and we can know that it's the same instance just by looking over the class immediately

#

if it were Any and type, it could mean that those differ from call to call, but that's not true since once it's initialized, these will always be the same

blazing nest
# twilit badge isn't protocol only really useful if you need to do something like `Protocol[int...

Not really, it's static type checking. Say you have this one protocol, that say that the types need a fetch() method.

class Fetchable(Protocol):
    def fetch(self) -> None: ...

Now you can define these classes that implement the protocol without subclassing Fetchable.

class Monkey:
    def fetch(self) -> None:
        ... # Do something

Instances of Monkey can now be passed to parameters and attributes annotated as Fetchable

#

All of those Sequence, Iterable, etc. type of types can be implemented as protocols (I am not 100% sure if they truly are).

rustic gull
wicked scarab
#

hmm?

trim tangle
#

because they provide some utility methods afaik

twilit badge
oblique urchin
#

Also a more general response to what you were talking about earlier: avoid Any if at all possible, because it basically turns off the type system, so if you use it, you don't get the benefits of static type checking

#

instead, look for some way to give a safe type, perhaps a Protocol or object

grave fjord
trim tangle
#

I only recently learned that I should be using object if I really don't care about the object type, not Any

#

it's not obvious

brazen jolt
trim tangle
little hare
#

run that by me again?

trim tangle
#

So this passes @brazen jolt @little hare : py def f(x: Any) -> None: print(x + "!") and this fails: ```py
def f(x: object) -> None:
print(x + "!")

#

in other words, you can do anything with Any, but you can't do much with object.

little hare
#

oh even when that interaction on x would be invalid

void panther
#

Any disables the type checking around it, object only allows what's valid on a plain object

trim tangle
#

yep

brazen jolt
#

that's interesting

little hare
#

!e print(str(object()))

rough sluiceBOT
#

@little hare :white_check_mark: Your eval job has completed with return code 0.

<object object at 0x7f322eeb4130>
trim tangle
#

You can still do type narrowing on object though: ```py
def f(x: object) -> None:
if isinstance(x, str):
print(x + "!")

little hare
#

interesting

brazen jolt
little hare
#

huh is Any runtime checkable

#

!d typing.Any

rough sluiceBOT
#

typing.Any```
Special type indicating an unconstrained type.

β€’ Every type is compatible with [`Any`](https://docs.python.org/3/library/typing.html#typing.Any "typing.Any").

β€’ [`Any`](https://docs.python.org/3/library/typing.html#typing.Any "typing.Any") is compatible with every type.
trim tangle
#

But with Any, you are just allowed to cast it implicitly to any type

brazen jolt
#

but you can also cast an object to any other type, can't you? ```py
def f(x: object) -> int:
return cast(int, x)

trim tangle
#

Yes, but this is an explicit cast

little hare
#

wow

trim tangle
#

by 'implicit cast' I meant just not checking anything

little hare
#

the typing source code is actually... annoying

rough sluiceBOT
#

Lib/typing.py lines 412 to 424

@_SpecialForm
def Any(self, parameters):
    """Special type indicating an unconstrained type.

    - Any is compatible with every type.
    - Any assumed to have all methods.
    - All values assumed to be instances of Any.

    Note that all the above statements are true from the point of view of
    static type checkers. At runtime, Any should not be used with instance
    or class checks.
    """
    raise TypeError(f"{self} is not subscriptable")```
little hare
#

that's not helpful

#

i can't even tell how it works

buoyant swift
little hare
brazen jolt
#

though I'm not sure I like object more than Any in that case, since with Any, I can directly cast it to something else when I know what will be returned and work with that, and when i don't know, it won't give me any errors, whereas with an object, it would be failing when I didn't cast it explicitly, making it a pain to work with for the user. If each time you'd do self.x when x is a descriptor, you'd also have to specify a type, I feel like that would just get way too distracting

buoyant swift
rough sluiceBOT
#

Lib/typing.py lines 426 to 439

@_SpecialForm
def NoReturn(self, parameters):
    """Special type indicating functions that never return.
    Example::

      from typing import NoReturn

      def stop() -> NoReturn:
          raise Exception('no way')

    This type is invalid in other positions, e.g., `​`​List[NoReturn]`​`​
    will fail in static type checkers.
    """
    raise TypeError(f"{self} is not subscriptable")```
buoyant swift
#

yeah, you can't subscript it

little hare
#

but how does it work, internally

brazen jolt
#

although I can see why it could be useful

brazen jolt
#

I don't think there's really anything Any would need to implement on it's own, the type-checking functionality isn't present in python directly, that's up to an external static type checker tool, there's pretty much no significance to type-hints in pure python, it will run even if all of those type-hints are completely wrong

#

at most the ability to do isinstance(x, Any), but python doesn't implement that, there are external tools that can check these type-hints on run-time with a decorator, but direct runtime checks aren't implemented with Any at all afaik

buoyant swift
#

because everything is an Any

brazen jolt
#

yeah, but I meant it more generally, Any is just one such type

trim tangle
#

Yeah, type checkers just special-case Any

grave fjord
#

It's annoying you can't do def get(self, v: V) -> K | NoReturn[KeyError]:

brazen jolt
#

that would be cool though NoReturn wouldn't be the type-hint to use, since it can also be used in cases like these: py def foo() -> NoReturn: while True: pass so NoReturn taking exception as an argument is a bit weird, perhaps if there were something like Raises

blazing nest
brazen jolt
#

yes, that is a possibility, but it's still a bit weird imo

void panther
#

hinting exceptions is a bit weird as there are some that can be raised anywhere as normal behaviour and then there's also an API that'd allow you to do that for any exception

brazen jolt
#

if we could pass exceptions into NoReturn there should also then be a way to pass in an infinite loop, since just doing NoReturn could then mean any exception or function hangs

grave fjord
#

Generators would need a throw type

soft matrix
#

maybe its time to work on var args for type annotations like generator and coroutine

grave fjord
#

It you could just use the SendType | NoReturn[CancelledError]

green gale
#

Does pyright have a setting to error if a type hint is not present? (Mandate them, basically)

#

Or another tool like flake8, perhaps

brazen jolt
#

there's flake8-annotations

green gale
#

Thanks!

green gale
#

I shall try, thank you!

brazen jolt
#

strict does a more than just enforce type hints (in fact, I'm not even sure if it does enforce them). flake8-annotations will most likely be a better option as it gives you the ability to ignore specific things, such as annotations for special variables, such as self, or cls and just gradually fine-tune everything, either with global flake8 exception ignores or with individual noqa comments. But yeah, certainly try out both (and if you don't mind, let me know whether strict mode does actually enforce type hints, since I don't really think it does, but I may be wrong)

#

the general way to think of strict is that it always imposes stricter types, if it can, for example: py lst = [1, 8, "foo"] will not be of type list[Any], but rather of type: list[Union[int, str]

#

@green gale ^^

green gale
#

I'll try out both and get back to you, thanks!

boreal ingot
rustic gull
#

ive been confused every time i read this example in the pep

def async_query(on_success: Callable[[int], None],
                on_error: Callable[[int, Exception], None]) -> None:

does it mean Exception class and subtypes like?

>>> def foo(x: Exception): pass
... 
>>> foo(Exception)

Exception and a subtype of Exception like IndexError are both the type metaclass so that doesnt make sense

soft matrix
#

it means an instance of Exception not the type Exception

#

Exception() (or subclasses) should type check Exception should not

brazen jolt
#

if you wanted to check for a type directly, you would want to do Type[Exception]

void panther
#

What's incompatible with these signatures?

    @t.overload
    def value(
        self,
        categories: collections.abc.Iterable[str],
        key: str,
        *,
        default: t.Any = _DEFAULT_SENTINEL,
        sync_on_missing: bool = False,
    ) -> TOMLType:
        ...

    @t.overload
    def value(
        self, key: str, *, default: t.Any = _DEFAULT_SENTINEL, sync_on_missing: bool = False
    ) -> TOMLType:
        ...

    def value(
        self,
        categories_or_key: t.Union[collections.abc.Iterable[str], str, object] = _MISSING_SENTINEL,
        key: t.Union[object, str] = _MISSING_SENTINEL,
        *,
        default: t.Any = _DEFAULT_SENTINEL,
        sync_on_missing: bool = False,
        categories: t.Union[collections.abc.Iterable[str], object] = _MISSING_SENTINEL,
    ) -> TOMLType:
#

Is it the names? I thought I had it working with differing names before as long as it was usable in the same manner but I can't get it now

#

the more succinct version would be

@t.overload
def f(a: int, b: str):
    ...
@t.overload
def f(b: str):
    ...
def f(a_or_b: t.Union[str, int], b: str = _MISSING_SENTINEL, *, a: int = _MISSING_SENTINEL):
    ...
pastel egret
#

The names are important yes, since you could do f(a="blah"). You could use the / positional-only marker, or fix the names.

void panther
pastel egret
#

No, the args on the right are keyword only, but the ones to the left are still positional or keyword.

#

You can call value(categories=x), and that should work by your sig but it doesn't.

void panther
#

should it? If only categories is passed in then key will be missing

#

the actual implementation would work with it, but that can be taken care of in the body

pastel egret
#

Ah yeah you need key too, but I mean it can be passed by keyword, but then categories_or_key isn't defined for your implementation?

#

Ah, misread slightly for the categories but.

#

But value(key=x) is valid for sig 2, but your implementation wouldn't take that.

void panther
#

It should work, as they all have defaults

pastel egret
#

Ah yeah.

#

Tricky

void panther
#

but anyway, is there any way to get it working in some way like I had it, or do I either have to use nonsensical names (key when it's actually a category etc.), or positional/kwarg only ?

pastel egret
#

What I normally do is use the nonsensical names yeah, then on the first lines swap the values around to the right vars, or reassign them into say key_ and use that thereafter.

void panther
#

hmm, that sucks, as it also muddies it a bit for the user when they're in the overloaded signatures

trim tangle
#

@void panther I think the central issue is that categories will map to different parameters depending on whether it's is positional on keyword.

#

so the type checker thinks this is invalid

void panther
#

is there a better way to typehint the sentinels I used above than just ... | object or creating an enum for it ?

oblique urchin
#

Not yet, PEP 661 is hoping to fix that

pastel egret
#

What I’ve done a few times is cast them to the non-sentinel type or Any, so they’re just allowed. Though then you need to ensure you do the is-checks, since the type checker won’t catch it anymore.

soft matrix
#

id just annotate it as Any as well, im assuming you dont want people to actually pass these as values

void panther
#

ah that'll probably be a bit better, thanks

grave fjord
#

I tend to use an Enum with only one item

#

Although sometimes you need a bunch of sentinels and they can all go in the same Enum

trim tangle
#

I usually do py class _Unset: pass _UNSET = _Unset() and then do Union[actual_type, _Unset] and then use isinstance to narrow

void panther
#

I used the enum approach previously but it feels a bit weird

grave fjord
#

You can use Literal[...] with Enums

viral bramble
#

I wrote something like this recently:

class _Missing:
    pass

Missing = _Missing()

value: Union[SomeType, None, _Missing] = mapping.get(key, Missing)
#

didn't like it but did it anyway 🀷

soft matrix
#

you are missing the Union part of that

viral bramble
#

thanks

soft matrix
#

it probably cant be None would be my guess?

viral bramble
#

the mapping can contain None as a valid value

soft matrix
#

oh right idk then

#

theoretically you shouldnt need to annotate the .get call anyways

viral bramble
#

I annotated it purely for developer convenience; we're not even using a type checker

#

I really enjoy being able to do if value is Missing:

#

really I think most of the weirdness in the annotation comes from None being special-cased

#

🀷

soft matrix
#
from typing import Union

class _Missing:
    pass

Missing = _Missing()

mapping: dict[str, int | None] = {}
key = ""
value = mapping.get(key, Missing)
reveal_type(value)```this works for me with pyright
#
No pyproject.toml file found.
Assuming Python platform Linux
Searching for source files
Found 1 source file
/home/runner/PyrightPlayground/pyright.py
  /home/runner/PyrightPlayground/pyright.py:11:13 - info: Type of "value" is "int | _Missing | None"
0 errors, 0 warnings, 1 info 
Completed in 7.447sec```
viral bramble
#

πŸ‘

soft matrix
#

so did the old version

leaden oak
#

difflib.py:41: error: Module "typing_extensions" has no attribute "GenericAlias" pithink

#

Is this due to fact the name definition is guarded in typing_extensions's source by an indirect version check?

trim tangle
soft matrix
#

would you mind updating the pyright version?

#

last time i checked it wasnt very up to date

trim tangle
#

@soft matrix done

#

updated to 1.1.190

#

I also made it create random files so that two people can use it at the same time

soft matrix
#

:)

trim tangle
#

maybe I should make a proper service tomorrow

trim tangle
#

I asked if I can register a domain name with 'pyright' in it

#

if not, I'll just put it somewhere else

boreal ingot
trim tangle
#

yeah that's better

#

the repl.it thing is just one big hack

boreal ingot
#

a proper playground should be fun

#

I wonder if it is possible to do it without creating temp files

trim tangle
#

Maybe I could display mypy & pyright & something else side by side

boreal ingot
#

right, it should not be to hard

soft matrix
#

pyright doesnt support passing text as input atm afaik

boreal ingot
trim tangle
#

Btw, is there a way to speed up pyright? it takes, like, 5-8 seconds to run.

soft matrix
#

maybe ask eric nicely πŸ₯Ί

trim tangle
#

Maybe try in watch mode?

soft matrix
#

that sounds like a decent idea

#

its probably a case of replit machines not being very powerful

upbeat wadi
tranquil turtle
#

why mypy gives no error?

fierce ridge
#

!e print(isinstance(True, int))

rough sluiceBOT
#

@fierce ridge :white_check_mark: Your eval job has completed with return code 0.

True
brazen jolt
#

interesting

green gale
#

booleans are subclasses of ints

brazen jolt
#

pyright gives x possibly unbound

soft matrix
#

thats not the issue here

brazen jolt
#

I thought mypy detected those too

soft matrix
#

its that x is not defined for the False case

brazen jolt
#

apparently, it does not

#

but I don't see how True/False being int is relevant here, since it's not the bool parameter that gets returned it's the unbound variable x

soft matrix
#

i think they just misread the snippet

brazen jolt
#

it's really surprising to me that mypy didn't find this though, I usually use pyright so I didn't even realize this wasn't a thing in mypy, but I guess it just isn't (perhaps there's some way to turn it on?) Apparently, it just sees that x was at some point set to an int, so it carries that type definition and just works with x as being int without any checks that ensure that it was even set before it's used

tranquil turtle
#

is it possible to create some object x, such that annotation var: x = x would be correct?

#

i want to create singleton for which you do not need to use two names for annotation

#

example:```py
from ... import sentinel

def f(x: int | sentinel = sentinel) -> int:
if x is sentinel:
return 0
return x

#

but now i must use two names: sentinel and SentinelType, which is annoying

soft matrix
#

no

brazen jolt
#

I don't think you can, apart from maybe: x: type = type since type is of type type (type(type) is type)

#

but that's only because type has quite a unique implementation in python source itself I don't believe this is replicable with any new class with pure-python code

#

apparently x: None = None also works

soft matrix
#

oh maybe theoretically you could do something like

class sentinal(type):
    def __new__(mcs, name) -> object: return super().__new__(mcs, name, (), {})()
    @property
    def __class__(cls): return cls
    if TYPE_CHECKING: 
        __class__: sentinal
brazen jolt
#

type checkers will likely complain anyway since they won't run that code

soft matrix
#

and that might fool type checkers

#

anyways dont do this

brazen jolt
#

if you had active type checking, i.e. some implementation that checked types at runtime, perhaps this would be ok, but we usually implement add typing so that it can be checked statically

soft matrix
#

this can be statically type checker idk what you mean

tranquil turtle
brazen jolt
#

this would be what you'd actually need to pass mypy even on the definition of such class: ```py
from typing import TYPE_CHECKING

class sentinel(type):
def new(cls, name):
return super().new(cls, name, (), {})()

@property
def __class__(cls):  # type: ignore # mypy doesn't like this being obscured later
    return cls

# mypy doesn't like read-write __dict__ attribute being
# overridden by read-only property, so we need a setter
# to suppress that warning
@__class__.setter
def __class__(cls, _):  # type: ignore # mypy doesn't like this being obscured later
    pass

if TYPE_CHECKING:
    __class__: "sentinel"  # type: ignore # mypy doesn't like this already being defined
#

and even with all this py s = sentinel("s") x: s = s still fails mypy with: ```
error: Variable "file.s" is not valid as a type

#

so yeah, you can't really do it

#

pyright also fails with: ```
error: Expression of type "Type[__class_sentinel]" cannot be assigned to declared type "__class_sentinel"
Β Β "Type[sentinel]" is incompatible with "Type[__class_sentinel]" (reportGeneralTypeIssues)

tranquil turtle
#

class sentinel:
    sentinel: ClassVar[sentinel]

sentinel.sentinel = sentinel  # type: ignore[assignment]


def test1(x: sentinel = sentinel.sentinel) -> sentinel:
    return x

def test2(x: int | sentinel = sentinel.sentinel) -> int:
    if x is sentinel:
        return 0
    assert isinstance(x, int)
    return x
#

sentinel sentinel sentinel sentinel sentinel sentinel sentinel sentinel sentinel sentinel

#

it works at type-check time and runtime

brazen jolt
#

then again, sentinel.sentinel isn't just sentinel this would still be failing: ```py
s: sentinel = sentinel

tranquil turtle
#

yea, but this solves my issue

brazen jolt
#

please don't actually use this

tranquil turtle
#

:)

#

it is beautiful

little hare
tranquil turtle
#

it fails when compiled with mypyc (

brazen jolt
#

well, problem with the above code is that even though it should logically work, it actually won't because type checkers will just not accept s as the type like this, but yeah even just attempting anything like this is kind of crazy

brazen jolt
#

what's so wrong with SentinelType? lol

tranquil turtle
#

it is additional name to import

brazen jolt
#

not necessarily

#

you could do x: Type[sentinel] = sentinel

tranquil turtle
#

πŸ€”

#

it is ugly

brazen jolt
#

I mean, doing what you're doing is completely counter-intuitive and if it's a library, users will have no idea how to type-hint your sentinels at all

tranquil turtle
#

type[variable] is not valid

brazen jolt
#

are you on python >=3.9?

tranquil turtle
#

3.10

#

Variable "rangers.std.sentinel.sentinel" is not valid as a type

brazen jolt
#

hm

#

use the class directly then

#
class Sentinel:
    pass

def foo(x: type[Sentinel] = Sentinel) -> type[Sentinel]:
    return x
#

this one should work

#

!pep 661

rough sluiceBOT
#
**PEP 661 - Sentinel Values**
Status

Draft

Created

06-Jun-2021

Type

Standards Track

tranquil turtle
#

!e

class _sentinel_meta(type):
    def __repr__(cls) -> str:
        return '<sentinel>'

class sentinel(metaclass=_sentinel_meta):
    pass



def test1(x: type[sentinel] = sentinel) -> type[sentinel]:
    return x

def test2(x: int | type[sentinel] = sentinel) -> int:
    if x is sentinel:
        return 0
    assert isinstance(x, int)
    return x

print(f'{sentinel = }')
print(f'{test1() = }')
print(f'{test2() = }')
print(f'{test2(1) = }')
brazen jolt
#

it may be a bit neater with this syntax: ```py
class SentinelFactory(type):
def new(cls, name: str):
return super().new(cls, name, (), {})

def __repr__(cls) -> str:
    return "<sentinel>"

MISSING = SentinelFactory("MISSING")

def foo(x: type[MISSING] = MISSING) -> type[MISSING]:
return x

#

until PEP 661 is addressed that is

#

hm, apparently mypy doesn't like that though, but pyright is fine with it

hallow flint
#

a singleton enum is one of the nicest ways to do sentinels, since you can use is checks, like you would with normal sentinels

loud osprey
#

what is wrong in the type hint here?

trim tangle
hasty hull
brazen jolt
#

you're returning none and type-hinting that you're returning a callable

loud osprey
#

def fib_memoization() -> Callable:
    """
    >>> fib_memoization()(5)
    5
    >>> fib_memoization()(100)
    354224848179261915075
    >>> fib_memoization()(25)
    75025
    >>> fib_memoization()(40)
    102334155
    """
    # Cache must be outside recursuive function
    # other it will reset everytime it calls itself.
    cache: dict[int, int] = {1: 1, 2: 1}  # Prefilled cache

    def recursive_fn(num: int) -> int:
        if num < 1:
            raise ValueError("Enter positive integer")
        if num in cache:
            return cache[num]

        value = recursive_fn(num - 1) + recursive_fn(num - 2)
        cache[num] = value
        return value
loud osprey
trim tangle
brazen jolt
#

you probably forgot return recursive_fn

trim tangle
#

yep

loud osprey
trim tangle
#

Also, please don't use generics like List or Dict or Callable without parameters (e.g. use Callable[[int], int] instead of Callable).

#

it makes the type gods very sad 😦

brazen jolt
#

yeah, if your function is returning a callable that takes an int and returns an int, you can specify that directly rather than just specifying that it's any kind of callable

loud osprey
brazen jolt
#

callable needs 2 arguments

#

first one is the list of arguments your fucntion takes and second one is what it returns

hasty hull
trim tangle
#

Callable[int] is not valid, yeah

#

Callable[[int], int] is a function taking an int and returning an int

trim tangle
#

well, not necessarily a function

#

a callable

#

So CachedFibo() would be a callable as well

class CachedFibo:
    def __init__(self):
        self._cache = {}

    def __call__(self, num: int) -> int:
        if num < 1:
            raise ValueError("Enter positive integer")
        if num in self._cache:
            return self._cache[num]

        value = self(num - 1) + self(num - 2)
        self._cache[num] = value
        return value

fibo = CachedFibo()

c: Callable[[int], int] = fibo  # OK
brazen jolt
#

yeah, this would probably pass this type-hint```py
class Foo:
def new(cls, num: int) -> int:
return num

x: Callable[[int], int] = Foo

#

not sure all type-checkers would support it though since it's a bit weird

soft matrix
#

they definitely do

#

otherwise duck typing would just be broken for that type checker

brazen jolt
trim tangle
#

classic

#

btw, I asked Eric if I'm allowed to register pyright-play.net, he'll get back on Monday

soft matrix
#

well it violates the super()'s return type

brazen jolt
#

looking forward to that playground!

soft matrix
#

i actually dont think pyright should allow that

brazen jolt
soft matrix
#

yeah but the singleton should be an instance of the class

brazen jolt
#

yeah, but I don't think it should strictly be frobidden, I agree that it's weird though

#

mypy does complain about it, though you could do type: ignore there

#

problem is that even if you ignore it, mypy still complains about the type-hint for x not being correct. But yeah, pyright could also probably detect it as a violation and allow you to ignore these cases specifically

soft matrix
#

makes sense

upbeat wadi
trim tangle
#

also

#

can you ping me if you're replying?

upbeat wadi
#

sure

trim tangle
#

πŸ™

little hare
#

does pyright have a block on all domain names or some licensing thing?

#

I mean like a trademark

trim tangle
brazen jolt
#

I wasn't able to find a trademark, but yeah, I would double-check that and be careful with using it, though I think it's fine

grave fjord
#

usually it's ok to use a trademark to refer to the thing it's trademarked for

#

eg if you put M&Ms in a cookie you can sell them as M&M cookies

#

there's some fancy stuff like complying with a specification eg Thunderbolt 4 tm

#

where to use the branding you have to comply to a specification

#

that's different because you can't just go and make some sugar coated chocolates and call them M&Ms, as there's no public specification or compliance process

brazen jolt
#

that's just a license for that code, it has nothing to say about using the name itself

grave fjord
#

It forces you to use the name when you sublicense the software

#

In the "above copyright notice"

#

When you redistribute it (etc) you have to include:

Pyright - A static type checker for the Python language
Copyright (c) Microsoft Corporation. All rights reserved.
brazen jolt
#

but there's no sublicensing happening afaik, isn't the playground just supposed to utilize pyright without actually editing it's source or anything like that? Just using an MIT licensed software in any other license only requires you to mention the usage of it

grave fjord
#

It would come under "use"?

brazen jolt
#

well, you're installing the tool and making calls to it, then taking the output and utilizing it to nicely show where is an error, etc. it's essentially like having an MIT library in your dependencies

#

mentioning the usage of that tool may be required by the MIT license, but it's mentioned in the dependencies which is usually enough. It has nothing to say about you using the name of that software in your own name, that's a completely different issue and I really don't see how MIT license allows nor prohibits the usage of the name of the software licensed under it

loud osprey
#

okay, what is wrong here now?

soft matrix
#

this is returning a list not a Callable

loud osprey
#

oh okay

#

right

soft matrix
#

it should return list[int]

dusk osprey
#

Hi, I am doing a node graph where I procedurally generate nodes based on a user defined execute method's .__annotations__ dictionary. This works wonderfully with inputs, only one slight problem, I can't figure out a way of supporting multiple return types? Currently a return type is specified the default python with the -> type syntax, this works quite well for nodes that only require a single output, but when I want to return something more complex, it becomes a little annoying. My system currently names the function inputs based on the __annotations__ variable names, I would like this to persist and work similarly on the return type system.

Are there some type, that supports typeing, that lets the user define keys and types exactly as when specifying function parameters?

Example of current syntax:

class StringConstantNode(KSNodeItem):
    _title: str = "Sting Constant"

    def execute(self) -> str:
        return "String1234"

class PrintString(KSNodeItem):
    _title: str = "Print String"
    
    def execute(self, string: str, boolean: bool) -> None:
        print(string)

This would be the ideal syntax:

def execute(self, string: str, boolean: bool) -> (outputString: str, outputInt: int):
  ...

Or more realistically:

def execute(self, string: str, boolean: bool) -> MyDynamicNodeReturnType(outputString: str, outputInt: int):
  ...
oblique urchin
grave fjord
#

Is there a pep for inline TypedDict yet?

#

Eg -> {"key": int}:

#

Maybe I dreamt it

dusk osprey
#

I don't really mind the specific type since the execute function's return type will only be evaluated once, but I need some sort of key/value type where I can specify the key and value type in the return type.

dusk osprey
#

I think it wants me to use typing.Dict[]

soft matrix
grave fjord
#

Also would be clean in a type alias

#

Foo: TypeAlias = {"foo": str}

dusk osprey
#

Can I do a TypeAlias inline?

soft matrix
#
from typing import TypedDict

class Foo(TypedDict):
    foo: str

def bar() -> Foo: ...```this is how you are meant to do this atm
dusk osprey
#

What versions does this support?

soft matrix
#

3.8+

#

if you want 3.6+ you need typing_extensions

dusk osprey
#

Welp, I am in 3.7.7 because of the host software (Autodesk Maya)

#

Can I extend the typing module myself?

soft matrix
#

no

dusk osprey
#

Huh?

soft matrix
#

just do pip install typing_extensions

#

then it will work

dusk osprey
soft matrix
#

ok then you need to use

def bar() -> typing.Dict[str, str]: ...```
#

you cant get any better than that

grave fjord
#

You can use:

if typing.TYPE_CHECKING:
    from typing_extensions import TypedDict
    class Foo(TypedDict):
        foo: str
else:
    Foo = dict
dusk osprey
#

There is really no way of extending typing? This seems kinda wired?

grave fjord
#

mypy considers typing_extensions installed even when it's not

soft matrix
#

extending typing would be really hard for type checkers to implement

trim tangle
#

mypy supports custom plugins.

#

But yeah, arbitrarily extending the type system is not easy πŸ™‚

dusk osprey
#

Remember, I am only using this for syntax and not any sort of type checking or deep logic.

soft matrix
#

it can be manually changed yes, but only at runtime and typecheckers cant tell that happens

trim tangle
#

So if you're not using a type checker, just put the more complicated information into the docstring

#

or in a comment

dusk osprey
#

The function annotations are not used anywhere at runtime?

trim tangle
#

they are not

#

Python is dynamically typed

soft matrix
#

not normally, some things like pydantic do use them at runtime

#

but you probably arent using that

trim tangle
#

yeah, you can inspect them

#

but other than that, they don't change anything

dusk osprey
#

How would I access a docstring at runtime?

trim tangle
#

but why?

dusk osprey
#

My nodes are created at runtime aswell.

trim tangle
#

but why do you need to access the docstring?

dusk osprey
#

I just need some way of linking some return specification to the method statically that I can access later when creating the nodes in runtime.

trim tangle
dusk osprey
#

Yeah, that's how i generate inputs.

soft matrix
#

!d typing.get_type_hints

rough sluiceBOT
#

typing.get_type_hints(obj, globalns=None, localns=None, include_extras=False)```
Return a dictionary containing type hints for a function, method, module or class object.

This is often the same as `obj.__annotations__`. In addition, forward references encoded as string literals are handled by evaluating them in `globals` and `locals` namespaces. If necessary, `Optional[t]` is added for function and method annotations if a default value equal to `None` is set. For a class `C`, return a dictionary constructed by merging all the `__annotations__` along `C.__mro__` in reverse order.

The function recursively replaces all `Annotated[T, ...]` with `T`, unless `include_extras` is set to `True` (see [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated "typing.Annotated") for more information). For example:
soft matrix
#

use this

trim tangle
#

yeah

soft matrix
#

cause you cant use the more lenient inspect.get_annotations

dusk osprey
#

Right.

#

I still need to find a way to force the return key/type dict into the __annotations__['return'] value.

#

Could properly be done with a method decorator?..

void panther
#

Shouldn't this work?

import collections.abc
import typing

a: collections.abc.Iterable[str] = "abc"
typing.cast(str, a)
a.split()
#

or do I have to narrow it through an assert or something

soft matrix
#

you need to reassign a to the cast value

#

its not inplace

void panther
#

ah

trim tangle
#

Hm... looks like mypy and pyright both reject this code. If you have typing_extensions or Python >=3.10, you can use ParamSpec.

import functools
from typing import Callable, TypeVar, ParamSpec


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


def log(function: Callable[P, T]) -> Callable[P, T]:
    @functools.wraps(function)
    def wrapper(*args: P.args, **kwargs: P.kwargs):
        try:
            output = function(*args, **kwargs)
            return output
        except Exception as e:
            raise e
    return wrapper
``` Sadly mypy doesn't support `P.args` and `P.kwargs` yet, so I'd just put `# type: ignore`
upbeat wadi
#

It does on master afaik

trim tangle
upbeat wadi
upbeat wadi
#

Not sure

trim tangle
tame spear
buoyant swift
#

you need a list of arguments

tame spear
#

But in the official doc, [] is not included

brazen jolt
#

what pycharm version did you use? does it have support for paramspec yet?

tame spear
#

I'm using 2021.2.3 professional ed. I have checking for 3.10 turned on. Maybe PyCharm doesn't have that support yet?

brazen jolt
#

it's possible, paramspec is still a fairly new thing and not many people are using 3,10 yet, try running the type-checking tools manually aside from pycharm

tame spear
#

Yeah I checked Pycharm issue checker. The support for ParamSpec seems to be poor

#

Thanks!

spare mauve
#

what is the story for annotating the names of a return tuple? e.g. "multiple return values"

#

is there a standard way? 😦

spare mauve
#

that works good for indicating the types

undone carbon
#

can anyone explain to me wat's generic and typevar ?

tranquil turtle
#
def f() -> tuple[x=int, s=str, l=list[int]]:
  return 0, '', [0]
``` πŸ€”
buoyant swift
#

?

oblique urchin
#

(or why not use a dataclass already)

tranquil turtle
#

but it is legal with

#

!pep 637

rough sluiceBOT
#
**PEP 637 - Support for indexing with keyword arguments**
Status

Rejected

Python-Version

3.10

Created

24-Aug-2020

Type

Standards Track

tranquil turtle
#

Status
Rejected

little hare
#

what is the comment to make mypy or pyright ignore something?

undone carbon
#

# type: ignore

undone carbon
#

can someone explain to me wat's TypeVar & Generics??

acoustic thicket
undone carbon
#

haha okayy thx

trim tangle
#

Generic is not explained there πŸ™‚

undone carbon
#

how bout generics?

trim tangle
#

well

  1. I'm lazy
#

maybe I'll get to it some day

undone carbon
#

ya i mean, is there any suggested articles?

undone carbon
#

ook thx

undone carbon
#

i would like to ask why doesnt python allow List[int, str] like Tuple does? i mean, why not?

trim tangle
#

although I'm not sure about the "official" reasoning

undone carbon
#

oh okayy

trim tangle
#

also, for example asyncio.gather returns a list, but it's type-hinted to return a tuple

#

because of this

undone carbon
#

?

trim tangle
# undone carbon ?

The type stub for asyncio.gather says that it returns a tuple, like py asyncio.gather(a, b, c) where a is Awaitable[int], b is Awaiatable[str] and c is Awaitable[bool] should return Awaitable[tuple[int, str, bool]] according to it.

#

At runtime, it returns a list. But because it's impossible to write an accurate type annotation for a list like that, the tuple hack was used.

undone carbon
#

haha okayy thx

acoustic thicket
#

why not return a tuple?

undone carbon
#

ya, just curious

trim tangle
#

you can't just change a stdlib function

undone carbon
#

lol

acoustic thicket
#

well yeah

#

but when a function returns "multiple things" its usually as a tuple anyway

trim tangle
#

so converting to a tuple is extra overhead

acoustic thicket
#

hm makes sense

hasty hull
#

that is amazing

#

thank you

trim tangle
#

I do not understand if that is sarcasm or not πŸ™‚

hasty hull
#

oh sorry lol

soft matrix
#

Where's the mobile version :angerycat:

hasty hull
#

I actually mean that, I hate having to use the mypy playground

trim tangle
#

the monaco editor is a giant pain in the butt... doesn't seem possible to make responsive

little hare
#

huh so far, mypy, black, and now pyright, neat

#

i'm actually slightly surprised

#

that they don't share a similar framework or something

trim tangle
soft matrix
#

Sounds like good software

#

Thank you for this!

little hare
trim tangle
#

Eric also mentioned that it's theoretically possible to run Pyright in the browser

#

Shameless plug

little hare
trim tangle
#

Now the frontend will invoke a lambda function instead of my crappy VPS

#

the response time seems to be twice as low

hasty hull
# trim tangle why?

Just cause it's mypy and I've had to implement horrible workarounds in my library just to support mypy

#

Like if I add a certain field to a certain TypedDict then it causes mypy to OOM

#

And making a class generic makes mypy take ~30 minutes to run

trim tangle
hasty hull
#

yeah, sorry should've clarified that

trim tangle
#

I think the only advantages mypy has over pyright are:

  1. it's written in python, so you don't need to install node
  2. it has a plugin system
#

which might be pretty important

hasty hull
#

Yeah the plugin system is the biggest advantage

trim tangle
#

it's a hack though, right?

hasty hull
#

yes

void panther
#

does pyright work without an internet connection? It hanged yesterday when I tried it once

hasty hull
#

It just downloads node if you don't already have it and then runs pyright through node

hasty hull
#

If so, it doesn't have support for offline usage yet

void panther
#

no, normal pyright

trim tangle
#

Normal pyright works without the internet

hasty hull
#

normal pyright should work without an internet connection

trim tangle
#

I just checked

void panther
#

I wonder what the issue was then, it got stuck for over a minute without output so I just killed it

#

when I tried it now after disabling the network adapter I got a pyright.errors.VersionCheckFailed: Version check for pyright failed, see output above. after a bunch of npm errors

#

But yesterday I had a network connection, just not internet

hasty hull
#

Thats the wrapper I wrote

little hare
#

!pypi pyright

rough sluiceBOT
hasty hull
#

That's why it's not working

little hare
#

This project works by first checking if node is in the PATH and if it is not then we download node at runtime using nodeenv and then install the pyright npm package using npx.

We also automatically upgrade the pyright npm package to it's latest version on every run, see below for how to change this behaviour.

hasty hull
#

^

#

Eric asked me to make sure the python package was never out of date

void panther
#

ah sorry I got confused around the naming

#

it could use some kind of timeout then

hasty hull
#

@void panther You can disable this behaviour by explicitly setting the version

#

e.g. set the evironment variable PYRIGHT_PYTHON_FORCE_VERSION to 1.1.190

little hare
#

gonna modify that and not submit it as-is, but would like to see a way to set update checking

hasty hull
#

Yeah I do want to support that in the future

#

A cli for the wrapper itself is a good idea, I hadn't thought about that before

little hare
#

RobertCraigie/pyright-python#19

mighty lindenBOT
hasty hull
#

I have considered automatically releasing a new version whenever a new pyright version is released

little hare
#

huh, that would be neat too

#

basically a mirror

#

hmm

#

wouldn't want to change pyright tool itself since it already works right but a wrapper that will uses its own version to get the other version would be neat

hasty hull
#

idk I think it would be better to consolidate that into the existing wrapper as it would probably get confusing

#

people already get confused over the current python package

little hare
#

trueee

hasty hull
#

If I do end up implementing that I would change the current autoupdate behaviour to simply output a message that the current package is out of date instead of automatically upgrading

trim tangle
#

TODO:

  • clean up the code (it is a mess)
  • add Pyright version selection
  • add Python version selection
  • fix squiggly line not showing up if the error is at the last char
  • reset the tooltips somehow after a new set of diagnostics is received
  • colour-code toolitps depending on the severity
#

global TODO: rewrite the backend for Node

brazen jolt
#

for some reason, the red highlight is off by 2 characters to left for me (on firefox), bit weird

oblique urchin
#

what would be really cool is a single playground that lets you choose between mypy, pyright, and even other typecheckers

trim tangle
#

yeah I was thinking about that

#

that would be cool

trim tangle
brazen jolt
trim tangle
#

y

#

okay, I will

#

tomorrow

#

or actually,

#

License added, now you can steal my code

brazen jolt
#

YAY

trim tangle
trim tangle
#

That's what you get for not writing tests

tranquil turtle
little hare
#

!rule 4 which is probably why it was deleted

rough sluiceBOT
#

4. Use English to the best of your ability. Be polite if someone speaks English imperfectly.

little hare
#

(if you were wondering)

tranquil turtle
#

lol

trim tangle
#

@tranquil turtle @brazen jolt I fixed all the off-by-N bugs πŸŽ‰

#

(hopefully)

brazen jolt
#

nice

trim tangle
#

so I pushed a few updates, should look nicer now

deep pendant
#

How to type MongoDB aggregation keywords an TypedDict?
E.g.:

[{
    $match: {
        name: {
            $in: ['John', 'Smith']
        }
    }
}]

and

class MyQuery(TypedDict):
    x: int
    y: int
    $in: str # neither this
    '$in': str # nor this work
    label: str

(both is sample)

deep pendant
#

Should work, thanks!

#

And another (probably with fast answer) question - I have a custom type AggQuery = TypedDict(...) and I pass it (a pure - but typed - dict!) to a function which takes dict.
Type checker (mypy) complains about type of dict which I pass to the function.
In TypeScript I will write obj1 as dict, but how to get rid of this issue in Python?

Do not get me wrong - all of benefits of typing the dict are consumed, no need to do it further

oblique urchin
deep pendant
#

Probably no

hasty hull
trim tangle
#

@deep pendant Can you show your code?

deep pendant
trim tangle
#

@deep pendant Here's the issue:

class Foo(TypedDict):
    bar: int
    baz: str

def fizz(foo: dict[str, Any]):
    foo["new field"] = 42
    foo["bar"] = "wrong type"

foo: Foo = {"bar": 1, "baz": "aaa"}
fizz(foo)
deep pendant
# deep pendant

Argument 1 to "get_tracks_list" has incompatible type "AggQuery@189"; expected "Dict[Any, Any]"

trim tangle
#

dict is "invariant" relative to its key and value parameters. That means that even if A is a subtype of B, dict[A, V] is not a subtype of dict[B, V].

#

What you need to do is make that function accept a Mapping[your-key, your-value].

#

Mapping is "covariant". It means that if K1 is a subtype of K2 and V1 is a subtype of V2, then Mapping[K1, V1] is a subtype of Mapping[K2, V2].

oblique urchin
#

Mapping is covariant only in its value type, the key type is still invariant. But usually it's the value type that causes problems, the key type should just be str for TypedDict

trim tangle
#

why is the key invariant though?

deep pendant
#

Nevertheless, I anyway have to modify the function argument's types, yea? Even if it doesn't matter if this would be Mapping (friendly to other callers) or my type

trim tangle
#

wdym?

oblique urchin
deep pendant
#

I meant that I have to change (function) get_tracks_list: (query: dict, offset: int) -> list to either (query: Mapping[...], offset: int) or (query: AggQuery, offset: int)

#

I've loss the point πŸ˜…

Thank you a lot!

twilit badge
#

How should I type-hint a traceback object (I need this for __exit__ method in my context mgr class), I know there's traceback module, but i wasn't able to find the traceback class in it

trim tangle
#

!d types.TracebackType

rough sluiceBOT
#

class types.TracebackType(tb_next, tb_frame, tb_lasti, tb_lineno)```
The type of traceback objects such as found in `sys.exc_info()[2]`.

See [the language reference](https://docs.python.org/3/reference/datamodel.html#traceback-objects) for details of the available attributes and operations, and guidance on creating tracebacks dynamically.
twilit badge
#

oh, I see, thanks

pastel egret
#

Most of the internal types are found in types.

tranquil turtle
#

not even close

#

most of the public internal types are found in types

#
>>> iter(()).__class__
<class 'tuple_iterator'>
>>> iter([]).__class__
<class 'list_iterator'>
>>> iter({}).__class__
<class 'dict_keyiterator'>
>>> iter('').__class__
<class 'str_iterator'>
>>> iter(b'').__class__
<class 'bytes_iterator'>
>>> iter({0,}).__class__
<class 'set_iterator'>
>>> iter(bytearray()).__class__
<class 'bytearray_iterator'>
pastel egret
#

Indeed yes, those are a notable exception since all you should care about is that they're an Iterator[X].

tranquil turtle
#

!e
print(object.__subclasses__()[52])

rough sluiceBOT
#

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

<class 'symtable entry'>
tranquil turtle
#

oh no, space in class name

pastel egret
#

Spaces and other rules for names are just a syntactic restriction - manually constructing things let you use any string for class names, globals, attributes etc.

leaden oak
#

!eval

Printer = type(
    "my-basic-printer", (), {"hello-world": lambda self, x: print(x)}
)
p = Printer()
print(p.__class__)
getattr(p, "hello-world")("hello, world!")
rough sluiceBOT
#

@leaden oak :white_check_mark: Your eval job has completed with return code 0.

001 | <class '__main__.my-basic-printer'>
002 | hello, world!
rotund flax
tranquil turtle
#
from typing import Callable, Generic, TypeVar

T = TypeVar('T')


class X(Generic[T]):
    f: Callable[..., T]

    def __init__(self, f: Callable[..., T]) -> None:
        self.f = f  # mypy: error   assignment - Cannot assign to a method
#

is it bug in mypy?

pastel egret
#

That doesn't look right, yeah.

trim tangle
#

The best workaround is with a protocol ```py
class Function(Protocol[T]):
def call(self, *args, **kwargs) -> T:
...

undone carbon
#

whoa...an issue tat hasnt been fixed in 6 years? gg

opal spoke
#
@overload
def foo(t: T) -> List[T]:
    ...


@overload
def foo(t: List[T]) -> List[T]:
    ...


def foo(t: Union[List[T], T]) -> List[T]:
    if isinstance(t, list):
        return t
    return [t]
#

does anyone know how to make pyright happy here?

undone carbon
#

Lmfaoooooo

trim tangle
#

From the first overload, it should return List[List[int]].

#

Right now you can't really specify "any-type-except-list"

#

Actually, why do you even have these overloads? just do ```py
def foo(t: Union[List[T], T]) -> List[T]:
if isinstance(t, list):
return t
return [t]

#

Although that still has the same issue.

#

Actually, isn't this a bug? πŸ€”

upbeat wadi
#

Oh that might be specific for TypeGuard nevermind

opal spoke
spiral fjord
trim tangle
#

well, depends on the context

#

if the type variable is already decided by some outer context, like ```py
class Foo(Generic[T]):
def init(self):
self._items: list[T] = []

def insert(self, items: Union[list[T], T]) -> None:
    ...

ints = Fooint
ints.insert(42) # ok
ints.insert([1, 2, 3]) # ok

#

(of course this will break if the type variable is assigned list[something]... but that's another story)

#

so the issue here is how T is inferred

spiral fjord
#

If a TypeVar can represent any type, that would also include List[T]

trim tangle
#

which is why I opened the pyright issue

fair elbow
#

trying to figure out how properly should I use Literal type with Union πŸ€” List[Literal["a", "b"]] works fine as a type argument but if I add a Union to it then mypy starts to get confused and wraps it in a wrong way, is this intended? what I would like is an argument that can take Union[List[Literal["a", "b"]], List[Literal[1, 2]]]

oblique urchin
fair elbow
fair elbow
void panther
#

How do I typehint this? RecursiveDefaultDict is RecursiveDefaultDict(dict[_KT, _VT], t.Generic[_KT, _VT])

        hint = RecursiveDefaultDict[str, t.Union["TOMLType", "hint"]]
        self._settings_dict: hint = RecursiveDefaultDict()
trim tangle
#

works on pyright tho

#

did you also discover this trick? ```py
def Y():
return defaultdict(Y)

void panther
#

Yeah, but unfortunately when used like that it's not easy to control the default factory of all the dicts it creates

#

so the above typehint is correct?

trim tangle
#

so the type hint is correct, but mypy doesn't support it

void panther
# trim tangle works on pyright tho

hmm with pyright on this code it detects it as Type[RecursiveDefaultDict[str, ...]] for some reason

a : TypeAlias=RecursiveDefaultDict[str, t.Union["union_arg", "a"]]
b:a = RecursiveDefaultDict()
reveal_type(b)
reveal_type(b["a"])
\scratch.py:115:13 - info: Type of "b" is "RecursiveDefaultDict[str, int | str | Type[RecursiveDefaultDict[str, ...]]]"
\scratch.py:116:13 - info: Type of "b["a"]" is "int | str | Type[RecursiveDefaultDict[str, ...]]"
#

pyre gets it right but then complains about the b assignment

trim tangle
trim tangle
#

what's wrong?

void panther
#

Since it's a type alias, it should be seeing the RecursiveDefaultDict as a hint for an instance of it no?

#

so b["a"]["b"] would work as it's a RecursiveDefaultDict[str, t.Union["union_arg", "a"]]

trim tangle
#

What is union_arg in your case?

void panther
#

Just union_arg = t.Union[int, str]

void panther
#

right now b["a"]["b"] fails as it think I'm indexing the string from the union:arg

#

Or well, if I'm not understanding it correctly, how would I typehint the case where the dict's values can be anything from the union_arg, or its own type with the same generic arguments

trim tangle
#

in general, an operation is only allowed on a union if all variants support it

#

maybe I don't understand how your class works?

void panther
#

so should I just keep it at [str, t.Any] if any key can be either a value or an another recursive dict?

trim tangle
#

if you want to do it in a "type-safe" way, you could make a method for RecursiveDict that takes a list of keys and traverses the dict, checking each step

void panther
#

So there'd be no way of making an arbitrary amount of direct accesses work?

trim tangle
#

no

#

because operations on unions are strict

#

as in, you can't do an operation if just one of the variants allows it

#

which makes sense from a type safety perspective

hasty hull
#

Would be hacky but couldn't you overload __getitem__?

tulip hearth
#

I usually tend to avoid hackyways since typecheckers go brrr with them

void panther
#

I don't think that would work as the keys will always be strings

surreal forum
#

Hello fellow typing friends. What's the correct way of distinguishing an async generator from an async function that eventually returns an async iterator? Those two things are very different, but they look absolutely the same in terms of hinting :/

async def v1() -> AsyncIterator[int]:
    yield 0

async def v2() -> AsyncIterator[int]:
    return v1()
oblique urchin
surreal forum
brisk hedge
#

AsyncGenerator[int, None]?

#

Or is that just a way to rephrase the original type hint

surreal forum
# brisk hedge AsyncGenerator[int, None]?

Thats synonymous to AsyncIterator, and most importantly, that's the thing we're supposed to use for typing an async generator... but I'm afraid we can't declare an async function returning said thing.

brisk hedge
#

Then can you wrap v2 in Awaitable[], is that valid?

#

This seems like an unfortunate edge case

surreal forum
#

Doesn't feel that way.. Awaitable[] should be implied by the function being async, else you couldn't distinguish between Awaitable[int] and Awaitable[Awaitable[int]]

brisk hedge
#

(of the implicit async typing of functions)

surreal forum
#

So sad ^^

#

Maybe just use AsyncIterable[T] because that one never gets picked for annotating generators ^^

soft matrix
#

this seems to work fine for me

#

unless im missing something

surreal forum
#

Hm, it's kind of an unfortunate effect of python not declaring the nature of function-vs-generator in the def-line..

brisk hedge
#

The type is not determined by the signature

#

Though that's just how generator functions generally exist so

surreal forum
soft matrix
#

That's just how it is I don't see how else it could be

surreal forum
#

That's the correct answer I suppose.. without looking at the body, one cannot truly know the type of a function for sure :/

upbeat wadi
#

could someone explain the error pyright is giving? py def a(iterable: Iterable[T]): # type: ignore itertools.pairwise(iterable)``````Argument of type "Iterable[T@a]" cannot be assigned to parameter "__iterable" of type "Iterable[_T@__new__]" in function "__new__" Β Β TypeVar "_T_co@Iterable" is covariant Β Β Β Β Type "T@a" cannot be assigned to type "_T@__new__"

upbeat wadi
#

Regular TypeVar (regular meaning not covariant or contravariant - is invariant the term?)

#

Tried with a covariant one too

grave fjord
#

Also you need more than one use of a type variable for it to do anything

upbeat wadi
#

Yeah in my actual implementation I did

grave fjord
#

Ok show your actual code?

upbeat wadi
#
import itertools
from typing import TypeVar
from collections.abc import Iterable, Iterator

T = TypeVar('T')

def triplewise(iterable: Iterable[T]) -> Iterator[tuple[T, T, T]]:
    "Return overlapping triplets from an iterable"
    # triplewise('ABCDEFG') -> ABC BCD CDE DEF EFG
    for (a, _), (b, c) in itertools.pairwise(itertools.pairwise(iterable)):
        yield a, b, c```
grave fjord
#

Mypy likes it

#

There's also more_itertools.triplewise

upbeat wadi
#

yeah this code is copied directly from that

#

though i should probably just use that instead, since there's no reason not to

upbeat wadi
grave fjord
#

Seems like it, make sure you have the latest version?

upbeat wadi
#

yeah, i do

#

i'll make an issue

oblique urchin
rough sluiceBOT
#

stdlib/itertools.pyi line 206

class pairwise(Iterator[_T_co], Generic[_T_co]):```
grave fjord
#

Woah it's a class

upbeat wadi
#

yeah seems like that's the case

grave fjord
#

Not a def

#

You can do pairwiseT?

upbeat wadi
#

not at runtime

oblique urchin
#

Curious if itertools.combinations has similar issues, it has an even more complicated __new__

grave fjord
#

I feel like way too much stuff is secretly a class

oblique urchin
#

can't write a generator function in C

upbeat wadi
#
def foo(iterable: Iterable[T]) -> Iterator[tuple[T, T]]:
    return itertools.combinations(iterable, 2)```successfully type checks, so not sure
little hare
#

.topic

fierce ridge
#

doesn't fix your problem though

#

that's actually really weird situation

#

the first return type is correct because that's literally what it is

#

the second return type is correct because implicitly it gets wrapped in Awaitable

#

however the revealed types are in fact correct

#
main.py:9: note: Revealed type is "def () -> typing.AsyncIterable[builtins.int]"
main.py:10: note: Revealed type is "def () -> typing.Coroutine[Any, Any, typing.AsyncIterable[builtins.int]]"
#

i don't know that there's a solution

#

unfortunate

#

would be worth a post on the typing-sig mailing list

brisk hedge
trim tangle
grave fjord
#
trim tangle
#

@grave fjord the type hint of the actual implementation is not taken into account

#

So your first function's type is just ```py
@overload
def bar(name: str, *, return_length: Literal[True] = ...) -> Tuple[str, int]:
...

@overload
def bar(name: str, *, return_length: Literal[False]) -> str:
...

grave fjord
#

ah except that it has to be compatible with the overrides

trim tangle
#

yep

grave fjord
#

ah of course so you can do stuff like:

@overload
def ham(name: Literal["ham"]) -> Ham: ...
@overload
def ham(name: Literal["spam"]) -> Spam: ...
def ham(name: object) -> Ham | Spam:
    if name == "ham": return Ham()
    if name == "spam": return Spam()
    raise ValueError
trim tangle
#

yep

grave fjord
#

but it's annoying if your literal is exhaustive

#

eg bool or enum

oblique urchin
#

yeah there's been some talk about making bool equivalent to Literal[True, False]. might even be a mypy PR for it already

cosmic tree
#

That would be cool indeed mopCool

grave fjord
#

also is:

class Foo(enum.Enum):
    ham = auto()
    spam = auto()

is Foo equivalent to Literal[Foo.ham, Foo.spam] ?

grave fjord
trim tangle
little hare
#

yes?

#

that's literally how you use them

trim tangle
#

um

#

I mean, can you subclass Foo?

oblique urchin
trim tangle
#

then yeah, they're the same

oblique urchin
#

you cannot extend a subclass of enum that defines enum members

trim tangle
#

right, that makes sense

oblique urchin
#

thanks to some dark metaclass magic

little hare
#

wait ik you can

little hare
#

I have a place in my code that does that

#

making a BitwiseAutoEnum for a bitmask enum

trim tangle
#

!d enum.IntFlag

rough sluiceBOT
#

class enum.IntFlag```
Base class for creating enumerated constants that can be combined using the bitwise operators without losing their [`IntFlag`](https://docs.python.org/3/library/enum.html#enum.IntFlag "enum.IntFlag") membership. [`IntFlag`](https://docs.python.org/3/library/enum.html#enum.IntFlag "enum.IntFlag") members are also subclasses of [`int`](https://docs.python.org/3/library/functions.html#int "int").
trim tangle
#

?

little hare
#

...

#

I was new but let me look at that

grave fjord
#

!e

import enum

class Ham(enum.Enum):
    pass

class Spam(Ham):
   umad = enum.auto()
   brohiem = enum.auto()

print(Spam.umad)
#

urrgh

rough sluiceBOT
#

@grave fjord :white_check_mark: Your eval job has completed with return code 0.

Spam.umad
grave fjord
#

ah I see it's special cased for empty enums

grave fjord
soft matrix
#

latest fixes it

#

yeah

grave fjord
#

right but I'm using 0.910

dim trail
#

how would i typehint that a variable is storing any type, such as a = int a = str
i tried just using Type since that seemed most intuitive to me, but no luck
https://img.laundmo.com/u/C2YqVq.png

#

obviously Literal would work, but it would be quite hard to collect all the possible values

oblique urchin
dim trail
#

it is Pyright (or Pylance to be more accurate)

trim tangle
#

type should work

dim trail
grave fjord
#

@dim trail what are you using this for?

#

often it's better to use a type annotation that more reflects how you'll use it, eg Callable[[object], object]

dim trail
#

i made a class that represents a mapping from a incoming type to a outgoing type