Skip to content

Change flag names for silent modes and add a new mode ("follow silently") #2513

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 21 commits into from
Dec 9, 2016

Conversation

gvanrossum
Copy link
Member

@gvanrossum gvanrossum commented Nov 30, 2016

Originally called "half-assed mode (not its real name)".

@refi64
Copy link
Contributor

refi64 commented Nov 30, 2016

(not its real name)

Well, I'd hope not. :O

@ambv
Copy link
Contributor

ambv commented Nov 30, 2016

This makes sense to me. What's the rationale of keeping the current behavior of --silent-imports? Apart from worse performance, your new halfassed mode looks strictly better.

@gvanrossum
Copy link
Member Author

What's the rationale of keeping the current behavior of --silent-imports?

Backwards compatibility. When I use this on our internal semi-annotated codebases (which are currently clean using --silent-imports), I get thousands of new errors. I don't have the time to fix all of those instantly -- realistically it can take months. (It's similar with --strict-optional and even --warn-no-return; we have to be able to gradually turn these new strictness options on for sections of the code.)

@@ -222,6 +222,11 @@ def process_options(args: List[str],
# which will make the cache writing process output pretty-printed JSON (which
# is easier to debug).
parser.add_argument('--debug-cache', action='store_true', help=argparse.SUPPRESS)
# --half-assed mode is a bit like --silent-imports in that it distinguishes
# modules listed on the command line; however instead of ignoring such
# imports, it follows them but silences errors. Stubs are unaffected.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Perhaps mention how blocker errors work in --half-assed mode. What if we have both --half-assed and --silent-imports -- would we then follow imports (but ignore errors) if we can find an implementation of a module not listed on the command line, and ignore the import silently if no implementation can be found?

Copy link
Member Author

Choose a reason for hiding this comment

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

Blockers still abort the run. If both flags are set, indeed we process the module silently if found, and skip it silently if not found.

@rowillia
Copy link
Contributor

rowillia commented Dec 1, 2016

@gvanrossum In --half-assed mode, how would mypy differentiate between imports that should be checked and ones that are effectively just stubs? Files to be checked get passed in the command line and ones to just type check against go on MYPY_PATH?

@JukkaL
Copy link
Collaborator

JukkaL commented Dec 1, 2016

It would be useful to have a short specification of how this works. It would also make it easier to check whether the implementation matches the spec.

@gvanrossum
Copy link
Member Author

OK, here's an attempt at a specification. First the categories of files (modules) we consider:

  1. files on the command line (including all files in packages/directories on the command line)
  2. .pyi stubs (in typeshed or MYPYPATH)
  3. .py files found on MYPYPATH found by following an import and not in (1) or (2)
  4. modules that are needed to satisfy an import but cannot be found at all

Remember that when looking for a module on MYPYPATH, if a .py file and a .pyi stub are found in the same directory, the .pyi stub always takes preference.

All three options involved here (--silent-imports, --almost-silent and --half-assed) only affect categories (3) and (4). When considering combinations, --almost-silent implies --silent-imports but otherwise they are all independent.

For (4), we get an error except when --silent-imports is present without --almost-silent.

For (3):

  • by default these are processed the same as (1)
  • with --silent-imports and/or --almost-silent these are silently ignored and replaced with Any
  • with --half-assed (regardless of the other two) these are processed as usual, extracting class and other definitions, but any errors encountered during this processing are suppressed

Another way of looking at it:

  • just --half-assed processes category (3) suppressing errors, and reports (4) as errors
  • --haf-assed combined with --silent-imports (but without --almost-silent) processes (3) suppressing errors, and skips (4) silently
  • just --silent-imports skips (3) and (4) silently
  • --almost-silent (with or without --silent-imports) skips (3) but reports (4) as errors
  • all three flags together processes (4) suppressing errors, and reports (4) as errors

@gvanrossum
Copy link
Member Author

Maybe we need to create a different set of flags? E.g. one tri-value flag that determines how to handle (3) (normal, quiet or skip) and one Boolean flag that determines whether (4) is reported as an error or not. That might be simpler to explain and understand.

@gvanrossum
Copy link
Member Author

gvanrossum commented Dec 2, 2016

Still left to do:

  • maybe some tests specifically aimed at this functionality
  • there's an XXX in mypy/test/testcheck.py
  • some tests probably had redundant flags added; audit these
  • compatibility measures for mypy.ini?
  • docs? (maybe in a follow-up PR so readthedocs isn't messed up yet)

@gvanrossum gvanrossum changed the title WIP: half-assed mode (not its real name) Change flag names for silent modes and add a new mode ("follow silently") Dec 3, 2016
@gvanrossum
Copy link
Member Author

This is ready for more review. The only thing left to do is documentation, and I'd like to do that in a separate PR.

To summarize the new flags:

  • For (4), i.e. import not found:

    • by default this is an error
    • with --ignore-missing-imports this is silent
  • For (3), i.e. .py files that aren't listed on the command line but are needed to satisfy some import and found on $MYPYPATH, --follow-imports {normal,silent,skip,error} determines what happens:

    • normal (default): same as current processing without --silent-imports
    • silent: what I originally envisioned "half-assed" to do: process, but suppress (ignore) all errors
    • skip: like old behavior of --silent-imports: ignore silently (module is given type Any)
    • error: like old behavior of --almost-silent: issue an error and ignore

print("Warning: --almost-silent has been replaced by "
"--follow=imports=errors", file=sys.stderr)
if options.follow_imports == 'normal':
options.follow_imports = 'skip'
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this be 'errors'?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch! Will fix.

@@ -229,9 +229,9 @@ mypy.ini: [mypy]: Unrecognized report type: bad_report
# cmd: mypy -c pass
[file mypy.ini]
[[mypy]
silent_imports = nah
ignore_missing_imports = nah
Copy link
Collaborator

Choose a reason for hiding this comment

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

What about adding test cases that use config file to use the new options?

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do.

@JukkaL
Copy link
Collaborator

JukkaL commented Dec 7, 2016

What about test cases that use the new silent import mode together --incremental? Do we persist knowledge about whether a file was processed silently in the cache json file?

@gvanrossum
Copy link
Member Author

What about test cases that use the new silent import mode together [with] --incremental?

If there are existing tests for that I'll convert them.

Do we persist knowledge about whether a file was processed silently in the cache json file?

Yes, the new flags are included in PER_MODULE_OPTIONS (see options.py) and those are included in OPTIONS_AFFECTING_CACHE. Whenever such an option changes the cache is invalidated.

@gvanrossum
Copy link
Member Author

@JukkaL, what do you think of this now?

# cmd: mypy main.py
[file main.py]
import a
a.x + 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

Test generating an error here, e.g. a.x + 'x'?

[out]
main:2: note: Import of 'a' ignored
main:2: note: (Using --follow-imports=error, module not passed on command line)
[out2]
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder if it would be useful to have test cases where the output differs between the first and the second run (e.g. error -> no error, no error -> error)? Is it possible to test incremental with changes in config file only (so that config file change should affect output)?

@gvanrossum
Copy link
Member Author

I did everything you requested, no new problems were revealed by the added tests.

Note that I also added an error message if the config file cannot be found (only when specified explicitly).

@JukkaL
Copy link
Collaborator

JukkaL commented Dec 9, 2016

Thanks for the updates! This is now ready to merge. This will likely conflict with the error context PR and I leave it up to you to decide which should go in first.

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.

5 participants