Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rule “too many positional arguments in function definition” #8946

Closed
flying-sheep opened this issue Dec 1, 2023 · 12 comments · Fixed by #8995
Closed

Add rule “too many positional arguments in function definition” #8946

flying-sheep opened this issue Dec 1, 2023 · 12 comments · Fixed by #8995
Labels
rule Implementing or modifying a lint rule

Comments

@flying-sheep
Copy link
Contributor

flying-sheep commented Dec 1, 2023

Since #4329, PLR0913 is useless to us. We need a rule that doesn’t restrict the number of total arguments, but the number of non-keyword-only arguments. See also pylint-dev/pylint#9099

I’m happy to do a PR, but I don’t know how I should go about the rule identifier: Should I go for a new RUFF rule, or is there a way I can reserve a new code like e.g. PLR0914 in Pylint for this even if I don’t plan to make a PR for Pylint?

@charliermarsh charliermarsh added the rule Implementing or modifying a lint rule label Dec 2, 2023
@charliermarsh
Copy link
Member

I'm totally fine to add a rule for this, though ideally it'd be added to Pylint so that we can use the same identifier... Or perhaps @Pierre-Sassoulas, if there's interest from the Pylint side, could reserve an identifier for it?

@Pierre-Sassoulas
Copy link
Contributor

We can definitely create the msgid / symbol before someone implement it. too-many-positional seems appropriate, and the next available value for the msgid would be R0917.

Pierre-Sassoulas added a commit to Pierre-Sassoulas/pylint that referenced this issue Dec 4, 2023
charliermarsh pushed a commit that referenced this issue Dec 4, 2023
## Summary

Adds a rule that bans too many positional (i.e. not keyword-only)
parameters in function definitions.

Fixes #8946

Rule ID code taken from pylint-dev/pylint#9278

## Test Plan
1. fixtures file checking multiple OKs/fails
2. parametrized test file
@buhtz
Copy link
Contributor

buhtz commented Oct 25, 2024

The pylint docu pointed me into this issue.
I am sure this issue is not the right place to ask for full explanation. But maybe some of you know a blog post or technical article describing the topic.
The pylint docu about too-many-positional-arguments is not detailed enough. I don't get it.
But I also don't like to "ignore" pylint. I am assuming there is a good reason behind this rule. I just try to understand it.

@flying-sheep
Copy link
Contributor Author

flying-sheep commented Oct 25, 2024

It’s pretty simple: Good APIs don’t have many positional parameters.

Most functions take 1–2 “main objects” to operate on and a few parameters that modify how they operate, e.g. zip(a: Iterable, b: Iterable, *, strict: bool). Then these main objects should be able to be specified as positional parameters and the rest as keyword-only.

There are few exceptions where 4 or more positional parameters make sense, e.g. rgba(1.0, 0.5, 0.3, 1.0) is a very well known conventional order of 4 floats/bytes.

@buhtz
Copy link
Contributor

buhtz commented Oct 25, 2024

It’s pretty simple: Good APIs don’t have many positional parameters.

But what is the difference compared to too-many-arguments?

@flying-sheep
Copy link
Contributor Author

positional vs total arguments. you can configure:

  • maximum number of positional arguments: 3
  • maximum number of arguments: 6

Then this would be legal:

def (a, b, c, *, x, y, z): ...

and this wouldn‘t (because while it has <=6 arguments, it has >3 positional ones)

def (a, b, c, x, y, z): ...

@buhtz
Copy link
Contributor

buhtz commented Oct 25, 2024

I played a bit around in my REPL.

def foobar(a, b, c, *, x, y, z): ...

Do I get this correct? The * is not a 7th argument to allow 7 or more arguments.
It is just a marker forcing the caller to give names to the 4th, 5th and 6th argument when calling that function?

In other words the "error" too-many-positional-arguments means that there are to many arguments in the function signature that can be called/givin without a name?

EDIT:
So when I use the * in the beginning like this:

def foobar(*, a, b, c, d):

This would force the caller to give every argument a name when calling the function?

@flying-sheep
Copy link
Contributor Author

Exactly: https://docs.python.org/3/tutorial/controlflow.html#special-parameters

@buhtz
Copy link
Contributor

buhtz commented Oct 25, 2024

Thank you very much with contributing to my current and future FOSS projects with teaching me such an essential Python feature. Was new to me.

@flying-sheep
Copy link
Contributor Author

Always happy to do so!

@Pierre-Sassoulas
Copy link
Contributor

Pierre-Sassoulas commented Oct 25, 2024

@buhtz did you read pylint's 3.3.1's doc or the latest ? And what helped you understand the concept so we can put it in the doc ? Sorry for not removing the link to this issue, it was relevant at a time when the pylint check was not implemented yet, but not anymore.

@buhtz
Copy link
Contributor

buhtz commented Oct 25, 2024

Not sure which version it was. But for myself I would say both versions you linked to wouldn't have helped me much. But he problem is more on my site not on yours.

I think I lacked of basic (?) python concepts. I am not sure if a pylint-rule-docu should be responsible to explain them. But I would say a link to the Python doc about special parameters would be helpful. And what also would help is to extend the example code. Maybe you can add to the "correct code" section something like this making the effect of * more obvious:

def foobar(pos_arg, *, key_arg):
   pass

# TypeError: foobar() takes 1 positional argument but 2 were given
foobar(1, 2)

# OK
foobar(1, key_arg=2)

To my experience, as someone reading all the free python learning material and working with Python for nearly 20 years on a hobby level, that "special parameters" are unknown to some of the beginners or hobby Python users. No one reads the full python docu, of course. ;) I do assume that you have to study computer science or book a payed python course to learn things like this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rule Implementing or modifying a lint rule
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants