Skip to content

Commit

Permalink
Preliminary cli for mypy options
Browse files Browse the repository at this point in the history
  • Loading branch information
altendky authored and CAM-Gerlach committed Apr 19, 2022
1 parent 5c7b09b commit 1d4131f
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 3 deletions.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ include README*
include SECURITY*
include pytest.ini
recursive-include qtpy/tests *.py *.ui
include qtpy/py.typed
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,25 @@ conda install qtpy
```


### mypy

A CLI is offered to help with usage of QtPy. Presently, the only feature
is to generate command line arguments for Mypy that will enable it to
process the QtPy source files with the same API as QtPy itself would have
selected.

```
--always-false=PYQT5 --always-false=PYQT6 --always-true=PYSIDE2 --always-false=PYSIDE6
```

If using bash or similar, this can be integrated into the Mypy command line
as follows.

```console
$ env/bin/mypy --package mypackage $(env/bin/qtpy mypy-args)
```


## Contributing

Everyone is welcome to contribute!
Expand Down
9 changes: 6 additions & 3 deletions qtpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,11 @@ class PythonQtWarning(Warning):
# Setting a default value for QT_API
os.environ.setdefault(QT_API, 'pyqt5')

API_NAMES = {'pyqt5': 'PyQt5', 'pyqt6': 'PyQt6',
'pyside2':'PySide2', 'pyside6': 'PySide6'}
API = os.environ[QT_API].lower()
initial_api = API
assert API in (PYQT5_API + PYQT6_API + PYSIDE2_API + PYSIDE6_API)
assert API in API_NAMES

is_old_pyqt = is_pyqt46 = False
QT5 = PYQT5 = True
Expand Down Expand Up @@ -201,8 +203,9 @@ class PythonQtWarning(Warning):
warnings.warn('Selected binding "{}" could not be found, '
'using "{}"'.format(initial_api, API), RuntimeWarning)

API_NAME = {'pyqt6': 'PyQt6', 'pyqt5': 'PyQt5',
'pyside2':'PySide2', 'pyside6': 'PySide6'}[API]

# Set display name of the Qt API
API_NAME = API_NAMES[API]

try:
# QtDataVisualization backward compatibility (QtDataVisualization vs. QtDatavisualization)
Expand Down
9 changes: 9 additions & 0 deletions qtpy/__main__.py
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()
77 changes: 77 additions & 0 deletions qtpy/cli.py
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=PYQT5 --always-false=PYQT6 --always-true=PYSIDE2 --always-false=PYSIDE6
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 == name for name in qtpy.API_NAMES}
print(' '.join(
f'{options[is_active]}={name.upper()}'
for name, is_active
in apis_active.items()
))
Empty file added qtpy/py.typed
Empty file.
45 changes: 45 additions & 0 deletions qtpy/tests/test_cli.py
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 = [
['mypy'],
['mypy', 'args'],
]


@pytest.mark.parametrize(
argnames=['subcommand'],
argvalues=[[subcommand] for subcommand in subcommands],
ids=[' '.join(subcommand) for subcommand in subcommands],
)
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.PYQT5:
expected = b'--always-true=PYQT5 --always-false=PYQT6 --always-false=PYSIDE2 --always-false=PYSIDE6\n'
elif qtpy.PYQT6:
expected = b'--always-false=PYQT5 --always-true=PYQT6 --always-false=PYSIDE2 --always-false=PYSIDE6\n'
elif qtpy.PYSIDE2:
expected = b'--always-false=PYQT5 --always-false=PYQT6 --always-true=PYSIDE2 --always-false=PYSIDE6\n'
elif qtpy.PYSIDE6:
expected = b'--always-false=PYQT5 --always-false=PYQT6 --always-false=PYSIDE2 --always-true=PYSIDE6\n'
else:
assert False, 'No valid API to test'

assert output == expected
4 changes: 4 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,7 @@ test =
pytest>=6,!=7.0.0,!=7.0.1
pytest-cov>=3.0.0
pytest-qt

[options.entry_points]
console_scripts =
qtpy = qtpy.__main__:main

0 comments on commit 1d4131f

Please sign in to comment.