#type-hinting

1 messages · Page 50 of 1

oblique urchin
#

then the unsoundness is that type checkers don't catch this, no?

trim tangle
#

hmm

oblique urchin
#

i think mypy flags some similar cases without generica

trim tangle
#

so the bug is that this definition is even allowed to exist. I think that makes sense

#

Sometimes it makes sense though, e.g.: ```py
@overload
def foo(x: str) -> list[str]: ...
@overload
def foo(x: object) -> list[object]: ...

jade viper
#

Hey yall, I'm not sure I understand the use case for the new sentinel built-in? Is it a cleaner way of doing things instead of | None for example?

Python documentation

The Python interpreter has a number of functions and types built into it that are always available. They are listed here in alphabetical order.,,,, Built-in Functions,,, A, abs(), aiter(), all(), a...

oblique urchin
#

There are a number of use cases in the standard library, e.g. functools.Placeholder, typing.NoDefault, dataclasses.KW_ONLY

leaden ember
#

Could someone help me type the following?

from abc import ABC, abstractmethod
from typing import Literal

XGRID_AXES = Literal["X", "Y", "Z"]
UXGRID_AXES = Literal["FACE", "DEPTH"]

class BaseGrid(ABC):
    @property
    @abstractmethod
    def axes(self) -> list[str]:
        ...

    @abstractmethod
    def get_axis_dim(self, axis: str) -> int:
        ...


class XGrid(BaseGrid):
    @property
    def axes(self) -> list[XGRID_AXES]:
        ...

    def get_axis_dim(self, axis: XGRID_AXES) -> int:
        ...

class UxGrid(BaseGrid):
    @property
    def axes(self) -> list[UXGRID_AXES]:
        ...

    def get_axis_dim(self, axis: UXGRID_AXES) -> int:
        ...


gives me a bunch of errors like

t.py:20: error: Signature of "axes" incompatible with supertype "BaseGrid"  [override]
t.py:20: note:      Superclass:
t.py:20: note:          list[str]
t.py:20: note:      Subclass:
t.py:20: note:          list[Literal['X', 'Y', 'Z']]

I like the specific typehints on UxGrid and XGrid since its useful for devs. Is the only way to fix this to remove those typehints and broaden to str?

restive rapids
# leaden ember Could someone help me type the following? ```python from abc import ABC, abstra...

variance strikes again
list[str] is a stricter requirement than "all elements are str", its also "this list allows arbitrary strings being appended to it". so like, if grid: BaseGrid, i should be able to grid.axes.append("hello")
and because class XGrid(BaseGrid), when grid: XGrid, this should also work, but when you said its -> list[XGRID_AXES], now you're overriding it with saying i cant append hello to it, only X Y Z, so the subclass is incompatible with the base class (thats where the signature incompatible with supertype comes from)

if you dont want axes to be mutable, typehint it as collections.abc.Sequence[str] rather than list[str], Sequence is covariant in its type parameter as its only used in outputs, so since XGRID_AXES is a subtype of str, Sequence[XGRID_AXES] will be a subtype of Sequence[str]

restive rapids
leaden ember
trim tangle
#

@restive rapids I was thinking about variance tutorials again and maybe it's time to invent an analogy. Like the burrito for monads. I know it doesn't really work for monads. But I have a good feeling for variance. What real world things would be:

  1. Generic in some other real world thing
  2. Have different variance?
leaden ember
#

Does the Python discord have a reading list btw? I would love to learn about type theory. I'm not sure if a Python oriented book is the best way to go given it was kinda bolted onto the language after the fact. Anyone have book recs? A quick reddit search leads me to "Types and Programming Languages" - keen to know what people think

restive rapids
#

a generic animal vet can accept a cat and a dog because cat and dog are animals
but a cat vet cant accept a dog
because dog is not cat
a profession dealing with accepting patients is contravariant over the patient type

stuck mica
#

Does anyone has the hackmd from the typing summit?

feral wharf
void dome
#

How would you type hint an empty list the correct way? On stackoverflow I found list[Never] (from the typing module), but can I use this?

restive rapids
void dome
#

I have a function that uses list comprehension on a iterable but if the iterable is empty the returning list will be empty too

restive rapids
#

and why cant you just typehint it as list[T] where T is the normal element type? [] is a valid list[int]

#

list[Never] is basically useless for the caller, they wont be able to use this list. you cant assign list[Never] to list[T] unless T=Never
(list is invariant)

void dome
#

yeah, good point

#

That will work, thanks

fiery canyon
#

Is there any reason to declare a Literal as a type expression? (type L = Literal[...] vs L = Literal[...])

strong jacinth
#

Is there any way to add typing hints automatically?

#

Like to a whole project

trim tangle
# fiery canyon Is there any reason to declare a Literal as a type expression? (`type L = Litera...

type statements are new in 3.12 and they're lazily evaluated. So it might be slightly better for performance (import time) and cannot cause issues due to ordering, e.g.: ```py
DarkColors = Literal[Color.black, Color.deep_blue] # error: Color not defined

class Color(Enum):
...

also just consistency, it's strange to have half of your type aliases be `type` statements, and half as normal assignments.
fiery canyon
#

thumbsup alright

trim tangle
soft matrix
#

There's also autotype which will do all the simple easy to determine type hints

last laurel
#

Write me a code to make call of duty

last laurel
#

Fahh

jade viper
bleak imp
#

Average typing experience, trying to code a normal parser combinator, accidentally makes a safe transmute function by combing two ty bugs. This has not been my week for typing.

trim tangle
#

(which has a different set of bugs, but at least no TODOs)

#

pyrefly recently released a 1.0 version apparently

viscid spire
bleak imp
trim tangle
bleak imp
#

Sadly no, it was basedpyright exclusive

trim tangle
#

did you file a github issue?

bleak imp
#

Yep, already fixed

trim tangle
#

I recently found a bug that's present in all four type checkers and I'm procrastinating on reporting it. Maybe someone else wants to do it

bleak imp
#

I haven’t found one on all four yet (unless you include unsupported narrowing), but I have reported 3 that were shared between mypy and ty

plain dock
#

4, huh? i suppose that answers the question about any opinions you may have on zuban

trim tangle
bleak imp
#

It’s in there

trim tangle
#

I still have to install something to run it on my computer

bleak imp
#

(Though I also rarely use it since web is easier)

plain dock
trim tangle
#

yes

trim tangle
bleak imp
#

I feel like the common thread here for me is that parsers are hard (the allure is irresistible)

oblique urchin
#

multiplay is really nice, it's just clone + uv run app.py and you have it locally

raw valve
restive rapids
raw valve
#

Ah

versed bone
#

So pure editor?

#

more Notepad++, less VS Code

languid bane
#

first!

#

Huzzah!

primal sun
#

Woohoo, new channel :)

coral relic
#

@acoustic thicket Your channel is born

solid light
primal sun
#

Best type hinting tool to use?

languid bane
solid light
coral relic
#

I use mypy to validate type hints - pretty great

rustic gull
#
#

might be more apt ones out there lirikHMM

fossil nest
#

first

#

oh

#

damn

soft matrix
#

:)

trim tangle
#

@rustic gull I'll steal your links

cerulean copper
#

noooo i lost first

soft matrix
#

i like the sound of this

fossil nest
#

import typing
boom solved this crisis

trim tangle
rough sluiceBOT
soft matrix
#

pretty sure you shouldnt install that anymore

cerulean copper
#

yeah i guess its useless

fossil nest
trim tangle
#

yeah, it's in the stdlib

#

yep, I'm evil

solid light
fossil nest
#

damn man

cerulean copper
solid light
#

It's in the pypi page

fossil nest
rustic gull
#

Also, these mypy settings for a VSCode settings.json is what I use, some might find it handy: ```json
{
"python.linting.enabled": true,
"python.linting.mypyEnabled": true,
"python.linting.mypyArgs": [
"--strict"
]
}

cerulean copper
soft matrix
#

oh i thought it broke stuff

#

clearly not

fossil nest
cerulean copper
#

noe

#

yes I am typing

#

btw math.nan is a float

trim tangle
#

yup

cerulean copper
#

so amazing

trim tangle
#

what else would it be 🤔

cerulean copper
trim tangle
#

NaN is part of the IEEE-754 floating point standard.

rustic gull
#

A not a number is a number lirikHMM

cerulean copper
#

typeof(NaN) -> number

twin arrow
#

type hinting 🤔

#

thinting for short :P

#

type hinting is literally just

def func(arg1: int, arg2: str, arg3: lemons keyboard) -> None:
  pass
pliant kettle
#

thin minting

soft matrix
cerulean copper
trim tangle
#

NaN is just a bit of a misnomer

twin arrow
#

omg its lemon's keyboard

cerulean copper
#

(!+[]+[]+![]) in javascript is "truefalse"

trim tangle
#

it's more like "numeric error"

dreamy oasis
#

hi

twin arrow
trim tangle
#

even though this channel is brand new, please keep the discussion on topic -- about type hinting

twin arrow
#

also what's mypy

cerulean copper
#

yessir

#

so

#
def main() -> None: ...
def main() -> typing.Union[None]: ...

which one is better

solid light
soft matrix
#

mypy is a static type checker for python

twin arrow
#

yo what this is sick

soft matrix
#

like typing makes it None

cerulean copper
#

i know

#

but it's called typing so you gotta love more typing

#

typing.Optional[typing.Union[None]] this is for people that loves typing

twin arrow
#

im reading mypy docs and what is a dynamically and dstatically typed function

cerulean copper
#

anyways is typehinting possible in lambdas

soft matrix
#

yes

trim tangle
#

you can't type hint lambda arguments, no

soft matrix
#

annotate them as Callable

#

ie

trim tangle
twin arrow
#

ah ok

cerulean copper
#

like

soft matrix
#

foo: Callable[[int, str], float] = lambda x, y: 1.0

twin arrow
#

what is type hinting good for anyways

#

what is Callable[[int, str], float]

cerulean copper
twin arrow
#

oh so its assigning x and y to be int and str and 1.0 to be float

rapid shoal
twin arrow
#

interesting

leaden oak
trim tangle
twin arrow
#

ahh ok

trim tangle
#

type hints on their own don't do absolutely anything at runtime

twin arrow
#

they dont slow ur code down?

rapid shoal
#

barely

trim tangle
#

they don't do anything at all

cerulean copper
soft matrix
#

they dont really if you use future annotations

twin arrow
#

is it possible to autocomplete type hints? (so formatting it like you can with black formatter)

soft matrix
#

jelle is working on this i think

trim tangle
cerulean copper
soft matrix
#

yes

leaden oak
#

I'm a little confused on the black mention pithink

rapid shoal
twin arrow
#

:P i think u should know u do stuff with black dont u

soft matrix
twin arrow
#

o ur a maintainer

leaden oak
#

Black can format type hints, what's the problem?

rapid shoal
trim tangle
#

it's a nanocost

soft matrix
#

thats why i said dont really

oak ferry
#

type hinting
hype tinting

rapid shoal
#

yeah, but it's still the same without from __future__ import annotations?

trim tangle
#

comments also have a cost - files with comments take longer to type - but you don't really notice it

soft matrix
#

no cause the annotations are strings

brisk hedge
#

if a type is already inferred by your static analysis tool, you probably don't need it to be auto-filled

twin arrow
twin arrow
leaden oak
#

Yes, type hints are normal Python code after all

twin arrow
#

how can u enable it

rapid shoal
leaden oak
#

No special DSL

twin arrow
#

it doesnt work for me

#

whenever i save it never type hints

leaden oak
#

oohhh

trim tangle
#

@twin arrow It doesn't place new type hints for you.

#

why would it?

leaden oak
#

black doesn't autocomplete anything

twin arrow
#

oh ok

cerulean copper
#

type hints are so op they can make undefined variables

rapid shoal
#

not really

twin arrow
#

can u automatically have type hints on save is what i mean

soft matrix
twin arrow
#

sorry i had a very stupid comparison 🤦

cerulean copper
#
varname: str

doesn't raise error Little_Pog

#

but varname is still undefined

cerulean copper
#

you can declare variables with typehints

trim tangle
twin arrow
#

oh but then what were u talking about that should be placed into code?

trim tangle
twin arrow
#

oh

trim tangle
#

It's mostly a tool for formal documentation

twin arrow
#

can you have something automatically do that

trim tangle
#

You can't automatically infer argument types

leaden oak
#

you can type hint variables too but you should only if the data type is complicated enough where it's non obvious to either you the human or your static analyser

twin arrow
twin arrow
little hare
rapid shoal
#

it's possible in some cases, but most of the time no

trim tangle
cerulean copper
#
from __future__ import annotations
@lambda x: x()
class __annotations__:
    def __setitem__(self, k, v):
      globals()[k] = v()
var: list

how to declare undefined variables

twin arrow
#

Well sometimes when im making __init__ VSCode makes it def __init__() -> None:

soft matrix
leaden oak
#

That sounds like a snippet not autocompletation

twin arrow
#

oh it is

prisma kelp
#

I am extremely a beginner so I am sorry if this is super basic but what is type hinting? What would you use it for?

twin arrow
twin arrow
#

and go to the first link unlike me i went to mypy and instantly got confused :\

leaden oak
#

Type hinting acts like machine verified API documentation - when reading the code you can skip a bunch of the "what is this code supposed to do in the first place" stage

spiral fjord
#

It can also help prevent bugs.

leaden oak
#

now the machine verified bit requires a type checker like mypy or pyright

trim tangle
# twin arrow hmm?

If you make very small programs - 200-400 lines - it doesn't really matter - just use good function names.
In bigger projects, documentation is essential so that people can easily navigate the code.

twin arrow
#

hmm

#

so if i post it on github and a README.md file?

trim tangle
#

README is part of documentation, yes

cerulean copper
#
def println(*args: str, l:bool = True) -> None:
  """
  This program will print a line in the stdout (Standard Output Stream) stream and do a flush.
  It can be disabled by using the key word argument `l`, if l set to False, it will NOT flush nor start
  a new line.
  """
  stdout = __import__('sys').stdout
  if not type(l) == bool:
    raise Exception("read the typehints my guy")
  if l:
    stdout.write(str(*args))
    stdout.write('\n')
    stdout.flush()
  else:
    stdout.write(str(*args))
  return
trim tangle
#

*args should be str

twin arrow
#

so:

  1. i cannot have something automatically do the type hints for me
  2. type hints is mostly a tool for formal documentation
  3. i dont know anything about type hints
spiral fjord
#

In newer versions of python you can use list

cerulean copper
sullen halo
#

why __import__

cerulean copper
sullen halo
#

i would just do a local import, but you really should be using a global import anyways

twin arrow
#

also i thought documentations r like markdown files, not Python files. How can you type hint markdown files :\

leaden oak
#

type hints are specific to Python code

trim tangle
#

you could also argue that function/class/variable/module names also act as micro-documentation

twin arrow
#

oh ok

#

that makes a lotta sense now

#

thanks for the assistance

leaden oak
#

And just for a real life example (pulled from psf/black):

def main(
    ctx: click.Context,
    code: Optional[str],
    line_length: int,
    target_version: List[TargetVersion],
    check: bool,
    diff: bool,
    color: bool,
    fast: bool,
    pyi: bool,
    ipynb: bool,
    skip_string_normalization: bool,
    skip_magic_trailing_comma: bool,
    experimental_string_processing: bool,
    quiet: bool,
    verbose: bool,
    required_version: str,
    include: Pattern,
    exclude: Optional[Pattern],
    extend_exclude: Optional[Pattern],
    force_exclude: Optional[Pattern],
    stdin_filename: Optional[str],
    src: Tuple[str, ...],
    config: Optional[str],
) -> None:

... I would not want to try and remember what types all of these parameters should be

leaden oak
#

now to be fair, the variable names makes it easier on their own, but the types makes it unambiguous

trim tangle
#

If you have a procedure with ten parameters, you probably missed some.
- Alan Perlis

leaden oak
#

This is a main function using click so 🤷

twin arrow
#

im crying right now

#

that traumatized me

spiral fjord
#

I'm just surprised that code is optional in a code formatter.

little hare
leaden oak
#

it's for the --code option which is different from src aka the files and directories passed

leaden oak
cerulean copper
#

is there a typing.void

#

or something like that

trim tangle
#

what would it do?

leaden oak
#

No? since functions will always return something

cerulean copper
little hare
trim tangle
little hare
#

!d typing.NoReturn

rough sluiceBOT
#

typing.NoReturn```
Special type indicating that a function never returns. For example:

```py
from typing import NoReturn

def stop() -> NoReturn:
    raise RuntimeError('no way')
```   New in version 3.5.4.

New in version 3.6.2.
trim tangle
#

NoReturn is different

sullen halo
#

thats different

leaden oak
#

well OK that's for erroring / exiting functions .

trim tangle
#

oh, that was a response to richard

#

sorry for objection

leaden oak
# little hare Actually false

If you really want to play the words lawyering game, you could argue that I meant that if a function is returning (not exiting or erroring!) it will return something

#

Not that a function will always return.

green gale
rapid shoal
#

if you really really want to play the words lawyering game, you said "functions will always return something"

hallow pollen
#

Woooo type annotations

trim tangle
#

hi Stelercus

leaden oak
#

Hi

hallow pollen
#

@trim tangle hello friend

trim tangle
#

hello

#

although maybe not necessary

leaden oak
#

anyway let's move on, I shouldn't be derailing a brand new channel 😅

rapid shoal
#

i wasn't saying it was true, i was just pointing out something he said

leaden oak
#

I'm confused but OK

trim tangle
#

I'm confused as well

#

what are we even talking about?

leaden oak
#

Another cool use of type hinting is transcompilation

leaden oak
#

mypyc (which is built on top of mypy) will use the type hints in your code to generate Python C extensions for sometimes massive performance wins

little hare
#

damn

#

so if it passes mypy you can probably create c-extensions

leaden oak
#

Well strict mypy but yes

little hare
#

yeah

soft matrix
#

if it doesnt segfault yes

little hare
#

lmao

leaden oak
#

or crash at runtime with a RuntimeError("hit apparently unreachable block") 😉

little hare
#

i wrote a logging statement like that

#

if you see this, its time to look for a different program. The core of this one is entirely broken

#

i deleted it because it was logging into my console too much ;-;

leaden oak
#

🔥

#

Anyway I'mma go back to what I was supposed to be doing.

rustic gull
#

Prepare for wall of text ducky_dave

#

Big PSA about uppercase vs lowercase hints for things like List, Tuple, and Dict:

In Python 3.9 and later you can use lowercase list with no imports to type hint things:

def printlist(lst: list[int]):
    print(lst)

printlist([1, 2, 3]) # -> [1, 2, 3]

But in Python 3.8 and earlier you'll get errors like TypeError: 'type' object is not subscriptable and need to use uppercase List with from typing import List:

from typing import List

def printlist(lst: List[int]):
    print(lst)

However, such usage is deprecated (see https://docs.python.org/3/library/typing.html#typing.List) so it's not ideal.

So alternatively, you can use lowercase list with from __future__ import annotations, but only in Python 3.7 and beyond:

from __future__ import annotations

def printlist(lst: list[int]):
    print(lst)

For Python 3.5 and 3.6, use the uppercase List method. (Type hinting was introduced in 3.5.)
A backport library might also be an option: https://pypi.org/project/typing-extensions/
In a pinch you can put quotes around a hint like 'list[int]' but that is usually for forward references (https://www.python.org/dev/peps/pep-0484/#forward-references).
Check out https://pypi.org/project/flake8-pep585/ for a package that helps you avoid deprecated types.

Any of the above methods will work for 3.9 and beyond.

Lots of people still use old versions of Python (see https://w3techs.com/technologies/details/pl-python/3 and https://dev.to/hugovk/python-version-share-over-time-6-1jb8) so choose your type hints carefully if you are making something other people will use!

soft matrix
#

when is the Union version of this text wall coming?

trim tangle
cerulean copper
#

the Rust or operator

spiral fjord
#

| for dicts is pretty nice

rustic gull
soft matrix
#

i think a link to pep 585 might be welcome in this

#

cause that has a list of all of the things that you can use this on

rustic gull
#

There's probably other peps to add lirikHMM

soft matrix
#

cool thanks

humble ice
#

yay type hinting p_sparkles3

rustic gull
#

oh wow

#

type hinting is really cool vibing

scenic dune
#

Type hinting plus static type checker like mypy is awesome

rustic gull
#

Worth mentioning, you can make an alias for a long type hint in the same way as defining a variable, like PathLike here: ```py
import os
from typing import Union, Any

PathLike = Union[str, bytes, os.PathLike[Any]]

def printfile(path: PathLike):
with open(path) as file:
print(file.read())

fierce ridge
#

!pep 613

rough sluiceBOT
#
**PEP 613 - Explicit Type Aliases**
Status

Accepted

Created

21-Jan-2020

Type

Standards Track

fierce ridge
#

Playground/"fiddle" web app for Mypy: https://mypy-play.net. It's very helpful to post a Mypy-Play link when asking questions, so we can see both your code and the Mypy output in one place.

#

@trim tangle can you pin that? ☝️

rustic gull
#

Time to talk about professional and extremely complex hinting.

#

Whoever can find the longest type hint in production code gets a (virtual) cookie lirikHMM 🍪

#

that won't come to my house

rustic gull
languid bane
#

I just stopped using tuples when I needed to type hint the code

acoustic thicket
shell plume
rustic gull
#

that is pinned

cunning plover
#

Nvm, yeah

#

Mobile discord is a bit different

#

Did not find from the start

earnest hazel
mystic robin
#

this is my first message in here

blazing nest
granite plover
#

Ok guys
I have a dict like this

Config = Dict[str, List[Union[one, two, three]]]

From this i need some relevant key-value pairs that are Union[one, two] and i need to extend them into a list

I guess i can provide a full example

def flatten(sections: Config) -> List[Union[Aliased, Unaliased]]:
    all_cols: List[Union[Aliased, Unaliased]] = []
    for k, col in sections.items():
        if k in [relevant cols]:
            all_cols.extend(col)
    return all_cols
#

The problem is that col is List[Union[aliased, unaliased, else]]

#

Which is more general i guess

#

Thatn the hint for all_cols

brisk hedge
#

Only manually

trim tangle
#

@granite plover Did you mean if col in [relevant cols]:?

#

I'm not sure type checkers understand in checks

blazing nest
#

I don't think they can narrow it down unless you use Literal though

trim tangle
#

hmmmmm I'm not sure how the code was supposed to work

wraith linden
#

If so, just assert the type.

#

assert isinstance(col, list[Union[Aliased, Unaliased]])

#

I think 🤔

granite plover
#

relevant columns is a list of strs, im checking if the dict has these sections as keys

#

Instead of the whole try except

trim tangle
blazing nest
wraith linden
#

Oh right

trim tangle
#

that won't work because list[...] isn't checkable

wraith linden
#

I'm not sure I understand your question actually @granite plover

#

You have a dict, and you want to extract a subset of the key-value pairs, for all of which the value has type list[Union[Aliased, Unaliased]]? And you want to then concatenate these lists together?

#

But the dict also contains values which are lists that might contain some other type, which you're not interested in?

granite plover
#

The dict is for configuration so not all of the values are the same type

#

Theres three types in the dict, theyre all tuples of different structures

#

I want a subset

#

But i keep getting an error from mypy

#

Im extending a list hinted as List[Union[one, two]] with something that is List[Union[one, two, three]]

#

And thats giving me incompatibility errors

granite plover
wraith linden
#

Oh right. Well obviously you can't add a three to a list of ones and twos. But you're sure that the values you're going to add to all_cols are in fact ones and twos?

granite plover
#

Yes

wraith linden
#

So you need to communicate this to the type checker?

granite plover
#

yes basically

#

not sure how to tell mypy that im only ever getting one and two in this list

wraith linden
#

@trim tangle Would something like this work? (I'm figuring this out as I go along tbh) ```py
for val in col:
assert isinstance(val, (Aliased, Unaliased))
all_cols.append(col)

#

Or would typing.cast be appropriate to use here?

trim tangle
#

yep, that should work

granite plover
#

error: Parameterized generics cannot be used with class or instance checks

#

lemme look up typing.cast

wraith linden
#

Oh right. So then it looks like you want to do ```py
for val in col:
val = cast(val, Union[Aliased, Unaliased])
all_cols.append(col)

trim tangle
#

assert isinstance(val, (Aliased, Unaliased)) should work

#

@granite plover you're trying to do isinstance(something, list[something_else])

#

or Aliased/Unaliased is a parametrized generic

#

in that case yeah, you'll have to use cast

granite plover
#

man, typing is hard

wraith linden
#

How about using a TypedDict for the config?

granite plover
#

i'll look it up

#

ultimately this isnt that important right now, but i should sort it out

#

i moved stuff around to make it easier for mypy to understand what i want

#

everything checks out now, thanks peeps

wicked scarab
#

why does is doesn't work with built-in types when making it Generic (list, dict, etc)? This works tho for typing module.

import typing

a = typing.List[str] 
b = typing.List[str] 
print(a is b)
>>> True

c = list[str]
d = list[str]
print(c is d)
>>> False
pastel egret
#

The typing module is written in Python and does a bunch of checks, as well as caching the objects it produces. But the builtin version is instead written in C and is a bit simpler.

wicked scarab
#

oh, so builtin type when making it Generic is not comparable with is? Means that it is stored in a different memory location?

primal sun
#

I have a discord command that has this in

@commands.command()
async def my_command(self, ctx, *, user_input: Optional[str] = None) -> None:
        if user_input is None:
            return await error(ctx, "No item given.")

        if user_input.startswith("e "):
            user_input = "extra"+user_input.removeprefix("e ")

But the problem is, mypy complains that "None" has no .startswith(), since it's Optional, what should I do in this situation?

#

I'm also getting a similar problem

#

Item "None" of "Optional[str]" has no attribute "lower"

trim tangle
primal sun
#

@trim tangle I think this should be good?

#

Do I only ever type hint it in the first case it appears, even if it never runs?

trim tangle
#

I mean, why do you want to type hint it?

primal sun
#

What do you mean

#

Sorry, I'm very new to this

trim tangle
#
a = "b"
if a == "b":
    my_number = 5
else:
    my_number = 6
primal sun
#

Am I stupid to be type hinting everything?

trim tangle
#

You shouldn't type hint everything - when you do an assignment, the type checker usually infers the type for you.

primal sun
#

Hmmmm

#

I guess I've been doing things wrong then, oops
So is it wrong to type hint something if it's being assigned to that type?
e.g.

a: int = int("7")
trim tangle
#

it's pretty clear both to programmers and to tools that it's an int

blazing nest
blazing nest
wicked scarab
#

!d typing.get_origin

rough sluiceBOT
#

typing.get_origin(tp)```
Provide basic introspection for generic types and special typing forms.

For a typing object of the form `X[Y, Z, ...]` these functions return `X` and `(Y, Z, ...)`. If `X` is a generic alias for a builtin or [`collections`](https://docs.python.org/3.10/library/collections.html#module-collections "collections: Container datatypes") class, it gets normalized to the original class. If `X` is a union or [`Literal`](https://docs.python.org/3.10/library/typing.html#typing.Literal "typing.Literal") contained in another generic type, the order of `(Y, Z, ...)` may be different from the order of the original arguments `[Y, Z, ...]` due to type caching. For unsupported objects return `None` and `()` correspondingly. Examples:

```py
assert get_origin(Dict[str, int]) is dict
assert get_args(Dict[int, str]) == (int, str)

assert get_origin(Union[int, str]) is Union
assert get_args(Union[int, str]) == (int, str)
```   New in version 3.8.
wicked scarab
#

wow

blazing nest
#

!e ```python
import typing

print(typing.get_origin(typing.Annotated[str, int]))
print(typing.get_args(typing.Annotated[str, int]))

rough sluiceBOT
#

@blazing nest :white_check_mark: Your eval job has completed with return code 0.

001 | <class 'typing.Annotated'>
002 | (<class 'str'>, <class 'int'>)
blazing nest
#

So from that you get the actual class used, and the arguments in it.

wicked scarab
#

alright CH_ThumbsUpSmile, but whats the reason normal is doesn't work?

proud sable
#

Hey new channel

primal sun
spare mauve
#

is there an API to check the compatibility of a type ?

blazing nest
blazing nest
#

Variables can then be inferred from that. If I have a function that returns an integer and assign this to a variable, then I know the variable is an int.

primal sun
#

So just parameters and return types, as well as classes? Seems like not a lot but sounds good

trim tangle
#

it's new, created yesterday

#

most big changes happen on sundays because that's when we have staff meetings

rich shell
rough sluiceBOT
#

@rich shell :white_check_mark: Your eval job has completed with return code 0.

001 | True
002 | False
003 | True
spare mauve
#

I mean like if I have List[...] and want to see if it's compatible with Sequence[...]

#

i figured out how to parse a file with mypy.parse, from inside python, but the various type checks mypy.subtypes.is_equivalent(left, right) for example) always return True if I pass types from the parsed module

#

maybe that's just not possible but I was just looking to see if there was any way to get more than a printout of errors (like the mypy command) from it

soft matrix
#

That sounds incredibly unnecessary

#

List is a subclass of sequence

#

So you can just check args

void ibex
#

So this is a fun one, never had this warning before, any suggestions on what a fix would be, or what it means in general? ```py
@dataclass
class PingConfig:
ping_string: str = "@here"
wait_duration: int = 5 * 60
ignored_categories: t.Optional[list[int]] = None
...
class PingDelay(commands.Cog):
def init(self):
...
self.config = t.Optional[PingConfig] = None

The t.Optional[PingConfig] typehint gives the pylance error Generic class type cannot be assigned
#

The only reason i want to do this is because the PingConfig is populated in a coro after init, so I just wanted to highlight that this instance var exists when initialising

solid elbow
#

@void ibex are you sure you don't want self.config: t.Optional[PingConfig]? i'm confused

void ibex
#

omg

#

I do, I have : everywhere

#

why didn't I see this facepalm

soft matrix
#

Personally I wouldn't assign this as None so you don't have to unwrap it every time you access and use it

wraith jay
#

how would y'all go about type-hinting a method that returns all objects in a list that match a given type?

def get_objects(self, obj_type) -> list[obj_type]:
    output = []
    for item in self.some_list:
        if isinstance(item, obj_type):
            output.append(item)
    return output
``` or is that even possible?
boreal ingot
# wraith jay how would y'all go about type-hinting a method that returns all objects in a lis...

I would use TypeVar here, it is basically a unknown type; together with Type ```python
from typing import TypeVar, Type
T = TypeVar("T")

def get_objects(self, obj_type: Type[T]) -> list[T]:
output = []
for item in self.some_list:
if isinstance(item, obj_type):
output.append(item)
return output```

https://docs.python.org/3/library/typing.html#typing.TypeVar
https://docs.python.org/3/library/typing.html#typing.Type

acoustic thicket
#

^
just type[T] would also work in 3.9+

soft matrix
#

I don't currently thing Type[T] works with type checkers, but yeah that is how I would do it

wraith jay
boreal ingot
soft matrix
#

They must have fixed it

#

I remember in the typing summit someone wanted to introduce what is basically this but everyone unanimously agreed that this should already be type-able

boreal ingot
#

interesting

soft matrix
#

Lemme find it I might be missing something

#

Greetings

Type Syntax Simplifications (Maggie Moss) 0:06

Validating JSON with TypedDict, trycast, and TypeForm (David Foster) 29:45

Type Variables for All (Pradeep Kumar Srinivasan) 54:20

Static Python: Types in Bytecode Compilation & Runtime (Carl Meyer) 1:25:16

Incremental Check in Pyre (Jia Chen) 1:50:55

Scaling Typeshed to 1000 Package...

▶ Play video
#

Oh I think that's slightly different

boreal ingot
#

well that is interesting

#

okay so I did some testing ```python
from typing import *

T = TypeVar("T")
def foo(a: Type[T]) -> T: ...

class Bar: ...
class Baz: ...

def fsfsdfs(x: Type[Union[Bar, Baz]]) -> None:
reveal_type(foo(x))
``` mypy says builtins.object* while pyright says Bar | Baz (which is clearly what it is )

#

so is this what they were talking about you think? @soft matrix

soft matrix
#

Yeah

#

That's it

boreal ingot
#

I guess I wont be using mypy anymore then 🙃

soft matrix
#

I just don't use a type checker :^)

boreal ingot
#

😮

#

I am currently in a really annoying situation,

I either use a async lib with no type hinting, or a sync one with.
ofc I wanna go async, but the lack of type hints are really annoying me

soft matrix
#

pyright can generate stubs

#

Or you can use monkey type to type hint all the functions you care about

boreal ingot
#

yhe that is a good idea, will look into that

little hare
#

@boreal ingot what libs btw

boreal ingot
little hare
#

oh

#

well

#

motor builds on pymongo iirc

boreal ingot
#

it does, but it still has its own types

boreal ingot
little hare
#

lol

#

ergh

#

i kinda want to write a stub library for motor

boreal ingot
#

that would be wonderful 😄

little hare
#

even though

#

i literally do not use motor

#

but whatever, I'm bored

boreal ingot
#

I use it basically all over my current project (discord bot)

little hare
#

postgres tho

boreal ingot
#

mongo was just easier to get started with

little hare
#

true

#

my project doesn't have a database yet

#

because I've been putting it off

boreal ingot
#

and I cant really switch now, since another developer on my team (tbh we are 2 people) is already using that db in "our" 2 other parts of this project

little hare
#

Lol

#

I need to get started on the database

#

Ugh

languid bane
#

Is this a good way to fetch the return value for a function? Or is there a better way?

from typing import Any, Callable, TypeVar

R = TypeVar("R")

def cast_input(
    func: Callable[[Any], R],
    err_text: str = "Not valid!",
    input_text: str = "> "
) -> R:
    while True:
        try:
            return func(input(input_text))
        except ValueError:
            print(err_text)
boreal ingot
languid bane
#

Oh yeah its the input.

#

I just quickly put something in there to test the TypeVar as i havent used that before

#

Thanks

#

Makes for a fun way to make any type without having to copy paste:

#
int_input = partial(cast_input, int, "Not a valid whole number!")
float_input = partial(cast_input, float, "Not a valid real number!")
languid bane
#

Whats a good way to check types of partially applied functions lol

#

As the signature that ipython shows is:
int_input(input_text: str = '> ') -> ~R

#

I guess its not a problem so to say. But it would be nicer if there existed a way to "parse" out the ~R as the function for the return value is applied.

#

But i think im asking a lot from the functools.partial doing that.

boreal ingot
soft matrix
languid bane
#

Yeah my editor is not very happy with the casting from R. Saying R@cast_input is not int :P

#

Because of a object* not being int which i find odd and likely a vital hint to whats wrong

boreal ingot
soft matrix
#

my word i cannot read

#

lol

#

time to stop discord

boreal ingot
languid bane
#

Is the error when trying to use it the partial functions to add data to a dataclass that has int and float types respectively.

boreal ingot
languid bane
#

Lines that mess it up is 72-74

#

I can make a simpler example if this one is too hard to read

boreal ingot
#

okay, so yhe partial is not to nice with type hints, what is the error with the nesting funcs approach (sorry my code above is weirdly indented, discord :/ )

languid bane
#

Dammit, i had a feeling it would be partial

boreal ingot
#

my guess is that the only type info it can keep when using partial is the return type, which is basically Any because it has no context

#

you could use some cast, but tbh that feels like a bit to much repetition

brisk hedge
#

I wonder whether you could implement partial using paramspec and (reverse?) concatenate

languid bane
#

Yeah. It doesnt seem like partial has any nice way of type support :/

languid bane
#

Ended up just making a one line function with the types set rather than trying to use the dynamic one:

def int_input(input_text: str = "> ") -> int:
    return cast_input(int, err_text="Not a valid whole number!", input_text=input_text)
little hare
#

!pep 561

rough sluiceBOT
#
**PEP 561 - Distributing and Packaging Type Information**
Status

Accepted

Python-Version

3.7

Created

09-Sep-2017

Type

Standards Track

little hare
#

what does the py.typed file mean?

soft matrix
blazing nest
little hare
#

ah

#

so I should add that to my typed projects

blazing nest
#

Yes

#

If they are fully typed that is

little hare
#

ye

#

i've set flake8 to complain if I don't type em

wicked scarab
rough sluiceBOT
#

@wicked scarab :white_check_mark: Your eval job has completed with return code 0.

True
wicked scarab
#

and also @blazing nest ^^

rich shell
wicked scarab
#

oooh

rich shell
#

i dont know how exactly that works but a string with the same value will always have the same id if you compare them

wicked scarab
#

is there any documentation of special types?

rich shell
#

!e ```py
test1 = "test"
test2 = "test"

print(f"{id(test1)} {id(test2)}")

rough sluiceBOT
#

@rich shell :white_check_mark: Your eval job has completed with return code 0.

139784128270192 139784128270192
rich shell
#

== is more than enough for most needs

wicked scarab
#

yeah True, just wondering..

rich shell
#

or rather the "secret sauce" if you wanna call it that is that int, float and string types are not mutable

#

which means that each time you change them you create a new object

#

unlike a list

wicked scarab
#

hmm, oh. So immutable types makes a new object in different memory location everytime?

rich shell
#

yes more or less

#

!e py test = 1 print(id(test)) test += 4 print(id(test))

rough sluiceBOT
#

@rich shell :white_check_mark: Your eval job has completed with return code 0.

001 | 140098835175728
002 | 140098835175856
wicked scarab
#

cool

#

how about mutable types?

rich shell
#

mutable types have the same memory space since you only mutate them

#

!e py test = [] print(id(test)) test = [] print(id(test))

rough sluiceBOT
#

@rich shell :white_check_mark: Your eval job has completed with return code 0.

001 | 139773859148160
002 | 139773859319552
rich shell
#

this will also create a new object, though

wicked scarab
#

!e

a = [1,2,3]
print(id(a)) 
a[0] = 4
print(id(a)) 
rough sluiceBOT
#

@wicked scarab :white_check_mark: Your eval job has completed with return code 0.

001 | 140433855390080
002 | 140433855390080
wicked scarab
#

okay cool

wicked scarab
rich shell
#

everytime you do {} or [] which are shorthands for dict() and list() respectively a new object is created

little hare
#

or set

rich shell
#

if you do test = [] the test variable will now refer to that object

rich shell
little hare
#

Expanded info:
{} is both for dict or set, its overloaded.

dictionary: {x: y}
set: {x, y}

#

hmm

wicked scarab
#

creates a new object: set, dict, list
wont creates new object: str, int, float, complex, bool

#

is this correct?

little hare
#

!e print(type({}))

rough sluiceBOT
#

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

<class 'dict'>
little hare
#

ok

rich shell
wicked scarab
#

yeah I mean if it exists already

rich shell
#

but yes more or less thats how it goes

little hare
rich shell
#

jup

little hare
#

which means that all 'hi' are actually the same string?

#

if it wasn't out of scope at a different point ofc

#

like uh

rich shell
#

there are ways to make a custom class behave the same but i really dont know much about that though

little hare
#

!e ```py
a = 'hi'
b = 'hi'
print(a is b)

rough sluiceBOT
#

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

True
rich shell
#

if you compare two strings with the same value they will always be the same object

#

at least during normal operation

little hare
#

ye

#

!e ```py
a = 'hi'
print(hash(a))
del a
b = 'hi'
print(hash(b))

rough sluiceBOT
#

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

001 | -4016080006415762537
002 | -4016080006415762537
rich shell
#

also here's smt funny

#

!e py import gc print(gc.get_objects())

rough sluiceBOT
#

@rich shell :white_check_mark: Your eval job has completed with return code 0.

[<function getregentry at 0x7fc5ebfbd5e0>, <codecs.CodecInfo object for encoding utf-8 at 0x7fc5ebfa9be0>, {'name': 'utf-8', 'encode': <built-in function utf_8_encode>, 'decode': <function decode at 0x7fc5ebfbd550>, 'incrementalencoder': <class 'encodings.utf_8.IncrementalEncoder'>, 'incrementaldecoder': <class 'encodings.utf_8.IncrementalDecoder'>, 'streamwriter': <class 'encodings.utf_8.StreamWriter'>, 'streamreader': <class 'encodings.utf_8.StreamReader'>}, {'utf_8': <codecs.CodecInfo object for encoding utf-8 at 0x7fc5ebfa9be0>}, {'utf_8': <codecs.CodecInfo object for encoding utf-8 at 0x7fc5ebfa9be0>}, ModuleSpec(name='_signal', loader=<class '_frozen_importlib.BuiltinImporter'>, origin='built-in'), {'name': '_signal', 'loader': <class '_frozen_importlib.BuiltinImporter'>, 'origin': 'built-in', 'loader_state': None, 'submodule_search_locations': None, '_set_fileattr': False, '_cached': None, '_initializing': False}, <module '_signal' (built-in)>, <built-in function default_int_han
... (truncated - too long)

Full output: too long to upload

rich shell
#

or smt more reasonable

#

!e py import gc print(len(gc.get_objects()))

rough sluiceBOT
#

@rich shell :white_check_mark: Your eval job has completed with return code 0.

3823
stray tide
#

new channel?

oblique urchin
upper tapir
indigo locust
#

If I want to hint that something is a Type of one of two forward references

#

is it

#

Type['A | B'] or Type['A'] | Type['B']

oblique urchin
indigo locust
#

Danke

blazing nest
#

How does subclassing generics behave?

#

What do I need to do "to satisfy it"? For example my parent class has the type variable T and I want to continue using this.

soft matrix
#

you dont need to reinsert Generic[T] if that was also what you were wondering

#

theres a lot of that in typeshed which idu

oblique urchin
#

I have to remind Sebastian every so often that it's redundant 🙂

soft matrix
#

has that always been in pep 484?

blazing nest
#

Wait now I am confused, so where is the variable?

soft matrix
#

Parent[T]

blazing nest
#

So I just give it a new variable ah?

soft matrix
#

class Sub(Parent[T]):

#

like that

#

doesnt need to be new necessarily

oblique urchin
soft matrix
#

oh cool

crisp steppe
#

Can Protocol take a generic param like class Foo(Protocol[T]) : def foo(self, t: T): ...
I'm trying this out but it doesn't work. But if I try Generic[T] it works. But aiui I need to use Protocol since I'm defining a protocol not a Generic type

soft matrix
#

that should work what error are you getting?

#

are you getting error: Invariant type variable "T" used in protocol where contravariant one is expected?

blazing nest
#
P = ParamSpec('P')


class Hider(Generic[P]):
    hidden: Callable[P, None]

    def __init__(self, value: Callable[P, None]) -> None:
        self.callback = value

    @property
    def value(self) -> Callable[P, None]:
        return self.hidden

    @value.setter
    def value(self, function: Callable[P, None]) -> None:
        self.hidden = function


O = ParamSpec('O')


class Subclass(Hider[O]):
    def is_set(self) -> None:
        print('The value is set:', self.value)  # Cannot access member "value" for type "Subclass[O@Subclass]"

#

I have simplified my code to this, could this be a PyRight bug?

#

MyPy straight up doesn't support ParamSpec yet

oblique urchin
#

That seems like a bug to me

blazing nest
#

The thing is, this works when I remove the [O] (but it is needed for the real functionality I have)

oblique urchin
#

Report it to pyright, Eric Traut will probably fix it immediately 🙂

blazing nest
#

Hahaha true

soft matrix
#

make it contravariant

#

do you know how to do that?

crisp steppe
#

TypeVar('T', contravariant=True) ?

soft matrix
#

yep

crisp steppe
#

ok because the error wrapped i misread it as 'where covariant expected' and tried covariant = True which didnt work

gray maple
#

Hello All,

I wanted to share another uni-model approach I have been working on to address the issue of type-hinted db model management, access, migrations, and caching. Much like sqlmodel, there is only a single model to manage( which happens to be pydantic) but with further abstractions to try and make model creation & relationships a bit easier. Happy to hear your thoughts, I hope this makes your lives a little easier 🙂

https://github.com/codemation/pydbantic

GitHub

A single model for shaping, creating, accessing, storing data within a Database - GitHub - codemation/pydbantic: A single model for shaping, creating, accessing, storing data within a Database

languid leaf
#

looks pretty cool

#

have they made pydantic smaller yet

#

it's a big lib

upper tapir
#

what is the typehint for any iterable or any generator?

upper tapir
#

generator?

fierce ridge
#

@upper tapir @rustic gull typing.Iterable and typing.Generator are deprecated now, you can (and eventually will be forced to) use collections.abc.Iterable and collections.abc.Generator

#

most of the time you can/should just use Iterable for generators, unless you want to support "push"-mode with .send

upper tapir
#

oh

fierce ridge
#

they changed it when they added support for [] to all classes in 3.9

upper tapir
#

is it only for these two from typing?

fierce ridge
#

no, a lot of the things in typing were aliases for things in collections.abc, those are all deprecated now

#

Iterable, Sequence, Mapping, etc.

upper tapir
#

whatever is _alias, i shud not use?

fierce ridge
#

same with typing.Tuple, typing.List, typing.Dict , you should use tuple, list, and dict now. unless you need to support python 3.8 specifically

upper tapir
#

ooh

fierce ridge
#

yeah, anything that is an alias for something in collections.abc

#

i assume they won't actually remove it until 3.8 is EOL which is not for a long time

upper tapir
#

so is it normal just like this?

from collections.abc import Iterable

def test(inpt: Iterable):
    ...
acoustic thicket
#

yeah

#

if you need an iterable of, say, ints, you can specify Iterable[int]

upper tapir
#

i see

blazing nest
#

Does anybody know a PyRight CI? Like Github Action?

#

If I remember correctly they're made in JavaScript/TypeScript and so is PyRight, so I would expect there to be one rather simply

oblique urchin
#

There is a GitHub Action, Jake Bailey built it

#

It's used in the typeshed CI

blazing nest
oblique urchin
#

yes

blazing nest
#

Public announcement: ParamSpec from typing_extensions does not work with Generics!

oblique urchin
rough sluiceBOT
#

typing_extensions/README.rst line 70

Certain types have incorrect runtime behavior due to limitations of older```
blazing nest
#

It is documented there

oblique urchin
#

Sorry, forgot about that

#

I guess we concluded it wasn't fixable

blazing nest
#

I really want to see if I can hack it. What would be the downside of having it be a subclass of TypeVar for example 🤔

#

Because that's the condition it fails to pass

#

ParamSpec fails the isinstance(x, TypeVar)

oblique urchin
#

Hm I think we considered that, check the PR where we added ParamSpec to typing_extensions

soft matrix
#

I saw the stuff about that couldn't you just add a metaclass that overwrites instance check?

blazing nest
soft matrix
#

For the list stuff and then have it inherit TypeVar

oblique urchin
#

oh yeah, I think the problem was that we had to make it inherit from list for other reasons, and we couldn't double-inherit from TypeVar and list

soft matrix
#

It was so it was valid as the first arg to Callable wasn't it?

oblique urchin
#

yes

#

But I don't think you can use a metaclass on X to make isinstance(X, list) true

soft matrix
#

Oh right yeah

oblique urchin
#

It might be possible with doing something sketchy with __class__ but that seemed too risky

blazing nest
#

Yeah LOL

#

Hahaha

oblique urchin
#

we did end up going with the __class__ hack

blazing nest
#

🚨 Crisis averted 🚨

soft matrix
#

ummm

class this(typing.TypeVar, _root=True):
    __class__ = list

isinstance(this("t"), typing.TypeVar)
Out[10]: True
isinstance(this("t"), list)
Out[11]: True
```seems to work on 3.9.3
oblique urchin
rough sluiceBOT
#

typing_extensions/src_py3/typing_extensions.py line 2286

class ParamSpec(list):```
soft matrix
#

Wait so it's not an issue

#

Oh the instance checks weren't passing for me cause I'm on 3.10.0

blazing nest
#

Wait doesn't it alias on versions where it exists?

#

Like an ```python
try:
from typing import ParamSpec
except ImportError:
class ParamSpec(list):
...

rough sluiceBOT
#

typing_extensions/src_py3/typing_extensions.py lines 2281 to 2286

if hasattr(typing, 'ParamSpec'):
    ParamSpec = typing.ParamSpec
else:

    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
    class ParamSpec(list):```
slender glen
rough sluiceBOT
#

typing_extensions/src_py3/typing_extensions.py lines 2333 to 2334

# Trick Generic __parameters__.
__class__ = TypeVar```
trim tangle
lapis zenith
trim tangle
#

well, the main word is 'considering'

lapis zenith
#

Hah, yeah

rapid shoal
oblique urchin
#

we had a meeting with folks from all the major type checkers about better callable syntax recently. Everyone is supportive, just needs to have a PEP written for it and the SC needs to accept it

trim tangle
#

that would be cool

#

btw, will typing have a type for a function (not just any callable?)

#

it seems to cause some issues because functions are descriptors

#

mypy treats Callable attributes weirdly, and pyright apparently doesn't let you make method decorators that produce methods

oblique urchin
#

No plans to address that part yet

hallow pollen
#

What type annotation should one use for *args and **kwargs?

oblique urchin
#

e.g. if your function accepts any number of ints, write *args: int

hallow pollen
#

Have you decided to adopt this channel?

oblique urchin
#

I don't know what adopting means

#

But I saw it got created and figured I could help 🙂

hallow pollen
hallow pollen
oblique urchin
#

there's currently no way to say that a specific kwarg should be of a specific type, but there's talk of using TypedDict for that

#

oh and ParamSpec already works

fierce ridge
# trim tangle

the roles should be reversed, the "virgin" side lists all the things you can't do easily with static types and no side effects, and the "chad" side would be a horrible perl one-liner

trim tangle
boreal ingot
trim tangle
#

TypeScript hints ...args as an array

#

e.g. here ts type ExtraArgs = [number, number, number] | [string] const f = (a: Thing, ...args: ExtraArgs) => { ... } f takes either Thing, number, number, number or Thing, string

boreal ingot
#

I guess in python to get that you might be able to use @overload

trim tangle
#

in Python it would be useful to do this with **kwargs, where e.g. 10 different functions take the same kwargs

#

there are two options:

  1. repeat the same arguments over and over
  2. just specify **kwargs: Any
    both options are bad
#

now there's ParamSpec, maybe it will solve some cases, but I"m not sure how

boreal ingot
trim tangle
#

yeah

boreal ingot
#

so something along the lines of ```py
P = ParamSpec("P", bound=Parent.init)

class Child(Parent):
def init(some_new_thing: int, *args: P.args, **kwargs: P.kwargs) -> None:
super().init(*args, **kwargs)```

#

depending on how it is decided that bound should work

trim tangle
#

well, this won't work because of self

boreal ingot
hallow pollen
fierce ridge
#

wat

hallow pollen
# fierce ridge wat

the PEP to have keyed arguments for __getitem__, __setitem__ __class_getitem__, etc was rejected and I'm still upset about it.

oblique urchin
oblique urchin
boreal ingot
#

Interesting, not sure which I like more, I guess ** makes the most sense

hallow pollen
fierce ridge
#

i like

#

i assume the custom syntax would be some special annotation

def f(**kwargs: Kwargs[MyTypedDict])
blazing nest
#
from typing import Generic, TypeVar

from typing_extensions import ParamSpec

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


class Testing(Generic[P, RT]):
    """I don't get why this fails."""
    ...
TypeError: Parameters to Generic[...] must all be type variables
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_6092/958801032.py in <module>
      7 
      8 
----> 9 class Testing(Generic[P, RT]):
     10     """I don't get why this fails."""
     11     ...

~\AppData\Local\Programs\Python\Python39\lib\typing.py in inner(*args, **kwds)
    266             except TypeError:
    267                 pass  # All real errors (not unhashable args) are raised below.
--> 268             return func(*args, **kwds)
    269         return inner
    270 

~\AppData\Local\Programs\Python\Python39\lib\typing.py in __class_getitem__(cls, params)
    978             # Generic and Protocol can only be subscripted with unique type variables.
    979             if not all(isinstance(p, TypeVar) for p in params):
--> 980                 raise TypeError(
    981                     f"Parameters to {cls.__name__}[...] must all be type variables")
    982             if len(set(params)) != len(params):

TypeError: Parameters to Generic[...] must all be type variables

Any idea what might've changed between 3.9.5 and 3.9.6 to affect this?

#

Because it doesn't fail on @rough sluice's eval, and it is on 3.9.6 when I am on 3.9.5

#

Aahhh, 3.9.5 is literally the only version this doesn't work on haha. (I use Windows and use the py command to run the same code on 3.6.8 as well as 3.5.4)

oblique urchin
#

are you sure you're running the same version of typing-extensions? there aren't any relevant changes in typing.py between 3.9.5 and 3.9.6

blazing nest
#

Yeah, I ran pip install -U typing_extensios (for each version)

fierce ridge
#

paramspec seems kinda broken and unsupported right now

oblique urchin
fierce ridge
oblique urchin
mossy leaf
#

How wil I typehint a parameter which will take in any iterable containing integers?

mossy leaf
#

thanks

fierce ridge
#

is there some way to tell mypy that a certain class is expected to implement a certain Protocol, and should be type-checked to that effect?

#

e.g. i want to have mypy enforce that A and B implement HasFoo:

from __future__ import annotations
from typing import Protocol, TypeVar

from _magic_ import implements_protocol

class HasFoo(Protocol):
    def foo(self) -> int: ...

@implements_protocol(HasFoo)
class A:
    def foo(self) -> int:
        return 5

@implements_protocol(HasFoo)
class B:
    def quack(self) -> float:
        return -3.3

is there no equivalent of @implements() supported by mypy? if not, how would one go about writing a plugin for something like this?

opal dagger
#

I need help writing a set of Websockets

fierce ridge
opal dagger
#

@fierce ridge yes, I was redirected here lol

#

this is what i have

connected: set((str, Websocket)) = set()
fierce ridge
#

set[str | Websocket]

#

if you're in python < 3.10, you need from __future__ import annotations to get the | syntax. otherwise you can do from typing import Union and then set[Union[str, Websocket]]

#

and in python < 3.9 you need to do from typing import Set and Set[Union[str, Websocket]]

flat viper
#

I need to use a for loop to predict the auc score for all my column values do let me know how can I do that
I have used list1 as my target value and the others I need column wise but the compiler is throwing an error
list1 = newdata['diagnosis']
for i in range(len(columns)):
auc = roc_auc_score(list1, newdata.columns[i])
print(auc)

opal dagger
#

ah [] @fierce ridge thanks

flat viper
#

Hi I need help regarding this

#

Was directed here

trim tangle
#

well, it will break if you'll try to evaluate the annotation

fierce ridge
#

i thought so too, but it does, only if you use the future import, and only in annotations

#

yeah exactly

fierce ridge
flat viper
oblique urchin
oblique urchin
fierce ridge
#

ah, interesting. it'd be nice if i could declare to mypy that "B and all subclasses of B must implement HasFoo

#

is that doable with a plugin?

oblique urchin
#

Honestly I don't know much about plugins, never written one. Seems likely to be possible though

fierce ridge
#

i'll have to look into it then. i know of some packages that implement plugins, e.g. Returns

#

!pypi returns

rough sluiceBOT
#

Make your functions return something meaningful, typed, and safe!

blazing nest
#

How do I use generics with unions?

#

Both of these classes are generics that take two arguments: Command = Union[SlashCommand, ContextMenuCommand]

#

I want to do Command[P, RT]

#

I can just do Command = Union[SlashCommand[P, RT], ContextMenuCommand[P, RT]] apparently

#

I did get a very long issue now that I don't even know where to start though: ```
Argument of type "SlashCommand[P@command, RT@command] | MessageCommand | UserCommand" cannot be assigned to parameter "command" of type "Command[Unknown, Unknown]" in function "register_command"
  Type "SlashCommand[P@command, RT@command] | MessageCommand | UserCommand" cannot be assigned to type "Command[Unknown, Unknown]"
    Type "SlashCommand[P@command, RT@command]" cannot be assigned to type "Command[Unknown, Unknown]"
      TypeVar "P@SlashCommand" is invariant
        Type "P@command" cannot be assigned to type "P@Command"
      "SlashCommand[P@command, RT@command]" is incompatible with "ContextMenuCommand[P@Command, RT@Command]"

The issue comes from this code: ```py
    def command(
        self,
        type: Union[CommandType, Callback[P, RT]] = CommandType.chat_input,
        *,
        name: str = MISSING,
        description: str = MISSING
    ) -> Union[Command[P, RT], Callable[[Callback[P, RT]], Command[P, RT]]]:
        def decorator(func: Callback[P, RT]) -> Command[P, RT]:
            if type is CommandType.chat_input:
                command = SlashCommand(func, name=name, description=description)
            elif type is CommandType.message:
                command = MessageCommand(func, name=name)
            elif type is CommandType.user:
                command = UserCommand(func, name=name)
            else:
                raise ValueError("Unknown value of 'type':", type)

            self.register_command(command)  # ERROR HERE
            return command

        if callable(type):
            return decorator(type)

        return decorator

P is ParamSpec and RT is a TypeVar

#

And register_command just looks like this: ```python
def register_command(self, command: Command) -> None:
"""Register a command handler to be called."""
self.commands[command.name] = command

fierce ridge
#

hah, nobody uses them really. except in enum, and it's really awkward there.

#

pyright error messages are really nasty

blazing nest
#

Why are basically "stateful" variants invariant with the "unknown" ones?

trim tangle
#

Can you show more definitions?

blazing nest
#

Of what exactly? Command or my generics classes?

#

The overloads for command()?

trim tangle
#

@blazing nest Where do you get this error?

blazing nest
#

It highlights command inside of self.register_command()

trim tangle
#

Does changing Command to Command[Any, Any] work?

blazing nest
#

The type of command just before it is Type of "command" is "SlashCommand[P@command, RT@command] | MessageCommand | UserCommand"

#

I got an idea, I can change Command to be Union[SlashCommand[P, RT], MessageCommand[P, RT], UserCommand[P, RT]]. I guess I was being lazy and using MessageCommand and UserCommand's parent type

trim tangle
blazing nest
#

I see the issue now I think, when I tried to do above I get "Expected no type arguments" because I had class MessageCommand(ContextMenuCommand): (without the variables)

#

Aaah, no?

#

What even is this haha: ```
Argument of type "SlashCommand[P@command, RT@command] | MessageCommand[P@command, RT@command] | UserCommand[P@command, RT@command]" cannot be assigned to parameter "command" of type "Command[Unknown, Unknown]" in function "register_command"
  Type "SlashCommand[P@command, RT@command] | MessageCommand[P@command, RT@command] | UserCommand[P@command, RT@command]" cannot be assigned to type "Command[Unknown, Unknown]"
    Type "SlashCommand[P@command, RT@command]" cannot be assigned to type "Command[Unknown, Unknown]"
      TypeVar "P@SlashCommand" is invariant
        Type "P@command" cannot be assigned to type "P@Command"
      "SlashCommand[P@command, RT@command]" is incompatible with "MessageCommand[P@Command, RT@Command]"
      "SlashCommand[P@command, RT@command]" is incompatible with "UserCommand[P@Command, RT@Command]"
    Type "MessageCommand[P@command, RT@command]" cannot be assigned to type "Command[Unknown, Unknown]"
      "MessageCommand[P@command, RT@command]" is incompatible with "SlashCommand[P@Command, RT@Command]"

trim tangle
#

can you perhaps upload this on github so that I can look at the error locally?

soft matrix
#

does pyright already implement variance of ParamSpecs?

blazing nest
#

I hope so? 🤞

soft matrix
#

i thought the variance for that was left for a future pep

trim tangle
#

I don't know how erictraut keeps up with the amount of new features and bug fixes

blazing nest
soft matrix
#

its in the pep i thought

blazing nest
soft matrix
#

eric's inhuman in the best way

blazing nest
fierce ridge
#

what is the @ in that output @blazing nest @trim tangle ?

soft matrix
#

its the name of the function/class that type var is used in

trim tangle
fierce ridge
#

ah, interesting

blazing nest
#

Since I can re-use type variables in different classes and they don't mean the same thing

trim tangle
#

reveal_type is type-level print debugging 😄

#

hmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm

blazing nest
#

Yeah exactly haha

#

It's like the same type.. and if you add [P, RT] to the argument then the error is just delayed to the insertion into the dict.

#

This is super weird too haha: ```
Type "P@command" cannot be assigned to type "P@Command"

#

I assume the former is referencing the variable?

trim tangle
#

I'd ask a question on the pyright repo in Discussion

#

and put # type: ignore for now

blazing nest
#

It's really odd, try removing the [P, RT] inside of the union. But now it fails at runtime

#
TypeError: typing.Union[wumpy.interactions.commands.slash.SlashCommand, wumpy.interactions.commands.context.MessageCommand, wumpy.interactions.commands.context.UserCommand] is not a generic class
#
c:\Users\%username%\Projects\wumpy\wumpy\interactions\commands\registrar.py in CommandRegistrar()
    110         name: str = MISSING,
    111         description: str = MISSING
--> 112     ) -> Union[Command[P, RT], Callable[[Callback[P, RT]], Command[P, RT]]]:
    113         """Register and create a new application command through a decorator.
    114 
soft matrix
#

you cant subscript a Union

#

what happens if you expand it to what it would be if you could subscript a union?

oblique urchin
#

You can subscript a Union if any of the members is subscriptable

soft matrix
#

i couldnt get it to work and afaict all of those should be subscriptable

#
class G(typing.Generic[typing.T]):
    ...
class H(typing.Generic[typing.T]):
    ...
typing.Union[G, H][int]
Traceback (most recent call last):
...
"/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/typing.py", line 292, in inner
    def decorator(func):
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/typing.py", line 989, in __getitem__
    super().__init__(origin, inst=inst, name=name)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/typing.py", line 211, in _check_generic
    tvars.extend([t for t in t.__parameters__ if t not in tvars])
TypeError: typing.Union[__main__.G, __main__.H] is not a generic class```
#

am i missing something

#

doesnt work with types.UnionType either

(G | H)[int]
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: 'types.Union' object is not subscriptable
oblique urchin
#

G is equivalent to G[Any]. Try G[T]

soft matrix
#

oh

#

yeah that works

blazing nest
#

What are the other static type checkers out there?

#

Except from PyRight and Mypy

#

I can't remember the rest, I want to see what other type checkers think about my issue above.

soft matrix
#

pytype and pyre are the other big(ish) two

blazing nest
#

Ah, I'll give those a try

atomic elbow
#

Does anyone know if there are known issues with flake8/mypy and forward referencing? Long story short: I'm passing an Optional['logging.Logger'] argument to my function. The logger gets defined elsewhere, so I use a forward reference, but mypy and flake8 won't leave me alone cryMeCarson

oblique urchin
#

Possibly within if TYPE_CHECKING

atomic elbow
#

Was hoping there'd be a prettier solution than TYPE_CHECKING, but alas.

oblique urchin
#

Restructure your code so you don't have circular dependencies 😄

atomic elbow
boreal ingot
#

well sometimes circular dependencies (when it comes to typing) are hard to avoid.

an example I can think of is having some object that stores a reference to its "parent".
like a discord bot, where client.py will need to import channel.py to use Channel, and Channel needs to contain a reference to a Client to be able to do stuff like channel.send().
at runtime Client is not needed in channel.py directly, but it is needed for proper type-hinting

oblique urchin
#

Yes, it's hard. You could get around it by defining a Protocol for the interface that Channel needs on Client, then using that as a type. But that doesn't necessarily make your code easier to understand.

boreal ingot
#

exactly, (tbh in this case you could have a seperate api.py that client.py uses at runetime, and channel.py for type-hinting)

fierce ridge
#

it'd be kind of nice if you could do this:

MagicNumber = '6'
MagicNumberString = AliasedLiteral[MagicNumber]

which would be equivalent to

MagicNumber = '6'
MagicNumberString = Literal['6']
tawny flame
#

When using a Literal for a method argument, is the method call supposed to throw an exception if the given value for that argument is not one of the defined values?

from typing import Literal

def test(arg: Literal["yes", "no", "maybe so"]):
    return True

test("check this out")

Because this works, as in I still get the return value of True even though the arg was not in the list of literals

trim tangle
#

@tawny flame Type hints don't do anything at runtime.

#

They exists purely for documentation, type checkers (like mypy and pyright), and inspection via the_function.__annotations__

tawny flame
#

Ah I see
So the better way to force a check at runtime would be to do something like this:

def test(arg: Literal["yes", "no", "maybe so"]):
    args_literal = ["yes", "no", "maybe so"]
    if arg in args_literal:
        return True
    raise ValueError('"{}" is not a valid arg value.'.format(arg))

print(test("check this out"))

which correctly has an error in pyright and throws the ValueError exception at runtime

fierce ridge
#

the way type checking works is that you have to "prove" to the compiler that the type is a certain thing

#

theoretically something like this would be valid:

valid_args = ["yes", "no", "maybe so"]
ValidArg = Literal["yes", "no", "maybe so"]

def _test_impl(arg: ValidArg) -> str:
    ...

def test_safe(arg: str) -> str:
    if arg in valid_args:
        return _test_impl(arg)
    else:
        raise ValueError()

but in practice the type checker isn't smart enough to understand this kind of thing without PEP 647 "type guards" https://www.python.org/dev/peps/pep-0647/

#

an example that does actually work is this:

def _test_impl(arg: Path):
    ...

def test_safe(arg: str | Path):
    if isinstance(arg, Path):
       return _test_impl(arg)
    else:
        raise ValueError() 

because mypy has hard-coded behavior to understand isinstance

boreal ingot
fierce ridge
#

yeah it basically lets you avoid having to write a mypy plugin for custom type refining behavior

boreal ingot
#

every day that passes I just cant wait for 3.10 and all the cool new typing stuff

fierce ridge
#

typeguards are available in typing-extensions and are supported by mypy .910 afaik

boreal ingot
#

oh nice!

fierce ridge
#
from typing import Literal, TypeGuard

valid_args = {"yes", "no", "maybe so"}
ValidArg = Literal["yes", "no", "maybe so"]


def is_valid_arg(arg: str) -> TypeGuard[ValidArg]:
    return arg in valid_args


def _test_impl(arg: ValidArg) -> str:
    ...


# Does typecheck
def test_safe_1(arg: str) -> str:
    if is_valid_arg(arg):
        return _test_impl(arg)
    else:
        raise ValueError()


# Does not typecheck
def test_safe_2(arg: str) -> str:
    if arg in valid_args:
        return _test_impl(arg)
    else:
        raise ValueError()
boreal ingot
#

I dont have any places I have use for them atm, but I am just loving the concept already 😄

fierce ridge
#

statically checking if something is a positive integer for example

boreal ingot
tawny flame
#

On a different note, is there a guide/tutorial explaining the use of Generics & TypeVars? I don't really understand why/when to use something like _T = TypeVar("_T") and then have Generics[_T] somewhere later in code

#

Tutorials I came across just assume you know what they're saying regarding those topics

boreal ingot
fierce ridge
#

if i find one that's python-oriented i'll let you know

#

the general idea is that a TypeVar is a placeholder in a type signature

tawny flame
#

I come from a C++ background but we glossed over templates in uni

fierce ridge
#

well i don't know much of anything about C++ so i'm no good there 😆

#

tldr: if you have def foo(x: T) -> T for some T = TypeVar('T'), it means that T can be any type, as long as both Ts are the same type

oblique urchin
fierce ridge
#

yeah, list would be a "generic"

#

so you can write functions like this:

from collections.abc import Callable, Iterable
from typing import TypeVar

A = TypeVar('A')
B = TypeVar('B')

def maplist(f: Callable[[A], B], data: Iterable[A]) -> List[B]:
    return [f(elem) for elem in data]
#

A and B are both "any type", but all appearances of A have to be the same type, and all appearances of B have to be the same type

#

it's the same idea as regular variables in programming

#

if you write a program (x + y) / x, it doesn't matter what x and y are, as long as both appearances of x refer to the same thing

indigo locust
#

Which generic would I use to designate something that contains other things, but is not necessarily iterable

#

Collection?

tawny flame
#

Collection is still iterable IIRC

boreal ingot
#

Container

oblique urchin
tawny flame
#

Actually collection should work since it inherits from Iterable, Sized, and Container

boreal ingot
indigo locust
#

I'm gonna play around with an @overload decorator, which means creating a FunctionGroup class

#

The FunctionGroup will contain functions of course, but its not exactly iterable (not by its core nature — it always could be iterable though)

oblique urchin
#

Sounds like you should just use FunctionGroup as an annotation?

indigo locust
#

?

soft matrix
#

are you making a container for functions?

indigo locust
#

Yeah

#

Just toying around, but, I'd imagine the first @overload on a function with the name somefunction would return a function group. Every subsequent @overload on a function with that name would be added to that function group

#

I know I'm slipping hairs—I'll just use Iterable for now

oblique urchin
#

Are you implementing a type checker?

#

@typing.overload doesn't really work like you describe (it doesn't do anything at runtime)

indigo locust
#

No, a decorator, to actually implement overloading

#

Just for kicks, mind you, just to see

soft matrix
#

if you want a sort of runtime overload you might be able to use functools.singledispatch

indigo locust
#

It shouldn't be too hard to rig up, assuming pattern matching works the I think it works

#

Just need to PM *args and **kwargs, and somehow map their patterns to the functions contained in the function group. I'm sure its all crossed our minds at some point

bleak wind
#

what wud be the typehint for a function returning either a str or raises an exception? i thought of Union[str, NoReturn] or Optional[str] or something else

#

the first one looks stupid to me btw

trim tangle
#

@bleak wind You can't specify exceptions in type hints.

#

So just str

bleak wind
#

but if an exception occurred there wont be any return

trim tangle
bleak wind
#

i dont, i was just curious

trim tangle
#

Java has a thing called "checked exceptions" where you can specify the exceptions thrown, and AFAICT people had a lot of pain with it

bleak wind
#

i see

brisk hedge
brisk hedge
#

For the most part, NoReturn is only useful to show static analyzers that a function will never return

bleak wind
#

hmm fine, ty

acoustic thicket
#

@bleak wind
fwiw most docstring styles specify a way of mentioning the exceptions that can be raised, such as:

def f():
  """
  blah blah
  
  :raises Exception: explain when Exception is raised
  """
  ...
bleak wind
#

yeah

fierce ridge
#

Nim has (optional) checked exceptions

#

It's a great feature because it's opt-in

#

At least as far as I know from my beginner level usage so far, it will only check exceptions if you annotate a function with the raises pragma

#

But python doesn't distinguish between raising an exception and looping forever, which really are two different effects

#

I'd love RunsForever/Diverges and Raises, both of which are subclasses of NoReturn

#

The problem with raising annotations in python is that damn near everything can raise an exception

#

And there isn't a good way to prove statically to a type checker that you know an exception will not be raised

#

I had that problem in Nim as well with fmt strings, where even though I as a human knew statically that it would not raise, the compiler did not

trim tangle
#

it would also make the type checkers for Python even more complex

blazing nest
#

Annoying to have the type checker complain "Ahhh this can raise a Value error" when you know that it won't unless something has gone terribly wrong

fierce ridge
#

wont_raise(ValueError, foo)(1, 2)

#

Are python type checkers significantly more complicated than other type checkers?

void panther
#

How are exceptions like SystemExit or KeyboardInterrupt handled if you were to typehint exceptions?

#

or I guess any exception as there is a func for raising an exception in a thread in the c api

tulip hearth
#

So for example List from typing is just for type hinting right? It doesnt do anything other than that?

rapid shoal
#

yep

keen girder
#

Current Python hinting, though, covers most of the functionality of typing.List I believe

rapid shoal
#

yeah, in 3.9+ it's not needed

tulip hearth
#

Oh then i can safely omit it in my dict to object implementation, thnx

rapid shoal
#

type hints are good though, you should have them

tulip hearth
#

No i mean im using em but my implentation is a bit different its similar to golang's json unmarshal u use structs and structs can have values and values can be other structs

So in python most basic implementation of dict to object would create instances of the same class but my implementation allows different classes being assigned to different values just like go so i have to check if like my value is from the typing module and it has a class as the input and if i have anything to map to that class...
A bit complicated

fierce ridge
#

For that matter if I don't know how Nim handles it

#

Maybe there is a certain category of "i/o errors" that aren't checked because they can happen literally anywhere by design

rapid shoal
#

fix error mentioned this before, but java has this thing where you put throws Error if your method can throw

fierce ridge
#

Yeah that's what I'm talking about, "checked exceptions"

#

I would think that mypy could make a concession to practicality and allow certain exceptions to remain unchecked by default, like SystemExit, unless you use a pragma or config option

boreal ingot
rough sluiceBOT
#

@boreal ingot :white_check_mark: Your eval job has completed with return code 0.

001 | False
002 | True
boreal ingot
#

also imo they should ignore TypeError as in theory it is their job to catch that error before runtime (meaning a function should not raise it if it type checks)

brisk hedge
#

Library code should not assume the users pass correct types imo

#

So I disagree

boreal ingot
#

and imo I would trust my users to actually respect my type-hints