Skip to content

Adding config file for stubtest.py #9203

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

Merged
merged 10 commits into from
Aug 4, 2020

Conversation

pikachu
Copy link
Contributor

@pikachu pikachu commented Jul 24, 2020

This fixes 9193.

Before: You could not use plugins during the stub loading step.

Problem: If you need plugins for regular static type checking, you may need them when loading stubs for runtime type checking as well.

Solution: Add support for loading plugins from a config file. The easiest way to do this is to just load the config file and let build take care of it.

mypy/stubtest.py Outdated

plugins = []
if options.config_file:
def set_strict_flags() -> None: # not needed yet
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if there's a better way to deal with this - seems like we just don't really need that param but I didn't want to pass in lambda _: None. open to suggestions here

@@ -1133,6 +1150,15 @@ def parse_options(args: List[str]) -> argparse.Namespace:
action="store_true",
help="Ignore unused whitelist entries",
)
config_group = parser.add_argument_group(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copied directly from main.py.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modified the description, though

Copy link
Collaborator

@hauntsaninja hauntsaninja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making this happen! You'll want to address the CI complaints.

It would be good to add a test, maybe using a simple get_type_analyze_hook plugin.

As I mentioned in the issue, I think I'd prefer not reusing mypy's config. stubtest and mypy are pretty different tools and this could cause problems with users' expectations. For instance, one might expect to be able to set --ignore-positional-only in the config.
The fact that we're ignoring most of the config could also lead to surprises in the other direction. For instance, if I'd used this approach initially (but not wired up the plugins code), it would have been harder for you to see that stubtest didn't support plugins.

It's also worth mentioning that I'm not a maintainer, so my opinions only count for so much, plus I don't have the ability to merge.

@pikachu
Copy link
Contributor Author

pikachu commented Jul 25, 2020

@hauntsaninja Thanks for the comment.

I don't have a great idea of all the cases where you want to use different settings (not just a subset, but actually different). If there are a bunch, then I agree we should somehow make it clear that this needs a different config. I wonder if there are other ways we could make it more clear? Maybe we rename the arg to plugin-config-file, or have a big print statement ™ that tells the user that we are only processing plugins and mypy_path (if you pass in a config file).

In my use case, it's nice to have the config file because it automatically picks up on all of the following settings that we want to be the same:

[mypy]
mypy_path = path
plugins = plugin1, plugin2, plugin3

[mypy.plugins.django-stubs]
setting1 = "value1"
setting2 = "value2"

Copy link
Collaborator

@hauntsaninja hauntsaninja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I hadn't thought of the fact that plugins set settings in mypy's config file as well (not a big plugin user myself). Renaming the option is a good idea then (maybe --mypy-config-file).

mypy/stubtest.py Outdated
def set_strict_flags() -> None: # not needed yet
return
parse_config_file(options, set_strict_flags, options.config_file, sys.stdout, sys.stderr)
errors = Errors(options.show_error_context,
Copy link
Collaborator

@hauntsaninja hauntsaninja Jul 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we don't ever set any of these options, the defaults match the defaults of Options, and because pretty is False we never use read_source, maybe we can just pass in Errors()?
That way you also no longer need to worry about cached_read etc not existing

edit: I'm a dummy, I forgot about the config file, lol

mypy/stubtest.py Outdated
config_group.add_argument(
'--config-file',
help="Configuration file, must have a [mypy] section "
"(defaults to {})".format(', '.join(defaults.CONFIG_FILES)))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't actually set this default anywhere (and maybe we shouldn't?)

@pikachu pikachu changed the title Adding plugin support through config file for stubtest.py Adding config file for stubtest.py Jul 29, 2020
Copy link
Collaborator

@hauntsaninja hauntsaninja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is much cleaner! This looks good to me; if you're willing to add a test, that would be the only improvement I could suggest :-)

mypy/stubtest.py Outdated
Comment on lines 1143 to 1152
config_group = parser.add_argument_group(
title='mypy config file',
description="Use a config file instead of command line arguments. "
"Plugins and mypy path are the only supported "
"configurations.",
)
config_group.add_argument(
'--mypy-config-file',
help="Configuration file, must have a [mypy] section",
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: a wording suggestion

Suggested change
config_group = parser.add_argument_group(
title='mypy config file',
description="Use a config file instead of command line arguments. "
"Plugins and mypy path are the only supported "
"configurations.",
)
config_group.add_argument(
'--mypy-config-file',
help="Configuration file, must have a [mypy] section",
)
config_group.add_argument(
'--mypy-config-file',
help="An existing mypy configuration file, currently used by stubtest to help determine mypy path and plugins",
)

@pikachu
Copy link
Contributor Author

pikachu commented Jul 30, 2020

@hauntsaninja Could you elaborate on how this test would work? I spent some time today trying to get one running with named_callable.py which uses get_function_hook. I defined a test case that looked like:

def test_config_file(self) -> None:
    runtime = (
        "from typing import Callable\n"
        "def decorator1() -> Callable[..., Callable[..., int]]: pass\n"
        "def decorator2() -> Callable[..., Callable[..., int]]: pass\n"
        "@decorator1()\n"
        "def f() -> None: pass\n"
        "@decorator2()\n"
        "def g() -> None: pass\n"
    )
    stub = (
        "import builtins\n"
        "def f(*args: Any, **kwargs: Any) -> builtins.int: ...\n"
        "def g(*args: Any, **kwargs: Any) -> builtins.int: ...\n"
    )
    config_file = (
        "[mypy]\n"
        "plugins={}/test-data/unit/plugins/named_callable.py\n".format(root_dir)
    )
    output = run_stubtest(
        stub=stub,
        runtime=runtime,
        options=[],
        config_file=config_file,
    )

If you take a look at named_callable.py, it looks for a fullname of m.decorator1, and converts that function's return type to a function with a return type of str. Using the debugger, I found out that get_function_hook is not being called for the decorator1 or 2 functions at all - it's only being called with builtins.object and typing.runtime_checkable.

I tried similar approaches with testTypeAnalyzeHookPlugin (trying to modify the test case to fit the stubtest input format) to use the get_type_analyze_hook (as you mentioned), but still couldn't get it to work.

Is there an example test case you could point me to? Thanks for all your help!

@hauntsaninja
Copy link
Collaborator

stubtest uses mypy to build the stubs. The runtime is only imported and introspected using inspect and friends; stubtest doesn't use the runtime source code at all (and for C extensions, it isn't even available to us). You'll need to use the decorators you define in the stub.

@hauntsaninja hauntsaninja merged commit 7938f5d into python:master Aug 4, 2020
@hauntsaninja
Copy link
Collaborator

Thanks, looks good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

stubtest with config file
2 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy