add: type hints to top level functions (#218)

* WIP: add type hints to top level functions

TODO:
- see if mypy picks up types when installing in new project
- setup mypy in CI

fixes https://github.com/pyca/bcrypt/issues/215

* add mypy to ci & move py.typed to correct folder?

* try and get mypy to run in CI

* run mypy in travis?

* use mypy defaults for python_version and platform

https://mypy.readthedocs.io/en/stable/config_file.html#platform-configuration

* update change log with changes
This commit is contained in:
Steve Dignam 2020-08-13 22:09:30 -04:00 committed by GitHub
parent f3c255ca15
commit f9066e2be5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 58 additions and 9 deletions

View File

@ -32,6 +32,8 @@ matrix:
env: TOXENV=pypy3 CC=clang
- python: 3.8
env: TOXENV=pep8
- python: 3.8
env: TOXENV=mypy
- env: TOXENV=packaging
- python: 3.8
arch: arm64

View File

@ -8,6 +8,6 @@ include src/build_bcrypt.py
recursive-include src/_csrc *
recursive-include tests *.py
exclude requirements.txt release.py .travis.yml
exclude requirements.txt release.py .travis.yml mypy.ini
prune .travis

View File

@ -56,6 +56,8 @@ Changelog
Unreleased
----------
* Add typehints for library functions
* Dropped support for Python versions less than 3.6 (2.7, 3.4, 3.5).
3.1.7

32
mypy.ini Normal file
View File

@ -0,0 +1,32 @@
[mypy]
show_column_numbers=True
pretty=True
disallow_any_unimported=True
# _bcrypt usage will result in any exprs
disallow_any_expr=False
disallow_any_decorated=True
disallow_any_explicit=True
disallow_any_generics=True
disallow_subclassing_any=True
disallow_untyped_calls=True
disallow_untyped_defs=True
disallow_incomplete_defs=True
check_untyped_defs=True
disallow_untyped_decorators=True
no_implicit_optional=True
strict_optional=True
warn_redundant_casts=True
warn_unused_ignores=True
warn_no_return=True
# _bcrypt is untyped so all calls involving it will be Any
warn_return_any=False
# keep backwards compatibility for users not using static type checking
warn_unreachable=False
strict_equality=True
ignore_missing_imports=False

View File

@ -208,10 +208,11 @@ setup(
author_email=__about__["__email__"],
python_requires=">=3.6",
install_requires=[CFFI_DEPENDENCY, SIX_DEPENDENCY],
extras_require={"tests": ["pytest>=3.2.1,!=3.3.0"]},
extras_require={"tests": ["pytest>=3.2.1,!=3.3.0"], "typecheck": ["mypy"]},
tests_require=["pytest>=3.2.1,!=3.3.0"],
package_dir={"": "src"},
packages=["bcrypt"],
package_data={"bcrypt": ["py.typed"]},
zip_safe=False,
classifiers=[
"Development Status :: 5 - Production/Stable",

View File

@ -22,7 +22,7 @@ import warnings
import six
from . import _bcrypt
from . import _bcrypt # type: ignore
from .__about__ import (
__author__,
__copyright__,
@ -54,7 +54,7 @@ __all__ = [
_normalize_re = re.compile(br"^\$2y\$")
def gensalt(rounds=12, prefix=b"2b"):
def gensalt(rounds: int = 12, prefix: bytes = b"2b") -> bytes:
if prefix not in (b"2a", b"2b"):
raise ValueError("Supported prefixes are b'2a' or b'2b'")
@ -75,7 +75,7 @@ def gensalt(rounds=12, prefix=b"2b"):
)
def hashpw(password, salt):
def hashpw(password: bytes, salt: bytes) -> bytes:
if isinstance(password, six.text_type) or isinstance(salt, six.text_type):
raise TypeError("Unicode-objects must be encoded before hashing")
@ -113,7 +113,7 @@ def hashpw(password, salt):
return original_salt[:4] + _bcrypt.ffi.string(hashed)[4:]
def checkpw(password, hashed_password):
def checkpw(password: bytes, hashed_password: bytes) -> bool:
if isinstance(password, six.text_type) or isinstance(
hashed_password, six.text_type
):
@ -132,7 +132,13 @@ def checkpw(password, hashed_password):
return _bcrypt.lib.timingsafe_bcmp(ret, hashed_password, len(ret)) == 0
def kdf(password, salt, desired_key_bytes, rounds, ignore_few_rounds=False):
def kdf(
password: bytes,
salt: bytes,
desired_key_bytes: int,
rounds: int,
ignore_few_rounds: bool = False,
) -> bytes:
if isinstance(password, six.text_type) or isinstance(salt, six.text_type):
raise TypeError("Unicode-objects must be encoded before hashing")
@ -167,6 +173,6 @@ def kdf(password, salt, desired_key_bytes, rounds, ignore_few_rounds=False):
return _bcrypt.ffi.buffer(key, desired_key_bytes)[:]
def _bcrypt_assert(ok):
def _bcrypt_assert(ok: bool) -> None:
if not ok:
raise SystemError("bcrypt assertion failed")

0
src/bcrypt/py.typed Normal file
View File

View File

@ -1,5 +1,5 @@
[tox]
envlist = pypy3,py36,py37,py38,pep8,packaging
envlist = pypy3,py36,py37,py38,pep8,packaging,mypy
isolated_build = True
[testenv]
@ -21,6 +21,12 @@ commands =
flake8 .
black --check .
[testenv:mypy]
deps =
mypy
commands =
mypy src/bcrypt
[testenv:packaging]
deps =
check-manifest