python / python/cpython

[docs] dictionary unpacking `fn(**kwargs)` should accept `SupportsKeysAndGetitem` and not just `Mapping`

Abierto
#157,747 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

docs
Lenguaje dominante
Python
Estrellas
77.2k
Forks
35.9k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

Brief

The docs say that for dict unpacking as in fn(**kwargs), kwargs must "implement the methods specified in the collections.abc.Mapping", when in reality only keys() and __getitem__ as in the dict constructor are required.

Documentation

The current documentation states that:

[6.2.5.4. Dictionary displays]
Instead of a key-value pair, a dict item may be an expression prefixed by a double asterisk **. This denotes dictionary unpacking. At runtime, the expression must evaluate to a mapping;

which points to

[mapping]
A container object that supports arbitrary key lookups and implements the methods specified in the collections.abc.Mapping or collections.abc.MutableMapping abstract base classes.

However, at runtime, fn(**kwargs) works whenever kwargs supports both a keys() -> Iterable[str] and __getitem__(self, key: str) -> object method:

class SupportsKeysAndGetitem:
    _dict = {"foo": 0, "bar": object()}

    def keys(self):
        return iter(self._dict)

    def __getitem__(self, key: str, /) -> object:
        return self._dict[key]

def expects_kwargs(**kwargs: int) -> None:
    print(kwargs)

d = SupportsKeysAndGetitem()
expects_kwargs(**d)  # works at runtime

Moreover, the relevant methods that Cpython executes under the hood explicitly supports this looser protocol:

class dict
If a positional argument is given and it defines a keys() method, a dictionary is created by calling getitem() on the argument with each returned key from the method.

and

int PyDict_Merge(PyObject *a, PyObject *b, int override)
Part of the Stable ABI. Thread safety: Safe for concurrent use on the same object.
Iterate over mapping object b adding key-value pairs to dictionary a. b may be a dictionary, or any object supporting PyMapping_Keys() and PyObject_GetItem().

Proposal

Update the documentation of dict-unpacking to reflect the actual runtime behavior in sync with how dict construction works, only requiring keys() and __getitem__ rather than inheritance from the non-protocol class collections.abc.Mapping.

Rationale

  • fn(**kwargs) only requiring keys() and __getitem__ is how it works at runtime, and has since ages
  • Supporting a protocol is nicer than just supporting a non-protocol ABC like collections.abc.Mapping due to duck-typing (a similar conclusion was reached in the discussion of #116938)
  • There are real world use-cases like fn(**dataframe) as for example a pandas DataFrame supports keys() (yields the list of columns) and __getitem__ (look up column by name), but does not inherit from the collections.abc.Mapping class, nor implements all of its methods.
Linked PRs
  • gh-157771

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Revisa el PR vinculado gh-157771 junto con la documentación sobre las representaciones de Dictionary y mapping citada en el issue. Verifica la redacción según el comportamiento en tiempo de ejecución descrito de keys() y getitem; se considera terminado cuando la documentación ya no exige heredar de Mapping y sigue siendo coherente con la construcción de dict.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
python
Área
documentation
Tipo de issue
Documentación
Dificultad
1/5
Tiempo estimado
1-3 horas
Estado de actividad
Estancado
Claridad
Bien especificado
Aptitud para principiantes
30/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.