#type-hinting

1 messages Β· Page 25 of 1

viscid spire
#

don't use assert

pastel egret
#

Why not? This is exactly what it's for.

rain warren
#

Works, but IMO it's nice when reading to code to be alerted that the list contains uninitialized elements (before it's fully initialized)

viscid spire
pastel egret
#

No?

viscid spire
#

asserts are ignored if the code is ran with a certain flag (optimization flag)

pastel egret
#

Yes, which is what's wanted here. Asserts describe invariants that you expect to always succeed, and indicate a bug in the code logic if they ever fail. The reason for the flag is that after you're sufficiently tested the program in development, it should be fine to omit them for performance.

rain warren
#

I think this is exactly what assert is for, too. It doesn't matter that this might be disabled at runtime. When the assert is run, I believe that there will never be a None in the list. I might be wrong, so it's nice that the assert is run at debug time, or in tests

viscid spire
#

guess so

pastel egret
#

And in a type-hinting context, checkers will use asserts to allow you to specify various narrowings.

viscid spire
pastel egret
#

assert isinstance() does work for example, Mypy 1.7 added len(some_tuple) == X. The is a open issue for Mypy, if someone wants to implement it.

viscid spire
#

doesn't work if you do match:case: tho

def f(t: tuple[int] | tuple[str, str]) -> None:
    match len(t):
        case 1:
            reveal_type(t)
        case _:
            reveal_type(t)
pastel egret
#

For match, using sequence patterns would probably be better.

viscid spire
#

tru

#

just was testing if it would work πŸ˜„

urban imp
#

so i was in here a bit ago with a typing problem for a class that can be instantiated with keywords, a dict or list of tuples, or alternating keys/values. It's meant to be a matcher for a dictionary, to see if that dictionary contains the key/value pairs it's initialized with.

The trimmed-down class:

K = TypeVar("K", bound=Hashable)
V = TypeVar("V")

class ContainsTheEntry:
    ...
    # Keyword argument form
    @overload
    def __init__(self, **kv_args: V) -> None:
        ...

    # Key to value dict or list of tuples form
    @overload
    def __init__(self, kv_args: Mapping[K, V] | list[tuple[K, V]]) -> None:
        ...

    # Alternating key/value form
    @overload
    def __init__(self, *kv_args: K | V) -> None:
        ...

    def __init__(self, *kv_args: Any, **kv_kwargs: Any) -> None:
        # init goes here

The test:

class TestContainsTheEntry:
    def test_can_be_instantiated(self) -> None:
        cte_single = ContainsTheEntry(key="value")
        cte_multiple = ContainsTheEntry(key1="value1", key2="value2")
        cte_dict = ContainsTheEntry({"key2": 12345})
        cte_alternating = ContainsTheEntry("key3", False)  # <-- line 149, the error

and this mypy error:

mypy.....................................................................Failed
- hook id: mypy
- exit code: 1

tests/test_resolutions.py:149: error: Argument 1 to "ContainsTheEntry" has incompatible type "str"; expected <nothing>  [arg-type]
tests/test_resolutions.py:149: error: Argument 2 to "ContainsTheEntry" has incompatible type "bool"; expected <nothing>  [arg-type]

If i remove the | V from the third overload, mypy is happy, but that's not a correct type. Only the keys need to be hashable for the dictionary.

What's wrong with this? How do i properly type this class?

#

i tried making the class Generic[K, V] but that caused even more mypy errors requesting type defs for the other three lines in the test

#

... what if i do V = TypeVar("V", bound=object)? πŸ€”

trim tangle
trim tangle
#

You need to do ```py
class ContainsTheEntry(Generic[K, V]):

urban imp
# urban imp i tried making the class `Generic[K, V]` but that caused even more `mypy` errors...

i did try that, but then i get these:

tests/test_resolutions.py:146: error: Need type annotation for "cte_single"  [var-annotated]
tests/test_resolutions.py:147: error: Need type annotation for "cte_multiple"  [var-annotated]
tests/test_resolutions.py:149: error: Need type annotation for "cte_alternating"  [var-annotated]
tests/test_resolutions.py:149: error: Argument 1 to "ContainsTheEntry" has incompatible type "str"; expected <nothing>  [arg-type]
tests/test_resolutions.py:149: error: Argument 2 to "ContainsTheEntry" has incompatible type "bool"; expected <nothing>  [arg-type]
urban imp
undone saffron
#

The alternating key/value bit isn't expressible with python typing for an arbitrary length the way you have up there. that's allowing the key type or the value type for any entry.

#

the iterable of tuples works fine, but not just iterable[K | V]

trim tangle
#

I think the overloads are ambiguous, namely overloads 2 and 3 when you provide a single argument

#

oh wait, are they evaluated in order?

#

anyway, that's not relevant because that's not the overload that causes a problem

undone saffron
#

it's not a typable concept to begin with as described, and the untypable concept matches where the original error was shown.

trim tangle
#

well, yes, it's not perfectly typeable

trim tangle
#

Right now mypy has no way of determining the parameters. It could be

  • K = str | bool, V = Never
  • K = str, V = bool
  • K = bool, V = str
    ...
#

why do you want to support this format btw? it seems cumbersome to use

urban imp
#

my library bends over backwards to make end-to-end tests very easy to read, so i wanted to support a lot of ways to ask "does this dictionary contain this entry"

#

i guess i can just say object instead of K or V for the last overload...

which brings me to another question, what am i saying when i use object instead of Any?

urban imp
tranquil turtle
#

i believe there is a better explanation of "object vs Any"

sick parcel
#

hello, I have a question about a subclass problem of Protocol:

E.g., I have an interface/ protocol A that would be generics, as I would expect there are interfaces that looks like A[str] and A[int], my approach is to inherit A and then create a modified version of protocol A, e.g., StringA being the same interface with generic A[str].

My question is:

  • why does MyClass even though implemented the interface of StringA, the issubclass check is still returning False?
  • but if I remove the protocol inheritance of StringA(A[str]), that check returns as True?
  • is there some way I can still maintain a check that relies on Generics[A]?
    from typing import Generic, Protocol, TypeVar, runtime_checkable

    T = TypeVar("T")

    @runtime_checkable
    class A(Protocol, Generic[T]):
        @staticmethod
        def hello() -> T:
            ...

    # I expect there are differen interface
    # e.g., A[str] A[int]
    class StringA(A[str]):
        @staticmethod
        def hello() -> str:
            ...

    ### Assuming MyClass cannot be modified, 
    ### as I expect this to be implemented by different people otuside of my package
    class MyClass:
        @staticmethod
        def hello() -> str:
            return "hello world"

    print(issubclass(StringA, A))  # true
    print(issubclass(MyClass, StringA))  # false??
tranquil ledge
#

That's my understanding, at least. Not an expert.

sick parcel
#

thanks. I guess if need to check protocol, I can only check it without subtyping protocl then 😦

tranquil ledge
#

You can make inheriting protocols runtime-checkable as well.

#

I think.

#

e.g. This works.

from typing import Generic, Protocol, TypeVar, runtime_checkable

T = TypeVar("T")

@runtime_checkable
class A(Protocol, Generic[T]):
    @staticmethod
    def hello() -> T:
        ...

@runtime_checkable
class StringA(A[str], Protocol):
    ...

class MyClass:
    @staticmethod
    def hello() -> str:
        return "hello world"


print(issubclass(StringA, A))  # True
print(issubclass(MyClass, StringA))  # True
#

Oh. But issubclass won't check the types.

#
@runtime_checkable
class IntA(A[int], Protocol):
    ...


class MyClass:
    @staticmethod
    def hello() -> str:
        return "hello world"

print(issubclass(MyClass, IntA))  # True
#

Like, this is misleading.

#

It's not behavior you want, I assume.

sick parcel
#

yep.

tranquil ledge
#

Yeah, so I take it back.

sick parcel
#

I mainly want to check the function is there.

#

:D:D

tranquil ledge
#

It'll do that, but it won't check the return types of protocol methods, I guess.

#

That's probably somewhere in the PEP as well.

sick parcel
#

oh wow it works. πŸ˜„

#

i know why it doesn't work the last time,

#

if I put Protocol, TypeA

#

it deson't allow it :D:D

#

only TypeA, Protocol could allow me to do this

tranquil ledge
#

Oh, didn't know order mattered there. Interesting.

sick parcel
#

I think it need s to check TypeA first, then runtime check the subtype protocol 😐

tranquil ledge
#

Huh. TIL.

tranquil ledge
sick parcel
#

thanks . this is super helpful. I need that interface inheritance, since I have that interface generic only two different classes.
which works like a bridge between the output from a interface, then convert the output from the same interface with different generics.

tranquil ledge
#

Yeah, no problem! It's not a perfect fix, since issubclass and isinstance don't seem to care about what types are in place of the generics at runtime, but better than nothing, I suppose.

sick parcel
#

need to write this behaviour down in my notebook. can't seem to find any thing related to this in google :D:D

tranquil ledge
#

However, it should be possible for protocol types to implement custom instance and class checks when this makes sense, similar to how Iterable and other ABCs in collections.abc and typing already do it, but this is limited to non-generic and unsubscripted generic protocols (Iterable is statically equivalent to Iterable[Any]).

Found it in the PEP. Unfortunate.

urban imp
tranquil turtle
#

typing as object is pretty rare thing

undone saffron
#

object makes sense over Any in some cases where Any would obscure other issues and object is sufficient, but most of those cases are better expressed with a protocol for what you actually need the object to have (Hashability, for example)

solemn sapphire
#

With the following code

1  from typing import Self
2
3  class Thing:
4      def __init__(self) -> None:
5          self.things: dict[int, list[Self]]
6
7      def insert(self, id: int) -> None:
8          self.things[id].append(Thing())
9
10     def __getitem__(self, id: int) -> list[Self]:
11         return self.things[id]

Mypy complains with the following strange message :

/private/tmp  ΞΆ mypy test.py
test.py:8: error: Argument 1 to "append" of "list" has incompatible type "Thing"; expected "Self"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

If I change self.things: dict[int, list[Self]] to self.things: dict[int, list[Thing]] then mypy complains about
__getitem__'s signature.

/private/tmp  ΞΆ mypy test.py
test.py:11: error: Incompatible return value type (got "list[Thing]", expected "list[Self]")  [return-value]
Found 1 error in 1 file (checked 1 source file)

I wonder if that's a bug?

#

also reproducible with --new-type-inference

oblique urchin
#

You should possibly use Thing instead of Self

solemn sapphire
#

Interesting, that would mean the Self type is different from "Thing" type, so I'll have to resort to using "Thing" within the class :(

viscid spire
#

you can also do from __future__ import annotations to just use the class as-is (all annotations becomes strings to avoid runtime errors)

#

(for "Thing" -> Thing)

solemn sapphire
#

Ah, Nice!

rustic gull
#

I am really having a hard time to understand in which kind of situations we may need to explicitly check the type of an object at runtime, with ABCs or typing.Protocol. I mean, a use case that cannot be solved with duck typing or that it is not very elegant in that case. Can anyone help me?

undone canopy
#

Whenever you have an interface that accepts a union of objects? E.g.: a function that accepts either a Distance object or a string such as "euclidean".

solemn sapphire
#

Its easy to show for union types like str | int, you'll need to check if something is a str to be able to safely call methods like .upper() on it.

T | None is also a common

but in those case a if x: check usually gets the job done if you know that T is never falsy, or if it is you don't care eitherway.

#

its usually if x is None: because None is a Sentinel

undone canopy
solemn sapphire
#

I agree

rustic gull
#

So, it is always possible to use duck typing but there are circumstances in which is better to check?

undone canopy
#

It is not always possible to use duck typing, that is false.

#

Consider this toy example:

class Cowboy:
    def draw():
        ...

class Painter:
    def draw():
        ...

def function(a: Cowboy | Painter):
    ...

Here in function is not possible to use anything except isinstance to distinguish between the two.

tranquil turtle
#

typecheckers do allow duck typing sometimes
in your example, you can do a.draw() without isinstance-checks because a have .draw in any case

hallow flint
mellow talon
#

Hello there,
I've a module containing two classes, which at some point need to self-reference them in the return type.

This cannot be achieved per se, hence me using the Type['classname'] approach to overcome the type hinting issue.

However, at some point I need also to return - in a list comprehension - a set of objects of one of these class, which when compared with the other instances within the code return a type hinting error as "Expression of type "MyClass | None" cannot be assigned to declared type "type[MyClass]"

How can this be solved?

I tried declaring an alias, and casting the return type of my new object within the comprehension list without success.

frigid jolt
#

type[MyClass] means that you return the class itself (not an object of that class)

mellow talon
#

still not clear how can I achieve the two of them though

#

I understand that at one point I'm instantiating the object but why comparing the object with its type does fail?
In my mind, Obj() ... has a signature that should be represented by Type['Obj'] and the 2 should be comparable at a type hint level, no?

trim tangle
#

type[...] means you're returning a class

eager vessel
mellow talon
frigid jolt
dim axle
#
class BoundText(tk.Text):
    """A Text widget with a bound variable."""

    def __init__(self, *args, textvariable: tk.StringVar | None = None, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self._variable = textvariable
        if self._variable:
            # insert any default value
            self.insert('1.0', self._variable.get())
            self._variable.trace_add('write', self._set_content)

    
    def _set_content(self, *_) -> None:
        """Set the text contents to the variable"""
        self.delete('1.0', tk.END)
        self.insert('1.0', self._variable.get())

"get" in last line (self.insert('1.0', self._variable.get())) is underscored in vscode:
"get" is not a known member of "None" PylancereportOptionalMemberAccess
even if i check self._variable, is that inevitable?

rare scarab
#

wrap it in if self._variable: like you did in __init__

#

or make _variable non-noneable

#

or pass _variable as an argument

solemn sapphire
#

assert self._variable would also work

dim axle
#

thanks

mellow talon
# frigid jolt if you want to return an object of `MyClass` then you just do `-> MyClass` ```p...

I might actually not expressed me very well. Sorry but yesterday I had no chance to write down some code. See you below a bare example:

class Class1:  
  def getlist(self) -> list["Class2"]:
    self.mylist = [Class2(1, 2, self) for x in something]

  def foo(self) -> Class2:
    retutn self.mylist[0]

class Class2:
  def __init__(self, x, y: Class1):
    ...

Both my classes live in the same file.

The problem is with Class1.foo that can return Class2.
But when I do so my other part of the code, where the class are implemented returns a type hint error message. If I convert into a Type["Class2"], I resolve the issue from the implementation side, but have problems with the class itself.

frigid jolt
#

or have you already imported it?

mellow talon
#

If I change the code as below ... the file containing my two classes laments the following type hinting message: Expression of type "Class2 | None" cannot be assigned to declared type "type[Class2]"

class Class1:  
  def getlist(self) -> list["Class2"]:
    self.mylist = [Class2(1, 2, self) for x in something]

  def foo(self) -> Type["Class2"] | None:
    x: Type["Class2"] = self.mylist[0] 
    retutn self.mylist[0]

class Class2:
  def __init__(self, x, y: Class1):
    ...

If I change the implementation of foo in def foo(self) -> myfile.Class2 | None: I get a partially initialized module error message.

#

So I am stuck in understanding how this can be done

mellow talon
frigid jolt
mellow talon
#

Apparently it does more than that, because I was able even to return the class itself as per my previous example.

Fair enough.

But am I wrong when I say it's not anymore recommended with 3.12?

frigid jolt
#

again, that type annotation is wrong, you're not returning the class itself, you're returning an object (an instance) of that class which is different from returning the class itself

#

and about that import

mellow talon
waxen prawn
#

Looking for some help with this:
I have a class based on TypedDict, and I want to write a function that can take an argument of any key from within the TypedDict, specified by a type annotation.

class MyType(TypedDict):
  key1: str
  key2: str

myDict: MyType = {"key1": "value1", "key2": "value2"}

def getValueFromMyType(key: SOME_TYPE_HINT_HERE) -> SOME_RETURN_TYPE_HERE:
  return myDict[key]

I could use Literal["key1", "key2"] for the key type hint, but in my actual implementation, MyType has too many keys for that to be reasonable and this doesn't feel very DRY.

Anyone have a solution for this?

trim tangle
#

If you want to also link the return type to the input key, that's definitely not possible

#

You can extract a type alias, like ```py
MyTypeKeys = Literal["key1", "key2"]

frigid jolt
waxen prawn
trim tangle
#

python's type system is pretty bad at mixing/transforming existing types

waxen prawn
#

yeah something like this would be relatively easy in typescript 😦

trim tangle
#

yep

undone saffron
#

actually, lemme double check, that may not compose, there were a lot of annoying limitations added against composition

undone saffron
#

Hmm, something I expected to work doesnt, and I seem to have also run into a bug in pyright that's only tangentially related in the process.

#

If you have a case like the above where the values are homogenous, you can do:

type MyKeys = Literal["key1", "key2"]

type MyType = dict[MyKeys, str]

myDict: MyType = {"key1": "value1", "key2": "value2"}


def getValueFromMyType(key: MyKeys) -> str:
    # matching the exact case you had
    return myDict[key]
#

I'm guessing that isn't actually the case in the corresponding real world code though

waxen prawn
undone saffron
#

πŸ€”

you can have typeddicts where the values vary in type:

eg:

class Movie(TypedDcit):
    year: int
    title: str
#

that wouldn't work with the way I gave above

#

(though whether you should have typed dicts like that is another matter, and I'd argue one should not)

waxen prawn
bleak imp
#

One thing you should be able to do is the entire process in reverse ```py
from typing import Literal, get_args

MyTypeKeys = Literal["key1", "key2"]
MyTypeValues = Literal["value1", "value2"]
keys: tuple[MyTypeKeys] = get_args(MyTypeKeys)
values: tuple[MyTypeValues] = get_args(MyTypeValues)
myDict: dict[MyTypeKeys, MyTypeValues] = dict(zip(keys, values))

def getValueFromMyType(key: MyTypeKeys) -> MyTypeValues:
return myDict[key] ```
I got this to work in MyPy strict, but as you can see it doesn't like it, and I couldn't get it working with the typed dict. Couldn't get anything working for pyright.

trim tangle
#

also TypedDict allows extra fields iirc

undone saffron
#

If the macro pep ever goes anywhere, you could have a macro that generates all the overloads needed in that case. There's also the fact that you should just be able to lie to the type checker and have it work here, but some intentional design decisions of both pyright and mypy preclude that

The precluded fix to just use the type of something that should have that signature being:

class MyType(TypedDict):
    key1: str
    key2: str

myDict: MyType = {"key1": "value1", "key2": "value2"}

if TYPE_CHECKING:
    # this should work, but neither mypy nor pyright generate internal overloads here
    getValueFromMyType = myDict.__getitem__
else:
    def getValueFromMyType(key)
        return myDict[key]
rustic gull
viscid spire
#

<@&831776746206265384> this guy spammed this message in multiple channels

bright tangle
#

E.G.

def foo(data: str): # return type inferred by pylance
    ... # filled in by user
def process_1(data: ReturnType[foo]) -> Any:
    ... # filled in by user
def process_2(data: ReturnType[foo]) -> Any:
    ... # filled in by user

framework.code(foo, process_1, process_2) # signature: (str) -> T, (T) -> Any, (T) -> Any
bright tangle
#

Dang

trim tangle
#

there's no way to refer to a value from a type

bright tangle
#

I mean reveal_type(foo) tells me the correct (inferred) type of foo, including its return value

#

It's a little frustrating I can't manipulate that type, or at least use the framework code from later on to narrow the argument types earlier

bright tangle
bright tangle
#

Argh, it's almost possible in two different ways but doesn't work for two different reasons

#
T = TypeVar('T')

class ReturnType:
    def __class_getitem__(cls, item: Callable[[str], T]) -> type[T]:
        return item.__annotations__['return']

def return_type(fn: Callable[[str], T]) -> type[T]:
    return fn.__annotations__['return']

These both nearly work, but don't because #1 causes type checkers to infer ReturnType (instead of the declared result of __class_getitem__) and #2 causes a type checking error because apparently you're not allowed to use calls in type expressions (despite the call being fully type-hinted so no actual code execution should be necessary for type inference)

#

(does anyone know the purpose of those limitations, btw? IMO __class_getitem__ would be a more useful feature if you were allowed to completely override the returned type, and allowing calls for arbitrary type transformations would also make type-hinting more powerful)

high wigeon
#

Why is this a type error (starting from mypy 1.6.0)? The types seem compatible to me.

def foo(x: str) -> str | None:
    return None

foo = lambda _: None  # Incompatible types in assignment (expression has type "Callable[[str], None]", variable has type "Callable[[str], str | None]")  [assignment]
#

It probably has something to do with assigning to a function. If I instead declare foo : Callable[[str], str | None], there is no type error.

acoustic thicket
#

def foo(x: str, /) or lambda x: None should work

high wigeon
#

Ah, good point, I see what is going on here!

#

I guess this could be called a bug of the error message being confusing.

#

I ran into this when trying to type 3rd party code that monkey-patches ctypes.util.find_library.

#

I guess that means there must be more to foo's type than reveal_type reveals. Is there a way to show it all?

#

Actually, I guess reveal_type does show it, I just didn't notice the x :P

tranquil turtle
#

pyright gives more info about function signature
not sure about mypy

high wigeon
#

Yes, it says Type of "foo" is "(x: str) -> (str | None)" with reveal_type(foo).

#

I think this is the bug, but the report is confused with return values and subclasses.

#

Interestingly, if I change the return value of foo to None (so there's no narrowing of the return value), mypy gives a better error message:

Incompatible types in assignment (expression has type "Callable[[Arg(int, '_')], None]", variable has type "Callable[[Arg(int, 'x')], None]") [assignment]

bleak imp
#

Would this not work? It passes pyright, and fails as expected if I say make foo return a str and pass an int into process_1 ```py
from typing import Callable, Any
def foo(data: str): # return type inferred by pylance
... # filled in by user

def return_type[T](fn: Callable[[str], T]) -> type[T]:
return fn.annotations['return']

def process_1[T: return_type(foo)](data: T) -> Any:
... # filled in by user
def process_2[T: return_type(foo)](data: T) -> Any:
... # filled in by user```

soft matrix
#

no thats not having the types be inferred correctly

cunning raven
#

guys how to type the instance of a class

#

*annotate

#

type[T] is the class, T would be the instance

#

is there a way to get only the instance

#
from typing import TypeVar

T = TypeVar("T")

no_use = type[T]

def foo(x: T) -> ...: ...
#

idk

#

annotate an instance of an instance of the class

fierce ridge
#

i'm not really sure what you're asking

cunning raven
#

well i want to annotate the instance of that class

#

the class can vary

#

thats why it cannot be static

#
class Foo: ...

def foo(x: Foo) -> None: ...```
fierce ridge
#

do you want foo to accept any subclass of Foo?

cunning raven
#

im now confused to myself

#

gimme a sec lol i am going to think

fierce ridge
#
class Foo: ...

class Bar(Foo): ...

def foo(x: Foo) -> None: ...

b: Bar
foo(b)
Success: no issues found in 1 source file
cunning raven
#

i want foo to accept any object which metaclass is any class

#

and that class is created from type

cunning raven
#

its exactly what i want but !, consider the class can vary

fierce ridge
cunning raven
fierce ridge
#

no, sorry. ignore me. too early

cunning raven
#

lol

#

people make mistakes

fierce ridge
#

but if you can clarify what you're looking for with a practical example, it might help

cunning raven
#

you are helping for free, no need to say sry

cunning raven
#

yeah honestly typing.Any seems accurate

#

i just thought of smth like this

#
# typevar stuff
AnyClass = type[T]

def foo(obj: T) -> None: ...
normal cape
soft matrix
#

!d typing.Literal

rough sluiceBOT
#

typing.Literal```
Special typing form to define β€œliteral types”.

`Literal` can be used to indicate to type checkers that the annotated object has a value equivalent to one of the provided literals.

For example...
soft matrix
#

It'll be using this

normal cape
#

Thanks for the help! Got it to work now aSDVjunimopat

old needle
#
class Node():
    def __init__(self, data):
        self.data = data
        self.next = None

    def set_next_node(self, node: Node):
        self.next = node

    def get_next_node(self):
        return self.next

Is it possible to set a type hint for a class within that class's definition? Getting an error on node: Node.

acoustic thicket
#

node: "Node"

old needle
#

Neat. Thanks.

viscid spire
pure badger
trim tangle
#

Well, it has different semantics

trim tangle
# pure badger You can use typing.Self which now allows referencing the current class. Availabl...

Actually isn't accepting Self as an argument kinda dubious? πŸ€”

class Node:
     def set_next_node(self, node: Self) -> None:
        self.next = node

class MySuperNode(Node):
    ...

super_node = MySuperNode(1)
other_super_node = MySuperNode(2)
normal_node = Node(3)

super_node.set_next_node(other_super_node)  # 1. ok, as expected
super_node.set_next_node(normal_node)  # 2. error, as expected

def f(hmm: Node) -> None:
    hmm.set_next_node(normal_node)  # obviously ok

f(super_node)  # also ok, but this is the same as 2.
#

Yeah it's hella unsound

#

@oblique urchin what do you think?

oblique urchin
trim tangle
#

oh wait you actually sponsored it

#

I just pinged you as an authoritative typing person lol

cunning raven
#

guys

def method(obj, *args, **kwargs):
    def wrapper(func):
        return func(obj, *args, **kwargs)
    return wrapper


class Foo:
    def func(self):
        print(self.x)
foo = Foo()

@method(foo, 1)
def __init__(self, x) -> None:
    self.x = x

foo.func()``` can you all type hint this for me but in a very cursed way
trim tangle
#

this code does not work

cunning raven
#

it works

trim tangle
#

oh wait yeah

#

why though

cunning raven
#

i just wanna add type hints but in a cursed way

trim tangle
#

well, the only way to type this is via ParamSpec

#

which is arguably cursed enough

cunning raven
cunning raven
trim tangle
#

!d typing.ParamSpec

rough sluiceBOT
#

class typing.ParamSpec(name, *, bound=None, covariant=False, contravariant=False)```
Parameter specification variable. A specialized version of [type variables](https://docs.python.org/3/library/typing.html#typevar).

In [type parameter lists](https://docs.python.org/3/reference/compound_stmts.html#type-params), parameter specifications can be declared with two asterisks (`**`):

```py
type IntFunc[**P] = Callable[P, int]
```  For compatibility with Python 3.11 and earlier, `ParamSpec` objects can also be created as follows:

```py
P = ParamSpec('P')
```  Parameter specification variables exist primarily for the benefit of static type checkers. They are used to forward the parameter types of one callable to another callable – a pattern commonly found in higher order functions and decorators. They are only valid when used in `Concatenate`, or as the first argument to `Callable`, or as parameters for user-defined Generics. See [`Generic`](https://docs.python.org/3/library/typing.html#typing.Generic) for more information on generic types.
cunning raven
#

lmao

#

this is perfect

#

thanks mate haha

#

this gonna be so fun

lunar dune
trim tangle
#

Python's typing cannot even begin to compete with the curses of some TypeScript

cunning raven
cunning raven
#

ngl im kinda not understanding paramspec

trim tangle
lunar dune
cunning raven
#

LMAO

#

wait

#

isnt unpack only for defined dicts

trim tangle
#

oh, is it supported actually?

lunar dune
trim tangle
#

well I meant the tuple with something stitched to the end

#

like tuple[str, ..., int]

oblique urchin
lunar dune
cunning raven
#

i dont think thats valid tho

trim tangle
#

well, yes, with the beauty of Unpack of course

oblique urchin
#

(or * in 3.11+)

#

tuple[*tuple[str, ...], int] in modern Python

trim tangle
#

!e

print(tuple[*tuple[str, ...]])
rough sluiceBOT
#

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

tuple[*tuple[str, ...]]
trim tangle
#

oh huh

#

!e

print(list(tuple[str, ...]))
rough sluiceBOT
#

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

[*tuple[str, ...]]
trim tangle
#

I uhh I suppose yeah, though this^ looks kinda cursed

cunning raven
#
from typing import Callable

def method(obj, *args, **kwargs):
    def wrapper[T, **P](func: Callable[P, T]) -> T:
        return func(obj, *args, **kwargs)
    return wrapper


class Foo:
    def func(self):
        print(self.x)
foo = Foo()

@method(foo, 1)
def __init__(self, x) -> None:
    self.x = x

foo.func()```
#

the wrapper func

#

is it well annotated

trim tangle
#

.uwu ```py
from typing import Unpack, Tuple, Any

def good(a: int, b: str, *args: Unpack[Tuple[Unpack[Tuple[Any, ...]], int]]) -> int: ...

mighty lindenBOT
#

fwom typing impowt unpack, tupwe, any

def good(a: i-int, b: stw, *awgs: unpack[tupwe[unpack[tupwe[any, ...]], i-int]]) -> int: . ^^ ʘwʘ

cunning raven
#

.uwu

tranquil ledge
trim tangle
#

yeah

gleaming mortar
#

Hello, using a type hint like
myfunct(x: pd.Series[int]), raises a TypeError.: type Series is not subscriptable. What can I do?

#

I have read about some solutions:

  1. adding from future import annotations
    or
  2. writing like x:"pd.Series[int]"
    Which of the two is more appropriate?
tranquil turtle
#

1

#

2 is needed only if you are on old python i think

gleaming mortar
paper salmon
gleaming mortar
paper salmon
gleaming mortar
#

yeah I was told it doesn't, so I guess it makes no sense to discuss it. I was just trying to write some good documentation for my code through type hints/docstrings. I already handle input validation myself

gleaming mortar
#

Assuming pandas objects just arent supported for type hints which I assume they are not, then tools like mypy would simply ignore that hint and not enforce it?

bleak imp
noble condor
#

is it possible to somehow typehint that the variable is a dataclass?

soft matrix
#

_typeshed.DataclassLike should do it

noble condor
#

_typeshed?

soft matrix
tranquil turtle
#

is it ok to use import _typeshed inside of if TYPE_CHECKING: in .py files?

oblique urchin
grave fjord
#

I forgot what it's called though

tranquil turtle
#

runtimeshed

#

rtshed

#

i have no idea what the word "shed" means in this context

lunar dune
tranquil turtle
#

!pypi useful_types

rough sluiceBOT
grave fjord
#

Thanks!

trim tangle
#

untapped market

grave fjord
trim tangle
#

I typed !pypi useless_types

rare scarab
rough sluiceBOT
#

@typing.dataclass_transform(*, eq_default=True, order_default=False, kw_only_default=False, frozen_default=False, field_specifiers=(), **kwargs)```
Decorator to mark an object as providing [`dataclass`](https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass)-like behavior.

`dataclass_transform` may be used to decorate a class, metaclass, or a function that is itself a decorator. The presence of `@dataclass_transform()` tells a static type checker that the decorated object performs runtime β€œmagic” that transforms a class in a similar way to [`@dataclasses.dataclass`](https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass).

Example usage with a decorator function...
rare scarab
#

you can put that on a base class, decorator, meta class, etc

signal brook
#

i cant win

tranquil ledge
signal brook
#

woah

#

ive never used assert before

rare scarab
#

Could also use typing.cast

frigid jolt
#

imo it's better to use assert in this case, because you can run python with the -O option

rustic gull
# signal brook i cant win

Your comment says "DM Handler", but you want a Member. Member implies that the user is in the context of a guild - so you will always get User for DMs.

#

Hi all, hopefully a quick one :)
I'd like to type my ABC as requiring a field implementation (i only care about get access). Usually I'd do this with an abstract property:

class Base(ABC):
  @property
  @abstractmethod
  def name(self) str: -> ...

class Impl(Base):
  name: str

but with pylance set to strict, I get the error: "name" incorrectly overrides property of same name in class "Base"
How would you do this?

trim tangle
rustic gull
#

That makes sense, thanks. Is there a way to require an abc implementation to have a field? I would use a protocol, but my ABC has shared logic in it (that depends on the field)

harsh sequoia
#

**

signal brook
#

is this irony

lunar dune
# signal brook is this irony

The dirty little secret is that some of the most dynamic code in the Python standard library is actually in the typing module

#

Don't tell anybody

tranquil turtle
#

i think only pickle might be as dynamic as typing.py

fierce ridge
#

the logging framework is pretty dynamic, it basically uses each LogRecord instance as an arbitrary mapping

tranquil turtle
#

and there is def __iter__(self): if False: yield somewhere in the stdlib, dont remember where exactly

lunar dune
trim tangle
#

yield from ()?

#

!e

import dis

@dis.dis
def f():
    yield from ()
rough sluiceBOT
#

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

001 |   3           0 RETURN_GENERATOR
002 |               2 POP_TOP
003 |               4 RESUME                   0
004 | 
005 |   5           6 LOAD_CONST               1 (())
006 |               8 GET_YIELD_FROM_ITER
007 |              10 LOAD_CONST               0 (None)
008 |         >>   12 SEND                     3 (to 22)
009 |              16 YIELD_VALUE              2
010 |              18 RESUME                   2
011 |              20 JUMP_BACKWARD_NO_INTERRUPT     5 (to 12)
... (truncated - too many lines)

Full output: https://paste.pythondiscord.com/LESRUUU45IF5HICTSI4AGG4K24

trim tangle
#

oh damn, that's a lot of bytecode

brisk hedge
#

I know the following probably raises warnings, but

#

!e ```py
import dis

@dis.dis
def f():
return
yield

rough sluiceBOT
#

@brisk hedge :white_check_mark: Your 3.12 eval job has completed with return code 0.

001 |   3           0 RETURN_GENERATOR
002 |               2 POP_TOP
003 |               4 RESUME                   0
004 | 
005 |   5           6 RETURN_CONST             0 (None)
006 |         >>    8 CALL_INTRINSIC_1         3 (INTRINSIC_STOPITERATION_ERROR)
007 |              10 RERAISE                  1
008 | ExceptionTable:
009 |   4 to 6 -> 8 [0] lasti
lunar dune
lunar dune
trim tangle
#

return iter(())
Or like
return EMPTY_ITER

#

unless you really want a generator

rare scarab
#
def f():
  # empty iterator/generator
  if False:
    yield
#

This one also works with async generators

jade viper
#

Hey guys, can I "dynamically lint" a function? What I mean is:

def function_router(*args, **kwargs):
    if kwargs.get(some_param) == 'some_value':
        return function_a(*args, **kwargs)

    return function_b(*args, **kwargs)

Let's say function_a and function_b have the exact same parameters, how can I make it so that when calling function_router your linter will recognize the correct params?

acoustic thicket
#

!d typing.ParamSpec this?

rough sluiceBOT
#

class typing.ParamSpec(name, *, bound=None, covariant=False, contravariant=False)```
Parameter specification variable. A specialized version of [type variables](https://docs.python.org/3/library/typing.html#typevar).

In [type parameter lists](https://docs.python.org/3/reference/compound_stmts.html#type-params), parameter specifications can be declared with two asterisks (`**`):

```py
type IntFunc[**P] = Callable[P, int]
```  For compatibility with Python 3.11 and earlier, `ParamSpec` objects can also be created as follows:

```py
P = ParamSpec('P')
```  Parameter specification variables exist primarily for the benefit of static type checkers. They are used to forward the parameter types of one callable to another callable – a pattern commonly found in higher order functions and decorators. They are only valid when used in `Concatenate`, or as the first argument to `Callable`, or as parameters for user-defined Generics. See [`Generic`](https://docs.python.org/3/library/typing.html#typing.Generic) for more information on generic types.
jade viper
# rough sluice

Reading the documentation I'm not quite sure how to use it?

tranquil ledge
#

Sounds a bit more like you're looking for overloads that *indicate different return types based on a specific kwarg's value/type.

opaque root
# jade viper Hey guys, can I "dynamically lint" a function? What I mean is: ```py def functi...

you might be able to jam enough typing.overloads on top to get the desired outcome, assuming the parameters are the same, but the return value is different and that's what you're interested in (and some_param is a string literal that won't change)
ParamSpecs very quickly hit limits when confronted with named arguments or any modification of them that isn't the very spare addition of positional arguments via Concatenate

jade viper
#

Aight

#

Thanks everyone:)

bleak imp
#

I have a system that basically does the reverse by using a decorator to register routable functions, sadly even though Literals are usable because I route by strings I don't think it's possible to dynamically construct a Literal that anything will actually parse.

granite ruin
#

!d typing.dataclass_transform

rough sluiceBOT
#

@typing.dataclass_transform(*, eq_default=True, order_default=False, kw_only_default=False, frozen_default=False, field_specifiers=(), **kwargs)```
Decorator to mark an object as providing [`dataclass`](https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass)-like behavior.

`dataclass_transform` may be used to decorate a class, metaclass, or a function that is itself a decorator. The presence of `@dataclass_transform()` tells a static type checker that the decorated object performs runtime β€œmagic” that transforms a class in a similar way to [`@dataclasses.dataclass`](https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass).

Example usage with a decorator function...
lethal delta
#

Is there a standard practice for documenting type variables when using Sphinx reST?

undone saffron
#
#: Doc
ADescriptiveTypeVarName = TypeVar("ADescriptiveTypeVarName")
#

with 3.12 syntax, you'd just describe it in the body, like below. Basically just provide people information where it makes sense to.

class G[T]:
    """
    ...
    Parameters
    ----------
    typ: T
        describe how this is used
    """

    def __init__(self, typ: T):
        ...
lethal delta
lethal delta
undone saffron
#

pretty sure #: is just a default thing for sphinx

undone saffron
lethal delta
#

Ah, a shame. Ty though.

undone saffron
#

for what it's worth, I actually prefer not using the 3.12 syntax if the typevar itself should be documented in any detail

#

it's much easier to attach the right information to it that way

#

little rare that that's the case (needing more than minimal detail on it) though, only come up twice personally.

lethal delta
#

I misunderstood what you meant by 3.12 syntax.

#

I've been away from Python for 2 weeks and I'm slowly reacquinting myself.

#

Also, I haven't used 3.12 much aside from building it and testing compatibility for a library. πŸ˜…

undone saffron
#

ah, right, I meant that the 3.12 generic syntax doesn't really provide an opportunity to attach a doc to the symbol, so you need to do it in the class doc, but since it can't be used outside the class in that case (no re-use) it's fine except when there's something specific worth noting extra.

#

There's a proposal for Annotated being used to attach docs, but there's a lot of problems with that proposal and it seems unlikely to be accepted.

lethal delta
#

Interesting, ty.

lethal delta
frigid jolt
#

ok ty

unborn acorn
unborn acorn
# rare scarab Not cursed enough.

well, it’s fully typed, so technically it can’t get more cursed. But because python type checkers aren’t β€œbidirectional” enough, it actually doesn’t work if you ever try to use this method function.

rare scarab
#

fully typed python = cursed

lethal delta
#

I disagree. I think it'd be an interesting but different language.

#

the list vs List distinction makes no sense to me

#

they could have made types and classes less different and simplified things for everyone

#

but the way things are, we're committed to some types having lower case names while others aren't.

oblique urchin
#

!pep 585

rough sluiceBOT
signal brook
#

does this look right

from interactions import Permissions
from typing import Any

PermDict = dict[str, tuple[Permissions] | tuple[()]]
RoleDict = dict[str, dict[Any, Any] | PermDict]

ROLES: dict[str, RoleDict] = {
    "staff": {
        "allow": (
            Permissions.VIEW_CHANNEL,
            Permissions.SEND_MESSAGES,
            Permissions.EMBED_LINKS,
            Permissions.ATTACH_FILES,
            Permissions.ADD_REACTIONS,
            Permissions.READ_MESSAGE_HISTORY,
        ),
        "deny": (),
    },
    "bot": {
        "allow": (Permissions.VIEW_CHANNEL,),
        "deny": (),
    },
    "ignore_messages": {},
    "everyone": {
        "allow": (),
        "deny": (Permissions.VIEW_CHANNEL,),
    },
}
#

the type hints

#

dict[Any, Any] was what i could come up with for an empty dict

#

i just dont like the auto hinting saying Unknown

viscid spire
signal brook
#

oh dang

oblique urchin
signal brook
viscid spire
#

you can still use Never

signal brook
#

well its not working out

oblique urchin
#

you want tuple[Permissions, ...] instead of tuple[Permissions]

#

tuple[Permissions] is a tuple of exactly one

oblique urchin
# signal brook

you truncated the error message, so hard to say what the issue is

signal brook
#
Expression of type "dict[str, dict[str, tuple[Literal[Permissions.VIEW_CHANNEL], Literal[Permissions.SEND_MESSAGES], Literal[Permissions.EMBED_LINKS], Literal[Permissions.ATTACH_FILES], Literal[Permissions.ADD_REACTIONS], Literal[Permissions.READ_MESSAGE_HISTORY]] | tuple[()]] | dict[str, tuple[Literal[Permissions.VIEW_CHANNEL]] | tuple[()]] | dict[str, PermDict]]" cannot be assigned to declared type "dict[str, RoleDict]"
  "tuple[Literal[Permissions.VIEW_CHANNEL], Literal[Permissions.SEND_MESSAGES], Literal[Permissions.EMBED_LINKS], Literal[Permissions.ATTACH_FILES], Literal[Permissions.ADD_REACTIONS], Literal[Permissions.READ_MESSAGE_HISTORY]]" is incompatible with "PermDict"
  "tuple[()]" is incompatible with "PermDict"
  "tuple[Literal[Permissions.VIEW_CHANNEL]]" is incompatible with "PermDict"
  "tuple[()]" is incompatible with "PermDict"
  "tuple[()]" is incompatible with "PermDict"
  "tuple[Literal[Permissions.VIEW_CHANNEL]]" is incompatible with "PermDict"PylancereportGeneralTypeIssues
oblique urchin
#

seems like you have one too many levels of dict[str, ...]

signal brook
#

shit youre right

#

okay

from interactions import Permissions

PermDict = dict[str, tuple[Permissions, ...] | tuple[()]]
RoleDict = dict[str, PermDict]

ROLES: RoleDict = {
    "staff": {
        "allow": (
            Permissions.VIEW_CHANNEL,
            Permissions.SEND_MESSAGES,
            Permissions.EMBED_LINKS,
            Permissions.ATTACH_FILES,
            Permissions.ADD_REACTIONS,
            Permissions.READ_MESSAGE_HISTORY,
        ),
        "deny": (),
    },
    "bot": {
        "allow": (Permissions.VIEW_CHANNEL,),
        "deny": (),
    },
    "ignore_messages": {},
    "everyone": {
        "allow": (),
        "deny": (Permissions.VIEW_CHANNEL,),
    },
}
#

hm it seems like i can remove the empty tuple part too

oblique urchin
#

you can also remove the tuple[()] part

#

yes

signal brook
#

h a h. when all that work becomes useless cause i realized i wanna turn it into a json file

#

i mean i learned stuff

leaden oak
signal brook
#

huh

#

Discussion of type hints, function annotations, and type checking, including tools such as mypy, pyright and pydantic.

leaden oak
#

oh lol

#

I am so sorry! I evidently need a nap πŸ’€

trim tangle
#

!ban @leaden oak smh using the wrong channel

rough sluiceBOT
#

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

hardy linden
#

I'm fairly certain I'm just being dense here, but Google isn't turning up anything useful.

High-level problem: My return type is Iterator[Whatever]. I'm returning a Generator[Whatever], and this is marked as incompatible with Iterator[Whatever].

I've got a method where you can either provide a single value as an input, or a Sequence of values. If you provide a single value, you get a single result. If you provide a Sequence of values, you get an Iterator with one result for each input value. I've added typing.overload definitions to indicate this.

The problem is that I want to return a generator for the second use-case; when I try to return it, I get a typing warning.

FooDict = dict[int, str]
class Bar:
  # (Just assume that list(Bar) returns a list of str)

  @typing.overload
  def foo(self, i: int) -> FooDict: ...

  @typing.overload
  def foo(self, i: Sequence[int]) -> Iterator[FooDict]: ...  # Typing warning on this line

  def foo(self, i):
    if isinstance(i, int):
      return {i: list(self)[i]}
    if isinstance(i, Sequence): 
      return ({x: list(self)[x]} for x in i)
    raise TypeError

This gives a typing warning:

Overloaded implementation is not consistent with signature of overload 2
Function return type "Iterator[FooDict] is incompatible with type "dict[int, str] | Generator[dict[int, str], None, None]"
"Iterator[FooDict]" is incompatible with "dict[int, str]"
"Iterator[FooDict]" is incompatible with "Generator[dict[int, str], None, None"

The documentation for typing.Generator even says "Alternatively, annotate your generator as having a return type of [...] Iterator[YieldType]", so I'm not sure what I'm doing wrong here.

The error goes away if I adjust the return type for the second overload to Generator[FooDict, None, None], but it's not clear to me why Iterator[FooDict] doesn't work here.

tranquil ledge
#

I'd guess Pyright strict doesn't implicitly widen to Iterator from Generator on overloads unless the actual function signature, with the union of the overloads, has Iterator in it.

e.g.
This adjustment is valid

trim tangle
#

Huh, that's strange

#

Sounds like a bug to me

hardy linden
hardy linden
#

I'll file an Issue, then. Cheers for the alteration - very much appreciated.

tranquil ledge
#

No problem. I have no idea whether the inference we expect here would be unsound, honestly; I'm just relaying what I know makes the code compatible with the tool.

#

Still worth asking about in an official issue, of course.

trim tangle
#

Pyright is comparable in code size to a Haskell compiler

#

I think it's a lot larger than JHC and on the same order of magnitude as GHC. But I might have calculated too many lines in pyright

hardy linden
tranquil turtle
#

eric is quick

bleak imp
#

I think that response makes sense in this case. By doing typing overloads, you are saying that you want to provide extra information, so you get held to a stricter standard. I can also imagine a counterexample where you overload with both a generator and iterator return, where the implicit widening would generate false positives.

indigo quartz
#

is there a way to inherit parameters from another function with the type hints?

#

like

def base_(*, a: int, b: int, c: str):
  pass

def derived_(*, extras, **inherited_from_base_):
  base_(extras=extras, **inherited_from_base_)
viscid spire
indigo quartz
viscid spire
#

oki

lunar dune
#

A fun new CPython bug just dropped:

>>> from typing import Annotated
>>> Annotated[str, 1]
typing.Annotated[str, 1]
>>> Annotated[str, True]
typing.Annotated[str, 1]
tranquil turtle
#

cache problems?

lunar dune
#

easy to fix, but fun while it lasts πŸ˜„

trim tangle
#

damn...

lunar dune
#

you'll always get some cursed behaviour out of it in the end

tranquil turtle
#

is there a type-aware lru_cache in yhe stdlib?

trim tangle
mighty lindenBOT
trim tangle
#

well, you could argue storing a URL as a string and then extracting stuff to get the port is also a mistake

lunar dune
tranquil turtle
tranquil turtle
trim tangle
#

well, it won't really compose with all the other existing code

brisk hedge
#

It just needs to call int.__str__(port) rather than a subclass

lunar dune
tranquil turtle
#
  1. overriding __str__ in int subclass is not a great idea (but bool and enum.IntFlag do this, and we cant change it)
  2. relying on __str__ to construct values is also not a great idea
dull stirrup
#

Is this greyed out text considered a type of type hinting?

soft matrix
#

yeah theyre called inlays

#

personally not a fan for just variables

dull stirrup
#

Thank you! I couldn't even figure out what they are called.

soft matrix
#

i have it on for returns

dull stirrup
#

It must be an extension that I don't understand running in VS Code

soft matrix
#

its pylance

dull stirrup
#

Okay, I see. I will turn that off. I think I get it now but don't quite understand why it's suggesting list[Any]. I guess I need to circle back to list types.

bleak imp
muted iron
#

Toy example:

def a(a: bool, b: bool) -> int:
    return {
        (False, False): 1,
        (True, False): 2,
        (False, True): 3,
        (True, True): 4,
    }[a, b]

print(a(True, True))
#

Why the error? Seems fine to me.

bleak imp
# muted iron Toy example: ```py def a(a: bool, b: bool) -> int: return { (False, ...

Here is a github issue where someone had the same problem, and it goes more in depth on why pyright does things that way, but the solution in there works for your code as well. ```py
def a(a: bool, b: bool) -> int:
responses: dict[tuple[bool, bool], int] = {
(False, False): 1,
(True, False): 2,
(False, True): 3,
(True, True): 4,
}
return responses[a, b]

print(a(True, True))```

GitHub

Describe the bug The type it chooses is too specific and it gets stuck not seeing that the dict is equivalent to Dict[Tuple[bool, bool], str] To Reproduce RESPONSES = { (True, True): "A",...

muted iron
#

Thanks, I understand it know and the solution worked.

heady flicker
solemn sapphire
#

Is there an existing discussion on not typing _ (usually to indicate discarded values during tuple destructuring)

data: list[tuple[str, float, int]]
for _, _, val in data:
    pass

This currently raises an error:

frigid jolt
#

iirc pyright ignores them, as expected

glossy canopy
#

hey there, i like unpacking values into a union as this is often nescessary as so:

Union[ *set( dataFormatForCommitType.values())]

this parses at the cpython interpreter, however the lsp gives a warning which prevents it from giving any other useful output:

#

are there any annotations i can use to tell the checker to ignore this?, the checker is pylsp with pyflakes

viscid spire
#

!e

x: list[print("This typehint doesn't make sense")] = ...
rough sluiceBOT
#

@viscid spire :white_check_mark: Your 3.12 eval job has completed with return code 0.

This typehint doesn't make sense
glossy canopy
#

nah still doesnt parse, probably causing an error to be raised in the checker

viscid spire
#

unpacking in typehints acts on other types

#

a set instance is not a type

#

and the result of dict.values is also not a type

glossy canopy
fathom river
#

I have a Game class that has some overall attributes which represents stats for the given game, and then I have a Gamemode class that has specific stats for a given gamemode which forms part of the game. In the Gamemode class, I have more specific stats for the different types of difficulties the gamemode can have, as well as overall stats. All of the stats of the gamemode class are also present in the overall Game stats. However, Game itself doesn't have specific stats depending on the difficulty.

class Gamemode:
    def __init__(
        self,
        room: Optional[Literal["one", "two", "three"]],
        difficulty: Optional[Literal["normal", "medium", "hard"]]
    ) -> None:
        self.best_time: int = ...
        self.completions: int = ...
        # etc.

        if room is not None and difficulty is None:
            self.normal = Gamemode(room=room, difficulty="normal")
            self.medium = Gamemode(room=room, difficulty="medium")
            self.hard = Gamemode(room=room, difficulty="hard")

class Game(Gamemode):
    def __init__(self) -> None:
        super().__init__(room=None, difficulty=None)
        self.total_games_played: int = ...
        # etc.

        self.room_one: Gamemode = Gamemode(room="one", difficulty=None)
        self.room_two: Gamemode = Gamemode(room="two", difficulty=None)
        self.room_three: Gamemode = Gamemode(room="three", difficulty=None)

Is there any way I can show that Game will never have the normal, medium, and hard attributes?

tranquil ledge
#

Have you considered reversing the class hierarchy? EDIT: On second glance, maybe not as easy as that.

paper salmon
# fathom river I have a `Game` class that has some overall attributes which represents stats fo...

generally for having different attributes based on state, i would look towards representing each of those states as classes, e.g. ```py
class Gamemode:
...

class NormalGamemode(Gamemode):
... # normal-specific stats

class MediumGamemode(Gamemode):
... # medium-specific stats

class HardGamemode(Gamemode):
... # hard-specific stats

class DifficultyGamemode(Gamemode):
def init(self, *args, **kwargs) -> None:
super().init(*args, **kwargs)
self.normal = NormalGamemode(*args, **kwargs)
self.medium = MediumGamemode(*args, **kwargs)
self.hard = HardGamemode(*args, **kwargs)

class Game(Gamemode):
...``` or by using composition, you might have GameStats, NormalStats containing GameStats, RoomStats containing GameStats+NormalStats+MediumStats+HardStats, etc.

fathom river
# paper salmon generally for having different attributes based on state, i would look towards r...

I'm not entirely sure what you mean by composition, but for the first part NormalGamemode, MediumGamemode, and HardGamemode would all be exactly the same classes, so I don't think that would be my best route to take. There is really no difference when it comes to the attributes, the values are the only ones changing.

You can use a car game for example:
Game would be the stats for all cars. So like, total hours driven, total amount of cars, total etc.
Gamemode would be the different stats for the different types of cars. So there would still be total hours driven and total amount of cars, they would just be for, say, Ferrari cars, or just for Nissan cars.
Then the difficulty could be for the model of the car. And so on so forth.
So Game, Gamemode and the difficulty are all exactly the same, with exactly the same attributes. They only thing that changes is how specific the stats are.

paper salmon
# fathom river I'm not entirely sure what you mean by composition, but for the first part `Norm...

by composition, i mean that instead of inheriting the stats directly, you store the stats objects in your classes, i.e. ```py
class GameStats:
def init(self) -> None:
self.best_time = 0
self.completions = 0
# etc.

class DifficultyStats:
def init(self) -> None:
self.total = GameStats()
self.normal = GameStats()
self.medium = GameStats()
self.hard = GameStats()

class Room:
def init(self, name: str) -> None:
self.name = name
self.stats = DifficultyStats()

class Game:
def init(self) -> None:
self.stats = GameStats()
self.room_one = Room("one")
self.room_two = Room("two")
self.room_three = Room("three")

game = Game()
game.stats.best_time
game.room_one.stats.total.best_time
game.stats.normal.best_time # Member "normal" is unknown```

fathom river
#

Hmm I guess that might work

#

Upon some further looking, I found typing.Never, so I thought I could maybe override the difficulties in Game with that?

trim tangle
#

because you might want to e.g. iterate over the results

weary wadi
#

I'm going to assume this would be the right channel to post this question in but it's not let me know and I'll remove it

my question is simple, i was just curious to knowing the difference between basic and strict when it comes to type checkers. e.g ruff etc.

tranquil turtle
weary wadi
#

their documentation.

oblique urchin
weary wadi
#

sorry for the assumption, was just curious.

never got in-depth understanding of them.

storm cloak
#

Does anyone know if there's a way to type narrow through a constant property, similar to something like this:

from typing import Literal

class A:
    is_a = True
class B:
    is_a = False

def foo() -> A|B:
    return A()
value = foo()
if value.is_a:
    assert type(value) is A

I've experimented with TypeGuards and Literals, but I can't get anything to work

pastel egret
viscid spire
pastel egret
#

Try using an explicit is True:?

viscid spire
#

that does it

pastel egret
#

Probably better to use something other than a bool, in case you need a third class at some point.

viscid spire
#

also == does it

#

so using truthiness won't narrow it with pyright, but using == or is will

pastel egret
viscid spire
#

x (where x is any expression that is statically verifiable to be truthy or falsey in all cases)

#

maybe "in all cases" is the kicker

pastel egret
#

Truthiness, but not obj.x.

viscid spire
#

obj.x is an expression

long pine
#

Hello, I've been trying to play around with pytype, I figured that pytype uses a typegraph representation of a program's AST. I was curious as to how one can parse the typegraph generated AST (like you would a normal python AST) and modify the types of every node of the AST. Is there a definitive approach one can take for this purpose?

bright tangle
#

What's the proper way of type-hinting specialisations of mixins? IIRC you can't re-generic a Self, so there's presumably a better way of doing it

brazen jolt
#

you'd need HKT (Higher Kinded Types) support for Self[T], currently there's no way you need to do MyClass[T]

bright tangle
#

Dang

brazen jolt
#

yeah, it's quite annoying

bright tangle
#

Do type checkers still correctly hint it if I do self: MyMixin[T] -> MyMixin[T] or does it erase the concrete type in favour of the mixin?

brazen jolt
#

this tells the type checker the returned type will be the mixin class, ereasing the concrete type

bright tangle
#

Dang

#

Wait would Self work on a specialisation method? I've never thought to try it

#

(also apparently you're supposed to define mixins in terms of Protocol, which I didn't know)

tranquil turtle
bright tangle
#

Lmao what

brazen jolt
bright tangle
#

Is that like Box<Box<T>> or something

brazen jolt
brazen jolt
bright tangle
#
class IteratorMixin(Generic[T]):
    def map_each(self: Self[Iterable[T]], fn: Callable[[T], U]) -> Self[Self[T]]:
        self.__class__(map(lambda itm: self.__class__(map(fn, itm)), self))
brazen jolt
#

you can't do self: Self[Iterable[T]] you can do self: IteratorMixin[Iterable[T]] though

#

similarly with the return hint

#

so, you can do specializations in this way, so that only IteratorMixin instances that have Iterable[T] as their generic attr can be used with this method (well, at least to the type-checker), but yeah, you will loose the information about the actual type self should be, and the returned type can then only be IteratorMixin[IteratorMixin[T]]

trim tangle
#

ooh, new pyright update

Added a new typeCheckingMode called "standard". It's a subset of "strict" but a superset of "basic". It is the new default mode, and it should cover all of the required checks for conformance with the Python typing standard.

mossy ocean
#

Pyright is being annoying - what's wrong with this? wtf does _ | Unknown mean??? https://paste.pythondiscord.com/7MRA Error:

Return type of generator function must be compatible with "Generator[_ | Unknown, Any, Any]"
  Β Β Type "_ | Unknown" cannot be assigned to type "BaseClient"
  Β Β Β Β "_" is incompatible with "BaseClient" (reportGeneralTypeIssues)
bleak imp
tranquil ledge
mossy ocean
rustic gull
#

Return type of generator function must be compatible with "Generator[_ | Unknown, Any, Any]"
Β Β Type "_ | Unknown" cannot be assigned to type "BaseClient"
Β Β Β Β "_" is incompatible with "BaseClient" (reportGeneralTypeIssues)

weary wadi
tranquil ledge
fossil nest
#

ive seen the bottom one done before however is the top one reccomended?

    queue = queue.PriorityQueue[tuple[float, str]]()
    queue: queue.PriorityQueue[tuple[float, str]] = queue.PriorityQueue()

it seems like a simpler approach

#

pyright seems to accept both under its "strict" mode

void panther
#

I try to use the top one but barely see it in libraries I look into

undone saffron
#

using the top one incurs extra runtime costs, and should generally be avoided.

chrome hinge
#

ah, that's a good point.

fossil nest
undone saffron
#
queue = queue.PriorityQueue[tuple[float, str]]()

Looks up the name queue, accesses an attribute of it PriorityQueue, attempts to get an item from that class using a key that also involves a runtime getitem on a type... (And gets back the class itself if used as intended), then initializes it. The extra steps in bold happen at every execution.

queue: queue.PriorityQueue[tuple[float, str]] = queue.PriorityQueue()

The steps from the previous version in bold only happen at annotation time (Specifically, the point in time when that annotation is evaluated), which can be never in some cases (from __future__ import annotations)

fossil nest
#

nice explanation, thank you!

chrome hinge
#

!e To be more specific, here's what it compiles to:

import dis
@dis.dis
def f():
    x=queue.PriorityQueue[tuple[float, str]]()
rough sluiceBOT
#

@chrome hinge :white_check_mark: Your 3.12 eval job has completed with return code 0.

001 |   2           0 RESUME                   0
002 | 
003 |   4           2 LOAD_GLOBAL              1 (NULL + queue)
004 |              12 LOAD_ATTR                2 (PriorityQueue)
005 |              32 LOAD_GLOBAL              4 (tuple)
006 |              42 LOAD_GLOBAL              6 (float)
007 |              52 LOAD_GLOBAL              8 (str)
008 |              62 BUILD_TUPLE              2
009 |              64 BINARY_SUBSCR
010 |              68 BINARY_SUBSCR
011 |              72 CALL                     0
... (truncated - too many lines)

Full output: https://paste.pythondiscord.com/XVJR6EPUWIQETGHVM53J7DN4ZA

chrome hinge
#

that's 6 extra instructions (lines 005 to 010) - name lookups, class __getitem__... It doesn't matter in most code, but I wouldn't want to do this in some hot loop.

tranquil turtle
#

TIL pyright can do this: ```py
class X[T]:
a: T

class Y:
b: X[t.Self]

class Z(Y):
...

z = Z()
reveal_type(z) # Z
reveal_type(z.b) # X[Z]
reveal_type(z.b.a) # Z
reveal_type(z.b.a.b) # X[Z]
reveal_type(z.b.a.b.a) # Z

viscid spire
#
class X[T]:
    n: T

class Y:
    a: X[t.Self]

class Z(Y):
    ...

b = Z()
reveal_type(b.a.n.a.n.a) # 🍌
rough sluiceBOT
#

:incoming_envelope: :ok_hand: applied timeout to @peak hollow until <t:1702054131:f> (10 minutes) (reason: duplicates spam - sent 4 duplicate messages).

The <@&831776746206265384> have been alerted for review.

muted iron
#

I have this class:

class NumberPattern(itertools.cycle): pass
pat = NumberPattern([0,1])
assert next(pat) == 0
assert next(pat) == 1
assert next(pat) == 0
val = next(pat)

Can I annotate this, so the IDE knows NumberPattern can only take Iterable[int] and val is int?
I tried:

class NumberPattern(itertools.cycle[int]): pass

and it works in the IDE, but then running the code gives me:

TypeError: type 'itertools.cycle' is not subscriptable

I think I'm missing something obvious here and am lost.

tranquil turtle
#
if typing.TYPE_CHECKING:
    base = itertools.cycle[int]
else:
    base = itertools.cycle

class NumberPattern(base): pass
#

tbh, this looks like a bug
i think itertools.cycle should have __class_getitem__

muted iron
#

I figured something like that'd work, even though it looks like a hack.
Thank you :)

tranquil turtle
solemn sapphire
#

Would be reasonable to raise an issue to make list act as a callable in this context rather than a type? Like how int works

def to_ints(nums: list[str]) -> list[int]:
    return list(map(int, nums)) # Ok

def to_chars(strings: list[str]) -> list[list[str]]:
    # Err: Argument 1 to "map" has incompatible type "type[list[Any]]"; expected "Callable[[str], list[_T]]
    return list(map(list, strings))
#

(This is mypy)

grave fjord
solemn sapphire
#

my natural question is how do you know? Are comprehensions always faster?

grave fjord
#

They made them much faster in 3.12 saves two function calls

solemn sapphire
#

interesting, thanks!

grave fjord
#

But it's weird mypy doesn't pass here

solemn sapphire
rough sluiceBOT
#

mypy/typeshed/stdlib/builtins.pyi lines 224 to 228

class int:
    @overload
    def __new__(cls, __x: ConvertibleToInt = ...) -> Self: ...
    @overload
    def __new__(cls, __x: str | bytes | bytearray, base: SupportsIndex) -> Self: ...```
`mypy/typeshed/stdlib/builtins.pyi` lines 920 to 924
```pyi
class list(MutableSequence[_T]):
    @overload
    def __init__(self) -> None: ...
    @overload
    def __init__(self, __iterable: Iterable[_T]) -> None: ...```
trim tangle
#

That sounds a bug anyway... list should match Callable[[str], list[str]]

grave fjord
#

What does

x: Callable[[str], list[str]] = list do?

solemn sapphire
#

that passes

solemn sapphire
#

I also stumped by this error:

from typing import Iterable, TYPE_CHECKING

if TYPE_CHECKING:
    from _typeshed import SupportsRichComparisonT, SupportsRichComparison


def fake_sorted(
    iterable: Iterable[SupportsRichComparisonT],
) -> list[SupportsRichComparisonT]:
    return list(iterable)


def fake_sorted_strict(
    iterable: Iterable[SupportsRichComparison],
) -> list[SupportsRichComparison]:
    return list(iterable)


def get_data() -> Iterable[tuple[int, int]]:
    return [(0, 0)]


# Err: mypy-bug.py:23: error: Argument 1 to "map" has incompatible type "Callable[[Iterable[SupportsRichComparisonT]], list[SupportsRichComparisonT]]"; expected "Callable[[Iterable[tuple[int, int]]], list[SupportsRichComparisonT]]"  [arg-type]
map(fake_sorted, [get_data(), get_data()]) 

# Ok
map(fake_sorted_strict, [get_data(), get_data()])
#

Is this a bug in mypy or is it in typeshed?

tranquil ledge
#

Seems fine, and pyright interprets it fine, imo. Might be an mypy bug where it’s not correctly correlating the TypeVar?

#

… markdown, why.

brazen jolt
tranquil ledge
brazen jolt
#

you also shouldn't have the arrow brackets there

tranquil ledge
#

There we go. Cheers. Wanted to prevent an embed in case one would be autogenerated, but I guess it wasn’t necessary.

muted iron
#
def g(): yield
print(g().__qualname__)

Cannot access member "__qualname__" for type "Generator[None, Any, None]"
IDE claims __name__ and __qualname__ don't exist on generator or async generators, but they do.
Works fine for functions and async functions.

viscid spire
#

!e

def g(): yield
print(g.__qualname__)
rough sluiceBOT
#

@viscid spire :white_check_mark: Your 3.12 eval job has completed with return code 0.

g
trim tangle
#

!e

def g(): yield
print(g().__qualname__)
print(g.__qualname__)
rough sluiceBOT
#

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

001 | g
002 | g
trim tangle
#

le huh

solemn sapphire
#

Is there a way to make pip scream at me for having outdated packages?

#

or should I use my system package manager to install mypy?

tranquil ledge
muted iron
#
async def f(): pass
print(f.__qualname__)
print(f().__qualname__)

What's surprising to me is, that the IDE correctly identifies both of these as having __(qual)name__.

tranquil turtle
rough sluiceBOT
#

stdlib/types.pyi line 366

__name__: str```
oblique urchin
#

@soft matrix opened some discussions about this recently

rare scarab
#

if you use poetry, github's dependabot will open pull requests for most package updates

tranquil ledge
#

I probably should've made that clear.

rare scarab
#

for non-global packages, use poetry, hatch, or pdm. Even pip-tools will work

soft matrix
#

though honestly might be something for the typing council to decide on

wheat lynx
#

hi guys, tough question for me tho, which option is more incentivized?
Annotated[str, 'test'] | None or Annotated[str | None, 'test'] ?
checking equality resulted in False ofc, but following the documentation it all resolves to str | None in type checking

oblique urchin
#

because None can't be greater or smaller than an int

wheat lynx
#

so I use pydantic and a common case I use Annotated is an optional field which needs validation, serialization or simply description
I will take an example like this

from pydantic import BaseModel, Field

class Foo(BaseModel):
    spam: Annotated[str | None, Field(description="just a spam")]

could you go deeper in this example and help me figure out which way using Annotated fits more?

oblique urchin
#

in this case Field applies to the whole field, so it should go on the outermost level

wheat lynx
#

so mine sounds correct right? the same might goes for a validation/serialization in Annotated?

oblique urchin
#

yes, what you wrote looks right to me

#

Validation could reasonably go at a deeper level. You'd basically say "this is either a None, or a str validated with this validator"

#

But haven't checked what pydantic supports there

wheat lynx
#

I see, so it boils down to the final intention right?
but am I correct stating that both options are resolved as str | None in type checking?

oblique urchin
wheat lynx
tranquil turtle
trim tangle
#

least insane type hint

grave fjord
#

I've never not seen a type checker spout this sort of nonsense

trim tangle
#

fix error's law: in a flexible type system, as the function is getting more dynamic its type signature starts to mirror its implementation

undone saffron
trim tangle
#

I think min is missing the same thing asyncio.gather, zip etc. are missing, wrapping every element of a TypeVarTuple into a generic

undone saffron
#

zip and map can't be fixed by just applying a generic with TypeVarTuples

#

gather, min, max, etc all could

tranquil turtle
#

-level* πŸ˜„

undone saffron
#

yeah

#

which would be neat in a few real world cases beyond some builtins too

#

Technically, zip operating on a homogenous set of homegnous iterators hsould "just work", but that's the simple case.

grave fjord
#

the result is an aggregate list of returned values.

#

A list though not a tuple I'm pretty sure

muted iron
#

that error
Christ allmighty

trim tangle
grave fjord
#

Ok now I'm not sure what it returns...

trim tangle
#

a luple

#

a tuste

undone saffron
undone saffron
# grave fjord Such as?
  1. (not niche) older python versions
  2. working with multiple event loops and transfering tasks to background loops/threads
grave fjord
#

Why would you need gather for 2?

hollow bane
#

Hi, I'm having an issue with pydantic model.

This is my JsonFeedOptions class.

class JsonFeedOptions(BaseModel):
    address: Optional[AddressType] = None
    signer: Optional[Union[AccountAPI, str]] = None
    Type: Optional[FeedType] = None

And I'm trying to validate the data like this.

opts = {"signer": "abcd"}

options = JsonFeedOptions.model_validate(opts)

But got this error

>           options = JsonFeedOptions.model_validate(opts)
E           TypeError: validate() takes 2 positional arguments but 3 were given
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 Paste! button in the bottom left, or by pressing CTRL + S. After doing that, you will be navigated to the new paste's page. Copy the URL and post it here so others can see it.

trim tangle
#

Is that the whole traceback?

#

Why does it say validate() takes 2 positional arguments but 3 were given? you're calling model_validate

hollow bane
# trim tangle Why does it say `validate()` takes 2 positional arguments but 3 were given? you'...
In [1]: opts = {"signer": "abcd"}

In [2]: from bee_py.types.type import JsonFeedOptions

In [3]: JsonFeedOptions.model_validate(opts)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[3], line 1
----> 1 JsonFeedOptions.model_validate(opts)

File ~/.local/pipx/venvs/pdm/lib/python3.9/site-packages/pydantic/main.py:503, in BaseModel.model_validate(cls, obj, strict, from_attributes, context)
    501 # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
    502 __tracebackhide__ = True
--> 503 return cls.__pydantic_validator__.validate_python(
    504     obj, strict=strict, from_attributes=from_attributes, context=context
    505 )

TypeError: validate() takes 2 positional arguments but 3 were given
hollow bane
#

other models are working just fine

#

the probelm shows in validate() not model_validate

undone saffron
# grave fjord Why would you need gather for 2?

you don't need it, but you can keep certain things somewhat simpler using it in a way that doesn't work as well with task groups. without it, you could implement the minimum needed from it without asyncio.wait, but I'd need to review some design docs and wouldnt be able to sahre the specific code where I ran into it being a cleaner option.

trim tangle
#

Also can you show AddressType, AccountAPI, FeedType?

hollow bane
trim tangle
#

Can you run it in a plane Python file?

#

also does it fail with this?

class JsonFeedOptions(BaseModel):
    address: Optional[int] = None
    signer: Optional[Union[int, str]] = None
    Type: Optional[int] = None
hollow bane
#

Hmm looks like it's taking ape's basemodel instead of pydantic's. when AccountAPI type is passed

trim tangle
#

can you show the whole module where the model is defined?

hollow bane
trim tangle
#

yes

rough sluiceBOT
#

src/bee_py/types/type.py line 811

class JsonFeedOptions(BaseModel):```
trim tangle
#

did you mean ```
signer: Optional[Union[Signer, str]] = None

hollow bane
#

this is the absctract class for accounts

trim tangle
#

Oh wait I completely misread

#

ignore me

hollow bane
#

just installed the latest version

trim tangle
trim tangle
#

I mean like python repro.py

hollow bane
# trim tangle I mean like `python repro.py`
➜  python3 funky_experiments/pydantic_test.py
Traceback (most recent call last):
  File "/home/avik/Desktop/Work/AlienRobotNinja/bee/bee-py/funky_experiments/pydantic_test.py", line 30, in <module>
    options = JsonFeedOptions.model_validate(opts)
  File "/home/avik/.local/pipx/venvs/pdm/lib/python3.9/site-packages/pydantic/main.py", line 503, in model_validate
    return cls.__pydantic_validator__.validate_python(
TypeError: validate() takes 2 positional arguments but 3 were given

from enum import Enum
from typing import Optional, Union

from ape.managers.accounts import AccountAPI
from ape.types import AddressType
from pydantic import BaseModel


class FeedType(Enum):
    """
    Enum class for feed types.

    Attributes:
        SEQUENCE: Sequential feed type.
        EPOCH: Epoch feed type.
    """

    SEQUENCE = "sequence"
    EPOCH = "epoch"


class JsonFeedOptions(BaseModel):
    address: Optional[AddressType] = None
    signer: Optional[Union[AccountAPI, str]] = None
    Type: Optional[FeedType] = None


opts = {"signer": "abcd"}

options = JsonFeedOptions.model_validate(opts)

print(options)
trim tangle
hollow bane
#

how can I add validation here so that when int is passed to encrypt is throws validation error ?

soft matrix
#

you need to change the model definition to pass a config for strict

#

honestly i think it was a mistake this wasnt on by default

fierce ridge
#

you can also make individual fields strict

fierce ridge
#

it's un-pythonic imo (normally python is relatively strongly typed) but it does make it easy to accept or process data from sloppy sources, or stringly-typed sources

hollow bane
soft matrix
#

that seems exceptionally unnecessary in this case

fierce ridge
hollow bane
#

Thanks

mellow talon
#

Hello guys,
I've a problem with the functools.wraps. Basically, upon implementation, the __name__ property returns what is needed, but the __annotations__ is not.
The documentation would suggest this to be captured without no additional efforts.

That said, I tried to be more specific with something like

@wraps(func, assigned="__annotations__")
def something(*args, **kwargs):
  return myfunc(*args, **kwargs)

But when I do so the __name__ is lost too.

Not sure what I'm getting wrong. So I though, I will implement directly the functools.update_wrapper but in this case I'm not sure what the first argument of the method should be.

Any ideas on what's wrong (on both cases)?

soft matrix
#

i want to say your python version is out of date

mellow talon
#

@soft matrix you are saying to me?

soft matrix
#

yes

#

because thesource code looks like it should get carried across

#

anyway assigned should be a tuple/list of str

mellow talon
#

@soft matrix I tried even with a tuple ... but in fact .. now that I'm realising it, the issue I have is with an internal method of my wrapper.

The source code of what I've been implementing is here:
https://github.com/andreamoro/Dispatcher

What I'm after is to get the annotations of the method where the register method is used (https://github.com/andreamoro/Dispatcher/blob/4f7a34b5addb48b0b5d845b4b2229e07c571b016/tests/test.py#L12) whereas today I'm getting the annotation of the method where is first added (https://github.com/andreamoro/Dispatcher/blob/4f7a34b5addb48b0b5d845b4b2229e07c571b016/tests/test.py#L9)

I've tried also to used a second wrap around the register method without success.

rough sluiceBOT
#

tests/test.py line 12

@foo.register```
`tests/test.py` line 9
```py
def foo():```
summer lark
#

I have a bit of a puzzle. I've been given a dataclass.Field with the promise that it's type is either a type like int or str, or else a union type declaration one item is a type like int or str and the other is None. However, this can be written at least 5 ways (Optional[int], Union[int, None], and int|None, and the order-swapped versions of the last two).

Furthermore, my project has to be portable back to python 3.9, where int|None isn't permitted. In this case, obviously the annotation won't be int|None since the syntax didn't exist then.

Programmatically, how do I write def get_field_type(field: dataclasses.Field[Any]) -> Any: ... so that it works in all these cases?

I have this, it seems to work, but it feels like I must be missing something that would make it easier.

UnionType: type
if sys.version_info >= (3, 10):
    from types import UnionType
else:
    UnionType = type(Union[int, float])

def get_field_type(field: Field[Any]) -> Any:
    field_type = field.type
    if isinstance(field_type, str):
        raise RuntimeError(
            "parameters dataclass may not use 'from __future__ import annotations"
        )
    origin = get_origin(field_type)
    if origin in (Union, UnionType):
        for arg in get_args(field_type):
            if arg is not None:
                return arg
    return field_type
#

(new here; is this better placed in the python help system? if so I can delete and re-post there)

fierce ridge
#

i thought | on types returned a UnionType, is that not the case?

#

oh, i see. there's a way to get the parsed annotation whether or not the future import was used

summer lark
#

That may be a 3.9 vs current difference, I thought I was seeing a different type depending on the notation

summer lark
#
import sys
from typing import get_origin, Union, Optional

print(sys.version_info)

try:
    print(get_origin(int | None))
except TypeError as e:
    print(e)

print(get_origin(Union[int, None]))
print(get_origin(Optional[int]))

3.9:

sys.version_info(major=3, minor=9, micro=2, releaselevel='final', serial=0)
unsupported operand type(s) for |: 'type' and 'NoneType'
typing.Union
typing.Union

3.11:

sys.version_info(major=3, minor=11, micro=2, releaselevel='final', serial=0)
<class 'types.UnionType'>
typing.Union
typing.Union
```so it's the 'origin' of `int | None` type annotation that's different than the 'origin' of `Union[int, None]` or `Optional[int]`. I don't know if this is intentional, but that's how it is in 3.11 (through the latest 3.13 alpha docker image) behaves.
rare scarab
#

The way to test if something is a union is to do get_origin(typ) in {typing.Union, types.UnionType}

#

Though I've heard talks to make typing.Union an alias for types.UnionType

summer lark
#

Thanks, I appreciate the confirmation that I didn't overlook a simpler alternative.

fierce ridge
#

from a purely theoretical perspective, would it be possible to declare a type like "if any input is a pd.Series, then the result is a pd.Series, otherwise it's a NDArray"?

#
def f(
    x: float | ArrayLike | NDArray[np.floating[Any]] | pd.Series[Any],
    y: float | ArrayLike | NDArray[np.floating[Any]] | pd.Series[Any],
    z: float | ArrayLike | NDArray[np.floating[Any]] | pd.Series[Any],
) -> NDArray[np.floating[Any]] | pd.Series[Any]:
    ...

this is what i have currently

#

i know there's really nothing i can do in the current system to improve this. but it would be really nice if mypy could narrow the return type here

trim tangle
fierce ridge
#

cool, i didn't know typescript had that

#

i know i can do it in e.g. idris, but it doesn't strike me as something that actually needs dependent types

#

i wonder if there would ever be something like that in python

soft matrix
#

i think theres a chance that might happen

#

i remember seeing a pep floating around for it

fierce ridge
#

which, type-level functions or mapped types?

soft matrix
#

mapped types

fierce ridge
#

please and thank you

#

i've been wanting that for years

#

it's a real pain if you don't have a protocol handy, or if you want to construct a protocol from a concrete type

tranquil ledge
fierce ridge
#

"combinatorial overload" in more ways than one

floral heron
#
from typing import *
from pydantic import BaseModel

class A(BaseModel):
    pass

class AA(A):
    pass

T = TypeVar('T', bound=A)

class Component:
    pass

class B(Component, Generic[T]):
    pass

class C(B[AA]):
    pass

c = C()
print(c.__orig_bases__) # (__main__.B[__main__.AA],)

class PydanticComponent(BaseModel):
    pass

class B(PydanticComponent, Generic[T]):
    pass

class C(B[AA]):
    pass

c = C()
print(c.__orig_bases__)
# pydantic 2: (<class '__main__.PydanticComponent'>, typing.Generic[~T])
# pydantic 1: (__main__.B[__main__.AA],)
#

Trying to figure out what kind of magic pydantic v2 has invoked to alter __orig_bases__ on class C. My goal here is to be able to recover information about AA from C when it ultimately inherits from a pydantic BaseModel.

#

Please feel free to ping on replies.

hardy aurora
#

using django, wondering how to fix this error?

soft matrix
#

!d types.get_original_bases

rough sluiceBOT
#

types.get_original_bases(cls, /)```
Return the tuple of objects originally given as the bases of *cls* before the [`__mro_entries__()`](https://docs.python.org/3/reference/datamodel.html#object.__mro_entries__) method has been called on any bases (following the mechanisms laid out in [**PEP 560**](https://peps.python.org/pep-0560/)). This is useful for introspecting [Generics](https://docs.python.org/3/library/typing.html#user-defined-generics).

For classes that have an `__orig_bases__` attribute, this function returns the value of `cls.__orig_bases__`. For classes without the `__orig_bases__` attribute, `cls.__bases__` is returned.

Examples:
floral heron
# soft matrix Orig bases is from mro entries

I understand that __mro_entries__ can change the bases, but from the text, I expect __orig_bases__ to be the bases before modification, but what I'm seeing is that the bases have been modified in some way when subclassing Pydantic's BaseModel. That pydantic would use some magic to change things makes sense, but it's not clear why __orig_bases__ would end up being any different between the version that subclasses pydantic vs doesn't.

soft matrix
#

im slightly confused as to what pydantic 2 is referring to here

#

B.orig_bases?

#

this all looks correct to me

#

this isnt pydantic changing them its surely just GenericAlias doing it

floral heron
#

There's two Cs in that example, one that inherits from Pydantic's BaseModel (through Component), and one that doesn't.

#

They give different answers for __orig_bases__ despite having effectively the same structure

#

With the only difference being the BaseModel class

soft matrix
#

oh ic now

#

so its not having orig bases be set on C for C (its not in its dict)

#

looks like a bug i remember someone complaining about something similiar recently

floral heron
#

orig bases is set, but what it's set to has been transformed somehow

#

In particular, it has typing.Generic[~T] instead of __main__.B[__main__.AA]

soft matrix
#
In [2]: C.__dict__["__orig_bases__"]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Input In [2], in <cell line: 1>()
----> 1 C.__dict__["__orig_bases__"]

KeyError: '__orig_bases__'```
#

its not

floral heron
#

oh right

#

sure

soft matrix
#

whereas with the normal case py In [4]: C.__dict__["__orig_bases__"] Out[4]: (__main__.B[__main__.AA],)

floral heron
rough sluiceBOT
#

pydantic/main.py line 61

class BaseModel(metaclass=_model_construction.ModelMetaclass):```
`pydantic/_internal/_model_construction.py` line 59
```py
class ModelMetaclass(ABCMeta):```
floral heron
#

Another interesting thing I found, in trying to work around this, I thought I could maybe put a __init_subclass__ on B

#

without the pydantic basemodel, it gets called. With the pydantic basemodel, __init_subclass__ on B is not called

#

Okay, it could be that pydantic is doing odd shenanigans with __dict__

rough sluiceBOT
#

pydantic/main.py line 862

_object_setattr(self, '__dict__', state['__dict__'])```
floral heron
rough sluiceBOT
#

pydantic/main.py line 234

_object_setattr(m, '__dict__', fields_values)```
floral heron
#

Found a workaround for myself, which is to use inspect.getmro() and then pull out what I'm looking for there.

heady flicker
soft matrix
#
INTERNALERROR>   File "/home/runner/work/steam.py/steam.py/steam/abc.py", line 89, in <module>
INTERNALERROR>     class Commentable(Protocol):
INTERNALERROR>   File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/typing.py", line 1825, in __init__
INTERNALERROR>     cls.__callable_proto_members_only__ = all(
INTERNALERROR>                                           ^^^^
INTERNALERROR>   File "/opt/hostedtoolcache/Python/3.12.1/x64/lib/python3.12/typing.py", line 1826, in <genexpr>
INTERNALERROR>     callable(getattr(cls, attr, None)) for attr in cls.__protocol_attrs__
INTERNALERROR>              ^^^^^^^^^^^^^^^^^^^^^^^^
INTERNALERROR>   File "/home/runner/work/steam.py/steam.py/steam/enums.py", line 80, in __get__
INTERNALERROR>     return self.__func__(type)
INTERNALERROR>            ^^^^^^^^^^^^^^^^^^^
INTERNALERROR>   File "/home/runner/work/steam.py/steam.py/steam/abc.py", line 102, in _COMMENTABLE_TYPE
INTERNALERROR>     return _CommentThreadType[cls.__name__]
INTERNALERROR>            ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
INTERNALERROR>   File "/home/runner/work/steam.py/steam.py/steam/enums.py", line 152, in __getitem__
INTERNALERROR>     return cls._member_map_[key]
INTERNALERROR>            ~~~~~~~~~~~~~~~~^^^^^
INTERNALERROR> KeyError: 'Commentable'```hmm @lunar dune is there a case for using getattr_static here or was that removed?
#

i can get around this but its a bit annoying

#

Commentable here is meant to only be used as an abc and hence doesnt support itself as a thread type

lunar dune
#

Can you give me an MRE easily?

#

I have Questions about why a getattr call is raising KeyError, though, that seems bad

#

But whether or not what you're doing is sensible is a separate question to whether typing.py should be more cautious here

#

So a MRE would still be interesting

soft matrix
#

!e ```py
from enum import IntEnum
from typing import Protocol
import abc

class classproperty:
def init(self, func):
self.func = func

def __get__(self, instance, type):
    return self.__func__(type)

class _CommentThreadType(IntEnum):
WorkshopAccountDeveloper = 2

class Commentable(Protocol):
"""A mixin that implements commenting functionality."""

__slots__ = ()
_state: int

@property
@abc.abstractmethod
def _commentable_kwargs(self):
    raise NotImplementedError

@classproperty
def _COMMENTABLE_TYPE(cls):
    return _CommentThreadType[cls.__name__]
rough sluiceBOT
#

@soft matrix :x: Your 3.12 eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "/home/main.py", line 15, in <module>
003 |     class Commentable(Protocol):
004 |   File "/lang/python/default/lib/python3.12/typing.py", line 1818, in __init__
005 |     cls.__callable_proto_members_only__ = all(
006 |                                           ^^^^
007 |   File "/lang/python/default/lib/python3.12/typing.py", line 1819, in <genexpr>
008 |     callable(getattr(cls, attr, None)) for attr in cls.__protocol_attrs__
009 |              ^^^^^^^^^^^^^^^^^^^^^^^^
010 |   File "/home/main.py", line 10, in __get__
011 |     return self.__func__(type)
... (truncated - too many lines)

Full output: https://paste.pythondiscord.com/J5RAI4FC42NI7YWMTY2F4VISRI

soft matrix
#

thats the gist of it

lunar dune
#

Thanks!

#

Yes, so, imo you should probably try to take steps to ensure that properties never raise non-AttributeError exceptions. Whether or not typing.py switches to using getattr_static here, there will be other code in various third-party packages that will make the same (fairly reasonable) assumption (that a getattr call could only ever raise AttributeError). So, if I were you, I'd change your _COMMENTABLE_TYPE property to something like this:

    @classproperty
    def _COMMENTABLE_TYPE(cls):
        try:
            return _CommentThreadType[cls.__name__]
        except KeyError:
            raise AttributeError("...") from None
soft matrix
#

ah didnt realise getattr caught AttributeErrors if you provided a default

lunar dune
#

As for whether we should change typing.py to use getattr_static:

  • the __callable_proto_members_only__ attribute is only computed at class creation time these days, so we could probably change that to use getattr_static fairly easily without too much of a performance cost (I'd want to measure the import time of typing.py before and afterwards to verify -- it's a reasonable benchmark as it's a module that defines a lot of protocol classes). But we also do a getattr() call here -- would we want to change that as well? https://github.com/python/cpython/blob/a545a86ec64fbab325db101bdd8964f524a89790/Lib/typing.py#L1895-L1896 That would slow down isinstance() checks. But maybe we could move more logic into _ProtocolMeta.__init__() to avoid the performance regression
  • There's also a risk that using getattr_static here could have other behaviour changes, though; need to think about that
rough sluiceBOT
#

Lib/typing.py lines 1895 to 1896

if val is None and callable(getattr(cls, attr, None)):
    break```
lunar dune
#

Can you create an issue for this? I think it's reasonable to ask typing.py to be more cautious about getattr() calls on class objects, given that we've already taken the decision to be more cautious about getattr() calls on instances.

#

!e ```py
import sys
print(sys.version_info)

rough sluiceBOT
#

@lunar dune :white_check_mark: Your 3.12 eval job has completed with return code 0.

sys.version_info(major=3, minor=12, micro=0, releaselevel='final', serial=0)
lunar dune
soft matrix
lunar dune
#

Hahah, okay, it's because sets don't have a deterministic iteration order. Sometimes, it iterates over the __protocol_attrs__ set in exactly the right order so the all() expression short-circuits before we get to the problematic getattr() call

heady flicker
soft matrix
#

mapped types definitely have been it was at the same time as an AttributeOf pep

lunar dune
# lunar dune Hahah, okay, it's because sets don't have a deterministic iteration order. Somet...

Here's a more reliable MRE that doesn't depend on the iteration order of sets πŸ™‚

from enum import IntEnum
from typing import Protocol

class classproperty:
    def __init__(self, func):
        self.__func__ = func

    def __get__(self, instance, type):
        return self.__func__(type)

class _CommentThreadType(IntEnum):
    WorkshopAccountDeveloper = 2

class Commentable(Protocol):
    @classproperty
    def _COMMENTABLE_TYPE(cls):
        return _CommentThreadType[cls.__name__]
lunar dune
heady flicker
#

I remember that a guy has fully implemented them in Mypy actually. He named it Basedmypy if you've heard of it

lunar dune
#

I'm also afraid I don't have the energy right now to properly engage with those discussions and meaningfully help them along

#

I have heard of basedmypy

heady flicker
soft matrix
#

fully implemented is interesting considering we dont have an actual spec for them

heady flicker
#

What i am saying is that what he has makes sense and works :)

#

Obviously it can take time to make it fully correct and consistent with other implementations but the biggest work has already been done in terms of raw implementation, imho.

lunar dune
#

That's great, I'm sure that work will be useful when it comes to implementing them in mypy proper! That's a pretty different thing from the maintainers of the type checkers agreeing that a hypothetical spec could be plausibly implemented, though. Part of the reason why basedmypy has so much freedom in what it does is that it isn't bound to conform to any particular spec; it's deliberately backwards-incompatible with parts of PEP-484 etc.

soft matrix
#

i dont think the implementation is the hard part here considering how long the discussion has been going on :p

heady flicker
#

Implementation is always an iffy part because a proof of concept usually helps to significantly speed up the discussion

lunar dune
# heady flicker <@362626733188448266> What do you think about mapped types (https://www.typescri...

Mapped types look interesting, but I don't think we should be prioritising them as a feature right now; I think there are more important things for us to be working on adding to the type system. (Also, I think the current mood is that we should maybe slow down feature development and work on thinking a bit more about how the different features all fit together for a bit. I think that's part of the purpose of the spec that the Typing Council is working on putting together.)

heady flicker
#

Agreed completely. Though for me the main blocker is always intersection types :)

soft matrix
heady flicker
#

Yup, I know. I follow it

undone saffron
#

personally, im more focused on just making sure the spec is fine, implementation can happen after we have a spec.

lunar dune
trim tangle
#

finally

#

production fails in rgb

lunar dune
#

Multiline source code snippets displayed, as well!

#

You get the whole source code of that multiline generator expression printed in your py313 traceback

trim tangle
#

oh that is actually good

tranquil turtle
fierce ridge
#
class GeoRow(NamedTuple):
    Index: int
    longitude: float
    latitude: float

x: tuple[Any, ...]
y: GeoRow

y = x

does anyone know why mypy might complain about this? i can't reproduce it in an isolated snippet like this, but it's complaining in my actual code:

error: Incompatible types in assignment (expression has type "tuple[Any, ...]", variable has type "GeoRow")  [assignment]
#

what's weird is that i have the same assignment in a later section of code, and mypy is perfectly fine with it there

#

ah, the later section is because mypy thinks something else is Any... separate problem

lunar dune
# tranquil turtle what are the differences? i can see this: - rgb (but it is doable without interp...

If you look at item (2) in the traceback in #type-hinting message, the py312 traceback only has return all( given for the source code of that frame. The py313 traceback prints out the full source code (a 3-line generator expression) for that frame

Discord

Discord is the easiest way to communicate over voice, video, and text. Chat, hang out, and stay close with your friends and communities.

#

I believe Pablo et al. also managed to achieve all this while reducing the amount of C code involved in printing tracebacks now, as well β€” it all just calls Python code in traceback.py now

fierce ridge
#

also: there's still currently no way to append an argument to a method without copying the whole signature from a parent class, right?

#
class B(A):

let's say A init has 5 overloads and 20 parameters. i just want to add a single foo: bool = False kwarg at the end. do i need to go find the .pyi file where A is defined and copy its entire init method signature, or do we have a better way to write that now?

lunar dune
fierce ridge
#

is there some open thread about this on the mailing list or discourse forum?

#

it's a really really painful case

lunar dune
#

PEP-692 might help you, but it's hard to know without seeing the specific code you're working with (it probably won't help if you do actually have overloads)

#

!pep 692

rough sluiceBOT
fierce ridge
#

i still can't even figure out where the type parameters for Series are defined...

#

and yes, the init method has > 10 parameters and overloads

lunar dune
#

If you didn't have overloads, with PEP-692 you could use one TypedDict for typing the kwargs in your base class A, then subclass the TypedDict (adding a single field) and use the TypedDict subclass for annotating the kwargs in your subclass B(A)

fierce ridge
#

that would still require me to copy the init method from a 3rd party's source code into a TypedDict, along with all the necessary imports and whatever cursed internal stuff they import in order to make their pyi file work

lunar dune
fierce ridge
#

i'll make a thread then.

ideally we'd be able to have something like this:

class B(A):
    def __init_(self, *args: InheritArgs, **kwargs: InheritKwargs, foo: bool = False) -> None:
#

i've wanted similar things in the past, like the ability to create a protocol by copying the signature of a class

FooLike = ProtocolFrom("FooLIke", Foo)

or at least by copying individual methods/attributes

soft matrix
#

its yeah under a proposed Super type (which'd be a paramspec) or something

fierce ridge
#

extending a union type would fall into the same category

fierce ridge
soft matrix
#

or an overload that doesnt get rid of the super's overloads has also been discussed

fierce ridge
#

yeah, ideally we'd have both behaviors here

#

maybe i'll write up my use case in the first thread, it seems like people aren't considering this as an important tool for managing complexity

soft matrix
trail kraken
#

Does it work if you swap them? ``None | "AbstractDefinition"`

tranquil turtle
#

no

#

do "X | None" instead of "X" | None

#

obviously your variant doesn't work

trail kraken
#

Ah, I see. TIL. Well... the fallback "X | None" should work? Doesn't feel nice though.

#

And huh. For some reason, I didn't think to ask about why the type is not used directly.

tranquil turtle
trail kraken
#

Ah. Didn't know about that. Doubt I will use it any time soon, but that's good to know!

tight prawn
#

How to type hint a custom list class just for a model?

class JobList(list):
    def __contains__(self, job: Job):
        return any(job.url == existing_job.url for existing_job in self)

In that example ^ existing_job doesn't highlight as a job. What can I do?

trail kraken
#

Are there examples of where you might use the JobList class?

soft matrix
tight prawn
#

the eq for Job checks if all fields are the same

#

for this I just want to check url only

soft matrix
#

thats interesting design

tight prawn
#

Yeah, could be over complicated haha

#

but I use it a lot

soft matrix
#

also you cant have the job param be Job because it should be able to take anything, you should first check if its an instance of Job

#

anyway your problem is that you arent subclassing list[Job]

tight prawn
# trail kraken Are there examples of where you might use the `JobList` class?
# save job
session = Session()  #sqlalchemy

def save_jobs():
    exiting_jobs = db.get_jobs()
    new_jobs = platform.get_jobs()
    for job in new_jobs:
        if job in existing_jobs:
            # I was thinking about these 2 lines
            existing_job = existing_jobs[existing_jobs.index(job)]
            if job != existing_job:
                existing_job.update(job)
                job = existing_job
        else:
            session.add(job)
    session.commit()
#

I'm working with basically an api client & database. The above is technically a method, I just wrote it as a function for example.

#

I'm trying to avoid a unique constraint error πŸ™„

#

job.url is the unique contstraint field

Basically I'm saying:

For each job, I want to see if the job exists, otherwise it's new and I can save it. If it does exist, if it has new data that the current job doesn't, update it's data. Otherwise, ignore it.

#

(I know it's outside the scope of typehinting but just answering her boni's question haha)

trail kraken
#

I think... I would just use filter.

#

As for the typing issue, my first thought was actually Protocol if .url was all you needed, but if you need to append, then list[Job] does make a lot more sense.

tight prawn
#

Thanks! That seems to be the overall consensus haha. It worked, thanks guys!

lyric palm
#
class ThingA:
  prop: bool
class ThingB:
  pass
for thing in lots_of_different_things:
  thing: ThingA|ThingB
  
  try:
    if thing.prop: # cannot access member prop for type ThingB
      break
  except AttributeError: # to account for ThingB, which doesnt have prop as an attribute
    pass

vscode typechecker doesnt like this :(
even with the try/catch, it gives "cannot access member prop for type ThingB"
is there a way to appease the type checker without using isinstance or something similar?

fierce ridge
#

ah, without isinstance? maybe you need a runtime-checkable protocol

#

i don't think mypy has any support for "discriminated union" like what you describe

lyric palm
#

that's a shame

#

i might just use isinstance then

trail kraken
#

May be in an assert.

bleak imp
#

Would casting the value to ThingB in the try work for a less costly option?

tranquil ledge
#

Depends a bit on if the check is running in a hot loop and if the code is running with optimization on (since that eliminates asserts and makes them a *type checking time–only tool for this case).

heady marlin
#

Hey there Python friends, would anyone have an idea why mypy is struggling with the following (see comments for details):

from enum import Flag, auto

class Problem(Flag):
    FIRST_ITEM = auto()
    SECOND_ITEM = auto()
    BOTH_ITEMS = FIRST_ITEM | SECOND_ITEM

    def friendly_name(self) -> str:
        # mypy error
        # Item "None" of "str | None" has no attribute "lower"
        #
        # self.name inferred as follows:
        # mypy: Union[builtins.str, None]
        # pyright: Literal['FIRST_ITEM', 'SECOND_ITEM', 'BOTH_ITEMS']
        return self.name.lower().replace("_", "-")
#

This doesn't seem to be a problem when using enum.Enum but specifically when using enum.Flag

heady marlin
glass lily
#

Lets say I wan't to provide a type-hint for any generic callable with takes a single argument of type Thing

#

With no return value

#

We'd be looking at Callable[[Thing], None]

#

Does this work for objects with custom __call__ methods which implicitly accept a self argument?

glass lily
#

Sick

#

Thanks

glass lily
#

I need a sanity check

#
T_Atomic = None | bool | int | float | str
T_Set    =   set['T_Atomic' | 'T_Tuple']
T_List   =  list['T_Data']
T_Dict   =  dict[str, 'T_Data']
T_Hash   = tuple[T_Atomic, ...]
T_Tuple  = tuple['T_Data', ...] | T_Hash
#

As a recursive type

#

This is anything that can be put into an html element's dataset

#

Its more or less "anything JSON encodable"

heady flicker
#

Seems to make sense on the first glance

#

Though the invariance of mutable types will likely diminish the benefits you are trying to receive from this

glass lily
#

Could you dumb that down a bit for me?

#

Also - it occurs to me that dataset items don't update when they change. In theory you can place a list or some such into a dataset, but when it changes, the value in the dataset won't (its frozen).

heady flicker
#

Mutable types are invariant.

This means that a function that accepts list[int | str] won't accept a list[int]

This is because there is a possibility that you append a str to that list within your function, causing the original list to change its type β€” without type checker noticing.

So type hinting with data structures such as the one you have here can prove very inconvenient.

#

My advice would probably be to use immutable types

glass lily
#

Hmmmmmmmmm

#

XD

#

In javascript, everything in the dataset is converted to a string

#

I was hoping to avoid that

#

I've got to start getting ready for work, but I'll keep my eye on this an reply as able

fierce ridge
#

"covariance" is when list[int | str] accepts list[str]

heady flicker
#

Someday I'll learn to dumb down better :(

fierce ridge
#

and as a concrete example of why this won't work for mutable data:

def append(data: list[int | str], newval: int | str) -> None:
    return data.append(newval)

x: list[int] = [1, 2, 3]
append(x, "q")
y: list[int] = [n + 1 for n in nums]  # Runtime error! Cannot add a string to an int.

it allows str to sneak into a list that should only contain int

glass lily
#

ol087v

fierce ridge
fierce ridge
glass lily
#

Sorry, cat stepped on the computer

#

XD

glass lily
#

Thank you

#

I guess I should do some investigation as what can actually be put into an element's dataset, and how its represented

fierce ridge
#

as another example

class Employee:
    id: int

class Manager(Employee):
    reports: list[Employee]

def append(employees: list[Employee], new_employee: Employee) -> None:
    employees.append(new_employee)

employee_456: Employee = ...
managers: list[Manager] = [...]
append(managers, employee_456)
for mgr in managers:
    print(mgr.reports)  # Runtime error! employee_456 is in thie list, but has no .reports attribute!
fierce ridge
#

that way you don't have to worry so much about these "variance" concerns, you get the intuitive covariance behavior

#

at the same time, if you don't expect users to be passing around specialized containers like a "a DIV containing only numbers" then the invariance is fine

#

that is, you can choose between:

  1. allow users to specialize the type of the data inside the container (like Div[int] instead of Div[HtmlElem]), but accept the constraint of type invariance OR prohibit in-place mutation

  2. do not allow users to specialize the type of the data inside the container (inserting an int inside a div causes you to lose knowledge of what you just inserted), but allow both covariance and in-place mutation

#

at least, those are the choices as i understand them. maybe someone with more formal knowledge in this area can correct any errors

rustic gull
#

Does anyone have any experience with pydantic and FastAPI request-response serialization? I'm trying to make it so that my response model allows me to not only leave a value blank, but to not show the value (key-pair) at all if I choose. Is there a way to do that?

fierce ridge
#

(this isn't really a type-hinting question, it's about runtime behavior)

#

you can definitely figure fastapi and pydantic to exclude default values / unset fields from the output

heady flicker
#

Why do you need something like this?

rustic gull
# fierce ridge you want to pass in a specific value that results in the attribute being omitted...

I'm currently following a spec detailed at https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0012.md#customer-put-verification, but the issue is that the client request may not (and is not required to by the spec) to input all of the keys to that particular request

GitHub

Developer discussion about possible changes to the protocol. - stellar/stellar-protocol

fierce ridge
rustic gull
#

I see

fierce ridge
#

actually response_model_exclude_unset seems like what you want

#

pydantic tracks whether the value was set by the input, or by default

#

so it can distinguish when serializing

#

pydantic has config options for this as well, for more general-purpose use outside of fastapi

rustic gull
#

Thanks for showing me that doc, I think thats what Im looking for

#

Just to remove something on a case

#

rather than generally

#

@bold wind is there any way to call response_model_exclude_unset conditionally?

#

So if you hit some case, remove a particular tag

fierce ridge
#

you can use response_model_exclude_default instead, where the default value is some sentinel or placeholder value

#
class Thing:
    quantity: int | None = None

if you use response_model_exclude_default=True, you can assign thing.quantity = None and it should be excluded, even if previously it was set to something else

rustic gull
#

I see

#

Thanks πŸ˜„

tranquil turtle
#

i was looking at my old code and found these cute type-hints:

[int]
(int, int)
(int, ...)
lambda: bool
lambda int: bool
``` i like them more than normal type-hints
chrome hinge
#

maybe one day

#

pylance's messages write them about like this already.

hallow flint
#

PEP 677 unfortunately

fierce ridge
trim tangle
viscid spire
heady marlin
#

Hi there guys, suppose I have:

class NamedTag(Flag, metaclass=ABCEnumMeta):
    @abstractproperty
    def description(self) -> str:
        raise NotImplementedError

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

@dataclass
class Section(Generic[T]):
    name: str
    tags: T

def run(sections: list[Section[T]]) -> None:
    ...

How can I obtain the type of T within the run() function?

Presently, I'm doing this but just wonder if there's a more elegant method:

def run(sections: list[Section[T]]) -> None:
    tag_type = sections[0].tags.__class__
soft matrix
#

you dont really get any better than that unfortunately

#

and youve added to my list of problems with runtime reification >:(

eager vessel
#

Also tag_type = sections[0].tags.__class__ could not work properly if your Sections have different tags types

#

e.g. Section[A], Section[B], Section[C] would give you just A

heady marlin
#

Thanks heaps for the reply 😊

soft matrix
#

well theres nothing stopping you passing list[Section[A] | Section[B]] as they pointed out

trim tangle
#

Yeah, generics are completely erased at runtime

spare thicket
#

how to create a new type after combining dictionaries?

lets say I have 2 TypedDict

import typing as t

class MainStat(t.TypedDict):
  DEX: int
  STR: int
  INT: int

class PersonalStat(t.TypedDict):
  LUK: int
  CRT: int
  TEC: int

and if I declare a dictionary MainStat

sample: MainStat = {"DEX": 1, "INT": 1, "STR": 1}

I get the expected type hints, same with PersonalStat

however I want to combine together both dictionaries and have their type hints combined like

sample: IdkTheType = {"DEX": 1, "INT": 1, "STR": 1, "LUK": 1, "CRT": 1, "TEC": 1}

I've tried Union[MainStat, PersonalStat], but it only includes the member keys of the first declared key, and will not include the keys on another dict

any workarounds on this?

trim tangle
#

Union does the opposite, it means it is either a MainStat or a PersonalStat

spare thicket
eager vessel
#

Actually this should only apply to inheritance, but I think it should be Section[A | B]

soft matrix
#

either would break it here

pure raptor
#

am I missing something or is it really annoying when you initialize anything with a literal value and now pylance/right continues to think it can only be that literal and not an int/str

i.e.

x = [(0, 0)]
# pylance thinks x: list[tuple[Literal[0], Literal[0]]]

x.append((1, 1)) # complains
#

even if I go like
x: list[tuple[int, int]] = [(0, 0)] it still does not work

heady marlin
solemn sapphire
heady flicker
#

A question to the type experts:

Pydantic schemas validate data. Recently, we started needing to analyze whether some schemas are subsets of other schemas. I.e. If data generated by A.json() (A.model_dump_json() in V2) is also parseable by B.parse_raw() (B.model_validate_json() in V2).

I have noticed a few quirks about this relationship. A field of A can be parsed by an equivalently named field in B if and only if its type is the same or wider (i.e. str | int in B can parse str from A but str in B cannot parse str | int in A). However, in terms of fields themselves, this relationship is reversed: B can only parse A if and only if it has the same number of required fields or less required fields than A (let's assume that all existing fields in B can be mapped to fields in A and let's assume there are no default values or validators that alter values).

This effectively yields the following relationship: "the set of fields in B must be a subset of the set of fields in A while each field type in B must be the superset of the respective field in A". This feels so much like Callable contravariance on arguments and covariance on return type but I cannot seem grasp two things:

  • Is there a relationship between Callable variance and these relationships in pydantic schemas?
  • If so, why? How does it work -- from a type theory standpoint?
muted iron
#

Is there a way to annotate something like this?

def f() -> int: return 127
def g() -> str: return "SRD"

def run(name: str):
    return globals()[name]()

v = run("f")  # v: Any, should be int
v = run("g")  # v: Any, should be str
chrome hinge
#

Well, two @overloads would work.

#

like, def run(name: Literal["f"]) -> int and similarly for the other.

muted iron
#
def f() -> int: return 127
def g() -> str: return "SRD"

@overload
def run(name: Literal["f"]) -> int: ...

@overload
def run(name: Literal["g"]) -> str: ...

def run(name: str):
    return globals()[name]()

v = run("f")  # v: int
v = run("g")  # v: str
#

This seems very repetetive. Not feasable for hundreds of f's and g's.

#

Esp considering the f's and g's are all in different files.

tranquil turtle
#

!e ```py
class X[T]: ...

print(X().dict)
print(Xint.dict)

rough sluiceBOT
#

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

001 | {}
002 | {'__orig_class__': __main__.X[int]}
brisk hedge
trim tangle
#

Why not pass the function itself, for example?

#

v = run(f)

pure raptor
muted iron
trim tangle
#

It's essentially def run(name: str) -> int | str

muted iron
#

Yeah, that'd be no good.

trim tangle
#

Why?