Skip to content

Potential false positives with ParamSpec "P" is unbound after upgrading from 1.14.1 to 1.15.0 #18745

Closed as not planned

Description

@alichaudry

Bug Report

After upgrading to mypy 1.15.0 from mypy 1.14.1, I am now experiencing hundreds of new ParamSpec "P" is unbound errors. I imagine quite a few of these are valid, and for my use-case, I'm going to just swap how I'm (incorrectly) using ParamSpec (*args: P.args, **kwargs: P.kwargs, where P is not bound to the function in many of these cases) with a separate appropriate version (*args: object, **kwargs: object; I don't want to use Any and turn off type-checking entirely). However, I am running into cases where I think P is bound, and yet still throwing a MyPy exception.

From what I can tell, the PR which added this update may have missed my specific use-case. @sterliakov, as the author of this PR, any thoughts on my specific example? Also, would love to hear your thoughts on switching out unbound *args: P.args to *args: object instead of *args: Any. Thanks!

Example and playground gist below.

To Reproduce

from collections.abc import Callable
from functools import wraps
from typing import ParamSpec, TypeAlias

from flask import Response
from flask_login import current_user


P = ParamSpec("P")
FlaskRestfulHTTPMethodResponse: TypeAlias = tuple[Response | dict, int]
FlaskRestfulHTTPMethod: TypeAlias = Callable[P, FlaskRestfulHTTPMethodResponse]


def check_auth(f: FlaskRestfulHTTPMethod) -> FlaskRestfulHTTPMethod:
    @wraps(f)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> FlaskRestfulHTTPMethodResponse:
        if not current_user.is_authenticated:
            return {"message": "Unauthorized"}, 401
        return f(*args, **kwargs)

    return wrapper

Playground link
Gist link

Expected Behavior

Should pass the mypy checker (as it does in mypy 1.14.1).

Actual Behavior

ParamSpec "P" is unbound [valid-type]

Your Environment

  • Mypy version used: 1.15.0
  • Mypy command-line flags: N/A
  • Mypy configuration options from mypy.ini (and other config files): strict = True
  • Python version used: 3.11.4

Activity

  1. A5rocks commented on Feb 28, 2025

    @A5rocks
    Collaborator

    If you enable --disallow-any-generics you will get errors about missing paramspecs or typevars. In this case for instance you have:

    def check_auth(f: FlaskRestfulHTTPMethod) -> FlaskRestfulHTTPMethod:

    and you should instead have

    def check_auth(f: FlaskRestfulHTTPMethod[P]) -> FlaskRestfulHTTPMethod[P]:

    I'm surprised --strict doesn't enable that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions