summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSybren A. Stüvel <sybren@stuvel.eu>2019-08-04 15:47:11 +0200
committerSybren A. Stüvel <sybren@stuvel.eu>2019-08-04 17:05:58 +0200
commit6760eb76e665dc81863a82110164c4b3b38e7ee9 (patch)
tree3a9cf1606c851ad4067c2419fec78e7f6ca044ac
parentded036cf988b0cf4b20002d88434282f30762638 (diff)
downloadrsa-git-6760eb76e665dc81863a82110164c4b3b38e7ee9.tar.gz
Added mypy for static type checking
-rw-r--r--.gitignore1
-rw-r--r--Pipfile3
-rw-r--r--Pipfile.lock54
-rw-r--r--rsa/cli.py10
-rw-r--r--setup.cfg13
-rw-r--r--tests/test_cli.py40
-rw-r--r--tests/test_mypy.py27
-rw-r--r--tox.ini3
8 files changed, 119 insertions, 32 deletions
diff --git a/.gitignore b/.gitignore
index 1f5a640..e31443a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@
.coverage
.coverage.*
.cache/
+.mypy_cache/
.pytest_cache/
__pycache__/
diff --git a/Pipfile b/Pipfile
index a03274c..b22cab3 100644
--- a/Pipfile
+++ b/Pipfile
@@ -13,7 +13,8 @@ Sphinx = "*"
coveralls = "*"
pytest = "*"
pytest-cov = "*"
-pathlib2 = {version = "*", markers="python_version < '3.6'"}
+pathlib2 = {version = "*",markers = "python_version < '3.6'"}
+mypy = "*"
[requires]
python_version = "3.7"
diff --git a/Pipfile.lock b/Pipfile.lock
index 6a399e6..9ec9947 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "65309370bad59af42cbf3b538e9a050dece94209b8b9657eb4e9f1ca8feedda4"
+ "sha256": "376fdc997e7b1f113fdc6c27ce5d1ba88f01a3dbe64f03d54132e5f7b5cd24a4"
},
"pipfile-spec": 6,
"requires": {
@@ -210,6 +210,30 @@
],
"version": "==7.2.0"
},
+ "mypy": {
+ "hashes": [
+ "sha256:0107bff4f46a289f0e4081d59b77cef1c48ea43da5a0dbf0005d54748b26df2a",
+ "sha256:07957f5471b3bb768c61f08690c96d8a09be0912185a27a68700f3ede99184e4",
+ "sha256:10af62f87b6921eac50271e667cc234162a194e742d8e02fc4ddc121e129a5b0",
+ "sha256:11fd60d2f69f0cefbe53ce551acf5b1cec1a89e7ce2d47b4e95a84eefb2899ae",
+ "sha256:15e43d3b1546813669bd1a6ec7e6a11d2888db938e0607f7b5eef6b976671339",
+ "sha256:352c24ba054a89bb9a35dd064ee95ab9b12903b56c72a8d3863d882e2632dc76",
+ "sha256:437020a39417e85e22ea8edcb709612903a9924209e10b3ec6d8c9f05b79f498",
+ "sha256:49925f9da7cee47eebf3420d7c0e00ec662ec6abb2780eb0a16260a7ba25f9c4",
+ "sha256:6724fcd5777aa6cebfa7e644c526888c9d639bd22edd26b2a8038c674a7c34bd",
+ "sha256:7a17613f7ea374ab64f39f03257f22b5755335b73251d0d253687a69029701ba",
+ "sha256:cdc1151ced496ca1496272da7fc356580e95f2682be1d32377c22ddebdf73c91"
+ ],
+ "index": "pypi",
+ "version": "==0.720"
+ },
+ "mypy-extensions": {
+ "hashes": [
+ "sha256:37e0e956f41369209a3d5f34580150bcacfabaa57b33a15c0b25f4b5725e0812",
+ "sha256:b16cabe759f55e3409a7d231ebd2841378fb0c27a5d1994719e340e4f429ac3e"
+ ],
+ "version": "==0.4.1"
+ },
"packaging": {
"hashes": [
"sha256:a7ac867b97fdc07ee80a8058fe4435ccd274ecc3b0ed61d852d7d53055528cf9",
@@ -362,6 +386,34 @@
"index": "pypi",
"version": "==3.13.2"
},
+ "typed-ast": {
+ "hashes": [
+ "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e",
+ "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e",
+ "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0",
+ "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c",
+ "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631",
+ "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4",
+ "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34",
+ "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b",
+ "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a",
+ "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233",
+ "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1",
+ "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36",
+ "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d",
+ "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a",
+ "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"
+ ],
+ "version": "==1.4.0"
+ },
+ "typing-extensions": {
+ "hashes": [
+ "sha256:2ed632b30bb54fc3941c382decfd0ee4148f5c591651c9272473fea2c6397d95",
+ "sha256:b1edbbf0652660e32ae780ac9433f4231e7339c7f9a8057d0f042fcbcea49b87",
+ "sha256:d8179012ec2c620d3791ca6fe2bf7979d979acdbef1fca0bc56b37411db682ed"
+ ],
+ "version": "==3.7.4"
+ },
"urllib3": {
"hashes": [
"sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
diff --git a/rsa/cli.py b/rsa/cli.py
index 77bd4ee..cbf3f97 100644
--- a/rsa/cli.py
+++ b/rsa/cli.py
@@ -21,9 +21,11 @@ These scripts are called by the executables defined in setup.py.
import abc
import sys
+import typing
from optparse import OptionParser
import rsa
+import rsa.key
import rsa.pkcs1
HASH_METHODS = sorted(rsa.pkcs1.HASH_METHODS.keys())
@@ -84,14 +86,12 @@ def keygen():
sys.stdout.buffer.write(data)
-class CryptoOperation(object):
+class CryptoOperation(metaclass=abc.ABCMeta):
"""CLI callable that operates with input, output, and a key."""
- __metaclass__ = abc.ABCMeta
-
keyname = 'public' # or 'private'
usage = 'usage: %%prog [options] %(keyname)s_key'
- description = None
+ description = ''
operation = 'decrypt'
operation_past = 'decrypted'
operation_progressive = 'decrypting'
@@ -102,7 +102,7 @@ class CryptoOperation(object):
expected_cli_args = 1
has_output = True
- key_class = rsa.PublicKey
+ key_class = rsa.PublicKey # type: typing.Type[rsa.key.AbstractKey]
def __init__(self):
self.usage = self.usage % self.__class__.__dict__
diff --git a/setup.cfg b/setup.cfg
index ed8a958..c5601b1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -3,3 +3,16 @@ universal = 1
[metadata]
license_file = LICENSE
+
+[flake8]
+max-line-length = 100
+
+[pep8]
+max-line-length = 100
+
+[mypy]
+python_version = 3.7
+warn_unused_ignores = True
+ignore_missing_imports = True
+follow_imports = skip
+incremental = True
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 7cf7ed4..1cd92c2 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -4,41 +4,37 @@ Unit tests for CLI entry points.
from __future__ import print_function
-import unittest
-import sys
import functools
-from contextlib import contextmanager
-
+import io
import os
-from io import StringIO, BytesIO
+import sys
+import typing
+import unittest
+from contextlib import contextmanager, redirect_stdout, redirect_stderr
import rsa
import rsa.cli
import rsa.util
-def make_buffer() -> StringIO:
- buf = StringIO()
- buf.buffer = BytesIO()
- return buf
+@contextmanager
+def captured_output() -> typing.Generator:
+ """Captures output to stdout and stderr"""
+ # According to mypy, we're not supposed to change buf_out.buffer.
+ # However, this is just a test, and it works, hence the 'type: ignore'.
+ buf_out = io.StringIO()
+ buf_out.buffer = io.BytesIO() # type: ignore
-def get_bytes_out(out: StringIO) -> bytes:
- # Python 3.x writes 'bytes' to stdout.buffer
- return out.buffer.getvalue()
+ buf_err = io.StringIO()
+ buf_err.buffer = io.BytesIO() # type: ignore
+ with redirect_stdout(buf_out), redirect_stderr(buf_err):
+ yield buf_out, buf_err
-@contextmanager
-def captured_output():
- """Captures output to stdout and stderr"""
- new_out, new_err = make_buffer(), make_buffer()
- old_out, old_err = sys.stdout, sys.stderr
- try:
- sys.stdout, sys.stderr = new_out, new_err
- yield new_out, new_err
- finally:
- sys.stdout, sys.stderr = old_out, old_err
+def get_bytes_out(buf) -> bytes:
+ return buf.buffer.getvalue()
@contextmanager
diff --git a/tests/test_mypy.py b/tests/test_mypy.py
new file mode 100644
index 0000000..c2a9745
--- /dev/null
+++ b/tests/test_mypy.py
@@ -0,0 +1,27 @@
+import pathlib
+import unittest
+
+import mypy.api
+
+test_modules = ['rsa', 'tests']
+
+
+class MypyRunnerTest(unittest.TestCase):
+ def test_run_mypy(self):
+ proj_root = pathlib.Path(__file__).parent.parent
+ args = ['--incremental', '--ignore-missing-imports'] + [str(proj_root / dirname) for dirname
+ in test_modules]
+
+ result = mypy.api.run(args)
+
+ stdout, stderr, status = result
+
+ messages = []
+ if stderr:
+ messages.append(stderr)
+ if stdout:
+ messages.append(stdout)
+ if status:
+ messages.append('Mypy failed with status %d' % status)
+ if messages:
+ self.fail('\n'.join(['Mypy errors:'] + messages))
diff --git a/tox.ini b/tox.ini
index 0401412..c9f1370 100644
--- a/tox.ini
+++ b/tox.ini
@@ -15,6 +15,3 @@ commands=
commands=
pipenv install --dev --ignore-pipfile
pipenv run py.test --doctest-modules rsa tests
-
-[pep8]
-max-line-length = 100