#type-hinting

1 messages · Page 57 of 1

grave fjord
#

you can basically only use a type[object] in isinstance checks

#

show your code?

dim trail
#

why? i mean, it works with type

tranquil turtle
#
from typing import Union

class X:
    __or__ = Union.__or__  #mypy: error   operator - Unsupported left operand type for | ("_SpecialForm")

bug?

#

not bug

#

Union is not type, it is instance of _SpecialForm

trim tangle
#

what are you trying to do?

tranquil turtle
#

i am implementing GenericAlias in python

grave fjord
#

Read the code in typing_extensions

spare mauve
#

like typing._GenericAlias ?

grave fjord
pastel egret
#

That’s an old backport for 3.4 and earlier.

trim tangle
spare mauve
#

or types.GenericAlias

spare mauve
#

looks like that's what e.g. list[int] gives you

#

yea, the API is only __args__ right

#

or some such thing

tranquil turtle
#

def __or__(self, other):
    return Union[self, other]
spare mauve
#

mine was actually no good

tulip hearth
#

How would i typehint an anyio task created by TaskGroup.start or start_soon

#

i see its none

blazing nest
#

That's because start_soon() doesn't return anything

#

So it is None

#

Do you want to get the result of a task?

#

@tulip hearth ^

tulip hearth
#

No, i just wanted to type hint the task but i figured out that anyio works a bit differently than normal asyncio code i.e in asyncio i can do sometask = asyncio.create(somecoro) which returns a future object, turns out anyio wraps everything

grave fjord
#

that's the trio magic

tulip hearth
#

O_o seems pretty magical indeed, its gonna take some time to adjust to this

blazing nest
#

Yeah if you wanted the result await it directly

tulip hearth
#

👍

soft matrix
spare mauve
twilit badge
#

Hi, first time I'm using overloads and I can't figure out how to solve this: ```py
@overload
def init(self, pattern: ValidPatternType, repetitions: int, or_more: bool = False):
...

@overload
def __init__(self, pattern: ValidPatternType, minimum: int, maximum: int):
    ...

def __init__(self, pattern: ValidPatternType, *args, **kwargs):
  pass
Basically, I don't know how to extract the attributes I need from `*args` or `**kwargs`, since the variables can be both positional and keyword, I'm struggling a bit with doing this, isn't there a better way to parse them out so that it matches the signatures from the overloaded functions?
#

I could probably figure out a way to parse it out manually, but I'd also like it to give out an exception if the *args or **kwargs can't be passed in the way the user provided them. i.e. if the function is called like with pattern="x", repetitions=12, random_variable="x") it should raise an exception that's at least similar to what python would give out if it were a real function taking those parameters, this just seems incredibly complex to do just to have some overloads

twilit badge
#

so, I found out I can do this: ```py
def init(self, pattern: ValidPatternType, *args, **kwargs):
s1 = inspect.Signature(
parameters=(
inspect.Parameter("repetitions", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD),
inspect.Parameter("or_more", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD)
)
)
s2 = inspect.Signature(
parameters=(
inspect.Parameter("minimum", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD),
inspect.Parameter("maximum", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD)
)
)
try:
bound = s1.bind(*args, **kwargs)
i = bound.arguments["repetitions"]
j = None
or_more = bound.arguments["or_more"]
except TypeError:
bound = s2.bind(*args, **kwargs)
i = bound.arguments["minimum"]
j = bound.arguments["maximum"]
or_more = False

  ...
oblique urchin
#

Why are you trying to use these overloads? If I were you I'd probably just write two different classmethods as alternative constructors

twilit badge
#

hmm, I could do that, though it would be a bit weird, I mean, I'd be defining 2 class methods that act very similarely to just init calls: ```py
class Foo:
def init(i: int, j: Optional[int] = None, or_more: bool = False):
...

@classmethod
def from_range(cls, minimum: int, maximum: int) -> "Foo":
    return cls(minimum, maximum, False)
    
@classmethod
def from_repetitions(cls, repetitions: int, or_more: bool = False) -> "Foo":
    return cls(repetitions, None, or_more)
To show the similarity, here are some examples for using init vs the class methods: ```py
x = Foo(1, 15)
x = Foo.from_range(1, 15)
y = Foo(15, or_more=True)
y = Foo.from_repetitions(15, or_more=True)
``` At this point, I might aswell just use the `__init__` alone, but to have a more descriptive way show up when initializing it I defined those overloads
oblique urchin
#

I find the classmethods a lot clearer and easier to reason about

twilit badge
#

they're certainly cleaner, but it just feels weird to have a class method that essentially does the same as __init__, also there was another purpose behind those overloads which was that they restricted what could actually be passed into __init__, they prevented things like setting both i and j along with or_more=True. While this could be handled explicitly in __init__ it's just more annoying to do

#

but I suppose it's better than the signature mess

rotund flax
blazing nest
#

Just use keyword arguments 😅

trim tangle
#

@hidden spoke list[int] is a list where each element is an integer. You can't append a list of integers to it.

#

If you want a list of lists of integers, that would be list[list[int]]

#

Sounds like you need a proper class 🙂

#

perhaps a dataclass

#

I've never seen someone learn about type annotations before classes 👀

#

I guess that's a good strategy

soft matrix
#

!d dataclasses

rough sluiceBOT
#

Source code: Lib/dataclasses.py

This module provides a decorator and functions for automatically adding generated special methods such as __init__() and __repr__() to user-defined classes. It was originally described in PEP 557.

The member variables to use in these generated methods are defined using PEP 526 type annotations. For example, this code...

red atlas
#
from typing import TypeVar, Iterable, Union

T = TypeVar('T')
Tree = Iterable[Union[T, 'Tree']]

this is valid, right? no type checkers ive tried seem to be able to handle the recursive aspect of it. am i doing it wrong? or recursive types are just poorly supported?

oblique urchin
spare mauve
soft matrix
#

oh i was thinking of UnionType

thin bridge
#

!d classes

rough sluiceBOT
#

Graph types

NetworkX provides data structures and methods for storing graphs.

All NetworkX graph classes allow (hashable) Python objects as nodes and any Python object can be assigned as an edge attribute.

The choice of graph class depends on the structure of the graph you want to represent.

rotund flax
#

!d metaclasses

rough sluiceBOT
#

By default, classes are constructed using type(). The class body is executed in a new namespace and the class name is bound locally to the result of type(name, bases, namespace).

The class creation process can be customized by passing the metaclass keyword argument in the class definition line, or by inheriting from an existing class that included such an argument. In the following example, both MyClass and MySubclass are instances of Meta...

loud osprey
#

hey

#

can anyone give a short summary of how type hinters like mypy work ?

trim tangle
loud osprey
#

I want to contribute to mypy but the code feels too complex and when I see the in code comments, they have too many technical terms

trim tangle
#

it is pretty arcane

#

I tried making a plugin for mypy, I didn't go very far

loud osprey
#

oh

spare mauve
#

I tried the same

#

I didn't get to the writing code part :p

trim tangle
#

!rule 5

rough sluiceBOT
#

5. Do not provide or request help on projects that may break laws, breach terms of services, or are malicious or inappropriate.

trim tangle
#

not the right channel, not the right server

soft matrix
hasty hull
trim tangle
#

I'm too stupid to understand mypy plugins

hasty hull
#

to be honest I don't really understand it anymore

#

it was just loads of trial and error really

fierce ridge
hasty hull
#

Yeah I regret even writing it lol

hasty hull
#

This bug has been fixed in pyright 1.193

trim tangle
#

Here's a cool trick that's now possible with TypeVarTuple:
https://paste.pythondiscord.com/okehayubut.py

@tail_rec
def factorial(n: int, acc: int) -> TailRec[int, int, int]:
    if n <= 0:
        return Return(acc)
    return Call(n - 1, acc * n)

reveal_type(factorial)  # function (int, int) -> int
fierce ridge
#

is this a mypy bug?

Incompatible types in assignment (expression has type "Sequence[Union[Literal['Parent'], Literal['Expert'], Literal['Educator'], Literal['Individual'], Literal['Other']]]", variable has type "List[str]")  [assignment]
#

those Literals are all strs pithink

#

is this some variance issue with List?

#

ah yep that was it

oblique urchin
#

that looks correct to me

#

not even related to variance

fierce ridge
#

i had this, more or less:

# 3rd-party code
get_some_text() -> List[str]: ...

# my code
parse_foo(parts: Sequence[str]) -> Sequence[Literal['a', 'b']]: ...

foo = get_some_text()
foo = parse_foo(foo)
#

i changed it to this and it worked:

# 3rd-party code
get_some_text() -> List[str]: ...

# my code
parse_foo(parts: Sequence[str]) -> Sequence[Literal['a', 'b']]: ...

foo_raw = get_some_text()
foo = parse_foo(foo_raw)
oblique urchin
#

--allow-redefinition 😄

fierce ridge
#

👀

#

is there a way to annotate a single line with that?

oblique urchin
#

no, but why wouldn't you always want it?

fierce ridge
#

because imo it shouldn't be default, "failing open" as it were

#

easy to assign the wrong thing to the wrong thing just because of a typo

blazing nest
trim tangle
blazing nest
#

But it would if you'd allow both args and kwargs?

trim tangle
#

but

blazing nest
#

Lol

#

But yeah.. that's cool

trim tangle
#

there isn't really a way to construct a ParamSpec from thin air

#

btw there should also be a return type here: ```py
def tail_rec(fn: Callable[[Unpack[P]], TailRec[Unpack[P], T]]) -> Callable[[Unpack[P]], T]:
...

but I removed it because of a pyright bug 🙂
blazing nest
trim tangle
#

lol nice error

trim tangle
#

I think there are no paramspec literals

fierce ridge
trim tangle
#

and I want a flat in Moscow

fierce ridge
#

i wish!

#

and i bet if i post that on python-ideas that's exactly what people will say

soft matrix
#

Protocol from a class is already a thing on the python/typing repo

#

param spec from a function sounds like what ambv suggested for callable syntax or maybe with the protocol pep

fierce ridge
soft matrix
#

Oh

#

Yeah I 100% agree, it should have come with ParamSpec

waxen raft
#

so uh if we have a class let's call it MyClass and we need to annotate an attribute of another class as being an instance of MyClass, do we do like var: MyClass = MyClass() or var: 'MyClass' = MyClass() or something else

oblique urchin
waxen raft
#

ok cool thanks

pastel egret
#

To the type checker (who doesn't care about definition order) it treats them the same, you just need the string in cases where the class wouldn't be defined yet.

#

On classes though, annotations are assumed to be instance attributes - you'd need typing.ClassVar[TheType] to indicate class attrs.

loud osprey
#

if I have to define a tuple of int of length 100, how would I type hint it?

#

because for tuple, I have to mention the type of each individual element

#

is there a shorter way to do it?

acoustic thicket
#

no, afaik

#

tuple[int, ...] would be a tuple of any number of ints, and i think thats the closest alternative

chrome thicket
#

Hi, is it possible to set type hints for **kwargs?

oblique urchin
chrome thicket
blazing nest
oblique urchin
#

Once I get PEP 464 into typing-extensions I'm planning to make **kwargs: Unpack[TypedDict] mean what you want it to mean in pyanalyze

#

Would still require a PEP for standardization though

trim tangle
#

that basically solves the nasty-kwargs problem

soft matrix
#

its not a thing yet

trim tangle
#

no I mean, in pyanalyze

oblique urchin
#

Not officially. PEP 646 supports the corresponding thing for *args so it seems logical to make **kwargs work the same way

oblique urchin
#

So just needs surface syntactic support

soft matrix
#

i personally dont really see the point in using a TypedDict for kwargs myself unless you are stubbing out a library. what other use cases are there that cant already be done now?

trim tangle
oblique urchin
#

You could have multiple wrapper functions for a function that takes a lot of kwargs

rough sluiceBOT
#

aiohttp/client.py lines 312 to 314

def request(
    self, method: str, url: StrOrURL, **kwargs: Any
) -> "_RequestContextManager":```
trim tangle
#

it just breaks my heart

soft matrix
trim tangle
# trim tangle it just breaks my heart

what was the point of carefully type hinting everything in _request if request is basically "you want to add an argument? well friend, you're up to a journey"

soft matrix
#

or something

#

i swear this reads so much better

#

instead of ```py
def request(
self, method: str, url: StrOrURL, **kwargs: Unpack[RequestFnKwargs]
) -> "_RequestContextManager":

trim tangle
#

that adds a new concept to the type system -- the ability to reference the type of a symbol

#

which is arguably more stuff to add to the already complex system

oblique urchin
#

It's also not necessarily strong enough because you may not support all parameters of _request

soft matrix
#

as i said i feel like this should have come with 612

soft matrix
#

this certainly could be a lot nicer if you could be named arguments in getitem aswell

trim tangle
#

Any considered harmful: ```py
return await response.json()

#

I think I made this mistake 3 times because I'm using httpx and not aiohttp

blazing nest
trim tangle
#

for some reason httpx decided to cram an async response and a sync response into one class 🤷

#

so I had to do json.loads(await response.aread())

#

but because .json() returns Any, type checking passed

blazing nest
trim tangle
#

just like in aiohttp

blazing nest
#

But you should've gotten an error then trying to await a dictionary?

trim tangle
#

yes

blazing nest
trim tangle
#

I did.

blazing nest
trim tangle
#

But it produces the same response object as the blocking client

oblique urchin
#

list[list[int]] means a list of lists of int

#

if you mean a numpy array you'd do something different

oblique urchin
#

I think mypy will recognize the type from later .append operations

trim tangle
#

huh, it does

#

pyright doesn't do that, for some reason

oblique urchin
#

I remember in mypy it's called "partial types" and it causes quite a bit of headaches

trim tangle
oblique urchin
#

Any. It only solves problems.

trim tangle
#

excuse the double ":)"

tranquil turtle
#

x: list[Obj] = []
or
x = list[Obj]()

rancid cloud
#

I am just looking at types in plain python3 (as opposed to mypy) and I noticed that type({}) returns <class 'dict'> but you can also use {} to create a set class, by doing myset = {1,2,5} then type(myset) returns <class 'set'>

#

is mypy a bit more "strongly" typed than generic python?

#

oh i showed up 5 hrs after that conversation. oh well.

blazing nest
#

!e print(type({,}))

rough sluiceBOT
#

@blazing nest :x: Your eval job has completed with return code 1.

001 |   File "<string>", line 1
002 |     print(type({,}))
003 |                 ^
004 | SyntaxError: invalid syntax
pastel egret
#

Basically, dicts were added to Python well before sets, so it had to stay that way for backwards compatibility.

blazing nest
#

There's no way to create an empty set with literals

pastel egret
#

Unless you count {*()}.

blazing nest
#

Lol

#

That's......

#

........

#

...odd

pastel egret
#

Not really, just unpacking a tuple. No colon, so it's a set. And the tuple is empty, so it's an empty set. Not too efficient though, it builds an empty set, then unpacks a tuple for no reason.

acoustic thicket
#

you almost never need to typehint self

pastel egret
#

Type checkers will deal with it correctly. However sometimes it's useful to specify it as a type var, so you can indicate the method returns/requires others of the same type (if the class is being subclassed).

#

Yeah.

trim tangle
#

@hidden spoke this

#

as you can see, Foo is hinted correctly, but BadFoo is not

#

so when you subclass BadFoo, everything kind breaks

rustic gull
#

is it because the self in BadFoo defaults to an invariant type?

pastel egret
#

Yeah, since it's not a typevar/generic at all.

trim tangle
#

and this type doesn't automatically get changed when you make a subclass

#

because you could have something like this: ```py
class BadFoo:
def do_something(self) -> "BadFoo":
return BadFoo()

rustic gull
#

oh I see it's fixed to BadFoo that's not great

kind bronze
#

whats type hinting

trim tangle
kind bronze
#

thanks

acoustic thicket
#

does mypy not support paramspec yet

#

sad

trim tangle
#
typeHintingChannel.on(/does mypy not support (.+?) yet/, message => message.reply(":pensive:"))
acoustic thicket
#

😔

trim tangle
#

!ban @acoustic thicket self-bot hyperlemon

rough sluiceBOT
#

:x: @trim tangle, you may not ban someone with an equal or higher top role.

trim tangle
#

Is there a way to type hint an instance of a class which inherits from some metaclass?

#

Like, I have ```py
class FooMeta(type):
...

class Foo(metaclass=FooMeta):
...

class Bar(metaclass=FooMeta):
...

#

and I want a type hint for an instance of Foo or an instance of Bar (or any other class based on FooMeta)

#

If I use FooMeta as the hint, it's for the class, not the instance

trim tangle
#

🥴

#

sad

soft matrix
#

You could theoretically do it with a protocol

#

T = TypeVar("T", bound=type)
class InstanceOf(Protocol[T]):
class: T

#

Although it needs type ignores

rocky anvil
rocky anvil
oblique urchin
rocky anvil
#

need to check

tranquil turtle
#

mypyc supports only what mypy supports

#

and mypyc isnt very fast, cython is better

#

mypyc classes cant be subclassed in pure python and patched

soft matrix
#

mypyc's benefit is that it should just directly speed up anything that currently can be typed checked by mypy

tranquil turtle
#

mypy has a lot of bugs and not implemented features

#

and mypyc build is very slow

#

cython build is a lot faster

soft matrix
#

cython is a lot older are you surprised?

#

cython also typing wise is a pain

rocky anvil
#

You always need cython where heavy computations need to be run in a single thread and don't want to spawn processes, I know it's a lot of pain but we can never escape that GIL thing....

oblique urchin
#

Until the nogil branch gets merged 🙂

rocky anvil
#

😌

trim tangle
#

RIIR

#

you get both speed an static typing 🙂

rocky anvil
#

I wonder to create a multithreaded desktop application with Python and C++ specifically for running cpu bound operation in a separate thread

#

but I need to learn language binding for that

soft matrix
#

pybind11 is nice so is cython

rocky anvil
#

what about swig?

soft matrix
#

never heard of it

rocky anvil
#

SWIG supports many languages and by SWIG you can bind language by writing inline code, not sure about it. You can Google it.

errant tulip
#

can you set type of a variable that was created using __setattr__? Say for an example object.__setattr__(obj, 'var', 5).

#

how do I set type of var?

soft matrix
#

set the type in what way?

#

like in __annotations__?

errant tulip
#

In the way I could have done var: int = 5, now if I use __setattr__, I can't define that var should be int

pastel egret
#

You just do var: int in the class block.

errant tulip
#
class Programme(object):
    all_: dict[int, Programme] = {}
    direct_conn: set

    def __new__(cls: Type[Programme], p_id: str):
        p_id = int(p_id)
        if p_id not in cls.all_.keys():
            obj = object.__new__(cls)
            obj.direct_conn = set()
            cls.all_[p_id] = obj
        return cls.all_[p_id]

Found this work around. Let me know if there is anything wrong in this code

errant tulip
pastel egret
#

Yep.

errant tulip
#

okay thanks

#

why can't I set type in __new__ block?

soft matrix
#

its probably a pycharm bug if you are using it

pastel egret
#

Which type can't you setL

errant tulip
#

if I do obj.direct_conn: set[Programme] in __new__, I get this error from pylance Type annotation not supported for this type of expressionPylance

#

I am using vscode

soft matrix
#

if you change obj to self does it work?

pastel egret
#

You don't need to do that there, that's what the annotation is doing in the class block.

errant tulip
soft matrix
#

also you should probably use super().__new__(cls)

pastel egret
#

Change that to direct_conn: set[Programme].

errant tulip
pastel egret
#

And don't annotate in new or init.

errant tulip
errant tulip
pastel egret
#

Because it's redundant if you annotate at the class level.

#

You can do it, but they'd have to match.

errant tulip
pastel egret
#

Because annotating self is a special case, normally annotationing attributes is invalid.

wicked berry
#

when passing in a FrozenSet to secrets.choice mypy complains:

Argument 1 to "choice" has incompatible type "FrozenSet[str]"; expected "Sequence[<nothing>]"

Isn't a FrozenSet a sequence? 🤔

soft matrix
#

No it doesn't support getitem

wicked berry
#

Ah

soft matrix
#

Atleast I don't think it does

wicked berry
#

checking

#
>>> dir(frozenset("abcd"))
['__and__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'copy', 'difference', 'intersection', 'isdisjoint', 'issubset', 'issuperset', 'symmetric_difference', 'union']
#

Alright, in this case I can coerce it to a string first

oblique urchin
wicked berry
#

While you're at it does random.choice have the same issue?

oblique urchin
#

yeah probably but I don't know how to edit multiple files in the web editor 😄

#

but now there's a branch so I can just put it on the same branch

#

secrets.choice actually is just random._SystemRandom.choice

proud brook
#

I was making my first abstract base class in Python and I thought "why not add type hints too?"

How do I annotate an abstract base class with properties?

#

With only a getter

rough sluiceBOT
#

@abc.abstractproperty```
Deprecated since version 3.3: It is now possible to use [`property`](https://docs.python.org/3/library/functions.html#property "property"), `property.getter()`, `property.setter()` and `property.deleter()` with [`abstractmethod()`](https://docs.python.org/3/library/abc.html#abc.abstractmethod "abc.abstractmethod"), making this decorator redundant.

A subclass of the built-in [`property()`](https://docs.python.org/3/library/functions.html#property "property"), indicating an abstract property.

This special case is deprecated, as the [`property()`](https://docs.python.org/3/library/functions.html#property "property") decorator is now correctly identified as abstract when applied to an abstract method:

```py
class C(ABC):
    @property
    @abstractmethod
    def my_abstract_property(self):
        ...
```...
trim tangle
#

uhh

#

yeah

proud brook
#

Yeah I've researched

trim tangle
#

it's deprecated but there's an explanation as to what to do 🙂

proud brook
#

Yes

#

Thanks

soft matrix
#

And you can do that with comma or period from GitHub

rigid narwhal
#

Python

#

What is prefix?

#

!print apple

buoyant swift
#

huh?

rigid narwhal
#

Umm

green gale
rigid narwhal
#

how to scearch

#

?

grave fjord
#

Show your code?

rustic gull
#

sure

#

!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.

grave fjord
#

And also what your mypy version is

rustic gull
#

0.910

soft matrix
#

try running this using the master version of mypy

#

(also you shouldnt use mysql here as it will block the bots event loop)

grave fjord
#
File "'mypy /main.py" line 165, in run_build WHERE fisher_id = %s

Seems odd

rustic gull
grave fjord
#

Do you have a file called mypy/main.py in your cwd?

soft matrix
#

i feel like they must have just edited the file as they were running it right?

rustic gull
#

there's just a mypy.ini

grave fjord
#

Show the output from tree?

rustic gull
rustic gull
#

hello, i have a sqlmodel base class that is the parent of all of my model classes. i added a get_or_create helper that accepts the same kwargs as __init__ and would like to type them, preferably generically on the base class. is that possible in python 3.10?

#

this is the class ```class BaseModel(SQLModel):
class Config:
arbitrary_types_allowed = True

uuid: Optional[UUID] = Field(default_factory=uuid4, primary_key=True)
timestamp: Optional[datetime] = Field(default_factory=datetime.utcnow)

@classmethod
def get_or_create(cls, session: Session, **kwargs):
    instance = session.exec(select(cls).filter_by(**kwargs)).first()
    if instance:
        return instance
    else:
        instance = cls(**kwargs)
        session.add(instance)
        session.flush()
        return instance```
pastel egret
#

You'd have to do it manually.

rustic gull
#

is there a way to do it without specifying every single class var?

#

and is there any pep that works towards this?

#

maybe an extension to paramspec?

tranquil turtle
#

you should use ParamSpec
mypy doesnt support it, but pyright does

rustic gull
#

how can ParamSpec be used for kwargs?

rustic gull
#

yeah im familiar with paramspec but i dont understand how to use it to type the kwargs of my get_or_create method

#

could you please demonstrate it with the BaseModle i posted above

tranquil turtle
#
P = ParamSpec('P')
T = TypeVar('T', bound=SQLModel)

class BaseModel(SQLModel):
    ...
    def get_or_create(cls: type[T], session: Session, **kwargs: P.kwargs) -> T:
        ...
        instance = cls(**kwargs) # it should be checked
        ...

#

i hope this should work

rustic gull
#

it first complains about missing args: "args" and "kwargs" members of ParamSpec must both appear within a function signature Pylance

#

and then after adding it ParamSpec "P" has no meaning in this context Pylance reportGeneralTypeIssues

#

and when get_or_create is used it says Param spec "P@get_or_create" has no bound value Pylance reportGeneralTypeIssues on the first kwarg

grave fjord
rustic gull
#
class BaseModel(SQLModel):
    class Config:
        arbitrary_types_allowed = True

    uuid: Optional[UUID] = Field(default_factory=uuid4, primary_key=True)
    timestamp: Optional[datetime] = Field(default_factory=datetime.utcnow)

    @classmethod
    def get_or_create(
        cls: Callable[P, T],
        session: Session,
        /,
        *args: P.args,
        **kwargs: P.kwargs,
    ) -> T:
        statement = select(cast(type[T], cls)).filter_by(**kwargs)
        instance = session.exec(statement).first()
        if instance:
            return instance
        else:
            instance = cls(**kwargs)
            session.add(instance)
            session.flush()
            return instance```
#

type hints in cpython are really coming along

#

is there a resource that gives a good overview of all type hint related stuff?

soft matrix
#

check the pins

rustic gull
#

neat, thanks!

trim tangle
rustic gull
#

POOL: Optional[aiomysql.pool.Pool] = None
do you need the optional for a global variable with a default of None?

soft matrix
#

yes

trim tangle
#

yes

#

smh global variables 😔

hallow flint
#

that is, if you know you'll initialise it before you ever use it, not using Optional + using type ignore can be useful, so you don't have to do assert POOL is not None everywhere you use it

#

(of course, then the type checker won't help you catch places where you might not have initialised POOL)

novel saffron
#

How would you type hint getattr functions?

oblique urchin
novel saffron
#

yeah, basically I have a util function which I want to typehint

def _recursive_getattr(obj: typing.Any, attribute: str) -> typing.Any:
    """Get an attribute recursively. All `.` in attribute will be accessed recursively."""
    for name in attribute.split("."):
        obj = getattr(obj, name)
    return obj
oblique urchin
novel saffron
oblique urchin
#

with pyanalyze you'd be able to write a custom plugin that handles this function

novel saffron
little hare
oblique urchin
little hare
#

^ iirc I'm using it on class instances as well as types

novel saffron
#

but only classes have attrs pithink

little hare
#

class object != class instance

#

class object == type

oblique urchin
little hare
#

and every attribute is an object, fun times

novel saffron
#

right, I was talking in our use cases

#

thanks

boreal ingot
rough sluiceBOT
#

stdlib/asyncio/tasks.pyi line 233

def sleep(delay: float, result: _T = ...) -> Future[_T]: ...```
soft matrix
#

defaults arent put in stubs

boreal ingot
#

why not?

#

in this case knowing the return type is None when no argument is provided would be useful

soft matrix
#

idk check pep 484

#

well you can tell from the type var that thats the case can you not?

boreal ingot
#

I can tell, because I know the default is None

#

but pyright does not see it as None

trim tangle
#

🤔

oblique urchin
#

This particular rule isn't in PEP 484, it's a convention we adopted in typeshed

boreal ingot
#

this is annoying: Type of "await asyncio.sleep(1)" is "_T@sleep"
because we can clearly know it should be None

oblique urchin
#

We might actually change it soon because it's useful for language servers to know the actual defaults

soft matrix
#

out of curiosity when do you use the result argument with asyncio.sleep?

boreal ingot
#

I dont, which is actually the problem 🙃
I have the strictest options on when using pyrigth, soo .....
Result of call expression is of type "_T@sleep" and is not used; assign to variable "_" if this is intentional

#

I can of course just slap on a # type: ignore (or set the argument explicitly), but it feels like this should not be a issue to begin with

spare mauve
#

you could probably do

_ = sleep()
boreal ingot
#

I can, and that I was I opted to do

trim tangle
#

why does sleep even have that parameter?

#

seems completely random

boreal ingot
soft matrix
#

from what i read on a not particularly convincing stack overflow post, they just used it for mocking a database call

boreal ingot
#

after a quick google, one use case is passing it as a coroutine to another function that expects a result

soft matrix
#

with a partial(sleep, 0)?

#

isnt result a kwarg

#

oh its not

#

cool

spare mauve
boreal ingot
#

no, imagine you have a function that takes a coroutine, awaits it and then uses the result ```py
import asyncio

async def foo(cor):
result = await cor
do_something(result)

async def bar():

I want to make foo sleep 2 seconds, and use this result

await foo(asyncio.sleep(2, result="abc"))``` that is one usecase I can think of

#

I cant think of a real world example of this, maybe for testing stuff

spare mauve
#

oh, so it would fail otherwise

#

because it's being awaited

boreal ingot
#

right

#

ofc if you dont want it to sleep you would use 0, or just another func like py async def returns_abc(): return "abc"

spare mauve
#

ok gotcha

crimson solstice
#

What's the best way to type around black-box functions like pickle? i.e.

import pickle

def deserialize() -> int:
    return pickle.load(open("my_local_file", 'rb'))
➜  pyexample python3 -m mypy main.py --strict
main.py:4: error: Returning Any from function declared to return "int"
Found 1 error in 1 file (checked 1 source file)****

It feels counter-intuitive to have to define a local variable when it's being immediately returned, and the type is signified by the return signature.

trim tangle
#

Generally, this signature is a lie. You never check that it's really an int.

void panther
#

or you could cast it if you know it's right

trim tangle
#

yes

soft matrix
#

!d typing.cast

rough sluiceBOT
#

typing.cast(typ, val)```
Cast a value to a type.

This returns the value unchanged. To the type checker this signals that the return value has the designated type, but at runtime we intentionally don’t check anything (we want this to be as fast as possible).
soft matrix
#

if you dont want to do that

crimson solstice
#

I think typing cast might be the best option, thanks! Ultimately this is a toy example, and some cases cannot be cast in a real way, so typing.cast seems like an okay alternative

trim tangle
#

wdym 'cast in a real way'?

crimson solstice
#

If I'm loading in a Torch model, I wouldn't want to then attempt to re-initialize that object.

oblique urchin
#

you could still insert a runtime assert isinstance(..., Torch model)

soft matrix
#

id just # type: ignore cause this isnt worth casting over imo

trim tangle
crimson solstice
#

Yes, I'm agreeing with you - the difference between casting to the toy example, i.e. int(x) and cast(x, int

trim tangle
#

it's just a signal to the typechecker to narrow the type

soft matrix
#

it would be nice now that typing has a c module if this like NewType.call was just a no-op

upbeat wadi
#

it does?

soft matrix
#

id use it more if its call overhead was lessened

#

it does

trim tangle
#

you can't really do that kind of optimization in Python

#

at least without a JIT or something

oblique urchin
#

Mark Shannon claims that at some point calling a Python function will be faster than calling a C function 🙂

trim tangle
#

🙂

buoyant swift
rotund flax
#

Quick question, what's better for a (big) project - typing or __future__ module for #type-hinting .

void panther
#

what do you mean with the future module? annotations?

oblique urchin
#

What Python versions do you support?

rotund flax
#

Thinking typing is the way to go, for a project at such scale, yet saw that __future__ (annotations) is very popular as of late.

rotund flax
oblique urchin
#

Then from __future__ import annotations isn't an option because it's not in 3.6

void panther
#

You'll need to use things from typing no matter what you do, the future import just (may) allows you to use certain things even if they're not valid in that python version

oblique urchin
void panther
#

it'll also break the moment something tries to inspect the annotations that were postponed and aren't valid

rotund flax
boreal ingot
#

is there something I am missing here? to me this looks like it should work ```py
from typing import ParamSpec, Concatenate, Generic, Callable, Any

P = ParamSpec("P")

class Foo(Generic[P]):
def init(self, func: Callable[P, Any]) -> None: ...
def bar(baz: Foo[Concatenate[int, P]]) -> Foo[P]: ...

def test(a: int, b: str) -> str: ...

abc = Foo(test)
reveal_type(abc)
bar(abc)but pyright is not happy
Type of "abc" is "Foo[(a: int, b: str)]"

Argument of type "Foo[(a: int, b: str)]" cannot be assigned to parameter "baz" of type "Foo[(Concatenate[int, P@bar])]" in function "bar"
TypeVar "P@Foo" is invariant
Type "(a: int, b: str)" cannot be assigned to type "(Concatenate[int, P@bar])"
Parameter 1: type "Concatenate[int, P@bar]" cannot be assigned to type "int"
Function accepts too few positional parameters; expected 2 but received 1
Keyword parameter "b" is missing in destination```

boreal ingot
#

it does feel like a bug, I was just not sure as it was my first time using Concatenate

trim tangle
#

it do be like that sometimes lol

boreal ingot
trim tangle
boreal ingot
#

I did have a quick look in the pep, and a example really similar to this is in it, so......

trim tangle
#

I tried to play around with it a little bit, it wasn't very fruitful

boreal ingot
#

from the pep ```py
T = TypeVar("T")
P_2 = ParamSpec("P_2")

class X(Generic[T, P]):
f: Callable[P, int]
x: T

def f(x: X[int, P_2]) -> str: ... # Accepted
def f(x: X[int, Concatenate[int, P_2]]) -> str: ... # Accepted
def f(x: X[int, [int, bool]]) -> str: ... # Accepted
def f(x: X[int, ...]) -> str: ... # Accepted
def f(x: X[int, int]) -> str: ... # Rejected``` so I feel like it should be supported

small sparrow
#

Thats cool

boreal ingot
#

the code I wrote that lead me to find this was py def with_argument( option: traits.CommandOption[O] ) -> Callable[[_SlashCommand[Concatenate[O, P]]],_SlashCommand[P]]: def decorator(command: _SlashCommand[Concatenate[O, P]]) -> _SlashCommand[P]: command.options.append(option) return command # type: ignore return decorator

#

well issue created, now I sleep 🙃

spare mauve
#

could make it a little more readable

jovial jacinth
#

Let's say I have a generic type, class B(Generic[T]): ..., what do I call something like B[int]? A B <foo>, what is <foo> here? (it's not exactly a subclass)

hoary pagoda
#

instantiation of the generic type is the name I hear often

tranquil turtle
#

!e

print(list[int].__origin__)
print(type(list[int]))
print(type(list[int].__origin__))
rough sluiceBOT
#

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

001 | <class 'list'>
002 | <class 'types.GenericAlias'>
003 | <class 'type'>
trim tangle
#

Like with function parameters and arguments.

#

You can think of B as a function taking a type and returning a type

tranquil turtle
#

why std library isnt annotated?

soft matrix
#

It is

#

But FR there are concerns about speed also making things slower, maintainers not wanting to update the code and it really slowing down typeshed stuff

tranquil turtle
#

what is "FR"?

soft matrix
#

for real

rain warren
#

Does anyone know what the two type parameters for numpy.ndarray are?

soft matrix
#

_ShapeType, _DType_co

rain warren
#

Is there any way I can find some examples? I tried searching for the Numpy typing docs, but they are very sparse

soft matrix
#

shape type is any and _DType_co is numpy.dtype

rain warren
#

Ah okay. So is it always any?

#

Simply because it's not implemented yet, or..?

soft matrix
#

the first param doesnt seem useful atm but im not an expert

rain warren
#

Okay thanks a lot!

soft matrix
#

i think pep 646 will bring support for it

urban root
#

Hello! Can someone explain me why the type of custom_id is still str | None even after that if?

oblique urchin
#

The type checker doesn't know that self.custom_id and custom_id are the same

urban root
#

oh

#

oh that makes sense now

soft matrix
#

if you want it to work just set custom_id to custom_id or os.urandom(16).hex()

oblique urchin
#

I'd generally recommend only assigning the instance attribute once

urban root
#

Got it

#

Thanks

blazing nest
soft matrix
#

for builtins and _asyncio etc yeah

boreal ingot
#

is there a way for a generic to only be considered a subclass of a Abstract class for one of its instances, to be specific, I have ```py
class ApplicationCommand(ABC):
@abstractmethod
def convert_to_dict(self) -> internal.CommandStructure:
...

class _SlashCommand(ApplicationCommand, Generic[P]):
def init(
self,
...j
func: CommandCallback[P],
) -> None:
...

def convert_to_dict(self: _SlashCommand[[]]) -> internal.CommandStructure:
    ...``` So if a function takes a `command: ApplicationCommand` I only want a `_SlashCommand[[]]` to be a valid type to pass, I know I could do a union in the function  `command: ApplicationCommand | _SlashCommand[[]]` , but I would like to keep `_SlashCommand` a subclass of `ApplicationCommand` as that makes sense
#

because _SlashCommand is a ApplicationCommand, but it is only valid to be passed to functions taking ApplicationCommand when P is []

#

(because functions taking ApplicationCommand are going to call convert_to_dict, which is only valid for _SlashCommand[[]])

oblique urchin
#

Sounds like you want to use Protocols

boreal ingot
#

oh right, that would work

#

completely forgot about those

#

ah, but, other classes also have convert_to_dict methods that are not subclasses of ApplicationCommand

#

oh wait, that is not a issue, because those other methods dont return a CommandStructure 🤦

trim tangle
#

@boreal ingot I would expect convert_to_dict to return a dict 🙂

boreal ingot
soft matrix
#

its probably a typed dict

trim tangle
#

ah

#

ok

hearty shell
#

Hey guys, do any of you know what is wrong with this?

#

I have been breaking my head for quite a while about this x)

#

Trying to add stubs to an external ORM library that doesnt have them and it is proving itself to be quite challenging

#

The first error about The erased type... might just be a mypy limitation, but if that is the case that is fine since I can just add a @classmethod decorator and mypy is fine with it then and the fact that these are just stubs

#

The second error I don't really know how to solve, and finally the type is revealed to be ItemSet*[T'-1] but is should in theory be ItemSet*[FooItem*]

rustic gull
#

any suggestions for a linter to check only for real errors? No styling or any of that nonsense.
it should catch things like missing or misnamed function parameters

soft matrix
#

Mypy

#

Pyright

hearty shell
#

If you are in vsc there is also Pylance

loud osprey
#

what is actually a language server?

#

Like, is there some remote server from which my editor gets autocompletion?

trim tangle
#

basically, it's like an editor plugin, but editor-agnostic

hearty shell
#

Just tried the same with Pyright x)

cosmic plinth
#

Is there a way to subclass pathlib.Path that Mypy accepts? I'm at a loss.

hearty shell
#

Humm what are you doing with it?

#

@cosmic plinth

cosmic plinth
#

adding extra methods

hearty shell
#

Well, can you give an example?

#

Just subclassing and adding random unrelated methods wont make it complain

cosmic plinth
#

well the issue is you must do it like:

# https://github.com/python/cpython/blob/7b78e7f9fd77bb3280ee39fb74b86772a7d46a70/Lib/pathlib.py#L1067
class Path(pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath):
rough sluiceBOT
#

Lib/pathlib.py line 1067

cls = WindowsPath if os.name == 'nt' else PosixPath```
oblique urchin
#

mypy will probably like it better if you do something like if os.name == "nt": _PathBase = pathlib.WindowsPath else: _PathBase = pathlib.PosixPath; class MyPath(_PathBase): ...

cosmic plinth
#

nope complains then too

#

also tried type(pathlib.Path())

#

I think there is no way

oblique urchin
#

oh you need to use sys.platform

hearty shell
#

I really thought it would evolve making your own small type factory xD

cosmic plinth
#

ty! why does that work? 😮

oblique urchin
#

mypy has special recognition for sys.platform

#

it's hardcoded in basically

cosmic plinth
#

tyvm!!!

pastel egret
#

There's a few specific conditions type checkers look for, sys.version_info is another (in case different Python versions change the definition).

tribal badger
#

Oof, just learned Pylance doesn't support the Numpy typing stuff yet --- until numpy 1.22.x gets out of rc. :'[ Also, is there a place for users to give samples of their code for others to check their style/linting/etc.? I'd like to get a bit better at my readability.

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.

hasty hull
#

Is this a valid use of ParamSpec? ```py
from typing_extensions import ParamSpec
from typing import Dict, Generic, Optional

P = ParamSpec('P')

class HTTP:
def init(self, headers: Optional[Dict[str, str]] = None) -> None:
...

class _Base(Generic[P]):
def init(self, foo: str, *args: P.args, **kwargs: P.kwargs) -> None:
...

Base = _Base[HTTP]

class Sub(Base):
def init(self) -> None:
super().init(foo='foo', headers={'foo': 'bar'})

#

Pyright complains: ```
foo.py:23:37 - error: No parameter named "headers" (reportGeneralTypeIssues)
foo.py:23:9 - error: Argument missing for parameter "__p0" (reportGeneralTypeIssues)

soft matrix
#

It's not in the pep but it would be a nice to have feature

blazing nest
#

Oooh

#

That's so cool though

#

What if you "convert" HTTP into Callable[P, HTTP]?

#

Since Type[HTTP] is an instance of type() that when called returns an instance of HTTP.

hasty hull
#

That doesn't work because pyright then complains that class Sub(Base): is missing a type variable

hasty hull
#

Yeah nevermind, I don't know how I got that error originally but retried it and the same original error occurs

trim tangle
#

(probably)

hasty hull
#

Oh :(

trim tangle
#

If you need just the positional args, it's like 42069 times better

hearty shell
#

Oh I was still trying to figure out what was wrong lol

#

xD

hasty hull
hearty shell
#

though maybe a annotating with a decorator that takes a callable of param to a callable of a callable that concas both would work

trim tangle
hasty hull
#

I actually probably could refactor it so I don't need the wrapper, I only originally wrote the wrapper as I needed to have a common interface spanning multiple different http client libraries but as I just support httpx now it could be refactored

#

I gave up trying to strictly type the kwargs as it's just used internally anyway

frank glade
#

currently we only have a way to filter by a checking type (function, class, method etc) and name (via regex)

#

but we're pondering ways to filter by signature

#

so, what i'm wondering whether there is a way to check if a given signature is a subset of another signature

#

like list would be a match to list|np.array

oblique urchin
frank glade
#

i need it during runtime

rustic gull
#

something other then typing everything by hand with regex

oblique urchin
#

My typechecker https://github.com/quora/pyanalyze would allow this, you'd call pyanalyze.annotations.type_from_runtime and then use the .can_assign method. I'm guessing runtime typecheckers like pydantic would also support this, but no idea what the API would be.

rustic gull
#

like individual functions or "collections" of functions to be filtered by signature

frank glade
oblique urchin
rustic gull
#

worth it to try tinkering with can_assign

#

then we can try implement it

oblique urchin
#

Like you want to match every function that takes a parameter typed as a list?

frank glade
#

yes

rustic gull
#

yeah

oblique urchin
#

I guess then you could use inspect.signature and iterate over the parameters?

frank glade
oblique urchin
#

yeah, you'd still need something (like pyanalyze) that knows to deal with that

#

Can you clarify why you need this though? I read over the README of your repo and it's not clear to me what you'd want this for

frank glade
#

you can decorate everything in one go, for instance to log, debug, time..

rustic gull
#

and we want to be to filter not only everything

#

but multiple functions aswell, not just everything

oblique urchin
#

Right, I don't see why you'd want to decorate "all functions that take a list as an argument" though

frank glade
#

it's a first step to get an overview, there's very little to go on property-wise

#

actually gives me an idea

#

we already build an html to get an overview for a dry-run "what-if" decoration.. we could build on that and provide a neat interface with different filters so the user only needs to copy&paste the line into their code

rustic gull
#

like a cheat sheet for the different filters

#

yeah that's good

frank glade
#

then it's easier to combine different filters

#

like "all functions that return a np.array, name starting with "sig"

#

and the user could directly see which functions would get hit

rustic gull
#

great

#

we got the idea of what we can try

#

I'm gonna do some stuff with decorators in vscode and then think about how to implement it

frank glade
#

excellent

soft matrix
#

is there a reason types.GenericAlias doesnt set __orig_class__?

spiral fjord
#

There doesn't seem to be a reference to that attribute in the python docs

soft matrix
#

there are a lot of typing things that are completely undocumented

#

__org_bases__ is another thing

#

not even sure why this is the case

#

they are incredibly useful for introspection

spiral fjord
#

What's supposed to be in orig class?

soft matrix
#

its the subscripted generic when subclassing something

#

mro_entries overwrites it in bases

spiral fjord
#

In the end GenericAlias just seems to be type(list[int]) with no changes to it anyway

soft matrix
#
In [11]: class this(list[int]):
    ...:     ...
    ...: 

In [12]: this.__bases__
Out[12]: (list,)

In [13]: this.__orig_bases__
Out[13]: (list[int],)```
#

this is what i mean

spiral fjord
#

I don't think type is a subclass like that though

soft matrix
#

im confused im not using type here

#

im subclassing an instance of GenericAlias

spiral fjord
#

Ah, I thought you were using it on GenericAlias itself

hearty shell
#

That is interesting as I thought Self would just be for convenience and clarity

soft matrix
#

it should but the way things are special cased for it in type checkers makes it possible for things where its descriptors and it might be hard for them to normally understand whats going on there

#

jelle said this was something they wanted to see improve in the issue you made on the typing repo i think

hearty shell
#

Oh wow, small world, I was just testing around with your fork 😂

#

Yeah I ended up opening an issue on Pyright as they are currently the only ones supporting pep 673 and they said it was just a limitation of the checkers

soft matrix
#

anyways im gonna try and fix whatevers up my fork

opaque lake
#

can sm1 explain to me the need for mypy etc. when using annotations along with an IDE? i don't really understand what else i get from using it.

void panther
#

a dedicated type checker will be stricter and helps you catch more errors

soft matrix
#

depending on what ide you use there might not be a point using mypy

#

if you use something that uses pyright like vsc or maybe sublime its pointless imo

rough sluiceBOT
#
NEGATORY.

Sorry, you can't do that here!

opaque lake
opaque lake
soft matrix
#

pycharms type checking is very very lenient and flat out wrong sometimes, using mypy with it will definitely catch bugs that pycharm doesnt

void panther
#

Yeah usually pycharm will just let things through it it's not something simple

#

It's also not very good in figuring out types in certain situations, e.g. it couldn't figure out the types from an iterable that had a typehinted iter

hearty shell
#

if you decide to use it I would recommend adding mypy as a precommit script on pycharm or bind the command to a hotkey using plain mypy. The pycharm extension is unbearably slow and buggy imo

trim tangle
#

also, not everyone on your team will use PyCharm

#

so there needs to be some reusable program that checks types.

rustic gull
#

Is there a standard/good way to type hint mapping keys?

soft matrix
#

mappings not dicts?

rustic gull
#

Is there a difference between the keys you can use for mappings and dicts? If there is, then I mean dicts

oblique urchin
#

I'm not sure I understand your question. You can use annotations like Mapping[int, str] or dict[int, str] to indicate that you want a Mapping (or concretely a dict) with int keys

#

There is also TypedDict for dicts with specific string keys

soft matrix
#

if you want string keys with specific values use

#

!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...
rustic gull
#

No I mean how can I type hint "anything that can be used as a dict key"

oblique urchin
#

oh Hashable

rustic gull
#

Ah thanks

oblique urchin
#

Though historically that hasn't necessarily worked too well because hashability is inherited in weird ways. Haven't tried using it recently though.

rustic gull
#

That sounds like an uncommon use case anyway so I won't worry about it too much

pastel egret
#

The problem is that object.__hash__ exists, and it's only subclasses of that that then remove hashability.

#

In typeshed right now, mappings and sets accept any object as a key.

eager vessel
soft matrix
#

i am aware

eager vessel
#

Yep, plus different people use different ide's

#

And sometimes you can forget to run mypy before committing 😵‍💫

stray summit
#

hmm, does anyone have any idea why usage/integration of signature objects/changes has not been considered for pep 612 (parameter specification variables) and pep 677 (function type specs)

soft matrix
#

You want to change a function signature or do you mean an inspect.Signature?

stray summit
#

@soft matrix in a way both - Callable[somesignature] would be a nice addition, in particular of parameter specs worked in a similar way (stuff like declared wrappers/changing decorators automatically fixing up type declarations, overrides and signatures in one go would be neat)

soft matrix
#

I think the authors were just concerned it would be too much to implement in one step

oblique urchin
#

At the typing meetups there was actually a lot of discussion of providing a more powerful syntax for callables, but the authors of PEP 677 decided to stick with only positional-only + ParamSpec because their data showed little usage of more complicated signatures

fierce ridge
#

i'd be curious about that data, because my subjective experience is the opposite, that people use extremely complicated / multi-function interfaces all the time without even thinking that they're complicated, because python is dynamically typed so it just doesn't matter

#

or maybe it's just a handful of libraries like django, pandas, etc

trim tangle
oblique urchin
oblique urchin
trim tangle
#

For example, mypy doesn't have narrowing of TypedDicts based on a key existing or not. That's the sort of thing that I mean.

#

Wait... what the hell? mypy disallows @final on a TypedDict?

#

what's the reasoning behind that?

oblique urchin
#

mypy tends to be very restrictive in disallowing things it doesn't understand

#

probably it just disallows all decorators on TypedDiict?

trim tangle
#

so it's pretty disappointing to see that code using this feature will now be totally incompatible with mypy

#

well, I guess you can put a # type: ignore on the final

#

It's pretty strange that you can't @final a TypedDict, given that you can subclass them...

fierce ridge
#

i wish you could decorate a class (including TypedDict) with @frozen that prevents mutation at the type-checker level (other than in __init__), or maybe just prevents mutation by things other than instance methods

#

then you could have things like subclassing a TypedDict with a more-specific type

soft matrix
#

theres an issue open on the typing github about this

fierce ridge
#
@frozen
class CMSEntry:
    id: str
    content_type: str

class Article(CMSEntry):
    content_type: Literal['article']
fierce ridge
soft matrix
#

oh it got closed

fierce ridge
#

hm i guess they decided to go with typing.Property

soft matrix
#

no i think they are going with builtins.property but that wouldnt solve the issue

fierce ridge
#

no, it wouldn't. should someone comment and request that it be reopened, since the special case proposed does not actually cover the original issue?

soft matrix
#

¯_(ツ)_/¯

#

probably

dim trail
#

Im struggling with how to type hint a Proxy object, that exposes the underlying objects attributes dynamically. Specifically, the Proxy object should match the Protocol that matches the underlying object. Heres a toy example:

from typing import Protocol


class TestMethod(Protocol):
    def test(self):
        ...

class A:
    def test(self):
        print("works!")

class Proxy:
    def __init__(self, _obj):
        self._obj = _obj
    
    def __getattr__(self, name: str):
        return getattr(self._obj, name)

a = A()
p = Proxy(a)

c: TestMethod = p
c.test() # works!

As you can see the Proxy works and allows me to call the method of the underlying object. I just have no clue what i would need to do to typehint this.
This is the error in case its relevant, on the second to last line.

Expression of type "Proxy" cannot be assigned to declared type "TestMethod"
  "Proxy" is incompatible with protocol "TestMethod"
    "test" is not present
#

my current line of thought is that i would need to somehow typehint the return type of getattr to the underlying objects attribute type for the name, though i have no clue how to accomplish this.

trim tangle
#

@dim trail that's impossible

#

your best option is p: A = Proxy(a) # type: ignore

#

or perhaps p: TestMethod = Proxy(a) # type: ignore

hasty hull
#

E.g. ```py
T = TypeVar('T', bound=TestMethod)

class Proxy:
@classmethod
def proxy(cls, obj: T) -> T:
# type ignore required as we are tricking the type checker
return cls(obj) # type: ignore

p = Proxy.proxy(a)
c = p.test()

#

The disadvantage of this approach is that you will then have to type ignore any attribute access for the actual proxy class

hasty hull
soft matrix
#

This is a good use case for intersection

#

If that ever happens

hasty hull
soft matrix
#

Not currently some people are working on it

#

Not sure when it will be out

#

proxy = classmethod[[type[Self], T], Self & T]

#

Would be my thinking

spare mauve
dim trail
#

@trim tangle @hasty hull thank you for the responses. i think i will try to figure out a way to do what im trying to do without the proxy object, as none of the possible solutions make sense in my case (deciding what instance of the proxied class to use when the attribute is accessed)

deep pendant
#

I am totally lost in typehinting this:

def validate_sent_data(validation_form) -> Callable:
    def decorated(f) -> Callable:
        @wraps(f)
        def inner(*args, **kwargs) -> tuple[Response, int]:
            if request.method == "GET":
                form = validation_form(request.args)
            else:
                valid_json_data, error_http_message = check_valid_json_data(
                    request
                )
                if error_http_message or not valid_json_data:
                    return jsonify(error_http_message), 400

                multi_dict = MyMultiDict(valid_json_data)
                form = validation_form(multi_dict)

            if not form.validate():
                form_errors = {}
                field_code_name: str
                for field_code_name in form.data:
                    # match fields errors keys names from a request
                    form_field = getattr(form, field_code_name)
                    field_error = form.errors.get(field_code_name)
                    if field_error:
                        form_errors[form_field.name] = field_error

                return make_error_response_bad_params(form_errors), 400
            return f(form, *args, **kwargs)

        return inner

    return decorated

Could you help me?

Details:

  1. validation_form is a subclass of Flask-WTF Form (so e.g. MyCustomForm)
  2. The presented decorator is used to validate Flask's requests arguments (both GET and the ones with body) - see the photo
#
  1. tuple[Response, int] is correct because make_error_response_bad_params is a wrapper around (Flask's) jsonify
trim tangle
deep pendant
#

Especially - what kind of Callable should I use and how to - generally - improve this code

trim tangle
#

Oh wait, I seee

deep pendant
#

It's rather empty by the way

trim tangle
#

Is it always empty?

#

also, what type checker are you using?

deep pendant
#

Noo, request have to be from these

#

latest mypy

#

and Python3.10

trim tangle
#

Unfortunately you can't do this with mypy yet

deep pendant
#

So what type-checker can do this?

hasty hull
trim tangle
#

yep

deep pendant
#

Okey, I will see.

trim tangle
dim trail
trim tangle
#

but theoretically it should be possible with a mypy extension

trim tangle
#

but hopefully that will not be a major obstacle

deep pendant
oblique urchin
#

yes, mypy does have some basic ParamSpec support now

deep pendant
#

There is a comparison of type checkers? Especially pytype, pyre-check, pyright and mypy...

#

There is a lot of them

oblique urchin
#

I follow the github trackers for all of them, and pyre and especially pytype rarely get outside bug reports

deep pendant
#

Isn't a Google's pytype a tool to get better, still bad (codebase)?

#

pyright also looks more mature regarding PEP implementation - but yea, I will not judge 😄

trim tangle
deep pendant
#

So what should I write in requirements.dev.txt in order to use pyright? NPM directly? pyright from PyPi is weird

hasty hull
#

pyright from PyPi is weird
You're welcome lol

hasty hull
#

It's just a wrapper over the npm CLI that downloads node for you if you don't already have it

trim tangle
#

I personally wouldn't want to install pyright via pip 👀

hasty hull
#

how dare you

trim tangle
#

at least it doesn't make remote calls to the pyright playground API 🙂

oblique urchin
#

don't give people ideas

trim tangle
#

I only have $3.38 on my cloud account 😦

#

although that's probably going to be a few million calls

#

I already got billed 0.04 roubles for storing docker images

hearty shell
#

Callable[[Callable[Concatenate[F, P], None]], Callable[P, tuple[Response, int]]]

#

pep 677 when?

#

😂

trim tangle
#

there isn't a good way to extract this to a type alias, unfortunately

hearty shell
#

I mean with 677 that would become
((F, **P) -> None) -> ((P) -> tuple[Response, int])

oblique urchin
#

The prospects for PEP 677 aren't looking good on the Python mailing list, unfortunately

hearty shell
#

Humm, I hand't seen the mailing list, interested to see their reasons

grave fjord
#

I'd rather have a nicer lambda syntax

fierce ridge
#

i wish they had used something other than Concatenate

#

or given us an infix operator + as shorthand

upbeat wadi
#

what do os.PathLike[str] and os.PathLike[bytes] indicate (the generic argument)?

oblique urchin
oblique urchin
upbeat wadi
#

thanks

oblique urchin
mighty lindenBOT
upbeat wadi
#

pyright --verifytypes gives me an error for the provided code: ```py
class SnowflakeList(array.array):
...

discord.utils.SnowflakeList
error: Type of base class "array.array" is partially unknown
Type argument 1 for class "array" has unknown type

When I do `class SnowflakeList(array.array[int])` the error goes away, but subscripting `array.array` like this fails at runtime. Is this an invalid error or is there a proper way to type this?
oblique urchin
#

So the only solution I'm aware of is something like if TYPE_CHECKING: _Base = arrray.array[int] else: _Base = array.array class SnowflakeList(_Base):

upbeat wadi
#

alright, thanks

soft matrix
#

Just up the base version to 3.9 :)

soft matrix
urban root
#

Hello. I have a class, whose init which looks like this

def __init__(
        self,
        author: Union[discord.User, discord.Member],
        options: List[SelectOption],
        content: List[str] = [],
        embeds: List[Optional[discord.Embed]] = [],
        clear_on_timeout=False,
        timeout: int = 60,
    ) -> None:

And I initialise the class like
ClassName(self.interaction.user, options, embeds=embeds)

But pylance gives this error

Argument of type "List[Embed]" cannot be assigned to parameter "embeds" of type "List[Embed | None]" in function "__init__"
  TypeVar "_T@list" is invariant
    Type "Embed" cannot be assigned to type "Embed | None"
      Type cannot be assigned to type "None"

What am I doing wrong here?

trim tangle
urban root
#

oh

#

hmm

trim tangle
#

What you should do is accept Sequence[Embed | None].

#

And don't use mutable defaults. But that's a separate issue

urban root
trim tangle
urban root
#

hmm I see

urban root
tranquil turtle
#

content: List[str] = [],
lists is mutable, so you shouldnt set empty list as default value

#

!e

def f(l = []):
  l.append(0)
  return l

print(f())
print(f())
print(f())

print(f([]))
print(f([]))
rough sluiceBOT
#

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

001 | [0]
002 | [0, 0]
003 | [0, 0, 0]
004 | [0]
005 | [0]
urban root
#

What

#

whenever I call the function, a new one isnt generated?

soft matrix
#

no

#

this is one of the main feature points of the new syntax for defaults

urban root
#

I see

#

what should I be doing then?

#

Is it better to pass in the list rather than using default?

oblique urchin
#

The normal workaround is to set the default to None and then inside the function do if content is None: content = []

urban root
#

I see

#

Also, if I use something immutable, like tuples, this should be fine right?

oblique urchin
#

yes

urban root
#

Because I do not do any assignment to the lists

oblique urchin
#

That's actually what I usually try to do. Mutating your arguments is a risky thing to do anyway.

urban root
#

Ah

trim tangle
oblique urchin
trim tangle
#

Yep

rocky anvil
#

What would be the type of object instance of a class? i.e count

import timeit

import typing
class Static:
    counter = 0
    
    def __setattr__(self, name, value):
        setattr(self, name, value)
    def __delattr__(self, name):
        del self.name
    
    @staticmethod
    def myfunc():    
        Static.counter += 1
        return Static.counter


if __name__ == '__main__':
    
    def main1():
        count = Static()                            #with instance of class and slower
        print(f"First call : {count.myfunc()}")
        print(f"Second call : {count.myfunc()}")
        print(f"Third call : {count.myfunc()}")
        return count 
rocky anvil
#

The return of main1() function I'm talking about.

soft matrix
#

its Static

#

you are returning count which is of the type Static

rocky anvil
#

how to define that

#

by ->

soft matrix
#

-> Static

rocky anvil
#

lemme see

#

That would be classname itself right?

trim tangle
#

Yes, just like int means an integer

rocky anvil
#

No need to import typing module for that?

#

Isn't there any build-in types there in the typing module for this?

soft matrix
#

no you dont need to use the typing module here

rocky anvil
#

okay.

grave fjord
oblique urchin
grave fjord
#

the reveal_type seems to show the correct annotation

__main__.SyncToAsync[def [T] (delay: builtins.float, value: T`-1), T`-1]
#

is there documentation on the known limits?

oblique urchin
grave fjord
#

Note that typing.Concatenate and some other use cases are still not supported.

fierce ridge
#

i didn't even know about NotRequired

#

very happy to see ParamSpec and TypeAlias support

#

i also really need to start using TypeGuard... we have one application at work with a lot of TypedDicts and there are currently casts all over the code to deal with it

fierce ridge
# grave fjord ?
def to_foodict(x: Mapping[str, Any]) -> SpecialDict:
    if 'specialKey' not in x:
        raise ValueError('x is not a SpecialDict')
    return cast(SpecialDict, x)

this is what we have now in several places

#

i'd rather have

def is_foodict(x: Mapping[str, Any]) -> TypeGuard[SpecialDict]:
    return 'specialKey' in x
grave fjord
#

oh I see

soft matrix
#

thats not a safe conversion is it?

#

you go from immutable to mutable

grave fjord
#

yeah I think you want something safer like this?

def is_foodict(x: Mapping[str, object]) -> SpecialDict | None:
    try:
        v = x["specialKey"]
    except KeyError:
        return None
    if isinstance(v, SomeType):
        return SpecialDict(specialKey=v)
    return None
#

like a mapping might not be a dict

#

it's annoying you can't do a Mapping-based TypedDict

fierce ridge
grave fjord
#

and that you can't match on a TypeDict

fierce ridge
grave fjord
#

also I avoid TypeDict in new code in favour of dataclasses (attr.s) and or pydantic

#

then parse it from the json.loads' Any at the "edges" of the codebase that does all the deep/recursive checks

#

then shallow isinstance will work for the rest of the code

fierce ridge
#

indeed, refactoring to use attrs is on my todo list. need to flesh out the test suite first to make sure it doesn't break in the process, which is what i happen to be doing this week

soft matrix
#

do you use mypy or pyright?

fierce ridge
#

mypy

soft matrix
#

if your loading these into classes it would be a really good idea to use pydantic as graingert suggested as i think it supports from/to_dict in a typed way

fierce ridge
#

it does, and also a good point

#

in this case the application is mostly marshalling data back and forth between a CMS and MongoDB

#

so really i need two layers of transformations, python <-> mongo and python <-> cms

#

i want somewhat precise manual control over that process anyway, so the convenience of pydantic isn't worth much in that case

#

if i wrote the whole thing from scratch using fastapi i probably would use pydantic though

trim tangle
#

TIL you can't make a generic TypedDict 🥴

grave fjord
#

Can't do anything fun ;)

#

I want a Generic NamedTuple or the ability to add typed unpack to custom types

soft matrix
#

its in my github stars

#

but i havent seen anything happen with it in a while

dire bobcat
#
def func(arg:int):
   ...
"""how can I see what `arg` is typehinted to?"""```
soft matrix
#

func.__annotations__["arg"]

#

is i think what you are asking for

dire bobcat
#

I think so, ty

soft matrix
#

or if you are on 3.10 use

#

!d inspect.get_annotations

rough sluiceBOT
#

inspect.get_annotations(obj, *, globals=None, locals=None, eval_str=False)```
Compute the annotations dict for an object.

`obj` may be a callable, class, or module. Passing in an object of any other type raises [`TypeError`](https://docs.python.org/3/library/exceptions.html#TypeError "TypeError").

Returns a dict. `get_annotations()` returns a new dict every time it’s called; calling it twice on the same object will return two different but equivalent dicts.

This function handles several details for you:
soft matrix
#

cause this will handle string-ized annotations

dire bobcat
#

what would you use if you wanted to get the typehint from the actual typehinted variable itself>

#

like inside func

#

i could see what arg itself is typehinted to

soft matrix
#

theoretically type(arg)

#

although that might not be entirely safe

dire bobcat
#

nah cant use thar

#

im making a check to make sure arg is of the typehinted value

#

without manually entering typehinted valeu

soft matrix
#

use something like beartype dont do this yourself

#

!pypi beartype

rough sluiceBOT
trim tangle
#

or just make a decorator?

#

That would not get you very far though. You can't really "verify" more complex types, like a function or a generator or even a list.

vapid quarry
#

If we have a dictionary that stores class instances, would following be accurate in terms of type hinting:

class Info:
  def __init__(self):
    pass

if __name__ == "__main__":
  data:dict[Info] = {}
trim tangle
#

yes

vapid quarry
#

ok thank you (:

dire bobcat
trim tangle
#

you can't really, without extra hacks

dire bobcat
#

i dont wanna manually write the func name, is there a builtin method or property that tells me the current func its in

#

oh ok

trim tangle
#

you can make a decorator

oblique urchin
#

sys._getframe(1).f_name?

#

(don't do that)

trim tangle
#

@dire bobcat
This should work for functions that only take keyword-only arguments.

from functools import wraps
from typing import TypeVar, Callable, cast

F = TypeVar("F", bound=Callablle)

def check_types(fn: F) -> F:
    @wraps(fn)
    def new_fn(**kwargs):
        with_defaults = {**fn.__kwdefaults__, **kwargs}
        for name, arg in with_defaults.items():
            if name not in fn.__annotations__:
                continue
            ann = fn.__annotations__[name]
            if not isinstance(arg, ann):
                raise TypeError(f"Argument {name!r} ({arg!r}) is not of type ({ann!r})")
        return fn(**with_defaults)
    return cast(F, new_fn)
#

For others you'll have to do a bit more work (with inspect), but the idea is the same

#

(this will obviously only work for very simple types, won't even work for None)

soft matrix
#

just use beartype

bright dome
soft matrix
#

this breaks if you have defaults i think

bright dome
#

I believe I tried it with defaults and it worked. Must be annotated

#

I could be wrong lol

#

I'm using wraps from functools btw

soft matrix
#

oh yeah actually that probably fixes it

#

TypeError: isinstance() arg 2 must be a type or tuple of types

#

generics dont work

vapid quarry
#

If I am appending different class instances to a list/array, how should it be type hinted.
Does below look about right?

from typing import List, Union
class Foo:
  pass

class Bar:
  pass

class FooBar:
  pass

if __name__ == "__main__":
  updates:List[Union[Foo, Bar, FooBar]] = []
soft matrix
#

it doesnt work with stringified types

bright dome
bright dome
soft matrix
#

are you planning on actually using this in production?

bright dome
#

I know it's full of holes. It was me just dabbling with decorators and seeing if I could validate some func params...

trim tangle
#

I generally think that this is a lost cause

little hare
#

well that's kind of insulting to inzane

trim tangle
#

Not saying it's not useful. It's pretty useful if it's in some library code that all kinds of people use

#

but:

  • there's a runtime cost (you can use some switch flag, but there's a good chance that few people will remember to turn it on/off). It will actually change the asymptotic complexity (big O) of some algorithms. Would you check every element of a list in a binary search function?
  • it will not work 100% even for simple things like list[int] or Sequence[str]
  • it will completely not work for more complex types (like functions or generators)
trim tangle
fierce ridge
trim tangle
#

What I would do "in production" is:

  • use a static type checker to catch most of the stuff
  • if something does break, hopefully your tests will catch that
  • take a look at https://pypi.org/project/deal/
vapid quarry
#

Thanks for mentioning that. I will look into transitioning to list.

#

How do we format it if classnames are long and we have several of them.
Does the following work by creating an alias of Classes in this case:

# create an alias of classes
Classes = Union[FooPerformsFoo, BarPerformsBar, FooBarPerformsFooBar]
updates:list[Classes] = []
#

Is that what aliases are for?

oblique urchin
#

yes

trim tangle
#

or if you repeat the same type over and over

#

or if you just want a type-level named constant

#

like YesNo = Literal["yes", "no"]

vapid quarry
#

And do we declare aliases globally?

oblique urchin
#

Usually yes

vapid quarry
#

ok

oblique urchin
#

Support for aliases at class or function scope is inconsistent

vapid quarry
oblique urchin
#

Some type checkers don't support it

oblique urchin
#

Don't remember the details, it was discussed on the mailing lists a few times

trim tangle
#

or... practical

#

I'm using pyright/pylance for development, but of course nobody has pyright in their CI

soft matrix
#

yes only use generics :)

oblique urchin
#

we do it all the time in typeshed 🙂

trim tangle
#

probably

oblique urchin
#

oh yeah that's a really long-standing mypy bug

trim tangle
#

yeah, 6 years or so

#

Pyright has the opposite issue - try writing a method decorator

#

that does something non-trivial

vapid quarry
trim tangle
#

that would be solved with something like an intersection type: And[Callable[[T, Unpack[P]], int], Descriptor[Callable[[Unpack[P]], int]]]...

#

yikes

trim tangle
#

™️

#

oh wait, I didn't see the ™️

#

because I have dark theme

rustic gull
#

How should I type hint something that can be a subclass, let's say of class A, but not A itself?

#

And I'm talking specifically about classes, not class instances

pastel egret
#

Aside from specifying a union of all the subclasses, it's not possible I think.