#type-hinting

1 messages · Page 71 of 1

covert dagger
#

I don't think numpy or anything else really made use of that yet, probably waiting for mypy

hearty shell
#

Yeah perhaps TypeVars should limit themselfs to types only, and not terms promoted to types, although maybe there is a better way to reconcile this?

#

Also, yeah from the direction it is going, literals will be specially useful when working with arrays where you want the type to contain information about the dimensions of the arrays

#

Does that not makes functions with Flags more cumbersome to use though?

#

Regardless of their merits

#

I mean the pep focuses on examples such as open

#

You would still have to have a concept of a literal if you used enums, as annotations would have to accept members of enums to properly fill the functionality that literals try to solve

trim tangle
#

open is tricky because the return type depends on the concrete mode

#

But it could accept something like TextMode.read() and BinaryMode.write().and_read()

#

I think what Python is kinda lacking is discriminated unions. You can replicate them in various ways, but they are kinda not really there

#

Maybe I just didn't have enough experience, but exhaustive pattern-matching is not easy to pull off.

hearty shell
#

or literal like concept

trim tangle
#

Or just have two Enums

hearty shell
#

Oh I see what you mean

trim tangle
#

If you also want to encode whether the file is writable or readable, you could have some generic monster like Mode[_CanRead, _CanWrite, _IsText]

hearty shell
#

I mean at the end of the day, the more expressive you can be with the type system, theoretically the better no? You could express some stuff by just making concrete types for everything but curtain things do line up better with just using values imo. The only downside I do agree is the part about complicating the type system for type checkers

trim tangle
#

I think complicating the type system is a considerable disadvantage.

#

Not just for type checkers, but also for humans

#

Enums are also less error-prone for people who don't use a type checker, I would say.

#

Literals are also not as flexible:

fierce ridge
#

i am big -1 on literals except as compatibility for existing apis that rely on magic strings. source: personal experience working with literals

#

and believe me, i love the idea. i want refinement types in python

trim tangle
#

I guess one theoretically valid application of Literal is when you want to return a subset of an Enum. I've seen it a few times but can't think of an example now

soft matrix
#

I just had a horrific idea for doing Starts/EndsWith (in ts) in python

#

LiteralString.add and radd

hearty shell
# trim tangle Not just for type checkers, but also for humans

I would say that Literals are a low hanging fruit when it comes to that, I think that teaching about literals types as they currently are is a lot easier when compared to other things in the type system. And yes I do agree they are not as flexible, but I see that as a reason to make them better, perhaps by adding template literals

trim tangle
hearty shell
#

I actually have missed the string type templates at some point, I think it was in an instance where I had to reduce the number of options a curtain function could take

#

and having multiple enums would mean a lot of code repetition

blazing nest
soft matrix
#

def foo(x: "this_" + LiteralString):
if not x.startswith("this_"):
reveal_type(x) # Never

terse sky
#

@covert dagger for something like that, you need to be able to lift any integer into a type, which isn't really what Literal is, but maybe I'm misunderstanding

#

this is also related to the other point being made: re the enums and different types being returned from read, there's really two separate issues

#

there's Literal vs enums, and there's lifting values, which are considered a "dynamic" thing generally, into the static world of types

#

In say C++, you can inject values into the static world by accepting them as non type templat eparameters

#

so in C++ you could do things like

reader r = open<Mode::Read>(filename);
#

if you want to return different types from read based on what the flags are then you need to ensure that the values passed in are known statically

terse sky
devout barn
#

So I was storing some common typehints in a seperate file, some of them are from typing_extension (which is not installed but available during type checking), and I defined some type aliases however during runtime TYPE_CHECKING resolves to false and these aren't defined.
So I was replacing these definitions with "something else", what should this "something else" ideally be?

from typing import TYPE_CHECKING, TypeAlias, Any

__all__: tuple[str, ...] = ("Self", "Unpack")

if TYPE_CHECKING:
    from typing_extensions import Self, Unpack
else:
    # Should we assign this to anything else?
    Self: TypeAlias = Any
    Unpack: TypeAlias = Any
oblique urchin
blazing nest
#

I've seen people do like if sys.version_info checks even when they should be fine importing from typing_extensions - which is unnecessary unless you only require typing_extensions for older versions

rustic gull
#

how would i type hint a coroutine would it be

Coroutine[arguments, ReturnType]

like Callable?

oblique urchin
rustic gull
oblique urchin
#

the return type

pastel egret
#

Coroutine is the type of the in-progress coroutine itself, it's what's returned by the function.

rustic gull
pastel egret
#

Note for async def, Coroutine is implied, just specify the return type like a sync function.

trim tangle
#

The docs often use "coroutine" to mean "coroutine function" (i.e. an async def function) which I think is very bad.

#

The discord.py docs have the same sin

soft matrix
#

They just copy asyncio's

near kernel
#

I've asked this before and did not get a straight answer, so here goes one more time: does numba (not numpy) compile faster if there are type-hints (it's compilation times are very slow in general, although the tool is otherwise fantastic)? Does it properly error out if the type-hints are incorrect?

elder pasture
#

hi everyone, i'm having trouble with hinting line 11 here

Pyright is complaining that args[0] is an object, not a class instance so I can't do that, I'm just disabling it for now

Is there a better way to do this?

My purpose:

  • to use self in the wrapper
  • to type hint all the stuff

I tried def wrapper(self: DummyTest, *args, **kwargs): (< 3.10) but it won't type hint args/kwargs

import logging
from typing import Callable, TypeVar, ParamSpec
from functools import wraps

P = ParamSpec("P")
Ret = TypeVar("Ret")
def authenticated(method: Callable[P, Ret]) -> Callable[P, Ret]:
    @wraps(method)
    # pylint: disable=used-before-assignment
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> Ret:
        self: DummyTest = args[0]
        if self.auth.valid is False:
            self.logger.info("Not logged in, logging in...")
            self.auth.authenticate()
        return method(*args, **kwargs)

    return wrapper

class DummyAuth:
    def __init__(self, key:str):
        self.key = key
        self.valid = False

    def authenticate(self):
        # check valid
        if self.key == "abc":
            self.valid = True

class DummyTest:
    def __init__(self, key:str):
        self.logger = logging.getLogger(__name__)
        self.auth = DummyAuth(key=key)

    @authenticated
    def req_api(self):
        # do api call here
        self.logger.info("API CALL")
covert dagger
# elder pasture hi everyone, i'm having trouble with hinting line 11 here Pyright is complainin...

I think this is the case where you need Concatentate https://docs.python.org/3/library/typing.html#typing.Concatenate
something like this

def authenticated(method: Callable[Concatenate["DummyTest", P], Ret]) -> Callable[Concatenate["DummyTest", P], Ret]:
    @wraps(method)
    # pylint: disable=used-before-assignment
    def wrapper(self: DummyTest, *args: P.args, **kwargs: P.kwargs) -> Ret:
        if self.auth.valid is False:
            self.logger.info("Not logged in, logging in...")
            self.auth.authenticate()
        return method(self, *args, **kwargs)

    return wrapper
elder pasture
hearty shell
#

Just one thing you might wanna do instead is T = TypeVar('T', bound="DummyTest") and use the T instead

#

that way you can subclass DummyTest with less problems

elder pasture
hearty shell
#

No, I mean def authenticated(method: Callable[Concatenate[T, P], Ret]) -> Callable[Concatenate[T, P], Ret]:

#

def wrapper(self: T, *args: P.args, **kwargs: P.kwargs) -> Ret:

latent swift
#

I'm having some trouble when it comes to Typeddict

for requirement, required_amount in requirements.items():
            if count[requirement] < required_amount:
                return False

requirements and count are both of the same typeddict type. Running the above still gives me error: TypedDict key must be a string literal; expected one of ("examplekey1", "examplekey2", "examplekey3", "examplekey4")

hearty shell
#

Yeah TypedDicts aren't great for this, also when you grab the values it just returns object so the < op is also disallowed

#

Not sure what can be done here other then # type: ignore that doesnt involve changing much

#

x)

latent swift
# grave fjord What type is count?
count = cast(PasswordRequirements, collections.Counter())

and

class PasswordRequirements(TypedDict):
    example1: int
    exampl2: int
    example3: int
    example4: int
grave fjord
#

Is it always 4 items?

latent swift
#

Yeah

grave fjord
#

I'd probably use a dataclass and unroll the loop

oblique urchin
#

count[arbitrary key] is of type object because TypedDicts may have arbitrary additional keys due to subtyping

grave fjord
#

Eg write all the comparisons out

latent swift
#

True, I might as well. I've spent a bit too much time on this one 😅

blazing nest
#

Thoughts on using Hashable for dictionary keys? I have objects that basically hash a field of theirs, so I want to be able to use both - which I've found to be the easiest to do with Hashable

#

It is still type-safe, although yeah it may not catch as many issues where you accidentally lookup the wrong thing

soft matrix
#

It would be nice if bound was hashable but mypy doesn't support it

oblique urchin
trim tangle
grave fjord
#

The Validation applicative functor!

brisk hedge
#

what's that in non-haskell?

grave fjord
#

Eg if a password needs 3 numeric digits and 2 special symbols

brisk hedge
#

right so an applicative lets you sequence the operations while accumulating errors

trim tangle
devout barn
#

Is it a better practice/idea to use typing.Tuple instead of just tuple to have backward compatibility or would it be just better to use tuple (convenience aside)?

trim tangle
#

If you want to be compatible with Python<3.9, then use typing.Tuple

#

Are you making a library or an application?

devout barn
#

half of the existing code uses Tuple

#

but meanwhile the new code uses tuple

trim tangle
#

An application usually targets a specific version of Python, not a range of version. So backwards compatibility is usually not a concern.

devout barn
#

so I was wondering which way to drift to have consistency across the codebase

trim tangle
devout barn
#

I heard it was for typing metadata or something but didn't really catch it

trim tangle
#

so if it's not present, the type checker basically says: "I have no idea what's going on in this library, I won't even bother" unless you supply stubs

#

probably not needed in a flake8 plugin, but I don't see any harm in it (you can technically still import stuff from it)

devout barn
trim tangle
#

yeah

devout barn
trim tangle
#

Not sure about being automatically generated though, if your project already has type annotations you don't need a stub

grave fjord
trim tangle
devout barn
#

oh so I can completely ignore it if I am making an application.

#

got it

trim tangle
#

yes, I think so

grave fjord
trim tangle
#

hmmm I don't rember

grave fjord
#

Note that for stub-only packages adding a py.typed marker is not needed since the name *-stubs is enough to indicate it is a source of typing information.

#

Exception proves the rule?

trim tangle
#

you're right then, thanks

oblique urchin
trim tangle
#

more typehints for the typehint gods

bold gazelle
#

my function either returns Tuple[bytes, bytes] or throws an Exception, what do I put as the return type so it doesn't complain about the Exception path?

oblique urchin
bold gazelle
#

how about if I have def foo(p: Path): ... but want to call foo with a str but inside foo I certainly want a Path.. is the only way def foo(p: Union[Path, str]): p = Path(p); .... ?

hallow flint
#

yes, that's the most reasonable way

fierce ridge
#
def foo(path: str | os.PathLike[str]): ...
sharp plover
#

btw, here is how you can use @dataclass and type annotation to represent multimodal data https://docarray.jina.ai/fundamentals/dataclass/
(most probably interesting to data scientists and ML engineers

DocArray 0.12.1 Documentation

DocArray’s dataclass is a high-level API for representing a multimodal document using nested Document structure. It follows the design and idiom of the standard Python dataclass, allowing users to represent a complicated multimodal document intuitively and process it easily via DocArray Document/...

fierce ridge
#

kind of unfortunate that they used the same name

#

unless it's strictly a superset and drop-in replacement, i think that's kind of an arrogant thing to do by the devs

#

that said, i never saw this library before and it looks very interesting

gusty kelp
#

I've been looking at how type inference works and I'm curious if there is a way to infer a class attribute from another?

class MyThing:
    output: object  # <-- can you hint this should be return type of output_function?
    output_function: Callable

    def __init__(self):
        self.output = self.output_function()

def my_func() -> SomeOtherClass:
    return SomeOtherClass()

class SubThing(MyThing):
    output_function = my_func
subthing = SubThing()

reveal_type(subthing.output)  # if possible this would infer `SomeOtherClass`
#

Or is that just too much magic for the type checker?

oblique urchin
trim tangle
#

you beat me to it

oblique urchin
#

but you'd have to explicitly inherit from MyThing[SomeType]

trim tangle
oblique urchin
#

oh yeah that issue 🙂

hearty shell
#

I think I am confusing it with something else because I have seen patterns where it can resolve the other TypeVar when inheriting

gusty kelp
#

Ok, to complicate it more, the baseclass already has a generic inheritance.

class MyThing(SomeBase[T]):

Is the proper way to add another Generic like this?

class MyThing(SomeBase[T], Generic[T, U]):
oblique urchin
trim tangle
#

SomeType (without generic) implies SomeType[Unknown] in other contexts, that might be surprising

#

but idk

soft matrix
#

theres no way to make this work is there?```py
from future import annotations

import types
from typing import *

from typing_extensions import Self

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

class FunctionType(Generic[P, T]):
def call(self, *args: P.args, **kwargs: P.kwargs) -> T: ...
@overload
def get(self, instance: None, owner: Any) -> Self: ...
@overload
def get(self, instance: InstanceT, owner: type[InstanceT]) -> MethodType[InstanceT, P, T]: ...
def get(self, instance: Any, owner: Any) -> Any:
...

class MethodType(Generic[InstanceT, P, T]):
self: InstanceT

def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
    ...

class This:
f: FunctionType[[Self, int, str], Any] = FunctionType()

reveal_type(This.f) # Type of "This.f" is "FunctionType[(This, int, str), Any]"
reveal_type(This().f) # Type of "This().f" is "MethodType[This, (This, int, str), Any]"

this should be MethodType[This, (int, str), Any]

gusty kelp
trim tangle
gusty kelp
#
T = TypeVar("T")
Z = TypeVar("Z")
class Matcher(Generic[T]):
    ...

class BaseMatcher(Matcher[T]):
    ...

class BaseResolution(BaseMatcher[T], Generic[T, Z]):
    output: Z
    output_function: Callable[..., Z]

    def __init__(self, *args: object, **kwargs: object) -> None:
        cls = self.__class__
        if args and kwargs:
            self.output = cls.output_function(*args, **kwargs)
        elif args:
            self.output = cls.output_function(*args)
        elif kwargs:
            self.output = cls.output_function(**kwargs)
        else:
            self.output = cls.output_function()

class SomeMatcher(BaseMatcher[str]):
    def __init__(self, thing: str):
        self.thing = thing

def my_func(thing: str) -> Matcher[str]:
    return SomeMatcher(thing)

class Resolution(BaseResolution):
    # output: SomeMatcher
    output_function = my_func

subthing = Resolution()

reveal_type(subthing.output)  # if possible this would infer `SomeMatcher`
#

This is more aligned with my actual code. I'm afraid my first example was boiled down a little too much.

#

reveal_type is "Any". If I can make this work it should infer SomeMatcher

hearty shell
#

Even then what would you bind the K in the concatenation 🤔

fierce ridge
gusty kelp
fierce ridge
#

you're expecting it to realize that my_func returns Matcher[str], so it should infer that output is also Matcher[str] because they are both Z, right?

gusty kelp
#

Ahh.. Yeah. I tried this too:
def my_func(thing: str) -> SomeMatcher:

trim tangle
#

but no, this unfortunately cannot be inferred

gusty kelp
trim tangle
#

Maybe you can avoid creating subclasses and just instantiate it with a function? ```py
subthing = Resolution(my_func)

fierce ridge
#

or declare the type of the variable, but that's kind of a workaround

gusty kelp
#

My best option, I think, is to just annotate each of the Resolution classes.

class Resolution(BaseResolution):
    output: SomeMatcher
    output_function = my_func

Feels like that's the price to pay with that much abstraction

gusty kelp
hearty shell
#
class Foo(Generic[T]):
    y: type[T]

class SubFoo(Foo[Any]):
    y = 42

No complaints from Pyright

#

But if you change T to Any it complains

#

I wonder what is going on internally in trying to resolve T x)

gusty kelp
hearty shell
#

Yup

gusty kelp
#

I'll be frank, I dont understand what's supposed to be going on with using type[T] here

#

is that equivalent to typing.Type ?

acoustic thicket
#

yes

gusty kelp
#

and if so, doesn't that mean we're saying y should be the function int ?

hearty shell
#

Any instance of type will do

gusty kelp
#

or more accurately, the uninstantiated object of whatever T is

oblique urchin
hearty shell
#

Yes, but but maybe it has been fixed already? I exclusively use fix errors pyright website

gusty kelp
#

oh derp -- I let the y = 42 blind me. if I understand correctly, y shouldn't be set to an actual int. To be correct it should be something like y = int.

hearty shell
gusty kelp
#

still groking type hinting

oblique urchin
gusty kelp
#

So does/should the Any here have any expected affect on y?

class SubFoo(Foo[Any]):
    y = int
#

I would look at that and say Any should not be used here. While not wrong.. it's not helping

#

right?

oblique urchin
gusty kelp
#

fair.. I'll ask in a different way. When would it make sense to use SubFoo(Foo[Any]) ?

hearty shell
#

It doesn't I think, it is always implied if left empty, I just added it there to see if it perhaps would help Pyright, since if I did y: type[Any] it would correctly report an error

gusty kelp
#

oh.. of course... class Sub(Base) ==> class Sub(Base[Any]) : while not specifically always useful, it's more explicit. 💡

trim tangle
hearty shell
#

So I did remember correctly xD, I was looking for my venv that had pyright installed to double check but Jelle had already reproduced it

oblique urchin
#

pyright in vscode is really nice

autumn light
#
def bet(amount: int):
  if amount == 'max':
    amount = # all of users money
  else:
    pass
#

How would I accomplish something like this

#

I'm getting amount from a discord message, so I have to type hint it to avoid putting int(amount) every time

#

But I want to check once to see if they want to bet max

mint inlet
#

amount: int | typing.Literal['max'] ?

#

You'll need to validate it's either an int or max before passing it in though

hearty shell
#

If it is a discordpy like library command maybe it already validates it, they makes weird uses of typehints

soft matrix
#

Yeah I was about to say it supports Literal as a type hint

autumn light
#

What does that mean?

soft matrix
#

So if you put what a5rocks sent in your command signature(assuming you are on 3.10+) it should just work™️

#

Although you'll probably need to change it to use

#

!d typing.Union

rough sluiceBOT
#

typing.Union```
Union type; `Union[X, Y]` is equivalent to `X | Y` and means either X or Y.

To define a union, use e.g. `Union[int, str]` or the shorthand `int | str`. Using that shorthand is recommended. Details...
autumn light
#

do I need to import typing

soft matrix
#

Yes

#

If you are on <3.10

autumn light
#

Perfect, thank you!

autumn light
#

How would I check if this
typing.Literal['max'] is lowercase

blazing nest
#

!d typing.get_args

rough sluiceBOT
blazing nest
#

Use this to get the tuple of arguments passed

rare scarab
#

I have this decorator that auto-wraps asyncio.run when needed. How should I annotate it?


def maybe_sync(f):
    """Wraps a function and turns it into a sync call if it is not called from
    inside an coroutine.

    """

    @functools.wraps(f)
    def decorator(*args, **kwargs):
        caller = inspect.stack()[1].function
        if inspect.iscoroutinefunction(caller):
            return f(*args, **kwargs)
        return asyncio.run(f(*args, **kwargs))

    return decorator
trim tangle
#

These functions are pretty much the same but will behave very differently under your decorator: ```py
async def f1():
return await g()

def f2():
return g()

hearty shell
trim tangle
#

this is not possible

#

there's no type hint for an async function

trim tangle
hearty shell
#

Well, I mean something that would do foo() -> T or await foo() -> T

trim tangle
#

You can't know at runtime if a function always returns an awaitable object

hearty shell
#

As in the best you can do is say that it will either return an awaitable[T], or T, but then you cant safely use await

hearty shell
trim tangle
hearty shell
#

It doesnt

#

Because the caller is in a sync context

trim tangle
#

if g2 returns an awaitable, f2 returns an awaitable

hearty shell
#

yes, but not with the hacks above

#

the thing about checking if the caller is an async caller or sync caller

trim tangle
#

right, according to maybe_sync it is a sync function

trim tangle
#

well, let's wait for the OP maybe 🙂

hearty shell
#

xD

rare scarab
#

Too much complexity, not enough type support.

hearty shell
#

As much as I like bashing the type support in python

#

idk about that one

rare scarab
#

Anyway, I'm doing a new thing now. ```py

P = ParamSpec("P")
R = TypeVar("R")

def copy_param_spec(_: Callable[P, Any]) -> Callable[[Callable[P, R]], Callable[P, R]]:
def decorator(func):
return func

return decorator

class MyClient:
...
@copy_param_spec(httpx.AsyncClient.request)
async def _request(self, *args: P.args, **kwargs: P.kwargs):
r = await self.client.request(*args, **kwargs)

#

It seems to work out alright.

#

it appears to copy the __doc__ too (pylance)

trim tangle
# rare scarab Too much complexity, not enough type support.

Well, you can do this:

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

@overload
def syncify(f: Callable[P, Awaitable[T]]) -> T: ...

@overload
def syncify(f: Callable[P, T]) -> T: ...

def syncify(f):
    ...

but it will lead to some sticky situations like: ```py
def foo(t: Callable[[int, str], T]):
return syncify(t)

reveal_type(foo) # (t: (int, str) -> T@foo) -> T@foo

^ which is unsound, because if `t` is an async function, then the return value is not `T`
rare scarab
#

I think the best solution would be to have a separate module for wrapped async methods.

#

Oddly, when I hover over a usage of self._request() it shows the full documentation for httpx.AsyncClient.request. But when I Ctrl+Space it, it shows undefined

opaque shard
#

Quick question regarding this snippet of code:

@t.overload
def ab(
    a: int,
    b: str,
) -> int:
    ...

@t.overload
def ab(
    a: str,
    b: str,
) -> str:
    ...

def ab(
    a: t.Any,
    b: str,
) -> t.Any:
    ...
```Is it intentional/expected that pylance is unable to infer the correct overload here? (Note that the snippet is just a dumbed down version of something I do actually need overloads for, but this was just the shortest way I could reproduce it.)
soft matrix
#

It should it just shows you both when you're typing

trim tangle
oblique urchin
#

I guess in theory it has enough information to figure out what the right overload is here

soft matrix
#

If you press the down key you should see the second overload

opaque shard
#

ah alright, for whatever reason I was convinced it would show the 'correct' overload

trim tangle
#

it will scroll through depending on the number of arguments, though

opaque shard
#

mhm, alright, thanks ^^

drowsy spade
#

s

fierce ridge
#

hy code is hideously ugly with type annotations

#
(defn ^None write-file
  [^(| str PathLikeStr) filename-or-path
   ^(| str bytes) content
   ^bool [text True]
   ^bool [append False]]
  (setv mode (if append "a" "w"))
  (when (isinstance content bytes)
    (setv mode (+ mode "b")))
  (with [fp (open filename-or-path mode)]
    (.write fp content)))
rustic gull
#

whats the difference between typing.Awaitable and typing.Coroutine ?

fierce ridge
#

you can define __await__ on anything, which makes it an Awaitable

#

whereas Coroutine is more specific

rustic gull
#

ic thank you!

deft merlin
#

why does it say that, its clearly a tuple of embeds

oblique urchin
deft merlin
#

oh

oblique urchin
#

your return annotation should be tuple[Embed, ...]

deft merlin
#

ok lol

#

thanks

#

so if i have a dict {'a': 'b', 'c': 'd'} i would annotate it like dict[str, str, ...]?

hearty shell
#

No, dict[str, str], where dict[key_type, value_type]

deft merlin
#

k

oblique urchin
#

the ... thing is just for tuples. Tuples are treated specially because it's common to use them for heterogeneous data

grave fjord
#

Would be nice to have heterogeneous support for more than tuples eg dict[k,v,k2,v2] q.q

rustic gull
#

is there a setting to not infer things as Literal values in pyright? this passes in mypy without the type hint in queue, but not pyright because pyright thinks it's List[Tuple[Optional[TreeNode], Literal[0]]]

    def verticalOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        ans = defaultdict(list)
        queue: List[Tuple[Optional[TreeNode], int]] = [(root, 0)]
        for node, k in queue:
            if node:
                ans[k].append(node.val)
                queue.append((node.left, k - 1))
                queue.append((node.right, k + 1))
        return [ans[k] for k in sorted(ans)]
soft matrix
#

That should pass on pyright

#

Literal[0] is a subclass of int

hearty shell
#

I think they mean without the type hint

soft matrix
#

Can you show the error then?

hearty shell
#

Without it pyright assumes Literal[0] so you can no longer append just any number

#

I would assume this:

Argument of type "tuple[TreeNode, int]" cannot be assigned to parameter "__object" of type "tuple[TreeNode | None, Literal[0]]" in function "append"
  Tuple entry 2 is incorrect type
    "int" cannot be assigned to type "Literal[0]"

But lets see what OP says x)

rustic gull
#

yes that's the error message for both the queue.append lines

#

the type hint tells pyright to infer the general int case, but that's kind of annoying to have to tell it

#

(if I understand it correctly)

hearty shell
#

Yeah, with tuples pyright infers it down to literals, I think it is due to their immutability so it feels confident to do so, but then when inferring the list, it just does list[whatever_this_was], which in this case is tuple[TreeNode | None, Literal[0]] . I had a look at pyright configs and there doesnt seem to be a way to change that

peak echo
#

How can I typehint an instance that is created with with?
For example

async with pool.acquire() as con:
    con.execute(...)```
`con` is not typehinted by default
trim tangle
#

also yes, asyncpg's types are not very good 😔

trim tangle
#

well, you can always make a PR 🙂

#

unless you are lazy like me

soft matrix
#

it already has a pr for type hints

#

it just hasnt been merged

soft matrix
#

id like a TypedMapping

#

name is a bit weird cause TypedDict is already a mapping but TypedDict would just be defined as```py
class TypedDict(TypedMapping):
pass

onyx kayak
#

Hello all, how should i write type hinting for example below:

        def func(param: int) -> int | str:
            if param > 50:
                return 100
            else:
                return 'poop'

        param = 100

        if param > 50:
            result: int = func(param)

With current code I have the following error.

Incompatible types in assignment (expression has type "Union[int, str]", variable has type "int")mypy(error)
#

Is there a way to limit expected type to specific types while using Optional[] ?

hearty hemlock
trim tangle
#

that's a really strange function if you ask me

#

why do you need this?

onyx kayak
# hearty hemlock Mypy isn't smart enough to see that func would return an int in this case I thin...

I have tried using it for my original issue, unfortunately this time I receive a different mypy error: ```Invalid type comment or annotation

```py
    def test_successfulUserQuery(self, instantiateRepo: TransactionRepo):
        """Check if query return values are correctly cast to User class

        Args:
            instantiateRepo (TransactionRepo): fixture instantiating repo
        """

        # Arrange
        expected = instantiateRepo.cur.fetchall.return_value = (
            1,
            "ZaiZu",
            "password",
            "John",
            "Smith",
        )

        # Act
        user: typing.cast(User, instantiateRepo.userQuery('ZaiZu')) = instantiateRepo.userQuery('ZaiZu') # mypy error

In this userQuery call I'm mocking my DB response, so it will always return a User

    def userQuery(self, username: str) -> User | None:
        """Query the DB with username to check if corresponding user exists

        Args:
            username (str): username

        Returns:
            User | None: User instance if user exists, None if not
        """
onyx kayak
acoustic thicket
#

the type of user isnt typing.cast(User, instantiateRepo.userQuery('ZaiZu')), its just User

#

user: User = typing.cast(User, instantiateRepo.userQuery('ZaiZu'))

#

tbf the annotation is kinda redundant here, just user = typing.cast(User, instantiateRepo.userQuery('ZaiZu')) is good enough imo

onyx kayak
#

Yup, my bad. That's a stupid syntactic mistake

hearty hemlock
#

though it may be better if you explicitly check if user is None

soft matrix
#

Typing.cast just let's the type checker know that you know best

#

If you're having to use it, it's usually a bad sign

onyx kayak
# soft matrix If you're having to use it, it's usually a bad sign

so is output (Optional[object, None]) of functions/methods like mentioned above userQuery inherently bad? userQuery is called in other methods and the User object it returns is processed, but only after explicit check that the return value is not None. Unfortunately this forces me to use typing.cast in any such function.

blazing nest
#

You could raise an error instead

#

One might even argue that it's more Pythonic

trim tangle
#

@onyx kayak If you want to check specifically for None, you can do assert user is not None

#

or maybe raise an error as Bluenix suggested

#

or have two methods, like get_user(name: str) -> User (raises error) for cases when you're mostly sure the user exists; and try_get_user(name: str) -> User | None when the two outcomes are equally likely

onyx kayak
#

Yes, I can see now that handling it with exceptions sounds better. Thank you all for help!

soft matrix
#

anyone know what this error means?

#

condition is (*Ts) -> bool

hasty hull
#

I don't know the reasoning behind it but you're only allowed to unpack args if the args itself is also a variadic arg

soft matrix
#

thats dumb

hasty hull
#

yeah I have no idea why ¯_(ツ)_/¯

hearty shell
#

🤔

#

Is it in the pep? It doesnt even give a reason

soft matrix
#

!pep 646

rough sluiceBOT
#
**PEP 646 - Variadic Generics**
Status

Accepted

Python-Version

3.11

Created

16-Sep-2020

Type

Standards Track

hearty shell
soft matrix
#

i cant find anything when i just do a search for *args but i still i dont think have read all of 646

hearty shell
#

I don't think it is in there, sounds like something that was discussed afterwords and a conclusion was reached that it would either cause problems or wasnt worth it x)

#

Or maybe it is an implicit consequence of one of the "Not allowed"s in the pep

hasty hull
#

Maybe it would've just complicated the code too much and it wasn't explicitly supported in the PEP

devout barn
#

apparently typechecker isn't happer assigning int(bool(...)) with Literal[0, 1]
is there anything that I am doing wrong here?

    async def exists(self, key) -> Literal[0, 1]:
        async with await self.__lock__():
            return int(key in self.__destinations__()) # 1 or 0

Expression of type "int" cannot be assigned to return type "Literal[0, 1]"
  Type "int" cannot be assigned to type "Literal[0, 1]"
    "int" cannot be assigned to type "Literal[0]"
    "int" cannot be assigned to type "Literal[1]"
  • pyright

Since construct int from a bool will always be either a 0 or a 1

hearty shell
#

That information is lost when you do int(...)

#
def foo(a: Literal[0]): ...

foo(int())  # Argument of type "int" cannot be assigned...
trim tangle
#

I don't think it's worth including such special cases in typeshed.

#

Why not just return bool?

#

(also, IIRC making your own dunder methods is discouraged, since these attributes can get added for internal use later)

hearty shell
#

reveal_type(1 ** 26) # Reveal type Any 🥲

hearty shell
#

Who is more correct here?

from typing import Protocol


class Foo(Protocol):
    x: int
    y: str
    
class Bar(Protocol):
    x: bool
    y: str
    z: float

def f(a: Foo): ...

def g() -> Bar: ...

x = g()

f(x)

Mypy:

error: Argument 1 to "f" has incompatible type "Bar"; expected "Foo"
note: Following member(s) of "Bar" have conflicts:
note:     x: expected "int", got "bool"

Pyright:

# No errors
#

Is there any reason that structural subtyping should not include subtyping of members?

terse sky
#

I mean its a mutable field

#

So subtyping wouldn't be enough

acoustic thicket
#

yea, pyright is wrong here i think

terse sky
#

You could add the frozen annotation

#

dataclass frozen that is

#

And see if that matters

#

Alright I would personally do it with a custom class

hearty shell
terse sky
#

Not with int and bool

#

Because that's a horror that mypy for example could choose to ignore

blazing nest
#

Setting an int to a bool field

terse sky
#

I mean regardless of what python says I think it's awful both ways

#

Hopefully static type checkers complain about both

#

Looks like mypy doesn't complain

#

Sad

blazing nest
#

I wonder if it would be nice to have immutable types, that means that stuff like this could be marked.. although it would probably still not pass even by Mypy because it has probably not implemented it 😂

terse sky
#

Well you can have a frozen dataclass

#

That will probably suffice

#

It's not really about immutable types

brisk hedge
#

Recursive = dict[K, SomeTypedDict | "Recursive"]? There's not really enough info to give a better solution

#

I think pyright supports recursive types

flint sonnet
#

Hi, is there a quicker way to do things depending on a bool quicker than py value = db.query(models....) if not value: raise HTTPException(status_code=404, detail="not found") return value I would imagine something like this but it doesnt work: ```py
return db.query(models....) | raise HTTPException(status_code=404, detail="not found")

fierce ridge
#

however you can use the assignment expression to make the if not form more succinct:

if not (value := db.query(models....)):
    raise HTTPException(status_code=404, detail="not found")
return raise:

idk if that's better though. the first form would be idiomatic and preferred imo:

value = db.query(models....)
if not value:
  raise HTTPException(status_code=404, detail="not found")
return value
#

(this isn't really a #type-hinting question, unless i misunderstand the question)

fierce ridge
#

mypy and pyright both don't know how to handle descriptors:

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

T = TypeVar('T')
S = TypeVar('S')

class Constant(Generic[S, T]):
    def __init__(self, value: S) -> None:
        self.value = value
    def __get__(self, obj: T | None, typ: type[T]) -> S:
        return self.value
    def __set__(self, obj: T, val: S) -> NoReturn:
        raise TypeError('Constant attribute cannot be set.')
    def __del__(self, obj: T) -> NoReturn:
        raise TypeError('Constant attribute cannot be set.')

class Thing:
    x: int = Constant(1)  # ERROR

is this feature on the roadmap for either of the two type checkers? or one of the other ones (pyre?)

oblique urchin
fierce ridge
#

it still doesn't like that though, because Thing.x "is" an int

oblique urchin
#

yes sorry, got the type params wrong

#

why do you need the annotation?

fierce ridge
#

hm... doesn't it not work anyway?

#

or is that supported now?

oblique urchin
#

mypy and pyright both have some descriptor support

fierce ridge
#

mypy says this

class Thing:
    x = Constant(1)

Thing().x + 5
main.py:27: error: Need type annotation for "x"
#

oh wow! it works if i do

class Thing:
    x = Constant[int, 'Thing'](1)

Thing().x + 5
oblique urchin
oblique urchin
fierce ridge
#

i see... but then won't the types be all wonky in help() etc?

#

ah, reveal_type actually says int for Thing().x

#

it'd be nice if Protocol supported optional methods

#

then you could have a Descriptor protocol for such annotations, so you can write an explicit annotation if you wanted

#

that or just a special typing.Descriptor

spare flume
#

hey! can someone please help me fix this

#

    def arange(self, start: float, stop: float, step: Union[float, None]) -> None:
        """Fill in 'values' with range of values."""
        assert step != 0
        while start < stop:
            self.values.append(start)
            if isinstance(step, float):
                start += step
            if isinstance(step, None): 
                start += 1```
#

it says that "none" cannot be assigned to a parameter

hearty shell
#

It can be a bit misleading but type checkers only allowed you to use None in the same way as you do say int, float for convenience

#

None is not a type, if you try to do isinstance(step, None) at runtime that will always throw an exception

#

You want to do if step is None

spare flume
#

so

#

none is not a type?

hearty shell
#

Nope, NoneType is, and None is its only instance

#

Technically you could do if isinstance(step, NoneType)

#

But for checking None, it is not the normal way of doing it

spare flume
#

uhh.. so then what's the difference between NoneType and None?

#

i had no idea NoneType was a thing

hearty shell
#

if you do type(None) you will see "NoneType", you can also find it in the types module from types import NoneType.

uhh.. so then what's the difference between NoneType and None?
Think of the type bool, True and False are it's only instances, but there is a difference between True and bool. None is the same, just instead of having two instances, it only has one, so it is a singleton

spare flume
#

oooh i see

#

okay so

#

that makes more sense

#

that's the error i have

#

i changed it to an else. instead of isinstance(step, None)

hearty shell
#

step: Union[float, None], you need to make that a default argument if you dont want to have to do positive.arange(1.0, 5.0, None)

#

..., step: Union[float, None] = None)

spare flume
#

this is what i ve got

#
   def arange(self, start: float, stop: float, step: Union[float, None]) -> None:
        """Fill in 'values' with range of values."""
        assert step != 0
        while start < stop:
            self.values.append(start)
            if isinstance(step, float):
                start += step
            else:
                start += 1```
hearty shell
#

No, you have step: Union[float, None]

spare flume
#

thats what you said?

#

oh you said i need to change taht

hearty shell
#
   def arange(self, start: float, stop: float, step: Union[float, None] = None) -> None:
        """Fill in 'values' with range of values."""
        assert step != 0
        while start < stop:
            self.values.append(start)
            if isinstance(step, float):
                start += step
            else:
                start += 1
#

You want this

spare flume
#

why would i default it to none?

acoustic thicket
hearty shell
# spare flume

You define 3 positional arguments, and then you ask why the ide is unhappy when you call it with only 2 :P

spare flume
#

oh that makes sense XD

#

yeah b/c it requires them

#

they're arguments

#

_< thank you!

rough sluiceBOT
#

stdlib/builtins.pyi lines 198 to 199

_PositiveInteger = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
_NegativeInteger = Literal[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20]```
rough sluiceBOT
#

stdlib/builtins.pyi lines 246 to 258

def __pow__(self, __x: int, __modulo: Literal[0]) -> NoReturn: ...
@overload
def __pow__(self, __x: int, __modulo: int) -> int: ...
@overload
def __pow__(self, __x: Literal[0], __modulo: None = ...) -> Literal[1]: ...
@overload
def __pow__(self, __x: _PositiveInteger, __modulo: None = ...) -> int: ...
@overload
def __pow__(self, __x: _NegativeInteger, __modulo: None = ...) -> float: ...
# positive x -> int; negative x -> float
# return type must be Any as `​int | float`​ causes too many false-positive errors
@overload
def __pow__(self, __x: int, __modulo: None = ...) -> Any: ...```
acoustic thicket
#

ahh right

#

for negatives int**int would have to be float

hearty shell
#

Yeah, we would need some predicates for this to work

#

Although I guess it could return an union as a fallback

acoustic thicket
#

interesting how other languages handle this

#

rust's pow for ints accepts only an unsigned int for the exponent

#

java accepts both args as doubles and returns a double

#

haskell's ^ just errors out for a negative exponent

hearty shell
#

Haskell doesn't let you raise an int no?

hearty shell
acoustic thicket
hearty shell
#

Oh right, ^

#

Jesus it has 3 exponentiation operators

#
(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a
acoustic thicket
#

oof

rapid karma
#

typing.Union[str, int] == str | int?

buoyant swift
#

yes

rapid karma
#

Just that unions is for before python 3.10?

buoyant swift
#

yeah

rapid karma
#

Right ok 👍

fierce ridge
hearty shell
#

A little bit of ad hoc never hurt no one xD

#

You are right though

fierce ridge
#

well a lot of things in haskell are ad-hoc

#

so many name collision workarounds

#

friendly reminder that idris allows overloading as long as the definitions are in 2 separate namespaces 🙂

nocturne dew
#

Actually i never understood from future import annotations does this mean I can use int | None in place of Union [int,None] in python3.9

hearty shell
fierce ridge
# hearty shell Is that even overloading at that point?

yeah because you can use them all in the same namespace

Data.Nat.Exponentiation.pow : Nat -> Nat -> Nat
  Totality: total
  Visibility: public export

Prelude.pow : Double -> Double -> Double
  Totality: total
  Visibility: public export
hearty shell
#

Ahhhh

#

Just the definitions

fierce ridge
#

they don't really have the "numerical tower" fleshed out well though. e.g. i don't think there's a generic Integral+Fractional exponent operator like haskell has (you'd have to write your own probably, unless i just didn't find it in the source/docs)

#

idris 2 is v0.5.x still, lots of room for improvement (and prs welcome :P)

hearty shell
fierce ridge
# hearty shell Just the definitions

yeah sorry that wasn't clear. you can do this for example

module Silly

namespace ForDouble
  addOne : Double -> Double
  addOne x = x + 1.0

namespace ForInteger
  addOne : Integer -> Integer
  addOne = (+ 1)
fierce ridge
#

also imo idris is a "better haskell" when you look at its standard libraries and prelude. and it has compiler backends for chez scheme, racket, and javascript, so you can actually do web dev in idris 2 (and there are some community members who are focused specifically on web dev in idris 2)

#

there's also a python backend taking shape!

#

there is also a reference-counting C compiler target, but imo it's great that it compiles to all these "high level" targets which makes it a lot easier to use in a lot of cases i think (and chez in particular is ridiculously fast and will sometimes beat c anyway)

#

there was a lua target (which could be run with the also-impressive luajit) but i don't think it's maintained and has fallen badly out of date

hearty shell
fierce ridge
#

yeah, see all the functions added for applicatives that already had names defined for monads

soft matrix
brisk hedge
#

I mean makes sense considering haskell is kinda old

fierce ridge
#

exactly. apparently people have the same feeling about purescript, its prelude and standard library were a chance for a "fresh start" and it is a lot more friendly to use

#

i think there are alternative haskell preludes but they never caught on as far as i know

oblique urchin
#

you need to use overloads

hearty shell
#

Interesting, pyright does not like that sum(values, start=start)

oblique urchin
#

one overload like that, another that takes start: Literal[0] = ... and returns Callable[..., Literal[0]]

hearty shell
#

You would do this

@overload
def wrapped_summation() -> Callable[..., int]: ...

@overload
def wrapped_summation(start: T) -> Callable[..., T]: ...

def wrapped_summation(start=0):
    """
    Wrapped summation function.
    """
    
    def inner_func(*values):
        return sum(values, start=start)

    return inner_func
#

But because you had inner func annotated, I would assume you would also want the Callable[..., T] annotated with receiving arguments of type T, but that is not possible in Mypy right now, for that matter I am not sure you can in other type checkers as well

fierce ridge
#
from typing import Protocol, TypeVar

S_contra = TypeVar('S_contra', contravariant=True)
T_co = TypeVar('T_co', covariant=True)

class VariadicCallable(Protocol[S_contra, T_co]):
    def __call__(self, *args: S_contra, T_co) -> T_co: ...

def wrapped_summation(start: S_contra = 0) -> VariadicCallable[S_contra, S_contra]: ...

reveal_type(wrapped_summation(1))
reveal_type(wrapped_summation(0.0))
reveal_type(wrapped_summation(""))

other than this error, it works:

main.py:9: error: Incompatible default for argument "start" (default has type "int", argument has type "S_contra")
#

i'm not sure what it's actually complaining about. wouldn't int just be the inferred type of start when no arg is passed?

#

pyright gets it right

#

is that error a bug in mypy?

hearty shell
#

Humm, I dont think so, because you arent suppose to be able to use defaults like that in generic functions I think

#

If you do wrapped_summation()(1,2,3) pyright complains

fierce ridge
#
error: Argument missing for parameter "T_co" (reportGeneralTypeIssues)

that's not the error i expected

hearty shell
#

Wasnt it supposed to be def __call__(self, *args: S_contra) -> T_co: ...

fierce ridge
#

oh, yes

#

does this work differently if i use a separate type stub?

#

like would that work?

#
error: Argument of type "Literal[1]" cannot be assigned to parameter "args" of
 type "S_contra@wrapped_summation" in function "__call__"
#

is that because it's contravariant?

#
x: list[int] = [1, 2, 3]
wrapped_summation()(*x)
error: Argument of type "int" cannot be assigned to parameter "args" of type "
S_contra@wrapped_summation" in function "__call__"
#

huh, i wonder why that's not allowed?

hearty shell
#

I think it has to do with the fact that when you call the VariadicCallable, it has not resolved S_contra to anything

#

It doesnt understand (start: T = 0) -> VariadicCallable[T, T] as (start: T = 0) -> VariadicCallable[int, int] when called with no arguments

hearty shell
#

def wrapped_summation(start: T) -> Callable[[Unpack[tuple[T, ...]]], T]: ...

#

But it ends up allowing any type regardless

fierce ridge
#

why isn't start() equivalent to start(0)?

oblique urchin
fierce ridge
#

interesting, it seems like it should be straightforward, but i trust the opinion of people who actually do these things

oblique urchin
#

Well I did implement this behavior in pyanalyze and it was pretty straightforward 🙂 Maybe I missed something though

hearty shell
#

Maybe it just has to do pyright's current way of resolving things, the moment when typevars are "stored" and default arguments's types are inferred are at different stages? One of the current pyright issues has the same "seems straightforward" but goes against the current architecture

hearty shell
#

Idk how much of a difference that makes anyway xD

oblique urchin
rustic gull
fierce ridge
stray summit
#

hi, anyone aware of a easy way to have "computed protocols" - i have a "sans" api for creating http requests/parsing the resulting response, and i want to create typesafe sync/async wrappers around it using sync/async http libs

fierce ridge
#

i would love the ability to derive a protocol from a concrete class, if that's what you're asking. i am not aware of that feature in any type checker, though

zinc fiber
#

'''
in_types_1 = [str, int]
in_types_2 = float

def something(input: Union[in_types_1 , in_types_2])
'''

#

how to do the above?

soft matrix
#

even though those values are constants its hard to type this without repeating yourself

soft matrix
#

but it would definitely be a welcome addition

fierce ridge
soft matrix
#

You can technically make this work by doing SomethingType = Union[str, int, float]
in_types_1 = SomethingType.args[:-1]
in_types_2 = SomethingType.args[-1]
def something(input: SomethingType):

#

But I wouldn't advise that, you might just be better repeating yourself

#

Although maybe you can if you use a tuple and unpack it using a type checker that supports pep 646

hearty shell
soft matrix
#

Unpack them?

#

Union[int, str, float]

hearty shell
#

But doing a union of a union already does that

#

Or you mean, grammatically

soft matrix
#

It's a list of types not a Union?

hearty shell
oblique urchin
blazing nest
#

Yay for data class transforms!

oblique urchin
#

wait it was accepted?

blazing nest
#

I don't think so but the PEP was announced (meaning that it's moving forward)

oblique urchin
#

ah yes, I guess we missed that before

blazing nest
rough sluiceBOT
#

typing_extensions/src/typing_extensions.py lines 1793 to 1802

def dataclass_transform(
    *,
    eq_default: bool = True,
    order_default: bool = False,
    kw_only_default: bool = False,
    field_descriptors: typing.Tuple[
        typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]],
        ...
    ] = (),
) -> typing.Callable[[T], T]:```
blazing nest
#

Afterall, Python doesn't fail because you defined an unknown dunder.

soft matrix
#

Hasn't been updated yet?

oblique urchin
#

because my PR adding that hasn't been merged yet 🙂

#

should do that soon

blazing nest
#

Well thank you for your efforts 😁

oblique urchin
soft matrix
#

was there ever a time where List.__origin__ == List?

#

I can't go back to 3.6 to check if it was unfortunately

oblique urchin
soft matrix
#

whaaa

#

is this in base 3.6 or all of 3.6?

oblique urchin
#

I'm on 3.6.10

soft matrix
rough sluiceBOT
#

src/betterproto/__init__.py lines 891 to 894

if hasattr(t, "__origin__"):
    if t.__origin__ in (dict, Dict):
        # This is some kind of map (dict in Python).
        return dict```
soft matrix
#

or could you do typing.Dict[str, int]()?

oblique urchin
oblique urchin
soft matrix
#

what a mess

oblique urchin
soft matrix
#

can you do typing.List[int]() I'd guess not?

oblique urchin
soft matrix
#

IDK how this code worked back then then

#

ok thanks Jelle

minor hedge
#

is Literal[x, y] different from Literal[x] | Literal[y]?

pastel egret
#

No, it's a convenient shorthand.

minor hedge
#

pyright sometimes doesn't like it for some reason and will flag this with an error 🤔
just pyright being bad?
formats: Sequence[Literal['png', 'jpg', 'webp', 'gif']] = ('png', 'jpg', 'webp', 'gif') if banner.is_animated() else ('png', 'jpg', 'webp',)

minor hedge
#

' | '.join(str(asset.with_format(f)) for f in formats)

acoustic thicket
#

str(asset.with_format(f)) is no longer a Literal nvm

minor hedge
#

asset.with_format expects literal['webp', 'gif', 'png', 'jpeg', 'jpg']
f should be from what's defined above but it sometimes complains that it's a str

trim tangle
#

This channel is about type annotations. If you have a general Python question, you should see #❓|how-to-get-help and claim a help channel

dim forge
#

my bad

blazing nest
#

I have a pair of overloads that look like this: ```python
@overload
async def auth(self, username: Union[str, bytes], password: Union[str, bytes]) -> str:
...

@overload
async def auth(self, password: Union[str, bytes]) -> str:
    ...
How would I write the implementation?
#

I originally had this... ```python
async def auth(self, username: Union[str, bytes], password: Union[str, bytes, None] = None) -> str:
# If password is None the username is treated as the password

...but as you can tell, if someone calls it with keyword arguments it'll fail
#

I can always use *args and **kwargs and handle things, but I would prefer not to

void panther
#

When I had something similar I used *args **kwargs verified by an inspect.Signature

hearty shell
#

I would just make both arguments default to None and raise the type error myself

#

If you pass no arguments the type checker will complain anyway as there is no overload implementation for no arguments

void panther
#

Rising a nice/proper error yourself can get annoying

blazing nest
trim tangle
#

then you can do username: Union[str, bytes, None] = None

#

don't see any harm in this

#

auth("foo", "bar") looks like f(true, true, false, null, null, "yes") to me 🙂

void panther
blazing nest
void panther
#

If it succeeds you can access them by name

blazing nest
grave fjord
#

you could also make them positional only

hearty shell
pastel egret
#

Also you could just rely on the type checker to verify.

grave fjord
void panther
grave fjord
#
from typing import overload


@overload
def auth(*, username: str | bytes, password: str | bytes) -> str:
    ...


@overload
def auth(*, password: str | bytes) -> str:
    ...


@overload
def auth(first: str | bytes, second: str | bytes, /) -> str:
    ...


@overload
def auth(first: str | bytes, /) -> str:
    ...


def auth(
    first: str | bytes | None = None,
    second: str | bytes | None = None,
    /,
    *,
    username: str | bytes | None = None,
    password: str | bytes | None = None,
) -> str:
    print(f"{first=} {second=} {username=} {password=}")
    match (first, second, username, password):
        case (None, None, None, None):
            raise TypeError("bad")
        case (first, None, None, None):
            return _clean_auth(username=first, password=first)
        case (None, second, None, None):
            raise TypeError("bad")
        case (first, second, None, None):
            return _clean_auth(username=first, password=second)
        case (None, None, username, None):
            raise TypeError("bad")
        case (None, None, None, password):
            return _clean_auth(username=password, password=password)
        case (None, None, username, password):
            return _clean_auth(username=username, password=password)

    raise TypeError("bad")


def _clean_auth(username: str | bytes, password: str | bytes) -> str:
    print(f"{username=} {password=}")
    return ""
#

might want to handle case (first, None, None, password): too

#

with:

@overload
def auth(first: str | bytes, /, *, password: str | bytes) -> str:
    ...
blazing nest
trim tangle
#
def auth(*, username: str | None = None, password: str) -> str:
    ...
``` simple and idiomatic 🙂
#

if someone has a bytestring, they can decode it as they wish

blazing nest
fierce ridge
#

but in that case why accept a string at all?

#

ok well i see why

#

it's really frustrating that the "combined" types can't be inferred from the overloads

trim tangle
blazing nest
#

It's sent to Redis, so it works like usual.

trim tangle
#

why would you use a non-unicode username and password?

fierce ridge
#

is it not possible to use Literal with an enum member?

#
from enum import Enum
from typing import Literal

class Sentinel(Enum):
    Empty = object()

Empty = Sentinel.Empty

x = Weird
reveal_type(x)

y: Sentinel = Empty

z: Literal[Empty] = Empty
main.py:10: note: Revealed type is "__main__.Sentinel"
main.py:14: error: Parameter 1 of Literal[...] is invalid
main.py:14: error: Variable "__main__.Weird" is not valid as a type
main.py:14: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
Found 2 errors in 1 file (checked 1 source file)
oblique urchin
fierce ridge
#

pyright gets it right of course 😛

No configuration file found.
No pyproject.toml file found.
Assuming Python platform Linux
Searching for source files
Found 1 source file
/home/runner/PyrightPlayground/e2403359.py
  /home/runner/PyrightPlayground/e2403359.py:10:13 - info: Type of "x" is "Literal[Sentinel.Empty]"
0 errors, 0 warnings, 1 info 
Completed in 3.185sec
#
from enum import Enum
from typing import Literal

class Sentinel(Enum):
    Empty = object()

Empty = Sentinel.Empty

v = Empty
reveal_type(v)
x: Sentinel = Empty
y: Literal[Sentinel.Empty] = Empty
z: Literal[Sentinel.Empty] = Sentinel.Empty
main.py:10: note: Revealed type is "__main__.Sentinel"
main.py:12: error: Incompatible types in assignment (expression has type "Sentinel", variable has type "Literal[Sentinel.Empty]")
Found 1 error in 1 file (checked 1 source file)
blazing nest
wicked berry
#

How can I type a lambda? 🤔

dumbpw/engine.py:102:15: error: Cannot infer type of lambda  [misc]
        validator=lambda charset, length: len("".join(charset)) > 0,
                  ^
dumbpw/engine.py:110:15: error: Cannot infer type of lambda  [misc]
        validator=lambda charset, length, result: all(char in "".join(charset) for char in result),
                  ^
dumbpw/engine.py:110:15: error: Argument "validator" to "ensure" has incompatible type "Callable[[Any, Any, Any], bool]"; expected
"Callable[[NamedArg(str, 'charset'), NamedArg(int, 'length'), str], Union[bool, str]]"  [arg-type]
        validator=lambda charset, length, result: all(char in "".join(charset) for char in result),
                  ^
Found 3 errors in 1 file (checked 1 source file)
rotund comet
#

If you have to type hint lambda, you're doing something wrong. Just write regular function def instead

rotund comet
#

lambda doesn't support type hinting

#

not out of the box anyway

#

quick-and-dirty doesn't really play well with typing

wicked berry
#

so is there any solution besides # type: ignore ?

wicked berry
soft matrix
#

You need to make the args for the lambda have the same names don't you?

blazing nest
#

Yeah I think that's the only reasonably verifiable part, the typehints should be treated as unannotated and implicitly Any.

grave fjord
#

I'd like lambda x: x.y.z().k to get a fancy automatically generated

class _HasY(Protocol, Generic[T]):
    @property
    def y(self) -> T: ...

class _HasZ(Protocol, Generic[T]):
    def z(self) -> T: ...

class _HasK(Protocol, Generic[T]):
    @property
    def k(self) -> T: ...

Callable[_HasY[HasZ[_HasK[T]]], T]
wicked berry
soft matrix
#

maybe you just need a callback protocol and mypy doesnt support NamedArg anymore? I can't see anything else wrong with it

terse trellis
#

Hello!, What would be the best way to create attributes in a python class, I always have this question.

Currently I use the self.name_attribute = 'something'
self.attribute_name2 = None

hasty hull
#

Or just like this: ```py
class Foo:
def init(self, attr2: str | None) -> None:
self.attribute_name2 = attr2

#

the difference is that type checkers will have to infer the type of attribute_name2 in the second example

#

where in the first one you are being explciit

#

if you're not setting the attribute directly from an argument you can do something like this: ```py
class Foo:
def init(self) -> None:
self.attribute_name2: str | None = None

rare vapor
#

I'm trying to come up with a protocol that will match a class that has descriptors for its properties because it seems that read-only descriptors don't match read-only properties on protocols... the problem is that I'm hitting issues with my protocol... here's what I have:

_T_co = TypeVar('_T_co', covariant=True)
_D = TypeVar('_D', bound='Descriptor')

class Descriptor(Protocol[_T_co]):
    @overload
    def __get__(self: _D, instance: None, owner: Any) -> _D:
        ...

    @overload
    def __get__(self, instance: object, owner: Any) -> _T_co:
        ...

class DescriptorRecord(Protocol):
    name: ClassVar[Descriptor[str]]
#

However, when I use this protocol with a class that has a class-level descriptor, it won't match:



# from sqlalchemy-stubs:


class TypeEngine(Generic[_T_co]):
    @property
    def python_type(self) -> type[_T_co]:
        ...


class String(TypeEngine[str]):
    @property
    def python_type(self) -> type[str]:
        return str


class Column(Generic[_T]):
    type: TypeEngine[_T]

    def __init__(self, type_: TypeEngine[_T]) -> None:
        self.type = type_

    @overload
    def __get__(self, instance: None, owner: Any) -> Column[_T]:
        ...

    @overload
    def __get__(self, instance: object, owner: Any) -> _T:
        ...

    def __get__(self, instance: Any, owner: Any) -> Column[_T] | _T:
        if instance is None:
            return self

        # some black magic happens here
        return cast(_T, {})


# end from sqlalchemy-stubs


class SARecord:
    __tablename__ = 'things'

    name: ClassVar[Column[str]] = Column(String())


def process_record(record: DescriptorRecord) -> None:
    ...

process_record(SARecord()) # error
#

(for the record, I'm typing this descriptor because read-only descriptors don't seem to match read-only properties on protocols, either)

terse trellis
long moss
#

What is the correct way of type hinting this? This: python class MyClass: def __init__(self, arg_1: int, arg_2: float) -> None: self.arg_1 = arg_1 self.arg_2 = arg_2 or python class MyClass: def __init__(self, arg_1: int, arg_2: float) -> None: self.arg_1: int = arg_1 self.arg_2: float = arg_2?

rare vapor
#

@long moss the latter

long moss
#

Ok thanks!

hearty shell
#
class A(Protocol):
    a: float
     
class B(Protocol):
    a: A

class Foo:
    a: float

class Bar:
    a: Foo


def foo(x: B): ...

x: Bar
foo(x)  # Error
rare vapor
#

uhhh... that seems... odd

hearty shell
#

I am not sure what is the harm here... It has to do with the fact that because those fields are mutable they should be invariant, but I am not sure how that works when the field itself is structurally typed

rare vapor
#

ultimately, I'm trying to figure out how to accept an object that can have the operation obj.prop done on it and return a string

#

it doesn't matter if it's a descriptor or a read-only attribute or a mutable attribute

#
class A(Protocol):
    @proptery
    def prop(self) -> str:
        ...

This works for most objects except descriptors

rare vapor
hearty shell
#

Yeah, the deeper issue here seems invariance on the attributes, seems that the @property route was just a 'hack' so you could have classes that had attributes of subclasses of that, but it is probably special cased, I think it is a blind spot on typechkers

#

I think that might be related but I am not sure

rare vapor
#

seems close

#

and I notice that @oblique urchin marked it... I wonder what his thoughts are on all of this

#

(sorry for pinging you... also, hi... we used to talk on the typing gitter)

oblique urchin
#

I probably just searched for "property" in the tracker and marked everything 🙂

rare vapor
#

hah

oblique urchin
#

Don't have any interesting thoughts, I'd need to look at this more deeply. Maybe we need a more principled way to indicate "read-only attribute" in a protocol

oblique urchin
rare vapor
#

my day job is working with TypeScript, so sometimes I try to make Python's type system do too much

oblique urchin
#

oh interesting, does discord.py do runtime introspection of type hints?

soft matrix
#

yep

#

mainly for parsing text into types in commands

rough sluiceBOT
#

discord/ext/commands/core.py line 450

self.params: Dict[str, Parameter] = get_signature_parameters(function, globalns)```
hearty shell
#
@dataclass
class A:
    a: int

@dataclass
class B(A):
    a: bool
    
def foo(x: A):
    x.a = 3

b: B = B(True)
foo(b)

reveal_type(b.a)  # Revealed type is "builtins.bool"
b.a # int at runtime
rare vapor
#

I'm still floored that it doesn't work

hearty shell
#

I think It doesnt work because of this

class A(Protocol):
    a: float
     
class B(Protocol):
    b: A

class Foo:
    a: float = 0.0
    c: int = 42

class Baz:
    a: float = 3.14

class Bar:
    b: Foo


def foo(x: B):
    x.b = Baz()  # This respects that B.b should be subtype of A

x: Bar = Bar()
foo(x)  # Maybe fine

x.b.c  # Runtime Error
rare vapor
#

hmm

hearty shell
#

I wonder if typescript just ignores this kind of error, one of their non goals is

Apply a sound or "provably correct" type system. Instead, strike a balance between correctness and productivity.

hearty shell
#

Yup, this would be less annoying if python's type system was more expressive, but for now you can't really do much about that other then ignore the errors, eg proper read-only attributes

rare vapor
#

this wouldn't be an issue if descriptors would match read-only protocol properties 😂

hearty shell
#

If they had gone with Jelle's idea of overloading the meaning of Final for this context or a variant of this, we probably wouldn't be here xD

rare vapor
#

meh... I'd like to see ReadOnly[]

#

it would match TypeScript's readonly 😄

#
class User(Protocol):
    email: ReadOnly[str]
hearty shell
#

There is now precedence of adding new symbols to typing.py for specific constrictions such as NotRequired for TypedDict so I think adding a new one like ReadOnly could be possible

rare vapor
#

right

#

I'd also like to see something like TypeOf[] to match TypeScript's typeof

#

I think someone pointed me to a proposal the other day for something similar

#

@hearty shell btw, the example above you gave helped me understand why your initial example is disallowed

#

thank you

soft matrix
#

if only Generic property

rare vapor
soft matrix
#

well if you could do ```py
class User(Protocol):
email: property[str]

rare vapor
#

hmm

#

I don't disagree... but then someone (cough) would argue that descriptors still don't match properties

oblique urchin
#

That would still often be more precise than you want

rare vapor
#

I just want a way to say, "pass in an object that can have obj.prop run on it to get a value out of it"

#

and it doesn't matter if prop is a property or descriptor or some other mechanism

#

for now, I guess I'll use # type: ignore 😦

runic acorn
#

can someone help me how to create notification desktop

grave fjord
minor hedge
#

what's the min python version for builtin subscripting like list[int] and tuple[thing]?

acoustic thicket
#

3.9

minor hedge
#

thank

hearty hemlock
# rare vapor ```python class User(Protocol): email: ReadOnly[str] ```

Would something like this work?

from typing import NoReturn, TypeVar, Generic, Protocol

T = TypeVar("T")

class Readonly(Generic[T], Protocol):
    def __get__(self, obj, objtype=None) -> T:
        ...

    def __set__(self, obj, value) -> NoReturn:
        ...
        
class A:
    x: Readonly[int]
    def __init__(self):
        self.x = 7```
rare vapor
round berry
#

Don't know if this is the right spot. But I want to add to my pydantic model a model where I define the key and the value of dict with pydantic:

class i18n(BaseModel):
  lang: str
  translation: str

class element(BaseModel):
  title: str
  translation: Dict[i18n.lang, i18n.translation]

so the translation value in element would be something like: `{ en: "english version", jp: "japanese version"}

tranquil turtle
#

translation: Dict[str, str]

round berry
acoustic thicket
#
ValidLangs = Literal["en", "jp", ...]
...
lang: ValidLangs
finite horizon
#

Incompatible types in assignment (expression has type "Union[CollectConfigBase, ProcessConfigBase, ForwardConfigBase]", variable has type "RegexMaskProcessConfig") [assignment]
RegexMaskProcessConfig is a subclass of ForwardConfigBase.

So, how do I properly type hint this?

hearty shell
#

Could you show where the error happens

finite horizon
#
async def forward(
    *,
    ctx: PipelineRunContext,
    event: CollectedEvent,
) -> None:
    """
    Method called to forward the event.
    """
    indent: Optional[int] = None
    config: DiskConfig = ctx.config

config: DiskConfig = ctx.config also throws the same error

class PipelineRunContext(BaseModel):
    """
    Class representing a pipeline run context.
    """

    config: Union[CollectConfigBase, ProcessConfigBase, ForwardConfigBase]
    cache: Dict[str, Any] = Field(default_factory=dict)
    shared_cache: Dict[str, Any] = Field(default_factory=dict)
#

fyi, BaseModel is a pydantic base model

#

if that makes a difference

#
class DiskConfig(ForwardConfigBase):
    """
    Configuration schema for the disk forward plugin.
    """

    path: pathlib.Path
    filename: Optional[str] = None
    pretty_print: bool = False
#

@hearty shell does the above bring enough context?

hearty shell
#

Not sure about the first issue yet, but when you do config: DiskConfig = ctx.config DiskConfig might be a subclass of ForwardConfigBase, but is it also a subclass of either CollectConfigBase or ProcessConfigBase

#

Also either way, you are telling it should be DiskConfig, that is not compatible with ForwardConfigBase

finite horizon
#

hmm, CollectConfigBase , ProcessConfigBase and ForwardConfigBase share the same base class

#

so, how do I define PipelineRunContext.config as subclasses of the three mentioned above?

#

and not those

#

Also, if it's a subclass of ForwardConfigBase, it will not be a subclass of CollectConfigBase or ProcessConfigBase, or, are you telling me that mypy can't know that?

hearty shell
#
class ForwardConfigBase: ...

class DiskConfig(DiskConfig):
    a: int

class PipelineRunContext:
    config: ForwardConfigBase

async def forward(ctx: PipelineRunContext):
    config: DiskConfig = ctx.config
    config.a
#

Here, you are telling config is of type DiskConfig

#

But ctx.config is of type ForwardConfigBase

#

You are trying to assign the super class to a subclass annotation

finite horizon
#

yes, makes sense.
Is there a way to tell mypy that PipelineRunContext.x is a subclass of ForwardConfigBase but not ForwardConfigBase?

#

looks like that's what I need?

hearty shell
#

Not sure that is what you need, the problem here is where do you want to sacrifice, you have an object PipelineRunContext that has a config parameter that could be an Union of any of these "Base" types of configs CollectConfigBase, ProcessConfigBase, ForwardConfigBase.

Then you have a function that accepts this very broad object, you access it's config and you then want to it to be DiskConfig

#

Maybe you want the function to take a more precise object that already has a DiskConfig as one of its parameters

finite horizon
#

dam, no, I do want that broad object :/

hearty shell
#

So how are you sure that it is in fact a DiskConfig

finite horizon
#

and not any other subclass of ForwardConfigBase?

hearty shell
#

Yup

finite horizon
#

well, I know, because it's what I'm passing in the code, but that's me, not mypy 🙂
I accept that mypy needs help in some cases, just haven't figured out how to help it, besides cast() :/

hearty shell
#

If you only call that specific function with a PipelineRunContext that always has a config compatible with DiskConfig, the your annotation for that function is too broad. But maybe that is more work then its worth x)

#

There are other ways of narrowing types, like using assert or checking the type inside the function

finite horizon
#

yeah, I'm doing something like

if TYPE_CHECKING:
    assert isinstance(ctx.config, DiskConfig)
#

which seems to appease mypy

#

was this what you were referring to?

hearty shell
#

Yup

#

do you also have async def collect, async def process?

finite horizon
#

yes

#

it's kind of a plugin system, so each module needs to define one of these three

hearty shell
#

Are these config objects read only?

#

in those 3 functions

finite horizon
#

the configs, yes

hearty shell
#

Ah, then you life could get a little easier with Generics, although I have never use Pydantic so I dont know how that interact

#

seems that pydantic has its own type of generics

finite horizon
#

pydantic is supposed to support generics

hearty shell
#

But then what you could have is something like this

T_co = TypeVar('T_co', covariant=True, bould=ConfigBase)

class PipelineRunContext(BaseModel, Generic[T_co]):
    """
    Class representing a pipeline run context.
    """

    config: T_co

async def forward(
    *,
    ctx: PipelineRunContext[ForwardConfigBase],
    event: CollectedEvent,
) -> None: ...
#

BaseModel, Generic[T_co] not this exactly, but something like that

finite horizon
#

ohhhh, interesting!

#

Thank You @hearty shell !

hearty shell
#

Np! 😄

finite horizon
#

Btw, why is it important that it's a read only object?

hearty shell
#

It doesnt have to, it is just that if you want PipelineRunContext[DiskConfig] to be a subtype of PipelineRunContext[ForwardConfigBase] you need PipelineRunContext to be covariant on the type variable, and if that attribute can be also settable you can run into problems

#

It is the same type of problem you have whenever you tried to give a list of subtype to a function that expected a list of the supertype

#

I say "It doesnt have to" because even though a settable attribute should not be covariant, you can still make it so if that makes your life easier

finite horizon
#

Thanks for the explanation

blazing nest
#

Hmm, I have code like this: ```python
CommandT = TypeVar('CommandT', bound=CommandMiddlewareMixin)
_MiddlewareDecorator = Callable[[CommandT], CommandT]
MiddlewareDecorator = _MiddlewareDecorator[CommandMiddlewareMixin]

Is there any other way of doing it? Originally I only had the second line, but then realized it became `Callable[[Any], Any]` as opposed to a shortcut for writing `Callable[[CommandT], CommandT]`
acoustic thicket
#

hmm

trim tangle
#

I don't think you're missing anything

blazing nest
trim tangle
#

wdym?

blazing nest
#
class Command(CommandMiddlewareMixin, CommandCallback[P, RT]):
    ...
#

Without that typevar, what will happen is that a Command is inputted but the output is CommandMiddlewareMixin

trim tangle
#

hmm I don't see how MiddlewareDecorator isn't exactly the same as Callable[[CommandMiddlewareMixin], CommandMiddlewareMixin]

blazing nest
#
def deco(command: CommandMiddlewareMixin) -> CommandMiddlewareMixin:
    ...


class Command(CommandMiddlewareMixin):
    ...

x: Command = ...  # type: ignore

reveal_type(deco(x))  # CommandMiddlewareMixin

..no?

trim tangle
#

no, it doesn't start acting like a typevar

#

unless I misunderstand what you mean

#

can you post a snippet that works on mypy-play or pyright playground?

blazing nest
#

Ah shit, so these aren't equivalent? ```python
def factory() -> Callable[[CommandT], CommandT]: ...

Shortcut = Callable[[CommandT], CommandT]
def factory() -> Shortcut: ...

trim tangle
#

they are equivalent

#

wait, what is CommandT?

#

ah I think I see what you mean

soft matrix
#

no they arent equivalent

#

to make them youd need to do ```py
def factory() -> Shortcut[CommandT]: ...

trim tangle
#

mypy thinks differently though

main.py:10: note: Revealed type is "def [T] (T`-1) -> T`-1"
main.py:11: note: Revealed type is "builtins.int*"
main.py:18: note: Revealed type is "def (Any) -> Any"
main.py:19: note: Revealed type is "Any"

which... I would expect

hearty shell
#

You can make links on the pyright playground too lemon_exploding_head

trim tangle
trim tangle
#

also, if someone non-lazy could contribute a UI control or something like that, it'd be awesome

hearty shell
#

Can I do that in python? Serious

trim tangle
#

if you rewrite the UI in Python, I'll merge your PR

#

🙂

trim tangle
#

I cba to read the typing peps

trim tangle
#

if you want the type of a generic function, you'd need to make a protocol:

MyT = TypeVar("MyT", bound=Foo)

class MyDeco(Protocol):
    def __call__(self, arg: MyT, /) -> MyT:
        ...
blazing nest
trim tangle
#

or I can start it if you don't want to

blazing nest
#

Feel free

blazing nest
blazing nest
#

Hmm, to follow pyright I am doing Command[..., object] now which introduces some complications: ```python

def command(
    self,
    callback: Optional[Callback[P, RT]] = None,
    *,
    name: Optional[str] = None,
    description: Optional[str] = None,
) -> Union[Command[P, RT], Callable[[Callback[P, RT]], Command[P, RT]]]:
    def decorator(func: Callback[P, RT]) -> Command[P, RT]:
        subcommand = Command(func, name=name, description=description)
        self.add_command(subcommand)
        return subcommand

    if callback is not None:
        return decorator(callback)

    return decorator

Argument of type "Command[P@command, RT@command]" cannot be assigned to parameter "command" of type "SubcommandGroup | Command[(...), object]" in function "add_command"
Type "Command[P@command, RT@command]" cannot be assigned to type "SubcommandGroup | Command[(...), object]"
"Command[P@command, RT@command]" is incompatible with "SubcommandGroup"
TypeVar "RT@Command" is invariant
Type "RT@command" cannot be assigned to type "object"

#

The other (somewhat unrelated) issue is that my tests on Python 3.10 > is failing: ```
E TypeError: Parameters to generic types must be types. Got Ellipsis.

#

What do I do? typing_extensions doesn't have a Generic backport from what I can find

oblique urchin
blazing nest
#

Oh right, yeah I'll quote it

blazing nest
oblique urchin
blazing nest
#

Usually you'd hear it on the tone

oblique urchin
#

Doesn't seem that bad. In typing-extensions, add a bunch of from typing import X, add to __all__, add some tests, add documentation. Similar thing in typeshed. Update type checkers to recognize typing_extensions.X is typing.X for more values of X.

oblique urchin
blazing nest
#

class Event:
    NAME: ClassVar[str]

class TypingEvent(Event):
    NAME = "TYPING_START"
  /opt/hostedtoolcache/Python/3.7.12/x64/lib/python3.7/site-packages/wumpy/bot/events/channel.py:error: Module "wumpy.bot.events.channel" is partially unknown
wumpy.bot.events.channel.TypingEvent.NAME
  /opt/hostedtoolcache/Python/3.7.12/x64/lib/python3.7/site-packages/wumpy/bot/events/channel.py:22:5 - error: Ambiguous base class override
    Type declared in base class is "str"
    Type inferred in child class is "Literal['TYPING_START']"

Any way for me to remove this warning or whatever it is bringing down my type-completeness score?

soft matrix
#

what if you reannotate it?

blazing nest
#

I could, I believe (sadly I don't get this locally when I verify the types), but if possible I want to keep the type Literal["TYPING_START"] if possible while simultaneously requiring subclasses of Event to define a NAME

#

If type narrowing can't be done on attributes then I might scrap it (and turn it into ClassVar[str]) - it was the primary motive but I haven't really played around with it much apart from with TypedDicts.

#

Yeah, rip Literal 😅

minor hedge
#

Where does Embed|None come from? self.embed_attr is dict[str, Any] and x is supposed to Any?

hasty hull
minor hedge
#

Yes but why is it complaining that embed.timestamp is incompatible with Embed|None when x should be Any?

hasty hull
onyx kayak
#

Can someone explain me why typehinting here is a problem? The code works, but mypy has a problem with hints.

Why is tuple not treated as a iterable and why mypy expects Optional[int] for .extend() or += operation?

def filterRepo(
        self,
        user: User,
        *,
        amount: tuple[float, float] = None
) -> list[Transaction]:

        filterValues = []

        # *snipped*
        filterValues += amount
        # Argument 1 to "__iadd__" of "list" has incompatible type "Tuple[float, float]"; expected                   "Iterable[Optional[int]]"mypy(error)

        filterValues.extend(amount)
        # Argument 1 to "extend" of "list" has incompatible type "Tuple[float, float]"; expected "Iterable[Optional[int]]"mypy(error)
hearty shell
#

Where does the Iterable[Optional[int]] hint come from?

#

Do you do anything to filterValues prior to filterValues += amount

onyx kayak
onyx kayak
hearty shell
#

Yes, Any, but the issue here is just you tried to append floats to a list that mypy has guessed takes ints, you could just annotated the list to be of list[float | None]

#

filterValues: list[float | None] = []

onyx kayak
hearty shell
grave fjord
#

Use object not Any

#

Or even better the full union of all the things you'll append to it

hearty shell
#

Using object as an annotation for your list gets really annoying though, if you dont care about correctly typing a list, any is usually less of a hassle then object

grave fjord
#

Or even better make the list in one expression

foggy thicket
#

don't know how to word this but is it possible to make variadic generic but one could provide it by providing it in square bracks, sort of like the first argument in Callable like,

foo: MyClass[int, [str, hex, bytes]] = MyClass(...) # How to implement this generic?

How to do this?

grave fjord
#
from typing import TypeVar, TypeVarTuple

DType = TypeVar('DType')
Shape = TypeVarTuple('Shape')

class Array(Generic[DType, *Shape]):
    ...
#

That gets you Array[T, U, V, ...]

#

But you can't do Array[T, [U, V]]

grave fjord
hearty shell
#

Yes, I agree with you that using a Union of all types would be better, I am just saying that if someone asks for "A list of whatever type, I dont care what type it is", telling them to use list[object] might do that but endup being more restrictive then they want, maybe they know that the last item of the list is always of a curtain type for example and then in that case they probably don't want a "Can't access member ... for type 'object', ..."

hearty hemlock
north widget
#

can we add annotation in the variable side? eg: prev_ranks: "that would be ID and rank" = dict() , which both ID and rank are integer

north widget
rustic gull
void panther
#

everything is a SupportsStr

rustic gull
#

oh is it lol

#

ok

#

oh yeah of course

trim tangle
#

It's actually kinda creepy if you think about it

hearty hemlock
agile tide
#

What is the industry standard for using typing_extensions? something like this?

install_requires =
    requests
    typing_extensions ; python_version < '3.8'
#

or should I expect it to be installed

#

or something else

oblique urchin
#

I feel like it's fine to just always install it

agile tide
#

Literal

#

Literal was added to the stdlib typing in 3.8

agile tide
oblique urchin
#

well, maybe tomorrow you want to use Self which is only in the stdlib in 3.11

agile tide
#

I guess

#

do other libraries do this too?

#

like install it by default?

oblique urchin
rough sluiceBOT
#

setup.py line 199

'typing_extensions>=3.10',```
agile tide
#

okay thanks

oblique urchin
rough sluiceBOT
#

setup.py line 105

"typing_extensions>=3.10.0.0; python_version < '3.10'",```
pastel egret
#

Installing it always is probably simplest, considering it already does version checks to avoid redefining things already implemented in typing. It'll just be a bunch of aliases.

hallow flint
#

it's regularly a top 10 most downloaded package on pypi, so i'm sure lots of people install it by default

rustic gull
#

@rustic gull

blazing nest
hasty hull
#

This appears to be a mypy bug

#

Pyright is happy with it

#

In the meantime you can rewrite it like this so that mypy is happy with it: ```py
from typing import Any, Optional, Callable

def test8():
def wow(
x: Any,
filter_func: Optional[Callable[[Any], bool]] = None,
) -> bool:
if filter_func is not None:
return filter_func(x)

    return bool(x)

print(wow(1))
print(wow(2, lambda x: x % 2 == 0))
print(wow(2, lambda x: x % 2 == 1))

if name == 'main':
test8()

#

Fair enough, you could also switch to pyright ;)

little hare
#

is there a way that overloads can be copied directly from the super?

#

a method is being modified to add different functionality but it does not have any overloads changed

#

how can i use the super's overloads for type checkers

soft matrix
#

Not that I'm aware of

#

You can probably do it with pyanalyze now though

little hare
#

hm

#

what if i .. use a.. brb

#

okay so it works, but its stupid

stark shuttle
frail rover
#

I'm using a Protocol that defines a single attribute, and a class implementing that Protocol implements that attribute as a read-only property which causes invariance due to mutability
The thing here is, if I don't care about the attribute being read-only or not, how would I type it?

oblique urchin
grave fjord
#

@property on the protocol

#

Or define a setter that throws a runtime error on your implementation with NoReturn

frail rover
#

thanks! read-only on the protocol worked

frail rover
#

I have another (kind-of) similar problem with mutability

Another protocol defines a Mapping attribute that two classes implement
the first one uses a plain dict since the attributes aren't known
the second one uses TypedDict as the attributes are received from an API and can benefit from typed attributes

however, since dict and Mapping aren't compatible, how would I go about making the protocol satisfy both cases?

grave fjord
#

You can downcast it to a Mapping

frail rover
#

downcast?

grave fjord
#

v: Mapping[str, object] = td

#

Or you could use another protocol instead of Mapping depending on what your consumers use

#

eg just the __getitem__(self, k: K)-> V: ... method

frail rover
#

thanks! that worked :D

floral ibex
#

first time using mypy and i got an error with aiohttp

http_client.py:1: error: Cannot find implementation or library stub for module named "aiohttp"
http_client.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
hasty hull
#

I don't know if aiohttp is untyped, provide types inline or has a stubs package but when you see that error that's how you can generally fix it

floral ibex
hasty hull
#

What mypy version are you on?

floral ibex
oblique urchin
#

he means --install-types, but that won't help here because aiohttp is not in typeshed

#

if I recall correctly aiohttp has inline types, so you may just have to point mypy to it

floral ibex
#

oh nevermind

#

i just now saw the link i'm dumb

#

actually how would you point mypy to aiohttp

#

because i'm still confused even after looking at the docs

hasty hull
# floral ibex 0.942

oops sorry for that typo, yeah I just checked and aiohttp does have inlined types, are you on an old version of aiohttp?

oblique urchin
hasty hull
#

yeah like Jelle said that'll be an env mismatch then

floral ibex
leaden oak
#

are you using pipx?

floral ibex
#

pipx?

leaden oak
#

OK nvm .. it's a tool to install Python tools in isolated environments

#

If you aren't using it it's not relevant, I just have to deal with mypy being isolated from my deps because I use pipx

floral ibex
hasty hull
#

How are you running mypy?

floral ibex
hasty hull
#

Try running python -m mypy

floral ibex
#

is that the default way of running mypy in a env?

hasty hull
#

yeah as far as I'm aware, mypy resolves your imports by looking at your current env

floral ibex
#

i see

#

thanks

#

i'll just use that from now on

onyx kayak
#

Hello, how should I proceed so mypy doesn't have a problem with rootObj being Optional[ET.Element]? This is a return value from XML parsing library method. Obviously using if statement won't work here, but I wanted to visualize my point.

        def parseAmount(rootObj: ET.Element, XPath: str) -> tuple[Any, Any]:
            """Parse tuple (Amount, Currency) from Equabank XML"""
            try:
                if isinstance(rootObj.find(XPath, namespace), Element):
                    return (
                        float(rootObj.find(XPath, namespace).text),
                        # Item "None" of "Optional[Element]" has no attribute "text"mypy(error) 
                        rootObj.find(XPath, namespace).get("Ccy")   
                    )

            except (TypeError, ValueError):
                return (None, None)
#

Generally I'm curious how can you make mypy 'work' when you have a method returning combos like int | None and you process the returned value only in case of it not being None. When I implemented such mechanism, I was told to rewrite it and instead of None, handle it with Exceptions. Here I'm using external library so I cannot do much.

soft matrix
#

you should assign the rootObj.find(XPath, namespace) to a intermediary variable so you can perform the narrowing on that

oblique urchin
#

Yes. That's also an optimization because currently you're calling .find() twice. Not sure if the perf difference matters in practice though.

onyx kayak
#

mypy is happy when it comes to the previous issue, altho now I receive ```Missing return statementmypy(error)

soft matrix
#

yeah thats it

onyx kayak
#

I found out the problem with missing return... mypy issue

#

Thanks a lot!

blazing nest
#

Hmm, I am having some issues with object - isn't all objects convertable to object (like Any except that you can't do anything with object)?

acoustic thicket
#

yeah

#

whats the error

blazing nest
#
from typing import Callable, Generic, ParamSpec, TypeVar

P = ParamSpec('P')
RT = TypeVar('RT')


class WrappedCallback(Generic[P, RT]):
    ...


def wrap(func: Callable[P, RT]) -> WrappedCallback[P, RT]:
    ...


def accept(callback: 'WrappedCallback[..., object]') -> None:
    ...


@wrap
def example() -> None:
    ...


accept(example)
#
Argument of type "WrappedCallback[(), None]" cannot be assigned to parameter "callback" of type "WrappedCallback[(...), object]" in function "accept"
  TypeVar "RT@WrappedCallback" is invariant
    Type "None" cannot be assigned to type "object"
#

I want to use object as opposed to Any so that if my code decides to call the wrapped callback, no assumptions can be done about the return type - but this is causing issues for me 🤔

acoustic thicket
#

RT = TypeVar('RT', covariant=True) I think? since callables should be covariant in the return type

acoustic thicket
#

the default is invariant

#

Type variables may be marked covariant or contravariant by passing covariant=True or contravariant=True. See PEP 484 for more details. By default, type variables are invariant.

blazing nest
#

Oh, huh, but typehints by default are covariant?

#
class A: ...
class B(A): ...

def accept(arg: A) -> bool: ...

accept(B())
#

This is covariance yeah?

trim tangle
#

that's a very obscure way to say it I guess...

#

but the logic is like ```py
b: B = B()
a: A = b # this is ok because B is a subtype of A
accept(A)

blissful loom
#

How do you guys remember the difference between Iterator and Generator?

void panther
#

same way as you remember the difference between iterable and iterator? I don't think there's much to it apart from knowing the methods it expects

blissful loom
#

Fair

#

I just have to google it every time or let mypy tell me I messed up but I just don't use them very often

void panther
#

well if you google it enough you'll memorize it I'd guess

#

though for most uses I'd say you want Iterator

oblique urchin
#

Generator is basically a concrete type, only use it when you actually write a function with yields

#

I think technically you can make your own, but I've never seen that in practice

blissful loom
#

also if I have a class that implements __iter__ and/or __next__, is it still proper to subclass something from typing?

oblique urchin
blissful loom
#

Nice

oblique urchin
#

Though not all typing classes are Protocols (e.g., Mapping isn't), so if you want to write your own Mapping, you should subclass explicitly

void panther
#

subclassing the abcs from collections can also give you some default behaviour and make it easier to spot if you miss something

void panther
#

well that falls under the needs to be a generator for other reasons

#

but I'd say the vast majority of generators are just iterators written with yield

blazing nest
pastel egret
#

An iterator you can loop through only once, an iterable is a general collection of some kind that you can loop over repeatedly. A generator is a specific kind of iterator which also has send() and throw().

floral ibex
#

hello

#

i ran into another problem with mypy

#

currently whenever i check my file with mypy it doesn't throw an issue when i'm obviously giving the wrong type to it

fierce ridge
#

in addition to the above... show the code! and use --strict

#

can you reproduce it on https://mypy-play.net/ ?

floral ibex
#
class HTTPClient:

    _BASEURL: str = "https://random-d.uk/api/v2/"

    async def _request(self, endpoint: int) -> int:
        async with ClientSession().get(f'{self._BASEURL}{endpoint}') as resp:
            return await resp.json()
#

currently this is my code and i'm passing a str to _request

#

and _request returns a dict

oblique urchin
#

resp.json() probably returns Any?

floral ibex
fierce ridge
#

(side note, you should write _BASEURL: typing.ClassVar[str])