python / python/mypy

request: Statically evaluating module-level hasattr checks

Abierto
#9,042 3 comentarios 3 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

feature priority-2-low
Lenguaje dominante
Python
Estrellas
20.6k
Forks
3.3k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

This is a feature request, for mypy to enhance its static evaluation for boolean expressions involving sys.platform/sys.version_info to also handle expressions like hasattr(module_object, "constant string").

background

A common idiom in the standard library is to have functions/constants/etc. that are only exposed on certain platforms. For example, select.epoll only exists on Linux and illumos, socket.fromshare only exists on Windows, and os.preadv only exists on "Linux 2.6.30 and newer, FreeBSD 6.0 and newer, OpenBSD 2.7 and newer".

Currently, the way mypy handles these cases is:

  • typeshed contains if sys.platform == ... checks that try to approximate the runtime availability. Example
  • then mypy statically evaluates these checks, so it knows which functions/constants/etc. are available

So that works about as well as anything could. The static approximations aren't always exactly correct (e.g. the epoll example linked above gives the wrong answer on illumos), but they're "good enough" and can be improved over time if they cause problems.

the problem

Say we have a library which wants to use or expose certain features in its API depending on whether or not the standard library exposes those features. For example, in Trio we define our IOManager class differently depending on whether select.epoll and select.kqueue exist, and we export a trio.socket.fromshare function iff there's a socket.fromshare.

Now we're trying to figure out how to add type annotations, and it's super awkward. So far our best attempt for the basic platform differences looks like:

if sys.platform == "win32":
    ...
elif sys.platform == "linux" or (not TYPE_CHECKING and hasattr(select, "epoll")):
    ...
# At type-checking time, assume that all platforms that aren't win32 or linux are some kind of BSD
elif TYPE_CHECKING or (not TYPE_CHECKING and hasattr(select, "kqueue")):
    ...
else:
    raise NotImplementedError

This has a few problems:

  • it's complex and awkward
  • it requires manually duplicating information that's already in typeshed
  • it requires manually propagating any typeshed changes into our project (which seems plausible, since typeshed often contains rough approximations for platform-specific stuff)
  • it requires copy/pasting this whole complex construct in multiple places around our code-base

This is a bit frustrating. It would be nice if mypy could just understand if hasattr(select, "epoll") and do the right thing. It already has all the information it needs to do that.

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

Comienza localizando la evaluación estática existente de mypy para sys.platform y sys.version_info, junto con sus pruebas. Sigue cómo se representan los atributos de módulo de typeshed y cómo se evalúan para comprobaciones de hasattr con cadenas constantes. Se considera completado cuando las expresiones hasattr a nivel de módulo puedan evaluarse de forma consistente a partir de la información disponible de typeshed, con pruebas que cubran atributos dependientes de la plataforma.

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

Evaluación

Stack tecnológico
python
Área
compilers, devtools
Tipo de issue
Nueva funcionalidad
Dificultad
5/5
Tiempo estimado
Más de una semana
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
35/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.