[issue45665] Problems caused by isinstance(list[int], type) returning True

2021-12-27 Thread Joseph Perez


Joseph Perez  added the comment:

There is also https://bugs.python.org/issue44293 about inspect.isclass

--
nosy: +joperez

___
Python tracker 
<https://bugs.python.org/issue45665>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue45418] types.UnionType is not subscriptable

2021-10-10 Thread Joseph Perez


Joseph Perez  added the comment:

Indeed, sorry, my example was bad. My library was raising at several place, and 
I've extrapolated about generic substitution.

I've indeed other substitutions (without `TypeVar`), and because they were 
failing, I've assumed that all of my substitutions were failing; I was wrong 
about generic one.

For example, if I want to substitute `int | Collection[int]` to `int | 
list[int]`, I will have to replace `types.UnionType` by `typing.Union` or use 
`reduce`, while it was not necessary in 3.9 where I could just write 
`get_origin(tp)[new_args]`.

So I'll have to add some `if` in my code.

--
stage:  -> resolved
status: pending -> closed

___
Python tracker 
<https://bugs.python.org/issue45418>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue45418] types.UnionType is not subscriptable

2021-10-09 Thread Joseph Perez


New submission from Joseph Perez :

`types.UnionType` is not subscriptable, and this is an issue when type 
manipulations are done.

A common maniputation I've to do is to substitute all the `TypeVar` of a 
potential generic type by their specialization in the given context.
For example, given a class:
```python
@dataclass
class Foo(Generic[T]):
bar: list[T]
baz: T | None
```
in the case of `Foo[int]`, I want to compute the effective type of the fields, 
which will be `list[int]` and `int | None`.
It could be done pretty easily by a recursive function:
```python
def substitute(tp, type_vars: dict):
origin, args = get_origin(tp), get_args(tp)
if isinstance(tp, TypeVar):
return type_vars.get(tp, tp)
elif origin is Annotated:
return Annotated[(substitute(args[0], type_vars), *args[1:])]
else:
return origin[tuple(substitute(arg) for arg in args)]  # this line 
fails for types.UnionType
```

And this is not the only manipulation I've to do on generic types. In fact, all 
my library (apischema) is broken in Python 3.10 because of `types.UnionType`.
I've to substitute `types.UnionType` by `typing.Union` everywhere to make 
things work; `types.UnionType` is just not usable for dynamic manipulations.

I've read PEP 604 and it doesn't mention if `types.UnionType` should be 
subscriptable or not. Is there a reason for not making it subscriptable?

--
messages: 403554
nosy: joperez
priority: normal
severity: normal
status: open
title: types.UnionType is not subscriptable
versions: Python 3.10

___
Python tracker 
<https://bugs.python.org/issue45418>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue44353] PEP 604 NewType

2021-06-08 Thread Joseph Perez


New submission from Joseph Perez :

`typing.NewType` doesn't support PEP 604.

```
>>> import typing
>>> A = typing.NewType("A", int)
>>> B = typing.NewType("B", str)
>>> A | B
Traceback (most recent call last):
  File "", line 1, in 
TypeError: unsupported operand type(s) for |: 'function' and 'function'
```

--
messages: 395359
nosy: joperez
priority: normal
severity: normal
status: open
title: PEP 604 NewType
versions: Python 3.10

___
Python tracker 
<https://bugs.python.org/issue44353>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue44293] PEP 585 breaks inspect.isclass

2021-06-03 Thread Joseph Perez


Joseph Perez  added the comment:

@Jelle Zijlstra Thank you for the explanation.

> The current implementation of GenericAlias has been around for a few releases 
> by now, though, so that change might break some use cases.

I agree that a "fix" could have unexpected side-effect, my issue comes quite 
late indeed. By the way, Python typing is so much unstable (every version 
breaks the previous one), it's very complicated to write code that support 
multiple versions, so whatever the typing internal implementation, we must 
adapt.

> This is not true; it is the same for e.g. `set[int]`. Unless you meant 
> something else here.

I have chosen `list[int]` as an example of `types.GenericAlias` introduced by 
PEP 585 (i could have chosen `set[int]` or `collections.abc.Collection[int]`). 
But other generic aliases, e.g. `typing.List[int]` or `MyClass[int]` (where 
`MyClass` inherits `Generic[T]`), are not instances of `type`.

> @Joseph Perez, is there a specific library or pattern that is broken by this?

Because `issubclass` requires a "class" as arg 1, I use the pattern `if 
isinstance(tp, type) and issubclass(tp, SomeClass)` (`isinstance` check being 
equivalent to `inspect.isclass`). With PEP 585, it breaks for `list[int]` and 
other builtin generic aliases.

> FWIW I did think rather carefully about which attributes to delegate or not, 
> and delegating __class__ was intentional.

I don't have the context of the decision, so I can quite understand that 
delegating `__class__` was the right thing to do, especially when `__mro__` and 
other `type` attributes are also delegated. 
I mainly wanted to highlight this side effect, especially on the pattern 
mentioned above. (My issue title is a little bit excessive in this regard)

But as I've written, I've already so many wrappers to maintain compatibility 
between Python versions of typing that I can write a new one to handle this 
particularity of PEP 585. So this issue is not critical to me.

--

___
Python tracker 
<https://bugs.python.org/issue44293>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue44293] PEP 585 breaks inspect.isclass

2021-06-02 Thread Joseph Perez


New submission from Joseph Perez :

PEP 585 has the side-effect of making `list[int]` an instance of `type`. This 
is not the case for other generic aliases.

It also implies that `inspect.isclass(list[int]) is True`, while `list[int]` is 
not a class; as a proof of this statement `issubclass(list[int], 
collections.abc.Collection)` raises `TypeError: issubclass() arg 1 must be a 
class`.

By the way, there is the awkward thing of having `isinstance(list[int], type) 
is True` while `issubclass(type(list[int]), type) is False`.

--
messages: 394950
nosy: joperez
priority: normal
severity: normal
status: open
title: PEP 585 breaks inspect.isclass
versions: Python 3.9

___
Python tracker 
<https://bugs.python.org/issue44293>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue42921] Inferred Optional type of wrapper function arguments

2021-01-12 Thread Joseph Perez


New submission from Joseph Perez :

`typing.get_type_hints` gives a different result for a wrapper created with 
`functools.wraps` in case of inferred `Optional` arguments (when the default 
value of the argument is None)

```python
from functools import wraps
from typing import get_type_hints

def foo(bar: int = None): ...

@wraps(foo)
def foo2(*args, **kwargs): ...

print(get_type_hints(foo))  # {'bar': typing.Optional[int]}
print(get_type_hints(foo2))  # {'bar': }
```

This is because `get_type_hints` use the defauts of the wrapper (`foo2`) and 
not those of the wrapped function (`foo`).
This is not consistent with some other tools like `inspect.signature` which 
gives the same signature (and thus same default argument) for the wrapped 
function and its wrapper.

I think this case has simply been forgotten in the resolution of 
https://bugs.python.org/issue37838 (fixing `get_type_hints` not taking `wraps` 
in account at all), because inferred `Optional` is a kind deprecated feature 
(https://github.com/python/typing/issues/275).

--
messages: 385005
nosy: joperez
priority: normal
severity: normal
status: open
title: Inferred Optional type of wrapper function arguments
type: behavior

___
Python tracker 
<https://bugs.python.org/issue42921>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41644] builtin type kwargs

2020-08-26 Thread Joseph Perez


Joseph Perez  added the comment:

That's why it's not an interpreter issue but a lack in the official 
documentation where the signature is documented.
I quote https://docs.python.org/3/library/functions.html#type:
> class type(object)
> class type(name, bases, dict)

The second line should be "class type(name, bases, dict, **kwargs)".

(I've mentioned Pycharm and Mypy, but i think it's a kind of side-effect of the 
incomplete official documentation on which is based their typeshed)

In fact, I've raised this issue because I've found myself needing to 
instantiate a class using `type` and kwargs, and I've not found in the 
documentation an example of it.

--

___
Python tracker 
<https://bugs.python.org/issue41644>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41644] builtin type kwargs

2020-08-26 Thread Joseph Perez


New submission from Joseph Perez :

Class definition can have kwargs which are used by `__init_subclass__` (and 
`__prepare__`).
However, passing these kwargs using `type` builtin function instead of class 
definition syntax is not documented; kwargs are not mentioned in the function 
signature.
https://docs.python.org/3/library/functions.html#type

However, passing kwargs to `type` works:
```python
class Foo:
def __init_subclass__(cls, **kwargs):
print(kwargs)
Bar = type("Bar", (Foo,), {}, bar=None) # mypy and Pycharm complain
#> {'bar': None}
```

By the way, the possibility to pass kwargs in `type` call is not documented  in 
https://docs.python.org/3/reference/datamodel.html#customizing-class-creation 
too.

--
assignee: docs@python
components: Documentation
messages: 375936
nosy: docs@python, joperez
priority: normal
severity: normal
status: open
title: builtin type kwargs
versions: Python 3.10, Python 3.6, Python 3.7, Python 3.8, Python 3.9

___
Python tracker 
<https://bugs.python.org/issue41644>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41370] PEP 585 and ForwardRef

2020-07-22 Thread Joseph Perez


Joseph Perez  added the comment:

However, PEP 563 will not solve the recursive type alias issue like `A = 
list["A"]` but this is a minor concern.

--

___
Python tracker 
<https://bugs.python.org/issue41370>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41370] PEP 585 and ForwardRef

2020-07-22 Thread Joseph Perez


New submission from Joseph Perez :

PEP 585 current implementation (3.10.0a0) differs from current Generic 
implementation about ForwardRef, as illustrated bellow:
```python
from dataclasses import dataclass, field
from typing import get_type_hints, List, ForwardRef

@dataclass
class Node:
children: list["Node"] = field(default_factory=list)
children2: List["Node"] = field(default_factory=list)

assert get_type_hints(Node) == {"children": list["Node"], "children2": 
List[Node]}
assert List["Node"].__args__ == (ForwardRef("Node"),)
assert list["Node"].__args__ == ("Node",) # No ForwardRef here, so no 
evaluation by get_type_hints
```
There is indeed no kind of ForwardRef for `list` arguments. As shown in the 
example, this affects the result of get_type_hints for recursive types handling.

He could be "fixed" in 2 lines in `typing._eval_type` with something like this :
```python
def _eval_type(t, globalns, localns, recursive_guard=frozenset()):
if isinstance(t, str):
t = ForwardRef(t)
if isinstance(t, ForwardRef):
   ...
```
but it's kind of hacky/dirty.

It's true that this issue will not concern legacy code, 3.9 still being not 
released. So developers of libraries using get_type_hints could add in their 
documentation that `from __future__ import annotations` is mandatory for 
recursive types with PEP 585 (I think I will do it).

By the way, Guido has quickly given his opinion about it in PR 21553: "We 
probably will not ever support this: importing ForwardRef from the built-in 
generic alias code would be problematic, and once from __future__ import 
annotations is always on there's no need to quote the argument anyway." (So 
feel free to close this issue)

--
messages: 374105
nosy: BTaskaya, eric.smith, gvanrossum, joperez, levkivskyi, lukasz.langa, 
vstinner
priority: normal
severity: normal
status: open
title: PEP 585 and ForwardRef
type: behavior
versions: Python 3.9

___
Python tracker 
<https://bugs.python.org/issue41370>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41341] Recursive evaluation of ForwardRef (and PEP 563)

2020-07-19 Thread Joseph Perez


Joseph Perez  added the comment:

Ok, I admit that I did not think about recursive type when proposing this "fix".
I've tried an implementation that just stop when recursion is encountered in a 
PR.

--

___
Python tracker 
<https://bugs.python.org/issue41341>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41341] Recursive evaluation of ForwardRef (and PEP 563)

2020-07-19 Thread Joseph Perez


Change by Joseph Perez :


--
keywords: +patch
pull_requests: +20699
stage:  -> patch review
pull_request: https://github.com/python/cpython/pull/21553

___
Python tracker 
<https://bugs.python.org/issue41341>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue41341] Recursive evaluation of ForwardRef (and PEP 563)

2020-07-19 Thread Joseph Perez

New submission from Joseph Perez :

(This issue is already broached in https://bugs.python.org/issue38605, and a in 
some way in https://bugs.python.org/issue35834, but only as a secondary 
subject, that's why I've opened a ticket on this particular issue)

ForwardRef of ForwardRef are not currently evaluated by get_type_hints, only 
the first level is, as illustrated in these examples:
```python
from typing import ForwardRef, Optional, get_type_hints
def func(a: "Optional[\"int\"]"):
pass
assert get_type_hints(func)["a"] == Optional[ForwardRef("int")] 
# one would expect get_type_hints(func)["a"] == Optional[int] 
```
```python
from __future__ import annotations
from typing import ForwardRef, Optional, get_type_hints
def func(a: Optional["int"]):
pass
assert get_type_hints(func)["a"] == Optional[ForwardRef("int")]
# one would expect get_type_hints(func)["a"] == Optional[int] (which is the 
case without the import of __future__.annotations!)
```
On the one hand I find this behavior quite counter-intuitive; I rather think 
ForwardRef as kind of internal (and wonder why there is no leading underscore, 
like _GenericAlias where it's used) and I don't understand the purpose of 
exposing it as the result of the public API get_type_hints. By the way, if 
ForwardRef can be obtained by retrieving annotations without get_type_hints, 
stringified annotations (especially since PEP 563) make get_type_hints kind of 
mandatory, and thus make ForwardRef disappeared (only at the first level so …)

On the other hand, the second example show that adoptions of postponed 
annotations can change the result of get_type_hints; several libraries relying 
of get_type_hints could be broken.

An other issue raised here is that if these ForwardRef are not evaluated by 
get_type_hints, how will be done their evaluatation by the user? It would 
require to retrieve some globalns/localns — too bad, it's exactly what is doing 
get_type_hints. And if the ForwardRef is in a class field, the class 
globalns/localns will have to be kept somewhere while waiting to encounter 
these random ForwardRef; that's feasible, but really tedious.

Agreeing with Guido Von Rossum (https://bugs.python.org/msg370232), this 
behavior could be easily "fixed" in get_type_hints.
Actually, there would be only one line to change in ForwardRef._evaluate:
```python
# from
self.__forward_value__ = _type_check(
eval(self.__forward_code__, globalns, localns),
"Forward references must evaluate to types.",
is_argument=self.__forward_is_argument__)
# to
self.__forward_value__ = _eval_type(
_type_check(
eval(
self.__forward_code__, globalns, localns),
"Forward references must evaluate to types.",
is_argument=self.__forward_is_argument__,
),
globalns,
localns,
)

And if this fix could solve the "double ForwardRef" issue mentionned in 
https://bugs.python.org/issue38605, it would also resolve 
https://bugs.python.org/issue35834 in the same time, raising NameError in case 
of unknown ForwardRef with postponed annotation.

--
messages: 373960
nosy: BTaskaya, eric.smith, gvanrossum, joperez, levkivskyi, lukasz.langa, 
vstinner
priority: normal
severity: normal
status: open
title: Recursive evaluation of ForwardRef (and PEP 563)
type: behavior

___
Python tracker 
<https://bugs.python.org/issue41341>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com