-
-
Notifications
You must be signed in to change notification settings - Fork 163
PR: Support mypy #217
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
PR: Support mypy #217
Changes from all commits
0a72a91
52c6039
80dda11
bbf4247
59c8ab7
203d4f4
d38fcab
91e5834
7754e75
4a3aeb4
135b74c
b279790
b05c7c7
3d171a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import qtpy.cli | ||
|
|
||
|
|
||
| def main(): | ||
| return qtpy.cli.cli() | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,77 @@ | ||||||||||||||||||||||
| # -*- coding: utf-8 -*- | ||||||||||||||||||||||
| # ----------------------------------------------------------------------------- | ||||||||||||||||||||||
| # Copyright © 2009- The QtPy Contributors | ||||||||||||||||||||||
| # | ||||||||||||||||||||||
| # Released under the terms of the MIT License | ||||||||||||||||||||||
| # (see LICENSE.txt for details) | ||||||||||||||||||||||
| # ----------------------------------------------------------------------------- | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| """Provide a CLI to allow configuring developer settings, including mypy.""" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Standard library imports | ||||||||||||||||||||||
| import argparse | ||||||||||||||||||||||
| import sys | ||||||||||||||||||||||
| import textwrap | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| class RawDescriptionArgumentDefaultsHelpFormatter( | ||||||||||||||||||||||
| argparse.RawDescriptionHelpFormatter, | ||||||||||||||||||||||
| argparse.ArgumentDefaultsHelpFormatter, | ||||||||||||||||||||||
| ): | ||||||||||||||||||||||
| pass | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def cli(args=sys.argv[1:]): | ||||||||||||||||||||||
| parser = argparse.ArgumentParser( | ||||||||||||||||||||||
| description="Features in support of development with QtPy.", | ||||||||||||||||||||||
| formatter_class=RawDescriptionArgumentDefaultsHelpFormatter, | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| parser.set_defaults(func=parser.print_help) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| cli_subparsers = parser.add_subparsers() | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| mypy_args_parser = cli_subparsers.add_parser( | ||||||||||||||||||||||
| name='mypy-args', | ||||||||||||||||||||||
| description=textwrap.dedent( | ||||||||||||||||||||||
| """\ | ||||||||||||||||||||||
| Generate command line arguments for using mypy with QtPy. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| This will generate strings similar to the following which help guide mypy | ||||||||||||||||||||||
| through which library QtPy would have used so that mypy can get the proper | ||||||||||||||||||||||
| underlying type hints. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| --always-false=PYQT4 --always-false=PYQT5 --always-false=PYSIDE --always-true=PYSIDE2 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Use such as: | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| env/bin/mypy --package mypackage $(env/bin/qtpy mypy-args) | ||||||||||||||||||||||
| """ | ||||||||||||||||||||||
| ), | ||||||||||||||||||||||
| formatter_class=RawDescriptionArgumentDefaultsHelpFormatter, | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
| mypy_args_parser.set_defaults(func=mypy_args) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| arguments = parser.parse_args(args=args) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| reserved_parameters = {'func'} | ||||||||||||||||||||||
| cleaned = { | ||||||||||||||||||||||
| k: v | ||||||||||||||||||||||
| for k, v in vars(arguments).items() | ||||||||||||||||||||||
| if k not in reserved_parameters | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| arguments.func(**cleaned) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def mypy_args(): | ||||||||||||||||||||||
| options = {False: '--always-false', True: '--always-true'} | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import qtpy | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| apis_active = {name: qtpy.API.lower() == name.lower() for name in qtpy.APIS} | ||||||||||||||||||||||
| print(' '.join( | ||||||||||||||||||||||
| f'{options[is_active]}={name.upper()}' | ||||||||||||||||||||||
| for name, is_active | ||||||||||||||||||||||
| in apis_active.items() | ||||||||||||||||||||||
| )) | ||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
A bit of boilerplate to make it a little easier to run as a script in case the user doesn't actually have QtPy installed, but rather just checked out locally
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really like encouraging not installing things. It makes it really hard to make things actually work. And why the indirection through
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good points, agreed this isn't necessary or desirable. FWIW, if I'd started this package from scratch, I'd have put the top-level package inside a
Just convention, and probably a foolish one, at least in this case where it doesn't do anything special.
I assume its a moot point, but I'm curious as to the need for |
||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,45 @@ | ||||||||||
| from __future__ import absolute_import | ||||||||||
|
|
||||||||||
| import subprocess | ||||||||||
| import sys | ||||||||||
|
|
||||||||||
| import pytest | ||||||||||
|
|
||||||||||
| import qtpy | ||||||||||
|
|
||||||||||
|
|
||||||||||
| subcommands = [ | ||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
As a top-level constant, shouldn't this be ALL_CAPS?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Many people do that. I don't like it. No need to be YELLING. I presume it will get changed.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given Python has no runtime enforcement of constants staying constants, particularly for mutable objects like this list here which are effectively global state, it is particularly important to clearly distinguish them from regular variables. |
||||||||||
| ['mypy'], | ||||||||||
| ['mypy', 'args'], | ||||||||||
| ] | ||||||||||
|
|
||||||||||
|
|
||||||||||
| @pytest.mark.parametrize( | ||||||||||
| argnames=['subcommand'], | ||||||||||
| argvalues=[[subcommand] for subcommand in subcommands], | ||||||||||
| ids=[' '.join(subcommand) for subcommand in subcommands], | ||||||||||
|
Comment on lines
+19
to
+20
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
To match previous |
||||||||||
| ) | ||||||||||
| def test_cli_help_does_not_fail(subcommand): | ||||||||||
| # .check_call() over .run(..., check=True) because of py2 | ||||||||||
| subprocess.check_call( | ||||||||||
| [sys.executable, '-m', 'qtpy', *subcommand, '--help'], | ||||||||||
| ) | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def test_cli_mypy_args(): | ||||||||||
| output = subprocess.check_output( | ||||||||||
| [sys.executable, '-m', 'qtpy', 'mypy', 'args'], | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| if qtpy.PYQT4: | ||||||||||
| expected = b'--always-true=PYQT4 --always-false=PYQT5 --always-false=PYSIDE --always-false=PYSIDE2\n' | ||||||||||
| elif qtpy.PYQT5: | ||||||||||
| expected = b'--always-false=PYQT4 --always-true=PYQT5 --always-false=PYSIDE --always-false=PYSIDE2\n' | ||||||||||
| elif qtpy.PYSIDE: | ||||||||||
| expected = b'--always-false=PYQT4 --always-false=PYQT5 --always-true=PYSIDE --always-false=PYSIDE2\n' | ||||||||||
| elif qtpy.PYSIDE2: | ||||||||||
| expected = b'--always-false=PYQT4 --always-false=PYQT5 --always-false=PYSIDE --always-true=PYSIDE2\n' | ||||||||||
| else: | ||||||||||
| assert False, 'No valid API to test' | ||||||||||
|
|
||||||||||
| assert output == expected | ||||||||||
Uh oh!
There was an error while loading. Please reload this page.