Skip to content

Commit 6760eb7

Browse files
committed
Added mypy for static type checking
1 parent ded036c commit 6760eb7

File tree

8 files changed

+119
-32
lines changed

8 files changed

+119
-32
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
.coverage
1313
.coverage.*
1414
.cache/
15+
.mypy_cache/
1516
.pytest_cache/
1617
__pycache__/
1718

Pipfile

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ Sphinx = "*"
1313
coveralls = "*"
1414
pytest = "*"
1515
pytest-cov = "*"
16-
pathlib2 = {version = "*", markers="python_version < '3.6'"}
16+
pathlib2 = {version = "*",markers = "python_version < '3.6'"}
17+
mypy = "*"
1718

1819
[requires]
1920
python_version = "3.7"

Pipfile.lock

+53-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rsa/cli.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121

2222
import abc
2323
import sys
24+
import typing
2425
from optparse import OptionParser
2526

2627
import rsa
28+
import rsa.key
2729
import rsa.pkcs1
2830

2931
HASH_METHODS = sorted(rsa.pkcs1.HASH_METHODS.keys())
@@ -84,14 +86,12 @@ def keygen():
8486
sys.stdout.buffer.write(data)
8587

8688

87-
class CryptoOperation(object):
89+
class CryptoOperation(metaclass=abc.ABCMeta):
8890
"""CLI callable that operates with input, output, and a key."""
8991

90-
__metaclass__ = abc.ABCMeta
91-
9292
keyname = 'public' # or 'private'
9393
usage = 'usage: %%prog [options] %(keyname)s_key'
94-
description = None
94+
description = ''
9595
operation = 'decrypt'
9696
operation_past = 'decrypted'
9797
operation_progressive = 'decrypting'
@@ -102,7 +102,7 @@ class CryptoOperation(object):
102102
expected_cli_args = 1
103103
has_output = True
104104

105-
key_class = rsa.PublicKey
105+
key_class = rsa.PublicKey # type: typing.Type[rsa.key.AbstractKey]
106106

107107
def __init__(self):
108108
self.usage = self.usage % self.__class__.__dict__

setup.cfg

+13
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,16 @@ universal = 1
33

44
[metadata]
55
license_file = LICENSE
6+
7+
[flake8]
8+
max-line-length = 100
9+
10+
[pep8]
11+
max-line-length = 100
12+
13+
[mypy]
14+
python_version = 3.7
15+
warn_unused_ignores = True
16+
ignore_missing_imports = True
17+
follow_imports = skip
18+
incremental = True

tests/test_cli.py

+18-22
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,37 @@
44

55
from __future__ import print_function
66

7-
import unittest
8-
import sys
97
import functools
10-
from contextlib import contextmanager
11-
8+
import io
129
import os
13-
from io import StringIO, BytesIO
10+
import sys
11+
import typing
12+
import unittest
13+
from contextlib import contextmanager, redirect_stdout, redirect_stderr
1414

1515
import rsa
1616
import rsa.cli
1717
import rsa.util
1818

1919

20-
def make_buffer() -> StringIO:
21-
buf = StringIO()
22-
buf.buffer = BytesIO()
23-
return buf
20+
@contextmanager
21+
def captured_output() -> typing.Generator:
22+
"""Captures output to stdout and stderr"""
2423

24+
# According to mypy, we're not supposed to change buf_out.buffer.
25+
# However, this is just a test, and it works, hence the 'type: ignore'.
26+
buf_out = io.StringIO()
27+
buf_out.buffer = io.BytesIO() # type: ignore
2528

26-
def get_bytes_out(out: StringIO) -> bytes:
27-
# Python 3.x writes 'bytes' to stdout.buffer
28-
return out.buffer.getvalue()
29+
buf_err = io.StringIO()
30+
buf_err.buffer = io.BytesIO() # type: ignore
2931

32+
with redirect_stdout(buf_out), redirect_stderr(buf_err):
33+
yield buf_out, buf_err
3034

31-
@contextmanager
32-
def captured_output():
33-
"""Captures output to stdout and stderr"""
3435

35-
new_out, new_err = make_buffer(), make_buffer()
36-
old_out, old_err = sys.stdout, sys.stderr
37-
try:
38-
sys.stdout, sys.stderr = new_out, new_err
39-
yield new_out, new_err
40-
finally:
41-
sys.stdout, sys.stderr = old_out, old_err
36+
def get_bytes_out(buf) -> bytes:
37+
return buf.buffer.getvalue()
4238

4339

4440
@contextmanager

tests/test_mypy.py

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import pathlib
2+
import unittest
3+
4+
import mypy.api
5+
6+
test_modules = ['rsa', 'tests']
7+
8+
9+
class MypyRunnerTest(unittest.TestCase):
10+
def test_run_mypy(self):
11+
proj_root = pathlib.Path(__file__).parent.parent
12+
args = ['--incremental', '--ignore-missing-imports'] + [str(proj_root / dirname) for dirname
13+
in test_modules]
14+
15+
result = mypy.api.run(args)
16+
17+
stdout, stderr, status = result
18+
19+
messages = []
20+
if stderr:
21+
messages.append(stderr)
22+
if stdout:
23+
messages.append(stdout)
24+
if status:
25+
messages.append('Mypy failed with status %d' % status)
26+
if messages:
27+
self.fail('\n'.join(['Mypy errors:'] + messages))

tox.ini

-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,3 @@ commands=
1515
commands=
1616
pipenv install --dev --ignore-pipfile
1717
pipenv run py.test --doctest-modules rsa tests
18-
19-
[pep8]
20-
max-line-length = 100

0 commit comments

Comments
 (0)