Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 40 additions & 26 deletions peps/pep-0827.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Status: Draft
Type: Standards Track
Topic: Typing
Created: 27-Feb-2026
Python-Version: 3.15
Python-Version: 3.16
Post-History: 02-Mar-2026


Expand Down Expand Up @@ -376,18 +376,37 @@ this PEP.

We introduce a ``Param`` type that contains all the information about a function param::

class Param[N: str | None, T, Q: ParamQuals = typing.Never]:
class Param[
N: str | None,
T,
K: ParamKind = typing.Never,
D = typing.Never,
]:
pass

ParamQuals = typing.Literal["*", "**", "default", "keyword"]
ParamKind = typing.Literal["*", "**", "keyword", "positional"]

type PosParam[N: str | None, T] = Param[N, T, Literal["positional"]]
type PosDefaultParam[N: str | None, T] = Param[N, T, Literal["positional", "default"]]
type DefaultParam[N: str, T] = Param[N, T, Literal["default"]]
type PosParam[T] = Param[None, T, Literal["positional"]]
type PosDefaultParam[T] = Param[None, T, Literal["positional"], T]
type DefaultParam[N: str, T] = Param[N, T, typing.Never, T]
type NamedParam[N: str, T] = Param[N, T, Literal["keyword"]]
type NamedDefaultParam[N: str, T] = Param[N, T, Literal["keyword", "default"]]
type ArgsParam[T] = Param[Literal[None], T, Literal["*"]]
type KwargsParam[T] = Param[Literal[None], T, Literal["**"]]
type NamedDefaultParam[N: str, T] = Param[N, T, Literal["keyword"], T]
type ArgsParam[T] = Param[None, T, Literal["*"]]
type KwargsParam[T] = Param[None, T, Literal["**"]]


The argument ``K``, of type ``ParamKind``, represents the parameter
kind of the parameter. The default "positional or named" kind of
argument is represented as ``Never``. It is an error to create
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like a strange use of Never. I would try to align these kinds with the ones in inspect: https://docs.python.org/3.15/library/inspect.html#inspect.Parameter.kind

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a planned follow-up to change the literals to actually be enums modeled directly after Parameter.kind that I wanted to keep separate from this. But I guess I can make t he default one "positional-or-named" now also while still strings

``Callable`` with a ``Param`` containing multiple kinds unioned
together.

The argument ``D`` carries the type of the parameter's default, if one
exists, and is ``Never`` otherwise. When the default value is a
literal (e.g. ``None``, an int, a string, an enum member), ``D`` may
be a ``Literal`` carrying that value. (Having it be such a ``Literal``
has no effect other than to make it available to introspection and
potentially for diagnostics.)

We also introduce a ``Params`` type that wraps a sequence of ``Param``
types, serving as the first argument to ``Callable``::
Expand Down Expand Up @@ -419,17 +438,18 @@ as::
Params[
Param[Literal["a"], int, Literal["positional"]],
Param[Literal["b"], int],
Param[Literal["c"], int, Literal["default"]],
Param[Literal["c"], int, typing.Never, Literal[0]],
Param[None, int, Literal["*"]],
Param[Literal["d"], int, Literal["keyword"]],
Param[Literal["e"], int, Literal["default", "keyword"]],
Param[Literal["e"], int, Literal["keyword"], Literal[0]],
Param[None, int, Literal["**"]],
],
int,
]


or, using the type abbreviations we provide::
or, using the type abbreviations we provide (though this version will not track
specific values for the defaults)::
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wording is a bit confusing. Anyways, maybe we should just add the default as a param with Never defaults.


Callable[
Params[
Expand Down Expand Up @@ -793,8 +813,9 @@ Callable inspection and creation
``Callable`` types always have their arguments exposed in the extended
Callable format discussed above.

The names, type, and qualifiers share associated type names with
``Member`` (``.name``, ``.type``, and ``.quals``).
The name and type associated type names with ``Member`` (``.name`` and
``.type``). ``Param`` also has a ``.kind`` associated type, which
exposes ``K`` and a ``.default`` associated type, which exposes ``D``.

.. _pep827-generic-callable:

Expand Down Expand Up @@ -1101,13 +1122,10 @@ based on iterating over all attributes.
p.name,
p.type,
# All arguments are keyword-only
# It takes a default if a default is specified in the class
Literal["keyword"]
if typing.IsAssignable[
GetDefault[p.init],
Never,
]
else Literal["keyword", "default"],
Literal["keyword"],
# GetDefault is Never when there's no default, so use it
# directly as D.
GetDefault[p.init],
]
for p in typing.Iter[typing.Attrs[T]]
],
Expand Down Expand Up @@ -1343,7 +1361,7 @@ functions with explicit generic annotations. For old-style generics,
we'll probably have to try to evaluate it and then raise an error when
we encounter a variable.)

With our real syntax, this look likes::
With our real syntax, this looks like::

type Foo = NewProtocol[
Member[
Expand Down Expand Up @@ -1888,10 +1906,6 @@ Open Issues
would be to mirror ``inspect.Signature`` more directly, and have an enum
with names like ``ParamKind.POSITIONAL_OR_KEYWORD``. Would that be better?

A related potential change would be to fully separate the kind from whether
there is a default, and have whether there is a default represented in
an ``init`` field, like we do for class member initializers with ``Member``.

* :ref:`Members <pep827-members>`: Should ``Members`` return all
methods, even those without annotations? We excluded them out of the
desire for some consistency with attributes, but it would not be
Expand Down
Loading