Repository navigation
Closed
Labels
3.10 (EOL)end of lifeend of life3.11only security fixesonly security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-typing
Description
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee = None
closed_at = None
created_at = <Date 2021-07-31.10:25:13.991>
labels = ['library', '3.10', '3.11']
title = 'Substitution of ParamSpec in Concatenate'
updated_at = <Date 2022-02-20.09:11:54.505>
user = 'https://github.com/serhiy-storchaka'bugs.python.org fields:
activity = <Date 2022-02-20.09:11:54.505>
actor = 'med2277'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Library (Lib)']
creation = <Date 2021-07-31.10:25:13.991>
creator = 'serhiy.storchaka'
dependencies = []
files = []
hgrepos = []
issue_num = 44791
keywords = ['patch']
message_count = 9.0
messages = ['398632', '398645', '398661', '411862', '411863', '411926', '411933', '413534', '413573']
nosy_count = 9.0
nosy_names = ['gvanrossum', 'serhiy.storchaka', 'JelleZijlstra', 'miss-islington', 'sobolevn', 'kj', 'AlexWaygood', 'cdce8p', 'med2277']
pr_nums = ['27518', '30959', '30969']
priority = 'normal'
resolution = None
stage = 'patch review'
status = 'open'
superseder = None
type = None
url = 'https://bugs.python.org/issue44791'
versions = ['Python 3.10', 'Python 3.11']Metadata
Metadata
Assignees
Labels
3.10 (EOL)end of lifeend of life3.11only security fixesonly security fixesstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-typing
Activity
serhiy-storchaka commentedon Jul 31, 2021
Substitution of ParamSpec in Concatenate produces weird results:
But all these results are invalid:
I expect that
Concatenate[str, Concatenate[int, P]] -> Concatenate[str, int, P]
Concatenate[str, P][Concatenate[int, P]] -> Concatenate[str, int, P]
Concatenate[str, [int, dict]] -> [str, int, dict]
Concatenate[str, P][[int, dict]] -> [str, int, dict]
Concatenate[str, ...]
Concatenate[str, P][...] -> Concatenate[str, ...]
Fidget-Spinner commentedon Jul 31, 2021
Should Concatenate support substitution to begin with? PEP-612 doesn't say anything, and I am fairly certain it's a special typing form, not a generic. So I don't really understand what it means to substitute Concatenate.
Then again, Callable with a nested Concatenate can be substituted, and we currently implement that by using Concatenate's substitution behavior as proxy. But again, the meaning isn't clear to me.
I also noticed this strange part in PEP-612 about user-defined generic classes:
"
Generic[P]makes a class generic onparameters_expressions(when P is a ParamSpec)":...
class X(Generic[T, P]):
f: Callable[P, int]
x: T
The grammar for
parameters_expressionis:parameters_expression ::=
| "..."
| "[" [ type_expression ("," type_expression)* ] "]"
| parameter_specification_variable
| concatenate "["
type_expression ("," type_expression)* ","
parameter_specification_variable
"]"
I'm very confused. Does this mean Concatenate is valid when substituting user generics? Maybe I should ask the PEP authors?
My general sense when I implemented the PEP was that it was intended primarily for static type checking only. IMO, runtime correctness wasn't its concern.
serhiy-storchaka commentedon Jul 31, 2021
My understanding is that type expression is valid when substitute TypeVar and parameters expression is valid when substitute ParamSpec.
serhiy-storchaka commentedon Jan 27, 2022
New changeset ecfacc3 by Serhiy Storchaka in branch 'main':
bpo-44791: Fix substitution of ParamSpec in Concatenate with different parameter expressions (GH-27518)
ecfacc3
miss-islington commentedon Jan 27, 2022
New changeset 89db090 by Miss Islington (bot) in branch '3.10':
bpo-44791: Fix substitution of ParamSpec in Concatenate with different parameter expressions (GH-27518)
89db090
serhiy-storchaka commentedon Jan 27, 2022
PR 27518 fixes a substitution of a ParamSpec variable with a Concatenate nad a list of types. It is not specified explicitly in PEP-612, but it does not contradict it.
PR 30969 makes an ellipsis be valid as the last argument of Concatenate to fix a substitution of a ParamSpec variable with an ellipsis. It contradicts with PEP-612 which allows only a ParamSpec variable there.
gvanrossum commentedon Jan 27, 2022
Before you start supporting things that contradict PEP-612 this should be discussed on typing-sig (or in the python/typing tracker).
Honestly I'd feel more comfortable if there was agreement on typing-sig with your previous PRs in this issue as well; "it does not contradict it" is not a ringing endorsement. :-)
JelleZijlstra commentedon Feb 19, 2022
I'm looking at #30969 and I'm not sure what the motivation for the change is. PEP-612 is quite precise here (https://www.python.org/dev/peps/pep-0612/#id1) and allows only a ParamSpec as the last argument to Concatenate.
What is the use case for using ... as the last argument? What should it mean to a static type checker?
med2277 commentedon Feb 20, 2022
Concatenate[int, ...] I would interpret as a function signature with first argument int, followed by arbitrary arguments afterwards. I haven't run into this case, but ... is allowed in other spots Paramspec is allowed currently.
Foo[...] # Allowed
Callable[..., None] # Allowed
Are there any other places a paramspec is allowed? Generic type argument, first argument to callable, last to concatenate, anything else?
I'm unaware of any type checking use case for Concatenate[int, ...]. You can practically get same thing by using a paramspec variable without using it elsewhere so that it captures, but then effectively discards that information.
feels like those two signatures should encode same information.
JelleZijlstra commentedon Apr 29, 2022
I still haven't seen a compelling reason to allow substituting
...in Concatenate, so I think we should leave things unchanged here and close #30969.JelleZijlstra commentedon Apr 29, 2022
I just talked to @pradeep90 and he is actually on board with allowing this; it seems like a reasonable generalization, and we can allow it at runtime already pending type checker support.
gvanrossum commentedon Apr 29, 2022
Mind summarizing the use case here? Maybe give a simple example?
JelleZijlstra commentedon Apr 29, 2022
Pradeep showed me an email thread from a few months ago (I think you were also on it) that showed a use case. I don't recall the exact code, but basically it would allow pyre to infer
Callable[Concatenate[T1, ...], T2]instead ofCallable[..., T2]in some cases.gvanrossum commentedon Apr 29, 2022
Sure. I wish we had allowed spelling that as
Callable[[T1, ...], T2]-- theConcatenateseems to do nothing except make everything longer and less readable. :-(pradeep90 commentedon May 1, 2022
@JelleZijlstra Reproducing the email thread (from August 2021)… for future reference:
Mark Mendoza said:
My reply to Serhiy's question:
Concatenate and
...and other edge casesAs Mark mentioned, Pyre's current behavior is to convert
Callable[Concatenate[int, P], str][...]toCallable[..., str].If we want to allow
Callable[Concatenate[int, ...], str], then we'd probably have to extend the grammar in PEP 612 to allow anyparameters_expressionas the second argument toConcatenateinstead of justparameter_specification_variable. That will make the following cleanly interchangeable in the syntax:P,[int, str],..., andConcatenate[int, P].before:
after:
This would allow things like
Concatenate[int, str, P]Concatenate[int, str, ...]Concatenate[int, str, [str, bool]]Concatenate[int, str, Concatenate[str, bool, P]](3) should be equivalent to
[int, str, str, bool]and (4) should be equivalent toConcatenate[int, str, str, bool, P].This would be more flexible than the previous behavior. It would handle different edge cases equivalently regardless of whether they came from alias substitution or from direct user-written types. But it would also require more parsing work from typecheckers. Pyre's current behavior is simpler but less flexible. This might be worth checking on in typing-sig since people from other typecheckers may have opinions.
I'm ok with adding these items to the PEP since they're not explicitly stated. Happy to extend the reference implementation in Pyre if we reach a consensus on the
...case.serhiy-storchaka commentedon May 2, 2022
I do not think we need this.
We need a way to express the result of
parameter_specification_variablesubstitution withparameters_expression, that's all. It is not necessary that substitution gives the same type. For example,Union[T, int][int]is justint, notUnion[int].The only problem was with ellipsis. There is no valid syntax to express the result. The specification should be extended to allow either
Concatenate[int, str, ...], or[int, str, ...], or some other syntax.AlexWaygood commentedon Mar 15, 2023
I believe that we eventually arrived on consensus here that the changes made were acceptable. (Apologies if this summary is inaccurate.) Is there anything left to do here from a CPython/runtime perspective, or can this now be closed?
Discussions about whether the PEP should be updated should probably continue over at
python/typing, if there are still unresolved questions.AlexWaygood commentedon Mar 23, 2023
Closing for now. Feel free to reopen (or open a new issue at python/typing) if there's still more to discuss :)