#type-hinting

1 messages · Page 8 of 1

rare scarab
#

I think more like TypeVar("_SystemObject", A, B)

rare scarab
#

The example that comes to mine is typing.AnyStr, which is ```py
AnyStr = TypeVar("AnyStr", str, bytes)

fierce ridge
#

this is correct, and i never quite understood the purpose of this design

#

maybe it's a variance/covariance thing again

hallow flint
#

no, it's to help better model python semantics. (you actually see a similar desire come up with literal types every now and then)

#

here, from pep 484:

fierce ridge
# hallow flint

wait.. i thought that MyStr would explicitly be disallowed by the constraint

#

i understand the distinction in that paragraph though, and i agree that it's useful

#

"you can give me any subtype of str, but i will only ever treat it as a str"

#

no kidding.. i've totally misunderstood constraints for years

#
from typing import TypeVar

AnyStr = TypeVar('AnyStr', str, bytes)

def foo(s: AnyStr) -> AnyStr:
    if isinstance(s, str):
        return s.replace('x', 'y')
    else:
        return s.replace(b'x', b'y')

class MyStr(str):
    pass

reveal_type( foo(MyStr('hello')) )
#

same with the variance/covariance/invariance of containers thing

trim tangle
odd oak
#

hai o.o

#

how to code o.o

slender timber
#

Why is types and typing separated?

soft matrix
#

Because one is for concrete types and the other is more abstract special forms

fierce ridge
#

ah, that's probably what i had tried in the past, and got confused about

#

thanks for clarifying!

urban root
#

Hey guys, why are my overloads not working correctly? It should be PartialUser | None right?

trim tangle
#

Oh wait

urban root
trim tangle
#

Why do you think it should be PartialUser | None?

urban root
#

because full and affirm are false by default

trim tangle
#

Overloads don't really "look" at the function definition

urban root
#

oh

#

so what can I do in this case then

trim tangle
#

Can you show the implementation btw?

urban root
trim tangle
#

you can also remove in each overload the parameters which are False

trim tangle
#

!paste

rough sluiceBOT
#

Pasting large amounts of code

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

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

lone widget
#

is this something i need to deal with that stuff like mypy would care about?

#

or is it pycharm being annoying?

spiral fjord
rare scarab
#

maybe you did x = bool instead of x: bool

balmy condor
#

I have parameter that I intend to be passed as str or None, is it still valid to use : str on it? Or maybe I should just use an empty string in that case.

acoustic thicket
#

: str | None

humble plume
#

: Union[str, None]

blazing nest
#

Can I type a callback with optional parameters without a Protocol?

trim tangle
#

why do you want a callback with optional parameters though?

#

can't it just accept Thing | None?

trim tangle
#

No, (x: int = ...) -> str is an intersection of () -> str and (int) -> str

#

not a union

#

because if you have a Callable[[int], str], it's assignable to Callable[[], str] | Callable[[int], str]

brisk hedge
#

A custom callable protocol is how it goes for most nontrivial function annotations

vivid ore
#

Is there a way to make recursive types in Python?
For instance, say I have a dictionary of int → dictionaries of dictionaries… and so on.
I can’t do:

rec_dict_type: TypeAlias = dict[int, rec_dict_type]

because I get an error when running the code.
I also tried this:

def get_rec_dict_type():
    return dict[int, get_rec_dict_type()]

rec_dict_type: TypeAlias = get_rect_dict_type()

but it doesn’t work.
Is there any way I can type this other than dict[int, Any]?

soft matrix
#

use strings

#

either rec_dict_type: TypeAlias = "dict[int, rec_dict_type]" or rec_dict_type: TypeAlias = dict[int, "rec_dict_type"]

vivid ore
#

oh cool

#

I tried it and I get an error: Cannot resolve name "rec_dict_type" (possible cyclic definition)

#

I used the code rec_dict_type: TypeAlias = dict[int, 'rec_dict_type']

soft matrix
#

is this mypy?

vivid ore
#

Yes

soft matrix
#

youll need the latest version to use recursive typehints

vivid ore
#

I am using

mypy 0.981 (compiled: yes)

#

oh it appears the latest version is 0.991

#

I will update

#

I am using my distro’s packaged version though so it will take a while

#

Thank you, though

vivid ore
#

Can you type-hint that a value should be the empty tuple?

#

tuple[] is a syntax error…

vivid ore
#

thank you

slender timber
#

I have a decorator:

def supports_slice(func: Callable[[ModelCollection[MT_co], str | int | slice], MT_co]):

However I cannot use it here:

class Arrangements(EventModel, ModelCollection[Arrangement]):
    @supports_slice
    def __getitem__(self, i: int | str | slice):

as this is the error:

Argument of type "(self: Self@Arrangements, i: int | str | slice) -> Arrangement" cannot be assigned to parameter "func" of type "(ModelCollection[MT_co@supports_slice], str | int | slice) -> MT_co@supports_slice" in function "supports_slice"
  Type "(self: Self@Arrangements, i: int | str | slice) -> Arrangement" cannot be assigned to type "(ModelCollection[MT_co@supports_slice], str | int | slice) -> MT_co@supports_slice"
    Parameter 1: type "ModelCollection[MT_co@supports_slice]" cannot be assigned to type "Self@Arrangements"
      "ModelCollection[Arrangement]" is incompatible with "Arrangements"

ModelCollection is just a protocol

@runtime_checkable
class ModelCollection(  # pylint: disable=abstract-method
    Iterable[MT_co], Protocol[MT_co]
):
    @overload
    def __getitem__(self, i: int) -> MT_co: ...

    @overload
    def __getitem__(self, i: str) -> MT_co: ...

    @overload
    def __getitem__(self, i: slice) -> Sequence[MT_co]: ...
soft matrix
#

youre getting messed up by the contravariance of Callable's parameter types

slender timber
#

i am confused between co- and contra- too

#

co- appears more useable, contra- almost always errors

#

so.. what should I do?

soft matrix
#

type: ignore, theres nothing you can do

slender timber
#

😦

#

why isn't there anything

soft matrix
#

¯_(ツ)_/¯

#

it might not be type safe 🤔

#

but honestly idk it seems to be a pretty big problem with typing decorators

slender timber
#

reveal_type is working correctly even after # type: ignore next to where I get errors

dry geyser
#
    return num*2

print(double("test"))
print(double(2))

How do I do that it has to return int and have the parameter as int

soft matrix
#

you already have pithink

dry geyser
#

@soft matrix I am running the code with a string and it executes instead of giving an error

soft matrix
#

well yeah python doesnt enforce types at runtime

dry geyser
#

i know i could do an if statement to check but I want to know if its possible if type hinting

soft matrix
#

no python type hints are only checked statically

tranquil turtle
dry geyser
#

thanks

trim tangle
#

You can do it for simple types like int or str | None, but with more complicated types it just breaks down. Or becomes very expensive

#

The point of type annotations is that you use a tool like pyright or mypy to find errors without even running your program

dry geyser
#

thanks

regal summit
#

how do you typehint classmethods that return the same class?

soft matrix
#

!d typing.Self

rough sluiceBOT
#

typing.Self```
Special type to represent the current enclosed class. For example:

```py
from typing import Self

class Foo:
   def return_self(self) -> Self:
      ...
      return self
```  This annotation is semantically equivalent to the following, albeit in a more succinct fashion...
regal summit
#

but its not actually returning self though right

#

its making a new instance

soft matrix
#

the typehint is still the same

trim tangle
#
class Point:
    def __init__(self, x: float, y: float) -> None:
        self._x = x
        self._y = y

    @classmethod
    def from_polar(cls, angle: float, distance: int) -> Self:
        ...
        return cls(x=some_x, y=some_y)
#

I often think twice about making a classmethod though, because __init__ usually changes in child classes

brisk hedge
#

The one place where LSP violations are considered okay BABAXD

cloud orchid
#

@craggy quest

fierce ridge
#

so it's not at all an LSP violation to have alternate constructors and init/new methods that are incompatible with the parent class, although practically i think a lot of code tends to assume that they are compatible

hallow flint
#

subtyping of types is a mess anyway

trim tangle
#

I think it's just a matter of convention

vivid ore
#

Is it possible to get mypy to understand implications?

#

suppose I have this code

#
from typing import Optional

def foo(num: int, val: Optional[str] = None) -> list[str]:
    assert num != 0 or val is not None
    if num == 0:
        return []
    else:
        return [val] * num
#

After the assert inside the else, val is definitely not None

#

How can I communicate this to mypy?

soft matrix
#

Why is val even allowed to be None here?

#

I mean it should understand asserting type guards like this

#

But this a strange function

vivid ore
#

yes, this is just an example

#

I guess I can introduce a superfluous assert

soft matrix
#

This just looks like a mypy bug

vivid ore
#

should I report it?

spiral fjord
chrome hinge
#

with or, it doesn't

#

actually, you know what? the type checker is right. What if num!=0?. Then val can be None and that's valid.

#

I think you meant to do the opposite, assert num == 0 or val is not None

vivid ore
#

Actually yes

#

Sorry

chrome hinge
#
def foo(num: int, val: Optional[str] = None) -> list[str]:
    assert num == 0 or val is not None
    if num == 0:
        return []
    else:
        return [val] * num

doesn't work for me in pylance though

#

sadly here it's just not smart enough to understand that num == 0 or val is not None (after the assert) and not(num==0) (inside the else) together imply val is not None.

vivid ore
#

Yeah

fierce ridge
#

mypy isn't smart enough to connect that to the num == 0 case

#

what about rewriting this way?

from typing import Optional

def foo(num: int, val: Optional[str] = None) -> list[str]:
    if num == 0:
        return []
    else:
        assert val is not None
        return [val] * num
frigid jolt
#
dc: typing.Dict[
    Literal[4004, 4010, 4011, 4012, 4013, 4014], str] = {
        4004: "reason",
        4011: "reason",
        ...: ...
    }

is there any other way to typehints the keys of a dict to a literal value and all the values to str?

trim tangle
frigid jolt
trim tangle
#

If you're receiving untrusted input from a remote service, there is always a possibility that the value is complete nonsense (or maybe they just added a new error that your code doesn't handle)

frigid jolt
#

ok

humble plume
#

Is there a way I could describe what does look PutObjectRequest like?

def put_object(request: PutObjectRequest):
    ...

# What vscode shows when you hover `put_object`: (function) put_object: (request: PutObjectRequest) -> None
# I want to describe what `request` dict  would ask or maybe use `**kwargs` but that gives me a `Dict[str, PutObjectRequest]` instead of just `PutObjectRequest`
#

And this is PutObjectRequest:

class PutObjectRequest(TypedDict):
    ACL: ACLType
    Body: Union[bytes, BinaryIO]
    Bucket: str
    CacheControl: str
    ContentDisposition: str
    # And more
soft matrix
#

if the signature was **kwargs would you want it to reveal (ACL: ACLTYPE, Body: bytes | BinaryIO, ...)?

brittle socket
#

How can I fix this? 😕

#

This is the most relaxed I could get it without mypy complaining

def unzip2(zipped: Iterable[tuple]) -> tuple:
    return tuple(zip(*zipped))
trim tangle
#

actually the correct return type would be tuple[tuple[T, ...], tuple[U, ...]]

#

wait... your return type doesn't make sense

#

!e

def unzip(zipped):
    return tuple(zip(*zipped))

print(unzip([(1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e")]))
rough sluiceBOT
#

@trim tangle :white_check_mark: Your 3.11 eval job has completed with return code 0.

((1, 2, 3, 4, 5), ('a', 'b', 'c', 'd', 'e'))
trim tangle
#

oh I see now

#

yeah it should be tuple[tuple[T, ...], tuple[U, ...]]

#

alternatively you could do:

def unzip2(zipped: Iterable[tuple[T, U]]) -> tuple[tuple[T, ...], tuple[U, ...]]:
    ts, us = zip(*zipped)
    return ts, us
``` although this isn't any more "type safe"
brittle socket
#

But I like the ts, us = zip(*zipped) line. It runtime-enforces that exactly 2 tuples are expected

#

Mypy doesn't complain, pyright does :/

#
Expression of type "tuple[tuple[T@unzip2 | U@unzip2], tuple[T@unzip2 | U@unzip2]]" cannot be assigned to return type "tuple[tuple[T@unzip2, ...], tuple[U@unzip2, ...]]"
  Tuple entry 1 is incorrect type
    Tuple entry 1 is incorrect type
      Type "T@unzip2 | U@unzip2" cannot be assigned to type "T@unzip2"

https://pyright-playground.decorator-factory.su/?gzip=H4sIACYIhWMC_02NvQ7DIAyEd57CW0iVMnSM1O7dIUvFkBCnQkoAETOkT1-aP9XDSf7uzh6in4CWYN0b7BR8JJBLwKaNbPhZxo8jGrLezaLtzJF5Esa2G5ExCfejwQtZlEz9A5UB63GA5D423HiWgH199l-UQlZZgdK6hOsDNnBiIYSudqi2VdcM8tBcQZrzs3yTX7bD5epEpBTdHljJFy_xMAvmAAAA

#

Man, linters still have a lot of work to do

tranquil turtle
#

Indeed

soft matrix
#

this isnt a linter problem

#

this is untypeable atm

#

needs a pep 646+

tranquil turtle
#

!pep 646

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

Accepted

Python-Version

3.11

Created

16-Sep-2020

Type

Standards Track

soft matrix
#

(it needs the map type)

tranquil turtle
#

Just put #type:ignore in function. It signature is good and calls can be typechecked

brittle socket
#

Yeah I think I'll just go with

def unzip2(zipped: Iterable[tuple[T, U]]) -> tuple[tuple[T, ...], tuple[U, ...]]:
    ts, us = zip(*zipped)
    return ts, us # pyright: ignore

Since it's just pyright that complains

#

Why does a variadic make more sense for mypy though? Shouldn't (1, 2, 3) be just tuple[T] rather than tuple[T, ...]?

#

Oh wait, no it really is tuple[T, ...]

oblique urchin
#

I bet mypy infers ts and us as Any so it doesn't complain

brittle socket
soft matrix
#

take a look at

#

!pep 692

rough sluiceBOT
#
**PEP 692 - Using TypedDict for more precise \*\*kwargs typing**
Status

Draft

Python-Version

3.12

Created

29-May-2022

Type

Standards Track

humble plume
#

I tried with

def put_object(**kwargs: Unpack[PutObjectResponse]):
  ...
soft matrix
#

that should work

humble plume
#

ok ty

brittle socket
#
def slide_2(seq: list[T] | tuple[T]) -> Iterator[tuple[T, T]]:
    return zip(seq[ :-1], seq[1: ])

How can I properly type seq here? I feel like list and tuple should fall under some generalization

#

Or rather a type that expresses anything indexable

trim tangle
trim tangle
#

yes

brittle socket
#

Just stumbled on it 🙂

trim tangle
#

also, as before, a tuple with any number of items is tuple[T, ...], not tuple[T]

#

tuple[int] is the type of e.g. (42,)

brittle socket
#

Oh but this is specifically a tuple of exactly 2 items

#

(1, 2, 3, 4) -> ((1,2), (2,3), (3,4))

trim tangle
#

yeah I meant the tuple[T]

brittle socket
#

Oh the input param right, that should be just Sequence[T] anyhow

trim tangle
#

yeah

brittle socket
#
def slide_2(seq: Sequence[T]) -> Iterator[tuple[T, T]]:
    return zip(seq[ :-1], seq[1: ])
trim tangle
#

also, I suppose you could make it accept Iterable

brittle socket
#

I can't slice/step into any iterables though

trim tangle
#

yeah, you'd use a different thing

#

wait, isn't it

#

!d itertools.pairwise

rough sluiceBOT
#

itertools.pairwise(iterable)```
Return successive overlapping pairs taken from the input *iterable*.

The number of 2-tuples in the output iterator will be one fewer than the number of inputs. It will be empty if the input iterable has fewer than two values.

Roughly equivalent to:

```py
def pairwise(iterable):
    # pairwise('ABCDEFG') --> AB BC CD DE EF FG
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)
```...
brittle socket
#

Oh, it is

#

I hate how overloaded the term pairwise is. Makes it hard to search for what you want

#

In scipy pairwise means all pairwise combinations (i.e. itertools.combinations(seq, 2))

#

Then you have contexts where pairwise means zip(it1, it2)

#

Anyhow, yep that'd be what I'm looking for thank you

slender timber
#

why are the type hints for ctypes so fucked

#

most of the really useful type hints are private

rare scarab
#

aren't those internals?

vivid ore
#

Does mypy having version 0.991 mean it’s close to a 1.0 release, or will there simply be an extra digit?

oblique urchin
vivid ore
#

cool!

tranquil turtle
#

mypy v0.9999 ducky_devil

oblique urchin
green gale
#

fun coincidence (star count)

slender timber
#

Is dataclass_transform supported by type checkers

#

mypy and pyright both

oblique urchin
regal summit
#

how do I typehint a funciton like this: py def moveable(self) -> ?: yield ... yield ...

spiral fjord
#

Either as a Generator or an Iterator

soft matrix
#

Generator is normally a better idea

spiral fjord
#

I annotate my generators that don't have a send or return type as iterators

#

which is like 99% of them

soft matrix
#

one day soon hopefully theyll (optionally) have the same number of arguments

#

so it should be a no brainer to use Generator

oblique urchin
buoyant swift
spiral fjord
#

Generator[YieldType, SendType, ReturnType] is the current structure

oblique urchin
#

Right, and with PEP 696 we'd default the last two to None

soft matrix
#

That and you then get more accurate types which in my book is always good

oblique urchin
#

sure, that's a case where you really do need a generator

#

but if I write a library that exposes some iterator that happens to be implemented as a generator, I wouldn't want my users to rely on the fact it's a generator

#

so I might annotate the generator as returning -> Iterator

soft matrix
#

thats fair

green gale
#

hello. i'm not sure how to type this correctly. essentially, i have a set of classes which each have a "kind." i care nothing about what this "kind" is, only that it's consistent between these classes. a heavily minimized example would be something like:

class GreenNode:
    kind: T
    tokens: list[GreenToken]

class GreenToken:
    kind: T # this must be the same as the other T, specifically if they're related. if they're unrelated, it can once again be whatever
#

i think this has to do with Generic, but i'm not 100% sure. maybe TypeVar?

deep saddle
#
from typing import TypeVar, Generic

T = TypeVar("T")

class GreenToken(Generic[T]):
    kind: T

class GreenNode(Generic[T]):
    kind: T
    tokens: list[GreenToken[T]]
#

yeah use generics

green gale
#

ahhhh, it's both together. that's what i was missing, thank you.

slender timber
#

What is some good resource to really learn dataclass_transform?

#

All there literally is a PEP

#

Which is good but ...

hallow flint
#

the PEP is all there currently exists that i'm aware of. (when mypy adds support, we'll add some mypy docs, which are usually quite good)

shadow sun
#

I think I read somewhere that wemake-python-styleguide was considering making a version of their plugin that is just their custom linters minus all the extra stuff. Does anyone know if there's a github issue or something I can watch for that?

soft matrix
#

where do people here draw the line between type safety and actual usability for public typehints?

#

im currently struggling to decide whether to make a lot of things a union between a fully resolved type and the partial version of that type when fetching failed

trim tangle
#

all pets want to live... where do you draw the line?

soft matrix
#

i think 99% of the time its gonna be a fully resolved version of the type but idk if that percentage is good enough for me to not care

oblique urchin
#

a good thing about typing is that it forces you to think about the 1% case when you write the code, not when it happens in production and the world breaks

soft matrix
#

but it makes the developer experience so bad 😢

#

although maybe the negativity bias outweighs that

trim tangle
#

I guess part of the issue is that the type system is "bolted on" to the language, they weren't really designed together

#

as opposed to statically typed languages where grass is green and unicorns are happy

soft matrix
#

i dont think this would realistically be fixed in other statically typed languages

brisk hedge
#

An interesting thought experiment is marking members of a sum type as "hot" or "cold" (within the type system)

soft matrix
#

im currently just throwing type ignore everywhere and leaving a todo

                    user,  # type: ignore  # TODO: do we care?
trim tangle
#

some languages make it more ergonomic, some not

#

in Python you'd probably do this with an exception

soft matrix
#

yeah but discarding the whole response cause i couldnt get one thing to completely resolve seems like bad design

trim tangle
#

well, you'll have to handle that exception

#

where you're calling the function

#

if you want to represent a partial response as a value, then you will have to handle it in some way

#

(because that's just the interface of the function, regardless of typing information)

soft matrix
#

at runtime the partial handling works fine

#

its just a pain to type

trim tangle
#

maybe you can introduce a common interface for the real and partial response?

#

like a Protocol

soft matrix
#

oh one is already a subclass of the other

trim tangle
#

or a type alias at least

trim tangle
#

it's literally the same

soft matrix
#

it just means losing so much type info and adding a bunch of asserts everywhere

soft matrix
trim tangle
#

If A is a subclass of B, then A | B is exactly the same as B

hallow flint
#

in some cases, you can write a function that encapsulates your type unsafety, so you don't have to propagate your type-ignores everywher

trim tangle
soft matrix
#

ok from a type theory perspective yeah

trim tangle
#

do you have some example where this doesn't work?

soft matrix
#

but from a user in an ides perspective they are different

#

especially if they only have basic type checking on

buoyant swift
#

surely any type checker can realize subclass relationships?

trim tangle
#

as a user, I would find it confusing if I saw a union of a parent and child classes 🙂

soft matrix
#

out of interest, why?

trim tangle
#

I don't know, it's just unexpected. It's not clear what the intention in the type was. Maybe I'd consider it a typo or something being overlooked

#

because functionally, that's redundant

slender timber
#

ParamSpec is a lifesaver

#

No more worrying about decorators erasing the wrapped function signature

vivid ore
#

I’m trying to type a Maybe class and I’m having some issues
I have the following code that works :

class Maybe(Generic[T]):
    @overload
    def __init__(self, present: Literal[False]) -> None: ...
    @overload
    def __init__(self, present: bool, value: T) -> None: ...
    def __init__(self, present: bool, value: Optional[T] = None) -> None:
        self.present = present
        self.value = value
    present: bool
    value: Optional[T]

But I want to add just and nothing methods.
If I do this:

T = TypeVar('T')
Q = TypeVar('Q', bound=Maybe[object])


class Maybe(Generic[T]):
    @overload
    def __init__(self, present: Literal[False]) -> None: ...
    @overload
    def __init__(self, present: bool, value: T) -> None: ...
    def __init__(self, present: bool, value: Optional[T] = None) -> None:
        self.present = present
        self.value = value
    @classmethod
    def just(cls: type[Q], value: object) -> Q:
        return cls(True, value)
    @classmethod
    def nothing(cls: type[Q]) -> Q:
        return cls(False)
    present: bool
    value: Optional[T]

then mypy can’t infer the type of Maybe.just(1) as Maybe[int].
I tried doing this:

T = TypeVar('T')
Q = TypeVar('Q', bound=Maybe[T])


class Maybe(Generic[T]):
    @overload
    def __init__(self, present: Literal[False]) -> None: ...
    @overload
    def __init__(self, present: bool, value: T) -> None: ...
    def __init__(self, present: bool, value: Optional[T] = None) -> None:
        self.present = present
        self.value = value
    @classmethod
    def just(cls: type[Q[T]], value: T) -> Q[T]:
        return cls(True, value)
    @classmethod
    def nothing(cls: type[Q[T]]) -> Q[T]:
        return cls(False)
    present: bool
    value: Optional[T]

but it doesn’t work.
Is there some way I can do this?

trim tangle
vivid ore
#

that doesn’t work, mypy complains that typing doesn’t have a Self attribute :(

#

I’m using Python 3.11

trim tangle
#

it's from typing_extensions

#

!pypi typing-extensions

rough sluiceBOT
trim tangle
#

not sure if Mypy already supports it

#

I would actually do it like this:

@dataclass(frozen=True)
class Just(Generic[T]):
    value: T
    ...

@dataclass(frozen=True)
class Nothing:
    ...

Maybe = Just[T] | Nothing
``` no booleans required, and will play nicely with pattern matching
vivid ore
#

OK

vivid ore
oblique urchin
#

generics are hard

vivid ore
#

should I try and reproduce it in a cleaner environment and then report it?

trim tangle
#

What is SearchStrategy?

vivid ore
#

that’s something from Hypothesis

#

but the problem seems to be that Mypy doesn’t think the class itself is callable

vivid ore
#

oh, it works

#

wow

trim tangle
#

Also could you try pyright? VSCode is like the natural habitat for it

#

(Pylance)

#

(why are you using mypy with vscode btw?)

vivid ore
#

because Pylance is proprietary

trim tangle
#

pyright isn't

#

there's an extension just for pyright

vivid ore
#

yes but that one is apparently no longer under development according to the readme

#

they apparently want to focus on pylance

vivid ore
#

yeah

trim tangle
#

🤔 it was last released yesterday

#

definitely still under development, as it's just connecting to the pyright LSP

#

which is also what powers pyright for vim, emacs, sublimetext etc.

vivid ore
#

oh ok

#

then I misunderstood the readme

#

sorry

#

it sounded like that

#

anyways why is mypy a bad choice for VSCode?

trim tangle
#

it's not really "bad"

#

it's just that pyright is more interactive, and adopts features much faster

#

like, you can hover on a variable and see its type

#

it also includes some IDE features like autocompletion, auto-imports, and some rudimentary refactorings

tranquil turtle
trim tangle
#

well, don't inherit from maybe 🙂

#

in that case, just make it a static method

devout barn
#

why does this happen?

def bar(): pass
from types import FunctionType
b: FunctionType = bar

Expression of type "() -> None" cannot be assigned to declared type "FunctionType"
  "object" is incompatible with "FunctionType"

?

#

bar is getting inferred as object here? I am using pyright

rare scarab
#

You should use typing.Callable

#
from typing import Callable

def bar(): pass

b: Callable[[], None] = bar
devout barn
rare scarab
#

So you want to use __name__ and such?

#

maybe __class__?

devout barn
rare scarab
#

What if you use the inspect.isfunction() typeguard?

devout barn
rare scarab
#

Callable has __name__

#

This works just fine ```py
from typing import Callable, Any

def accept(f: Callable[..., Any]):

print(f.__name__)
print(f.__module__)
print(f.__code__)
print(f.__globals__)

@accept
def foo():
pass

tranquil turtle
#

i cant find declaration of Callable in typeshed...

rough sluiceBOT
#

stdlib/typing.pyi line 175

Callable: _SpecialForm = ...```
soft matrix
#

go to the version in collections.abc

#

well in this case

#

_collections_abc

tranquil turtle
soft matrix
#

oh lol

#

must have misremembered

void panther
#

does it need anything more than just saying that it's a special form?

soft matrix
#

well all the stuff is handled by the type checker

tranquil turtle
#

_collectios_abc.py, why it is returning False from abstract __call__?

rare scarab
#

it's abstract. Ignore the body

soft matrix
#

im surprised it doesnt raise

shadow sun
#

mypy can't find my imports for some reason?

#stuff.py

import things
``` where

/module/
init.py
stuff.py
things.py

#

making it an explicitly relative import like import .things works but i don't prefer that stylistically

#

and import module.things fails at runtime

trim tangle
#

try going into the directory above module and doing from module.stuff import things

shadow sun
#

I intend module to be a module, and I intend module.stuff to import module.things

trim tangle
#

Will that module be used from some other module?

shadow sun
#

is it because module/__init__.py is empty?

#

I think I ran into something like this yesterday

trim tangle
#

When you do import module, you're doing an "absolute" import. Python will search for module in files directly in your working directory, then in 3rd-party libraries, then in the standard library.

shadow sun
#

if I from module import stuff, things in __init__.py will that allow import module.things to work in stuff.py?

trim tangle
#

So if you have something like this:

foo/
    bar/
        __init__.py
      hmm.py
      nice.py        # imports hmm
      stuff.py       # imports nice
    __init__.py
    baz.py

run.py
hmm.py
# run.py

import foo.bar.stuff
"^ will just fail"

import foo.bar.nice
"^ this will technically run, but the `hmm` module imported by foo.bar.nice will be the top-level hmm.py"
#

So if you want to import a module on the same level, you need to either specify the full path (i.e. from foo.bar.hmmm import thinking inside foo/bar/nice.py) or use relative imports

shadow sun
#

I think I want to do full path, then

shadow sun
#

I thought full path wouldn't work because it failed, and it failed because __init__.py was wrong

trim tangle
#

Python's module system is kinda messed up 🙂

#

Well, maybe not messed up. But not very intuitive

shadow sun
#

poorly taught, too; everywhere I google tells me oh __init__.py should be empty it doesn't need anything in it

trim tangle
#

yeah it should be empty pithink

#

unless you want to explicitly re-export stuff directly from module

shadow sun
#

except no it shouldn't be empty, it should re-export its own contents to itself

rough sluiceBOT
#

toml/__init__.py lines 6 to 7

from toml import encoder
from toml import decoder```
shadow sun
#

if __init__.py does not import module.things then outside scripts can't import module.things and module.stuff can't import module.things

#

aka if __init__.py is empty as recommended stuff.py cannot from module import things and must instead import .things

trim tangle
shadow sun
#

i checked around: if __init__.py is empty, import foo.hmm works, from foo import hmm works, but import foo denies access to foo.hmm with AttributeError

#

also: this shit is confusing

#

and now i'm wondering what issue i was even originally having

#

and i think it was that i have a test module for testing my project but its name collides with the standard library test so isort gets confused

#

actually wait no it wasn't that

#

i was definitely getting an import error on that too

#

let me try and reproduce that

#

wait no that was the same thing

#

it was colliding with the standard library test so the import failed because it was trying to look in the standard library test

trim tangle
shadow sun
#

i'm pretty sure

trim tangle
#

which... yeah

#

is definitely an interesting approach

#

and, of course, that attribute persists

shadow sun
#

perhaps the reason this is so confusing is that the dot means different things when you're importing a module vs when the module is imported

trim tangle
#

yep

#

Python's import system is by far the most confusing and complicated of all languages I've seen

#

wait until you see namespace packages

#

!pep 420

rough sluiceBOT
#
**PEP 420 - Implicit Namespace Packages**
Status

Final

Python-Version

3.3

Created

19-Apr-2012

Type

Standards Track

trim tangle
#

anyway... this is a bit off topic for type hinting, probably

shadow sun
#

and i guess my desired behavior is to make the behavior consistent, a.k.a. make submodules automatically import themselves if a parent is imported (which is kind of everything considering that a "module" here is just a folder), and the way to do that is to import explicitly in __init__.py

#

anyway thank you for educating me a little about this mess

sand raptor
#

What is the difference between returning None, types.NoneType, typing.NoReturn, and typing.Never?

hallow flint
#

you basically never want to use types.NoneType in type hints

#

typing.NoReturn and typing.Never mean exactly the same thing

sand raptor
#

then why are there two

hallow flint
#

typing.NoReturn is a confusing name, and in particular there are uses for it outside of return values

sand raptor
#

So when do you want to use None and when do you want to return typing.Never?

hallow flint
#

anyway, you'd use -> None to annotate any of the following functions:

def f() -> None:
    pass

def g() -> None:
    return

def h() -> None:
    return None
sand raptor
#

what about typing.Never

#

would it be

#
def f() -> typing.Never:
  raise BaseException()
hallow flint
#

it's for functions that loop forever or always raise

def exit() -> None:
  raise SystemExit
#

yup

sand raptor
#

ah so for an infinite loop too

#
def inf() -> typing.NoReturn:
  while True:
    pass
#

Alright I understand, thank you!

hallow flint
#

yup! and a reason this is useful is so that type checkers can understand stuff like:

def foo(x: int | str):
    if isinstance(x, int):
        loop_forever()
    x += "asdf"  # <-- we want the type checker to be able to figure out that x must be a str here
vivid ore
fathom river
#

I've got this rough setup:

ClassT_co = TypeVar("ClassT_co", bound="Foo", covariant=True)
P = ParamSpec("P")

class Foo:
    def __init__(self, func: Callable[Concatenate[Foo, P], Coroutine[Any, Any, Any]]):
        ...

class Bar(Generic[ClassT_co]):
    def convert(self, f: Callable[Concatenate[ClassT_co, P], Coroutine[Any, Any, Any]]) -> Foo:
        return Foo(f)

why is pyright complaining that f cannot be assigned to func?

Argument of type "(ClassT_co@Bar, **P@convert) -> Coroutine[Any, Any, Any]" cannot be assigned to parameter "func" of type "(Foo, **P@__init__) -> Coroutine[Any, Any, Any]" in function "__init__"
  Type "(ClassT_co@Bar, **P@convert) -> Coroutine[Any, Any, Any]" cannot be assigned to type "(Foo, **P@convert) -> Coroutine[Any, Any, Any]"
    Parameter 1: type "Foo" cannot be assigned to type "ClassT_co@Bar"
      Type "Foo" cannot be assigned to type "ClassT_co@Bar"
trim tangle
#

because in its __init__, you have a P typevar which doesn't make sense in that context

fathom river
#

if I want to subclass Foo, would I just pass ClassT_co as its generic argument?

class Baz(Foo[ClassT_co]): ...
soft matrix
#

yeah

tranquil turtle
#

did mypy became better at inference?

def foo() -> list[dict[str, int]]:
    result = []
    for _ in range(5):
        result.append({'x': 1, 'y': 2})
    return result
``` now i have no errors on `result = []` line and `reveal_type(result)` shows correct type
oblique urchin
tranquil turtle
#

yeah, youre right
it also works on mypy 0.900

fleet quartz
#

is there a way to print an image in python

wise rose
#

can anyone recommend an easy to understand tutorial on type-hinting and/or mypy? i really want to understand it. but i'm really dumb

soft matrix
#

have you tried anything listed in the pins?

wise rose
soft matrix
wise rose
#

ty i will give the realpython site dev.to one a try i 've been to the others they don't work on my rock brain

rustic gull
#

i recently migrated from mypy to pyright ( MS pylance extension ) and i'm facing issues with attrs defined classes. so for example i have this piece of code: ```py
import attrs

@attrs.define(kw_only=True)
class Foo:
_bar: int
```, attrs converts the class to be initiated without the _ in bar argument, which would be Foo(bar=1) but i get typing errors when i do that... (https://imgur.com/ycOgYAI.png)
i get errors if i change bar to _bar ( TypeError: Foo.__init__() got an unexpected keyword argument '_bar')

pastel egret
#

Pyright uses typing.dataclass_transform to support attrs, and doesn't have a specific implementation, so it deliberately does not support this along with a bunch of other attrs-exclusive features. https://github.com/microsoft/pyright/issues/3192

rustic gull
#

oh, that sucks. i'm using attrs because i need the kw_only argument support in 3.8 ( which dataclasses added in later versions ), guess i'll just make that classvar a public one

elfin nexus
#

how can i type hint
"a tuple of an varying amount of integers"
tuple[int] is for example a tuple of a single integer

def return_zeroes(a: int) -> tuple[*int]
  if a == 0:
    return tuple(0)
  elif a == 1:
    return tuple(0,0)
  elif a == 2:
    return tuple(0,0,0)
  else
    return tuple()
soft matrix
#

tuple[int, ...]

tranquil turtle
proud brook
#

Or (0,) * a since that's what they seem to want to do with the ifs

#

Maybe add one before multiplying

final maple
#

Would someone be willing to explain the TYPE_CHECKING constant to me? I read the pep484 part about it but still have questions

soft matrix
#

!d typing.TYPE_CHECKING

rough sluiceBOT
#

typing.TYPE_CHECKING```
A special constant that is assumed to be `True` by 3rd party static type checkers. It is `False` at runtime. Usage:

```py
if TYPE_CHECKING:
    import expensive_mod

def fun(arg: 'expensive_mod.SomeType') -> None:
    local_var: expensive_mod.AnotherType = other_fun()
```  The first type annotation must be enclosed in quotes, making it a “forward reference”, to hide the `expensive_mod` reference from the interpreter runtime. Type annotations for local variables are not evaluated, so the second annotation does not need to be enclosed in quotes.
soft matrix
#

i think the example here explains it well

#

is there anything that that doesnt clear up?

final maple
# soft matrix is there anything that that doesnt clear up?

Yeah. Excuse me if my question is kinda dumb or should be like common knowledge or sth but im pretty new to programming/python.
I dont really understand why/when i would use this. from my understanding the
if TYPE_CHECKING:
import expensive_mod
line is only true while type checking and not at runtime.
So when i run the program it will not import the expensive_mod?!

trim tangle
#

But the type checker thinks it's True

slender timber
#

!d typing.TypeVarTuple

rough sluiceBOT
#

class typing.TypeVarTuple```
Type variable tuple. A specialized form of [`type variable`](https://docs.python.org/3/library/typing.html#typing.TypeVar "typing.TypeVar") that enables *variadic* generics.

A normal type variable enables parameterization with a single type. A type variable tuple, in contrast, allows parameterization with an *arbitrary* number of types by acting like an *arbitrary* number of type variables wrapped in a tuple. For example:
slender timber
#

Seems immensely useful

soft matrix
#

its hopefully gonna get much more useful after they fully specify all of its behaviour

trim tangle
#

and Map

deep saddle
#

why doesn't this work

#
def get_database_type(python_type: type) -> str | None:
    return {int: "INTEGER", float: "REAL", str: "TEXT"}.get(python_type)```
#

error:

trim tangle
#

try ```py
_db_types: dict[type, str] = {your_dict_thing}

shadow sun
#

Usually you don't need to worry about it

round bolt
#

How would I typehint a function returns that returns a list that has N integers where N is inputed num

def n_digits(num: int | str) -> [int * num]:
    ...

n_digits(3) -> [1, 1, 1]
n_digits(8) -> [8, 8, 8, 8, 8, 8, 8, 8]
buoyant swift
#

you can't specify the length of a list

round bolt
soft matrix
#

if you wanted to (not that you can actually do this like this) use a tuple

#

youd need something like rust's const generics

pale otter
#

is it not possible to type hint multiple things when unpacking unless i unpack one thing at a time?

# this is doable
varA: str = some_func( ... )
varB: float = some_other_f( ... )

# but what about this?
varA: str, varB: float = big_func( ... ) # big_func returns two things, a str and a float
trim tangle
#

Well, you can do

varC: tuple[str, float] = big_func()
#

But again, the type is probably inferred correctly if big_func has a type annotation

rustic gull
#

hey, i was wondering if it was possible to create a typehint for a callable which takes a non-fixed amount of arguments and the annotation for the first argument should be a specific one, others can be typing.Any/whatever provided

oblique urchin
rustic gull
# oblique urchin maybe something with callable protocols, like `def __call__(self, x: SomeType, *...

tysm this worked for typing! i implemented it using a typing.Protocol subclass py class AppCommandCallbackT(typing.Protocol): def __call__(self, inter: ApplicationCommandInteraction, *args: typing.Any) -> typing.Any: ... but now it expects me to provide the first argument name as inter, lemme provide the full context. im actually trying to make a decorator that accepts an async function with the type above. so if i try something like ```py
@client.with_slash_command(name="test", description="")
async def foo(a: wyvern.AppCommandInter, b: str) -> None:
...

instead of

@client.with_slash_command(name="test", description="")
async def foo(inter: wyvern.AppCommandInter, b: str) -> None:
...

#

AppCommandInter is just an alias for ApplicationCommandInteraction here*

#

making inter in the protocol a positional-only argument fixed the issue, thanks!

fathom river
#

you could use typing.Callable and typing_extensions' Concatenate too

rustic gull
#

i looked into callable but it accepts either fixed number of args or no annotations for args at all

fathom river
#

you set Callable's first argument to Concatenate, and inside Concatenate you add the types that you would inject to your callback. After that, you add a ParamSpec type

from typing import TYPE_CHECKING, Any, Callable

if TYPE_CHECKING:
    from typing_extensions import Concatenate, ParamSpec
    P = ParamSpec("P")

def decorator(f: Callable[Concatenate[type1, types2, ..., P], Any]) -> Any:
    ...

in this case, you'd replace type1 with AppCommandInteraction, followed by the ParamSpec which would be the non-fixed arguments that would get called
see also: https://peps.python.org/pep-0612/

rustic gull
#

oh, i find protocol much easier but i'll take a note of this, thanks!

mighty lindenBOT
tranquil turtle
#

When join types?

tranquil turtle
viscid cedar
#

would type hinting self in methods be alright, to be consistent?

#

It would look a bit, extreme however unless theres a good reason not to; why wouldnt you

#

oh, its not even possible is it. At least it doesnt seem to recon its own type by name.

#

meaning:

class A:
    def func(self : A,  var : int):
        print(var)
        

will give "NameError: name 'A' is not defined"

round bolt
#

You could directly typehint typing.Self

pastel egret
#

You need to use quotes or from __future__ import annotations to use A, since that hasn't yet been defined when the inside of the class statement is being executed.

#

But unless you need to, no point annotating self, type checkers know what it is already.

viscid cedar
#

It awfully sounds like its not even worth it then

hallow flint
#

yeah, it's not really worth it

rustic gull
#

Is there any tool for static analysis to populate typehints?

hallow flint
#

jelle’s autotyping (when combined with pyanalyze) is probably the best thing there is. or maybe get a language model to do it

thick gazelle
#

hey everyone,
I want to ask whats benefit of mypy? I saw that its better for testing but what about performance. I saw that good example but I am bit confused how to implement my all codespace as well.

And the most important thing ; do you advice to use it?

soft matrix
#

i wouldnt currently recommend using mypyc its too early alpha and has a lot of memory safety issues

#

its however good for making sure your code works before you even run it

tranquil turtle
#

mypyc is stupid sometimes, it gives weird errors at runtime almost without explanation and it has a lot of edge-cases and exposed implementation details

atomic fjord
#

What is typing

blazing nest
#

I recommend using Cython for the majority of use-cases one might consider mypyc

soft matrix
#

As usual with these kind of things a better algorithm or data structure will probably give you a better performance increase than just compiling it

elfin nexus
#

How can i type hint lambda

def my_func()-> Lambda:
  ...
tranquil turtle
#

!d typing.Callable

rough sluiceBOT
#

typing.Callable```
Callable type; `Callable[[int], str]` is a function of (int) -> str.

The subscription syntax must always be used with exactly two values: the argument list and the return type. The argument list must be a list of types or an ellipsis; the return type must be a single type.

There is no syntax to indicate optional or keyword arguments; such function types are rarely used as callback types. `Callable[..., ReturnType]` (literal ellipsis) can be used to type hint a callable taking any number of arguments and returning `ReturnType`. A plain [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable "typing.Callable") is equivalent to `Callable[..., Any]`, and in turn to [`collections.abc.Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable "collections.abc.Callable").
elfin nexus
#
import typing
def my_func()-> typing.Callable:
  ...

Does not seem to work nevermind it works

blazing nest
#

You'll want to parametrize it for accurate typing

carmine phoenix
#

if you add overloads to youre function specifying each possible input-type and return-type, do you then make the final implemention's return type Any, empty, or a Union of all possible return types?

soft matrix
#

either Any or a union of all the return types

carmine phoenix
carmine phoenix
soft matrix
#

that doesnt sound like an awful idea

#

maybe ask on python/typing

carmine phoenix
#

the website? reddit?

soft matrix
carmine phoenix
#

thanks brother

carmine phoenix
#

Can anybody verify that builtin types like: list, tuple, etc. are subscriptable in Python version 3.8.10 (using from __future__ import annotations)?
Cause it says that it was introduced in Py 3.9 but works from 3.7 and beyond using the future imports but as you can see it raises an error.
Does anybody know whats wrong here because I got many similar errors where ppl say it works with future imports but dosent actually work on my PC. Could it be that I need to enable some feature or maybe this dosent work withing packages? just guessing...

  File "C:\Users\Shner\Desktop\poetry\pandasdb\pandasdb\table.py", line 15, in <module>
    test = list[str]
TypeError: 'type' object is not subscriptable

Process finished with exit code 1
oblique urchin
#

it doesn't make anything subscriptable, it just makes it so annotations aren't evaluated

carmine phoenix
#

Do I also need to add it to the module that imports the objects from package\table.py? (i.e the tests)

oblique urchin
#

the code in your sample is not in an annotation

carmine phoenix
#

do you mean I need to add quotes to list[str]?

#

then can I still save it to a variable ?

soft matrix
#

yes

#

you will however need to quote it and annotate it as a TypeAlias

carmine phoenix
#

wait does annotations mean quotes?

soft matrix
#

no

carmine phoenix
#

what does it mean literally?

carmine phoenix
oblique urchin
carmine phoenix
#

oh so the actual type-hint, my bad 😅

#

btw I need some advice, I'm currently converting all the str | int to Union[str, int] and also list[str] to List[str], all this to make my package compatible with Python 3.7 and upwards (currently only works in 3.10).

So basically I'm gonna do two different versions, one for 3.7 - 3.10, and another one for 3.10 upwards. And the only thing that I'm changing are the type hints, all the functionality is exactly the same,
so at this point I'm thinking I might as well remove all the type hints from the modules and create a separate files that annotate the functions and methods (kind of like std library does, although I dont know how it works exactly), any thoughts on how yall would approach this?

soft matrix
#

i seriously wouldnt bother

#

just leave them as is and import future.annotations

carmine phoenix
#

the thing is that most ppl using my package are data people, and they dont really update their Python as long as Pandas works lol

void panther
#

the future import automatically turns annotations into strings, so you'll only have to do that for things like the test = list[str] as that's just doing anormal assignment at runtime instead of an annotation

carmine phoenix
void panther
#

you have to manually use a string for those cases

#

potentially annotating with TypeAlias (which will be stringified automatically by the import)

oblique urchin
void panther
#

for versions below 3.10 you can import it from typing_extensions under an if TYPE_CHECKING

carmine phoenix
#

holy shit!! it works now!

from typing import Union, Generator, Callable, Any, overload, Literal, TYPE_CHECKING
from typing_extensions import TypeAlias

PrimitiveTypes = Union[str, int, float, bool, None]
if TYPE_CHECKING:
    TableRow: TypeAlias = list[str]
#

the weird thing is that I can actually do from typing import TypeAlias but only within if TYPE_CHECKING...

#

and just to confirm, can I just do: TableRow = list[str] cause its kina obvious its a type alias?

#

btw I never import TypeAlias, so I dont really know how it works..

trim tangle
#

TYPE_CHECKING is just False at runtime

vivid ore
#

is it defined that way in the module? just TYPE_CHECKING = False?

trim tangle
#

yeah

#

!e

import typing

print(typing.TYPE_CHECKING)
rough sluiceBOT
#

@trim tangle :white_check_mark: Your 3.11 eval job has completed with return code 0.

False
vivid ore
#

cool

#

I have a problem
I’m trying to make a class that inherits from classmethod, but mypy yells at me when I inherit from me because I don’t give the generic parameters—but classmethod doesn’t have the subscript functionality. Is there a Classmethod in typing or something?

#

…nope

trim tangle
#
if TYPE_CHECKING:
    Classmethod = classmethod
else:
    Classmethod = defaultdict(lambda: classmethod)
``` or something like that
vivid ore
#

wow that’s cursed

#

thanks

soft matrix
#

i generally prefer

class MyClassmethod(classmethod[Foo] if TYPE_CHECKING else classmethod): ...
#

although i dont think mypy will work with either of these

#

might be worth opening an issue on the cpython issue tracker about this and saying that classmethod should support subscription

rose root
#

Is it possible to typehint this properly or no ```py

from typing import Any, Callable, Coroutine

def create(channel_id: int | None = None) -> Callable[..., Any] | Coroutine[Any, Any, int]:

if channel_id is not None:
    async def _underlying(channel_id: int) -> int:
        return channel_id # placeholder

    return _underlying(channel_id)

def subscribe(func: Callable[..., Any]) -> Callable[..., Any]:
    return func # place holder

return subscribe

@create()
async def foo(bar: int) -> int:
return bar

await create(123)

#

I'm using Any as a placeholder rn

#

Oh yea, got it with overloads

#

nvm then ig

trim tangle
#

so an async def _underlying(channel_id: int) -> int would have the type of Callable[[int], Coroutine[Any, Any, int]]

#

or you could use Awaitable (Callable[[int], Awaitable[int]])

rose root
#

I'm returning _underlying(channel_id)

trim tangle
#

oh right

#

actually, why not make two different functions?

rose root
#

I don't know I just thought it would be cool to do something like that

#

I didn't know it would actually work until I tested, even though it was very obvious kek

trim tangle
#

if you have some sort of flag argument, and the body of the function branches on the flag as its first step, usually it's better to make two functions

#

that's just a simpler solution

stray summit
#

anyone aware of a detailed description of overloads

i'm having trouble with defining 2 interacting parameters that control type output

i have something that stores strings and has a get method like

def get(self, key, default: _D|None=None, convert:  Callable[[str], _T]|None = None) -> _D|_T| str | None

as far as i understood i need to declare at least 4 overloads or more
to make it understand that the return type union is limited in a way that (None/_D) match to the type of the default value and str/_T match whether convert was passed,

i tried a number of permutations and keep getting met with mypy overloads either not being able to produce all versions or missing some,

i'd like to understand the underlying logic so the usages of the api can imply the reduced types in use on a api call

trim tangle
#

Perhaps you could simplify it like this py def get(self, key, default: _T | None=None, convert: Callable[[str], _T] | None = None) -> _T | str | None then you will need these overloads: ```py
@overload
def get(self, key: str) -> str | None: ...

@overload
def get(self, key: str, default: _T) -> _str | _T: ...

@overload
def get(self, key: str, default: _T, convert: Callable[[str], _T]) -> _T: ...

@overload
def get(self, key: str, convert: Callable[[str], _T]) -> _str | _T | None: ...

stray summit
#

@trim tangle that's a good start, now what if i also wanted to call that one from a helper that passes convert/default over as parameters

trim tangle
#

wdym?

#

ah

#

well that's kinda complicated 🙂

#

maybe you could provide some more details/context?

stray summit
#

i just found a permutation that works thanks to your input ( i needed to add a few cases for none type vs not passed)

i'll clean up some more then link the pr i created for iniconfig

trim tangle
#

Why do you need convert though 🤔

stray summit
#

its part of the apis that where coined over 10 years ago and are still in use - its pretty convenient to do something like get("somekey", default=1, convert=int) when interacting with inifiles at a lower level

(no schema tools involved)

trim tangle
#

ah, ic

#

I guess that mostly stems from Python not having convenient ways of working with nullable values

stray summit
#

(those apis where first created somewhere between 2007 and 2009 so its slightly worse ^^)

trim tangle
# trim tangle Why do you need `convert` though 🤔

here's a super flexible and over-engineered solution 🙂

@dataclass(frozen=True)
class GetHandler(Generic[T, D]):
    on_missing: Callable[[str], D]
    convert: Callable[[str], T]

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

passthrough: GetHandler[str, NoReturn] = GetHandler(lambda key: {}[key], identity)
default_none: GetHandler[str, None] = GetHandler(lambda key: None, identity)
default_key: GetHandler[str, str] = GetHandler(identity, identity)

def with_default(default: D) -> GetHandler[str, D]:
    return GetHandler(lambda _: default, identity)

###

    def get_complicated(self, key: str, handler: GetHandler[T, D]) -> T | D:
        if key in self._data:
            return handler.convert(self._data[key]
        else:
            return handler.on_missing(ley)

    def get(self, key, default=None, convert=identity):
        return self.get_complicated(key, GetHandler(lambda _: default, convert))
#

no overloads required, and you can easily pass the default and converter through

#

but yeah it's not a thing you do in Python

stray summit
#

hmm, galaxy brain has a seizure moment

#

basically its a pain that the type narrowing for parameter combinations has to be exploded (so having 2 parameters with effect means one might have to have 4-6 overloads depending on downstream usage)

trim tangle
#

that's generally a problem with boolean/other flags that change the return type

#

well, that kinda stems from the fact that there are really 2^N possibilities

#

(or more if you have more possible classes of values)

#

and if I as a human am calling a function with 6 boolean flags, there are indeed 64 ways to configure it

#

but yeah, Python's type system is quite lacking in some more powerful transformations that TypeScript has

stray summit
rare scarab
#

About typescript?

stray summit
#

about the powerful transformations

#

it wouldbe nice to have partially conditional return types

rare scarab
#

In typescript, you can type a function that converts a object between snake and camel case

#

You can also build "literals" using template strings

stray summit
#

sounds like thats basically touring complete typing

rare scarab
#

Almost.

stray summit
#

i'd love to be able to do things like type a wrapper object based on what it wraps

rare scarab
#

No proxy objects yet

stray summit
#

(particular use-case would be api specs, have a sans-style spec of the transforms, and then dynamic sync/async wrappers one can use with the spec against requests/urllib/httpx/aiohttp)

#

are there recommended api consumer tools for that these days?
(i have a lot of openapi-code-generator hell i'd love to drop but the python-side for the clients is not quite there)

#

hmm, sorry for the ot

rare scarab
#

So an async version, then a sync wrapper?

#

You can at least type your args and kwargs with unpack and typeddict

stray summit
#

@rare scarab no, more like the wrapper that uses the api would need dynamic typing to map the request/response parsing/creation of the sans api onto the types of the used api

so one woudl have something like

api = RequestsApiClient(mylib.MYAPI); api.something(...)

#

(so one would need to infer the methods of the api object based on myapi)

rare scarab
#

That defeats the point of static typing

stray summit
#

@rare scarab its meta-typing in a sense - a structural wrapper that replicates a behavior combination over all methods of object passed in, its static in the sense that as the types are know, their methods are known,

trim tangle
#

there are interpreters for Brainfuck and SQL in typescript's type system

stray summit
#

basically if i have a api spec type that for example has methods in the form of


class MyApiSpec:
  def get_user(self, id: uuid) -> UserRequest[User]:
    return RequestWithReposneParser(parse_user, f"/users/{id}))

then i want to be able to auto-type proxies so that they behave as if thy where defined as


class MyApi:
   # type seen but not actualy implemented
   def get_user(self, id: uuid) -> User:
      request = self._spec.get_user(id)
      response = self.__client.request(request)
      return request.response_parser(response)

   def __getattr__(...):
     #sweet magic here to make/fake the methods
trim tangle
#

oic

#

yeah that is a thing in TypeScript 🙂

#

Hmmm well

trim tangle
#

Like, first you get a Personal Data Permit and then fetch the user with it

#

Or maybe you are making a nicer higher-level wrapper for this API and you want it to work with both sync and async

stray summit
#

if get_user needs 2 requests, then at the level i showed it would turn into 2 calls and a breaking change

both sync and sync higher level wrappers are not considered as that would need to deal with entities, cached/local data and their interaction + interaction patterns - it would need research and quite the amount of good luck

#

currently i'm of the opinion that the high level wrappers need to pick a coloring (either sync or async) - creating tools that enable to flip around that coloring is outside of what i can sensibly spend time one

someone that has more deep experience with making one of those may disagree, personally im not aware of a attempt

rare scarab
#

You can do that for single functions, but I don't think you can generate names like that on classes

stray summit
#

currently its nto possible as the type system doesnt have "parameter based annotation generation"

grave geyser
#

I missing something basic but my google fails me.
How can I check for at type so that the type checker accepts it?

    def add_text(self, text: str | list[str]):
        if type(text) == list[str]:
            self.add_text_list(text)
        ...

   def add_text_list(self, text: list[str]):
       ...
#

Argument of type "str | list[str]" cannot be assigned to parameter of type "list[str]"

soft matrix
#

youll need a typeguard function for this

#

the type(text) will never be list[str]

oblique urchin
#

or just do if not isinstance(text, str):

soft matrix
#

although saying that you can probably just check isinstance(text, list)

grave geyser
#

Well isinstance does not work with list[str] but yeah, inverting the check

soft matrix
#

no im saying isinstance(text, list) should work

grave geyser
#

Why does the type checker work like this?

soft matrix
#

doesnt need to be list[str]

oblique urchin
soft matrix
#

isinstance fails at runtime

grave geyser
#

I mean why cant isinstance check for list[str] ?

oblique urchin
soft matrix
#

i misread sorry

oblique urchin
#

it being list[str] is purely a concept at typechecking time

#

what should isinstance([], list[str]) return?

#

and for a really long list, should we check all elements to see if they are strings?

grave geyser
#

Ah, of course, now it makes more sense

soft matrix
#

didnt python 3.4 typing or something actually allow the isinstance checking?

#

did that have O(n) behaviour?

oblique urchin
soft matrix
#

yeah im talking about an early version

trim tangle
grave geyser
#

Thanks but I think I got it. Type checker and runtime needs to work together, and runtime of course has no concept of a list[str] since lists are heterogeneous

trim tangle
#

yep

grave geyser
#

(Now writing a python script to transform pybind11-stubgen generated stubs into something that works)

rose root
#

How would I be able to restrict the decorated functions params typehint?

#

I have Events.WILDCARD[str] basically, I now want to type error whenever the functions signature doesn't match that type for the arguments

#

Oh nvm got it to work

#

But I'm sure there is probably a better way out there

fathom river
#

what's Events/Event?

fossil nest
#

if i have a variable that can be an instance of an object or None, is the None implied if i set it as default
(is this valid or do i need the Instance | None typehint?

variable: Instance = None
fossil nest
#

thanks

elfin nexus
#
dict[str:int]

is there a way to type hint the expected keys of dict
(turn it to something like this )

dict['a': int, 'b':int]

or even

dict['a': int, 'b':str]

or this something that must be specified in the docstring outside of type hinting

soft matrix
#

!d typing.TypedDict

rough sluiceBOT
#

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

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

use this

slender timber
#

there a type safe way of module level properties?

#

or preferably a maintained library?

soft matrix
#

yeah you probably can if you write your own decorator for it

vivid ore
#

did whoever think of including ... in Python intend for its use as it is used in typing? because that’s one hell of a good way to use it!

tranquil turtle
#

It is used in numpy for slicing

#

x[..., a:b]

tranquil turtle
#

dict['a': int, 'b': int] i love this syntax

vivid ore
#

Why was the extra __class_getitem__ method introduced? Can’t a class just implement __getitem__?

oblique urchin
#

!pep 560

rough sluiceBOT
#
**PEP 560 - Core support for typing module and generic types**
Status

Accepted

Python-Version

3.7

Created

03-Sep-2017

Type

Standards Track

oblique urchin
vivid ore
#

I see

atomic lodge
vivid ore
vivid ore
#

it’s a real bummer you can’t parameterise Self

soft matrix
#

(It's hard)

clear zephyr
vivid ore
clear zephyr
#

or if you have class named Test
def f(x) -> "Test":
...

trim tangle
#

because of subclassing

soft matrix
#

i mean self wouldnt even work there cause its unbound

slender timber
#

Any PEPs for standardizing __orig_bases__?

soft matrix
#

Wdym standardising it?

#

It's in the same pep as class getitem iirc

#

It's just the return of mro_entries

slender timber
#

I mean its an implementation detail yet

soft matrix
#

No it's not an implementation detail

slender timber
#

There's no good way to access it

#

Why is it not in typeshed then?

soft matrix
#

It's probably cause it's really hard to type reliably

slender timber
#

Isn't it just a tuple?

soft matrix
#

And 99.999% of people literally never need it

#

Yeah but whether or not it's there is really hard to tell

#

Not every type has it

slender timber
#

Only subclasses of Generic have it

#

ok

oblique urchin
soft matrix
#

Wouldn't it be on type?

oblique urchin
#

oops yes

oblique urchin
#

I think we've added a few other attributes that are only sometimes present

#

open an issue on GitHub

soft matrix
#

Oh I didn't know that

#

Ok well then yeah sure it should probably be there

slender timber
oblique urchin
#

e.g. typeshed has object.__doc__

slender timber
#

why is it typed as str | None though?

#

If an object has no __doc__ it should raise AttributeError, no?

oblique urchin
#

it's None on functions with no docstring

soft matrix
#

is it to avoid incompatibility issues in subclasses?

oblique urchin
#

but it's kind of weird we have it on object, I think we tried to remove it but ran into some trouble

slender timber
#

The descriptor methods aren't present either

vivid ore
#

ok I have a problem

#

I moved my code into a module and apparently that makes mypy stricter

#

so now I get errors everywhere I didn’t before

#

like this one

#

for context: Just is a subclass of Maybe. The only thing different about the type layout of Just vs Maybe is that Just.value: T.

#

mypy should know that self.value == other.value is a bool

#

…but it says this: Returning Any from function declared to return "bool"

pastel egret
#

Well mypy is actually correct, __eq__ can return any value. Numpy arrays are one of the more well known objects that don't return bools.

green gale
# vivid ore

don't you want to raise that exception, not return it?

pastel egret
#

NotImplemented isn't an exception.

vivid ore
#

I thought NotImplemented is different from NotImplementedError()

pastel egret
#

They're unrelated yeah.

green gale
#

ah fuck, i always get those names confused.

#

how does that work with bool?

soft matrix
#

it generally doesnt

vivid ore
#

mypy appears to have an exception for it

#

because it doesn’t complain at all

pastel egret
#

NotImplemented is typed as Any / special cased in these methods.

soft matrix
#

im pretty sure you get a syntax warning or something nowadays if you do ```py
NotImplemented == True

vivid ore
#

Should I just type the method as -> object?

soft matrix
#

oh maybe you dont 🤔

pastel egret
#

Do return bool(self.value == other.value)?

vivid ore
#

Or should I set the parameters to SupportsRichComparison)

#

oh ok

#

that shuts it up, thanks

pastel egret
#

No you don't need rich comparison, but a __eq__() protocol would work.

vivid ore
#

I thought that wouldn’t work

soft matrix
#

everything should support eq

#

there are a couple exceptions

pastel egret
#

Yes, but this would add the constraint that it returns a boolean.

soft matrix
#

oh right

vivid ore
#

how do other containers do it?

#

do they coerce it to a bool?

#

is __eq__ always expected to return something with __bool__?

soft matrix
#

i wanna say you cant ever get a builtin container to return not a bool

pastel egret
#

I think they don't check, just let exceptions bubble up.

#

Everything also has a __bool__ method.

plain dock
#

!d object.__eq__

rough sluiceBOT
#

object.__eq__(self, other)``````py

object.__ne__(self, other)``````py

object.__gt__(self, other)```
These are the so-called “rich comparison” methods. The correspondence between operator symbols and method names is as follows: `x<y` calls `x.__lt__(y)`, `x<=y` calls `x.__le__(y)`, `x==y` calls `x.__eq__(y)`, `x!=y` calls `x.__ne__(y)`, `x>y` calls `x.__gt__(y)`, and `x>=y` calls `x.__ge__(y)`.

A rich comparison method may return the singleton `NotImplemented` if it does not implement the operation for a given pair of arguments. By convention, `False` and `True` are returned for a successful comparison. However, these methods can return any value, so if the comparison operator is used in a Boolean context (e.g., in the condition of an `if` statement), Python will call [`bool()`](https://docs.python.org/3/library/functions.html#bool "bool") on the value to determine if the result is true or false.
plain dock
# rough sluice

By convention, False and True are returned for a successful comparison. However, these methods can return any value [...] Python will call bool() on the value

vivid ore
#

could you give me an example of a numpy object that doesn’t return bool?

#

I want to test this

#

and I’m unfamiliar with numpy

oblique urchin
#
Out[23]: array([False])
buoyant swift
#

!e basically it vectorizes it, so you can do like,

import numpy as np

arr = np.arange(10)
print(arr)
print(5 > arr)
rough sluiceBOT
#

@buoyant swift :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | [0 1 2 3 4 5 6 7 8 9]
002 | [ True  True  True  True  True False False False False False]
vivid ore
#

ok it returns a bool
good to know that calling bool is usual behaviour

#

I want my container to feel as vanilla as possible

buoyant swift
#

bool(numpy_array) only gives an answer if you have a 1 element array

vivid ore
#

does anyone know why mypy gets stricter once you go into a module? I had mypy.ini say strict = True before…

#

yes, and as you can see here the builtin list just passes on that error

#

so me calling bool would match builtin behaviour

#

I want my Maybe type to feel like a list that can only have one element

vivid ore
#

hm I guess my settings weren’t coming through

#

weird

#

ok so this is a good reason not to use pyright
it gets stuff wrong

#

like in this case

#

I would expect that I have something wrong about variance

#

but No!

#

both the bottom end of the type hierarchy and the top end don’t shut it up!

#

I tried NoReturn and object

#

what I’m trying to express with

    @overload
    @classmethod
    def _class_call(cls) -> Nothing[G]:
        ...

is that the Nothing can contain literally anything! It doesn’t care about its contained value…

#

it’s basically both covariant and invariant

#

in Haskell we have Functor and Covariant for functors that do both ways, which is kinda different, but you can still infer that they don’t care about their type argument

#

sorry

#

Contravariant

#
phantom :: (Functor f, Contravariant f) => f a -> f b 
#

but no

#

TypeVar cannot be both covariant and contravariant

#

anyways mypy gets the weird case right

#

it doesn’t complain about G being incompatible with G

#

and it correctly works on one end of the type hierarchy and doesn’t on the other

pastel egret
#

If it’s both covariant and contravariant, is there any operations you can be sure it can do? Wouldn’t that just be object?

vivid ore
#

the point is that if it’s both its type argument is literally irrelevant

#

hm maybe I should make Nothing inherit from Maybe[NoReturn]

#

that worked fabulously

#

it still doesn’t shut up pyright

#

Type "NoReturn" cannot be assigned to type "G@_class_call"
yes it can be you silly idiot

#

it has no complaints about this

brisk hedge
#

there's slight precedence for it (in the sense that Any is top and bottom) but I don't believe there's a lot of motivation to support phantom variance in python

#

and inhabitation checks are also quite lackluster as you've seen (for similar reasons; the primary use case is diverging functions)

vivid ore
#

ok but I hope you agree this is a pyright bug

brisk hedge
#

I agree it's a limitation and you're free to post a gh issue about it

#

who knows, maybe eric has some extra time on his hands BABAXD

vivid ore
#

ok sorry about being so harsh about it

#

I’m just used to a robust type system so I get unreasonably upset at things like this

#

really sorry, again

pastel egret
#

I guess the difference is that Python itself is ultimately dynamic, so it’s fine if the type system can’t handle all cases.

vivid ore
#

yeah

soft matrix
#

just fyi i think the preferred variable here is Never but that is besides the point

#

they are the same thing

vivid ore
#

I am using 3.10 so I don’t have Never atm

soft matrix
#

can always use it from typing_extensions

vivid ore
#

ok

upbeat wadi
#

(using pyright) ```py
def convert(converter: Callable[[str], T]) -> Callable[[Callable[[list[T]], PT]], Callable[[list[T]], PT]]:
def decorator(func: Callable[[list[T]], PT]) -> Callable[[list[T]], PT]:
func.converter = converter
return func
return decorator

@convert(lambda x: int(x))
def part_one(lines):
reveal_type(lines) # Type of "lines" is "Unknown"
return ''

is there a way for the type to be inferred automatically
#

or would I have to explicitly typehint it

old oar
#

Can you make your own typehint?

vivid ore
#

You have to explicitly typehint it, I think

vivid ore
old oar
#

Lets say I have a class:

#
class Bob:
  def __init__(self): pass
#

Can I do

#

def apple(x: Bob) -> None:
  pass 
#

or does that not work?

oblique urchin
#

yes you can

old oar
#

Okay, thanks 🙂

heady flicker
upbeat wadi
#

Got it, thanks

fresh saffron
slender timber
#

Is TypeAlias not generic?

#

in Python less than 3.10

#

For this

SimpleAdapter: TypeAlias = ct.Adapter[T, T, U, U]

I get this error:

Bad number of arguments for type alias, expected: 0, given: 2  [type-arg]
#

but i dont get this error (nor the error which asks me to explicity use TypeAlias) in mypy on 3.10

pastel egret
#

It's not a type, it's a marker to indicate that a variable is a type alias. How is ct.Adapter defined?

slender timber
#

And I am using a TypeAlias to reduce the number of type params as they are same

pastel egret
#

That should work fine.

slender timber
#
class Adapter(Subconstruct[SubconParsedType, SubconBuildTypes, ParsedType, BuildTypes]):
pastel egret
#

Ah. You need to inherit from Generic, that class isn't generic.

slender timber
#

why is this an error only now?

pastel egret
#

Ah that is still generic, this is quite strange.

tranquil turtle
slender timber
#

so then how can i simplify the adapter?

#

make SimpleAdapter a subclass?

tranquil turtle
#
if TYPE_CHECKING:
    class SimpleAdapter(Adapter[T, T, U, U]): ...
else:
    SimpleAdapter = Adapter[T, T, U, U]
slender timber
#

:((

#

all this extra code to satisfy type checkers...

soft matrix
#

this does just look like a mypy bug lol

slender timber
#

ok so, somewhere in a stub file:

class Adapter(Subconstruct[SubconParsedType, SubconBuildTypes, ParsedType, BuildTypes]):
    def __init__(self, subcon: Construct[SubconParsedType, SubconBuildTypes]) -> None: ...

and still when I use it like this:

class Log2(SimpleAdapter[int, float]):
    def __init__(self, subcon: Any, factor: int):
        super().__init__(subcon)

I get an error:

Too many arguments for "__init__" of "object"

where SimpleAdapter is now @tranquil turtle 's snippet.

#

and also why does this setting in pyproject.toml

[tool.mypy]
python_version = "3.7"

still cause mypy to output different errors when ran under 3.7 and 3.10?

#

one more, errors with Argument 1 to "__init__" of "_VSTPluginProp" has incompatible type "int"; expected "_VSTPluginEventID" but only on Python <3.10

#

seriously wtf mypy

trim tangle
sand raptor
#

I have never understood the difference between an Iterator and an Iterable, could someone explain it to me?

fathom river
#

Iterators are objects that have a __next__ and __iter__ methods, meanwhile Iterables are objects that only have an __iter__ method

an iterable is something that has values that could be produced in an order. An iterator is the object that is actually producing them.

  • nedbat
sand raptor
#

Ah thank you

fathom river
#

lmao I just noticed I mentioned Iterable twice

rare scarab
#

Iterator's __iter__ function should always return itself

vivid ore
#

How can you specify that a function takes arguments according to a ParamSpec variable?

#

ah I see

#

you do

P = ParamSpec("P")
T = TypeVar("T")
def with_args(*args: P.args, **kwargs: P.kwargs) -> Callable[[Callable[P, T]], T]:
    def with_func(func: Callable[P, T], /) -> T:
        return func(*args, **kwargs)
    return with_func
vivid ore
#

I have a problem with parameter specs

#

I have the following abstract method:

#
    @abstractmethod
    def call(self: Maybe[Callable[P, G]], *args: P.args, **kwargs: P.kwargs) -> Maybe[G]:
        pass
#

and I inherit from Maybe[T] (T is covariant) in Just

#

So I type my method

#
    def call(self: Just[Callable[P, G]], *args: P.args, **kwargs: P.kwargs) -> Just[G]:
        ...
#

But mypy insists that these signatures are incompatible

soft matrix
#

that looks like a bug

vivid ore
#

OK Imma report it

#

also my issue with pyright I had earlier isn’t an issue (apparently)

#

since mypy doesn’t recognize it I reported it to mypy instead, as it is their mistake and not pyright’s (apparently)

trim tangle
#

oh wait, I misread, sorry

#

just ignore me

slender timber
#

Any way to avoid re-annotating the impl func?

soft matrix
#

Yeah you can just use Any 😉

slender timber
slender timber
#

or would that erase the type annotation of the wrapped / decorated function

soft matrix
#

it shouldnt do

slender timber
#

also can i merge the int and str overloads?

soft matrix
#

yeah that can just be a union

slender timber
#

will pylance be faster if i hardcode the type hints?

#

so it has to spend less time inferring return type

heady flicker
#

Probably not but I'm not an expert in the internals of pylance. Remember that pylance still needs to go through the entirety of the code to typecheck stuff

#

So I doubt hardcoding typehints will make your code significantly faster.

slender timber
#

why is this return type inferred as noreturn?

soft matrix
#

because it always raises?

slender timber
#

oh yea

#

dum me

#

it should be return instead of break

slender timber
heady flicker
#

Yes, I have the same problem. It's a bug that we should report but I can't find a clear way of reproducing it.

soft matrix
#

same :)

#

i just restart the lsp

slender timber
#

started happening a month or so ago

heady flicker
#

Exactly.

vivid ore
#

What LSP are you using?

slender timber
#

Pylance

#

@vivid ore

vivid ore
#

Ok

slender timber
#

?

vivid ore
#

just wanted to know

#

I don’t actually know how to solve the issue

slender timber
#

somehow nobody does

#

Its been unfixed since month+ now

vivid ore
#

I have an issue I don’t know how to reproduce well
I have a class ```py
class Maybe(CallableABC, Generic[T]):
...
@abstractmethod
def call(self: Maybe[Callable[P, G]], *args: P.args, **kwargs: P.kwargs) -> Maybe[G]:
pass
...
def call(self: Maybe[Callable[P, G]], *args: P.args, **kwds: P.kwargs) -> Maybe[G]:
return self.call(*args, **kwargs)
...

where `CallableABC` is an instance of a metaclass that defines a custom `__call__`.
Now there is one bug here I know how to reproduce: Mypy complains that `*args` is the wrong type, that it should be “`<nothing>`” (whatever that is)
…but also, Pyright has a bug here: It complains that “Arguments for ParamSpec "`P@__call__`" are missing”.
…but when I try to reproduce this in a clean environment, only the Mypy error shines through. I suspect it has something to do with the `CallableABC` metaclass…
vivid ore
#

oooOh

#

silly me

vivid ore
#

is there any support at all for a container changing the type of its contents?

#

can you explicitly mark a method as type-unsafe?

#

(for consumers of your library)

soft matrix
#

wdym by unsafe?

vivid ore
# soft matrix wdym by unsafe?

a function that cannot be typed correctly because any use of it will change the container’s internal type, which apparently there is no support for currently, is my specific use case

vivid ore
# acoustic thicket wdym

i.e. having an imap (in-place map) function on a subclass of list that could transform the type

soft matrix
#

ah

acoustic thicket
#

interesting

soft matrix
#

i cant think of any way to do that

acoustic thicket
#

does any other language support this

vivid ore
#

¯_(ツ)_/¯

oblique urchin
#

sounds kind of like linear types

trim tangle
#

transforming the container sub-type is not really a thing, so you'll just use Any or some other kludge if you really need it anyway

vivid ore
#

ok

#

would a mutable container type that can only have one entry be invariant, or could it be covariant?

#

the example in the mypy docs doesn’t really apply because you can’t add another element, and it says that most mutable containers are invariant

oblique urchin
trim tangle
oblique urchin
#

now if you do b = Box[int](1); boom(b)

trim tangle
#

yeah

oblique urchin
#

your box no longer has an int

vivid ore
#

ok thanks!

oblique urchin
#

lol nice, we came up with the exact same example

oblique urchin
#

except you are trying to put animals in boxes, poor kitty

vivid ore
#

at least the box doesn’t have radioactive gas inside it

trim tangle
#

I thought cats love boxes

#

A box without gas would be a disaster for an alive animal

vivid ore
#

I was trying to make a Schrödinger’s Cat joke
it’s the only other intellectual cat joke I know

trim tangle
#

yeah I realized

vivid ore
#

is it by design that mypy & pyright always assume that the type of type(self) is specifically the specialized generic type that was declared as the parameter type while at runtime it’s always the unspecialized generic type that can still be specialized? wouldn’t it allow for more flexibility if

@dataclass
class Spam(Generic[T]):
    x: T
    def eggs(self: Spam[G]) -> Spam[tuple[()]]:
        cls = cast(type[Spam[tuple[()]]], type(self))
        return cls(())

worked w/o the cast?

#

on an unrelated note, I feel that the fact that Pyright warns about type parameters that are only used once in a function is kinda annoying since apparently it can make a difference on invariant types

trim tangle
#

can it?

viral spade
#

how to state that you have a class object? Like Union[MyObj, int] I believe says you either have a instance of MyObj or an int, but what if I want to say that the argument is a type variable -- i.e. it is either int or the class of MyObj

soft matrix
#

the class of MyObj isnt a type variable

trim tangle
#

so that would be type[MyObj] | type[int]

#

maybe you could give an example of what you want?

vivid ore
# trim tangle can it?

yeah, if you have something typed as self: Foo[object] and Foo[T] is invariant in T, it can refuse to bind in other methods where self: Foo[U], where usage of self: Foo[S] would’ve communicated it can work for any type

#

just ran into this

viral spade
#

I think that might be what I want

if I say type[MyObj] does it just refer to MyObj's class, or is there anyway to refer to MyObj or any subclasses of it?

vivid ore
#

it refers to any subclasses of it too afaicr

soft matrix
#

yeah it does

trim tangle
#

y

vivid ore
#

? wdym

trim tangle
#

oh that meant yes

#

not why

vivid ore
#

oh ok

regal summit
#

how would I typehint the class instead of an instance, so something like this:

class Tile:
  def __init_subclass__(cls, id: int) -> None:
    cls.id = id

class T1(Tile, id=1): pass
class T2(Tile, id=2): pass

id_to_tile: dict[int, ???] = {1: T1, 2: T2}```
buoyant swift
#

type[Tile]

#

but why do you need this 🤔

regal summit
#

ty

vivid ore
#

I’m getting an unclear error with mypy.
I have these methods:

    def __or__(self: MMaybe[G], other: MMaybe[G]) -> MMaybe[G]:
        return self.alternatively(other)
    
    def __ior__(self: MMaybe[G], other: MMaybe[G]) -> MMaybe[G]:
        self.ialternatively(other)
        return self

but mypy complains: Signatures of "__ior__" and "__or__" are incompatible [misc]
am I doing something wrong?

trim tangle
#

Is that the full error?