Enforce parameter naming in usage of function

Readability is often strongly tied with the expressivity and naming of variables within one’s code. A badly named variable is prone to confuse, and make Pull Requests and Merge Requests quite lengthy and painful.

What about enforcing the naming during usage of function key-word arguments?

Consider this function signature:

def upload_file_to_s3(filepath, bucket, key: Union[str, pathlib.PosixPath], s3):
    """Upload file to S3 buckets."""
    ...

and its usage:

upload_file_to_s3(
    a,
    b.bucket_data,
    os.path.join(b.s3path_conf, a.name),
    context.resources.s3
)

We must agree that these variables are terribly named a, b, etc… it’s just bad. But unfortunately, unless we catch these badly named variables during the code review, there isn’t much we can do about it in terms of automated stylistic linting (Unless I’m mistaken, but for the sake of this example let’s say there isn’t). An option is to add some readability during function calls.

Consider this alternative of calling this function:

upload_file_to_s3(
    filepath=a,
    bucket=b.bucket_data,
    key=os.path.join(b.s3path_conf, a.name),
    s3=c.resources.s3
)

Here, despite the badly named variables, I know a is supposed to be a filepath, b some object that contains s3 info, and what’s more, I can see how they are combined in the key parameter, right away.

If during the continuous integration we have a PEP rule that warns us about a function’s use that doesn’t explicitely name its kwargs, it could go a long way into adding more readability to something as ubiquitous as function calls in python code.

In conclusion, here is what I’m proposing :

  • Creating a PEP rule that, when we call a function, checks if its signature has named parameters, and warns us when they are not explicitely included in the function call. Catching this in a C.I pipeline is significantly easier than arguing with teammates over variable names in each PR/MR.

What do you think of my proposal?

You don’t need to go through the whole PEP process to make your own opinonated linting rule for your third party project to enforce a specific—just go ahead and do it :slight_smile: (Nor is the PEP process appropriate for this).

For this case, you have a number of existing options:

  • If you control the callee side (upload_file_to_s3), add * to the beginning of the signature to mark all the arguments as keyword-only, so they can only be passed by keyword and not by position:

    def upload_file_to_s3(*, filepath, bucket, ...):
        ...
    
  • Use an existing flake8, etc. linting rule (or write your own) to enforce the use of keyword arguments on function calls following criteria that you specify, e.g. flake8-keyword-arguments, or its fork flake8-force-keyword-arguments.

  • I haven’t tried this myself, but you might be able to enforce this with a typechecker (MyPy, etc) as well, by e.g. writing type stubs for the called functions with keyword-only arguments.

Best of luck!

8 Likes

Thank you for your reply ! I think I might opt for the second option amongst the ones you proposed.

If you see no objection in prolonging the discussion a bit, I would be grateful if you could provide further explanation or examples as to why this suggestion would be a bad rule for PEP?

You don’t need to go through the whole PEP process to make your own opinonated linting rule for your third party project

It isn’t just for my third party project, it is to enhance readability of any code. If PEP’s aim is just more readability, what questions should I have considered before suggesting this as a general rule?

Sure; happy to explain further. I originally had a more detailed explanation of this aspect in my original post, but ended up cutting it out and just focusing on constructive suggestions for how to achieve this without a PEP.

For some background, PEPs are Python Enhancement Proposals_, i.e. proposals for new features, standards, processes, governance and other changes for the Python language, reference implementation (CPython), static typing and packaging infrastructure. Furthermore, with a few special-case exceptions, PEPs are generally only primarily intended to be normative for the core Python language, the CPython interpreter and the typing and packaging sub-communities), rather than aimed at third-party code. Given what you’re proposing isn’t a change to Python itself, but rather an (opinionated) suggestion or tool for users writing their own code, a PEP isn’t the right place for it (as opposed to, e.g. a blog post, whitepaper, style guide, linting tool, etc).

As a sidenote, despite being perhaps the most widely known PEP, PEP 8 is one of the relatively few special cases that is an active style guide rather than a change proposal—and if created nowadays, might better belong in the devguide instead. And while it’s widely adopted in various forms by the Python community at large, what many folks don’t realize is it was never explicitly intended to mandate a specific style for all Python code, but rather just (C)Python’s own standard library.

Aside from the points above explaining why a PEP isn’t really a suitable venue for this, you might want to consider the many potential cases where such a blanket rule might not apply: lambdas, callbacks, positional-only args, builtin functions, functions with only one or two args, functions where the args’ purpose is clearly obvious, callables with args with no meaningful names, callables with *args, etc.

For what it’s worth, while I personally generally prefer passing arguments by keyword rather than by position past the first or second argument or where the purpose of the arguments is not clearly obvious, with my PEP editor hat on:

  • There is much too great a number of cases where such a rule wouldn’t necessarily apply such that a blanket recommendation would not be appropriate or useful, and
  • There is much too great a diversity of opinions and approaches on how to best define and call functions, that there is little to no chance of a strong consensus developing around this sufficient to make this an official recommendation for the whole Python community

Hopefully this helps make the reasoning more clear.

8 Likes

Thank you ! Very informative

2 Likes