pyinfra icon indicating copy to clipboard operation
pyinfra copied to clipboard

Positional arguments in any operations result in `Too few arguments` mypy errors

Open karlicoss opened this issue 5 months ago • 4 comments

To Reproduce

Consider the following trivial deploy, using server here, but pretty much any operation would reproduce it:

$ cat test.py
from pyinfra.operations import server
server.shell('echo')
$ uv run --extra dev mypy test.py
test.py:2: error: Too few arguments for "__call__" of "PyinfraOperation"  [call-arg]
test.py:2: error: Argument 1 to "__call__" of "PyinfraOperation" has incompatible type "str"; expected "bool"  [arg-type]

Expected behavior

This should type check without mypy complaining.

Workaround

This is likely caused by this workaround https://github.com/pyinfra-dev/pyinfra/blob/39d1d31b374c85c69f6fe074b067f3aa7989f2a4/pyinfra/api/arguments_typed.py#L29-L79

It seems like using explicit keyword arguments for the operations works around the issue -- this passes mypy:

from pyinfra.operations import server
server.shell(commands='echo')

Proper fix?

I'm not sure if it's really possible, from reading

  • https://peps.python.org/pep-0612/#concatenating-keyword-parameters
  • https://typing.python.org/en/latest/spec/generics.html#id5

, it seems that it's only possible to provide extra positional arguments by using Protocol with __call__ and ParamSpec. So unless one specifies all the extra arguments (i.e. _sudo/_sudo_user/_use_sudo_login/...), it's not really possible to use arguments from P.args (i.e. the actual operation's arguments) as positional arguments.

So I guess I'm not really expecting a fix, but figured it's worth creating an issue so at least other people can find it and the workaround via search.

Meta

  • using latest 3.x pyinfra
  • related issue: https://github.com/pyinfra-dev/pyinfra/pull/1105

karlicoss avatar Jul 03 '25 00:07 karlicoss

This has been previously discussed on Matrix, it happens with Python 3.13+ releases.

pyinfra is currently only supported and tested on Python 3.10 to 3.12, so you should use a Python 3.12 venv until this is handled.

xvello avatar Jul 03 '25 14:07 xvello

Hmm, I'm not sure it only happens on 3.13?

$ uv run --python=3.12 --extra dev python3 --version
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
Python 3.12.6

$ uv run --python=3.12 --extra dev mypy test.py
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
Warning: Unpack is already enabled by default
test.py:2: error: Too few arguments for "__call__" of "PyinfraOperation"  [call-arg]
test.py:2: error: Argument 1 to "__call__" of "PyinfraOperation" has incompatible type "str"; expected "bool"  [arg-type]
Found 2 errors in 1 file (checked 1 source file)

I went through the matrix converstaion, but didn't really understand why 3.12 would fix this, changelog for 3.13 doesn't mention anything with ParamSpec except something about defaults handling (which I don't think is at play here?).

karlicoss avatar Jul 03 '25 15:07 karlicoss

it seems that it's only possible to provide extra positional arguments by using Protocol with call and ParamSpec. So unless one specifies all the extra arguments (i.e. _sudo/_sudo_user/_use_sudo_login/...), it's not really possible to use arguments from P.args (i.e. the actual operation's arguments) as positional arguments.

This, unfortunately I believe this is, as it stands today, the situation. Positional args will not pass type checking cleanly.

So I guess I'm not really expecting a fix, but figured it's worth creating an issue so at least other people can find it and the workaround via search.

👍

IMO the correct “fix” here is just to use keyword arguments for all operation calls, which the docs and examples do (or should). Personally I think it makes for more explicit code which is a good thing.

Fizzadar avatar Aug 06 '25 08:08 Fizzadar

IMO the correct “fix” here is just to use keyword arguments for all operation calls, which the docs and examples do (or should). Personally I think it makes for more explicit code which is a good thing.

Can confirm that moving all positional arguments to keyword arguments fixes these mypy errors. I'll fall into this trap more than once in the future, but I don't see an easy fix to this.

xvello avatar Sep 16 '25 12:09 xvello