Skip to content

Commit

Permalink
Mypy static type check
Browse files Browse the repository at this point in the history
  • Loading branch information
p1c2u committed Sep 1, 2022
1 parent a9cf988 commit 22b12e7
Show file tree
Hide file tree
Showing 13 changed files with 796 additions and 716 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/python-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,8 @@ jobs:
PYTEST_ADDOPTS: "--color=yes"
run: poetry run pytest

- name: Static type check
run: poetry run mypy

- name: Upload coverage
uses: codecov/codecov-action@v1
39 changes: 39 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
default_stages: [commit, push]
default_language_version:
# force all unspecified python hooks to run python3
python: python3
minimum_pre_commit_version: "1.20.0"
repos:
- repo: meta
hooks:
- id: check-hooks-apply

- repo: https://github.com/asottile/pyupgrade
rev: v2.19.0
hooks:
- id: pyupgrade
args: ["--py36-plus"]

- repo: local
hooks:
- id: flynt
name: Convert to f-strings with flynt
entry: flynt
language: python
additional_dependencies: ['flynt==0.76']

- id: black
name: black
entry: black
language: system
require_serial: true
types: [python]

- id: isort
name: isort
entry: isort
args: ['--filter-files']
language: system
require_serial: true
types: [python]
28 changes: 14 additions & 14 deletions openapi_schema_validator/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# -*- coding: utf-8 -*-
from openapi_schema_validator._format import oas30_format_checker, \
oas31_format_checker
from openapi_schema_validator._format import oas30_format_checker
from openapi_schema_validator._format import oas31_format_checker
from openapi_schema_validator.shortcuts import validate
from openapi_schema_validator.validators import OAS30Validator, OAS31Validator
from openapi_schema_validator.validators import OAS30Validator
from openapi_schema_validator.validators import OAS31Validator

__author__ = 'Artur Maciag'
__email__ = 'maciag.artur@gmail.com'
__version__ = '0.3.2'
__url__ = 'https://github.com/p1c2u/openapi-schema-validator'
__license__ = '3-clause BSD License'
__author__ = "Artur Maciag"
__email__ = "maciag.artur@gmail.com"
__version__ = "0.3.2"
__url__ = "https://github.com/p1c2u/openapi-schema-validator"
__license__ = "3-clause BSD License"

__all__ = [
'validate',
'OAS30Validator',
'oas30_format_checker',
'OAS31Validator',
'oas31_format_checker',
"validate",
"OAS30Validator",
"oas30_format_checker",
"OAS31Validator",
"oas31_format_checker",
]
72 changes: 40 additions & 32 deletions openapi_schema_validator/_format.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from base64 import b64encode, b64decode
import binascii
from base64 import b64decode
from base64 import b64encode
from datetime import datetime
from typing import Any
from typing import Tuple
from typing import Union
from uuid import UUID

from jsonschema._format import FormatChecker
Expand All @@ -9,7 +13,7 @@
DATETIME_HAS_RFC3339_VALIDATOR = False
DATETIME_HAS_STRICT_RFC3339 = False
DATETIME_HAS_ISODATE = False
DATETIME_RAISES = ()
DATETIME_RAISES: Tuple[Exception, ...] = ()

try:
import isodate
Expand All @@ -36,65 +40,67 @@
DATETIME_RAISES += (ValueError, TypeError)


def is_int32(instance):
def is_int32(instance: Any) -> bool:
return isinstance(instance, int)


def is_int64(instance):
def is_int64(instance: Any) -> bool:
return isinstance(instance, int)


def is_float(instance):
def is_float(instance: Any) -> bool:
return isinstance(instance, float)


def is_double(instance):
def is_double(instance: Any) -> bool:
# float has double precision in Python
# It's double in CPython and Jython
return isinstance(instance, float)


def is_binary(instance):
def is_binary(instance: Any) -> bool:
return isinstance(instance, bytes)


def is_byte(instance):
def is_byte(instance: Union[str, bytes]) -> bool:
if isinstance(instance, str):
instance = instance.encode()

try:
return b64encode(b64decode(instance)) == instance
encoded = b64encode(b64decode(instance))
except TypeError:
return False
else:
return encoded == instance


def is_datetime(instance):
def is_datetime(instance: str) -> bool:
if not isinstance(instance, (bytes, str)):
return False

if DATETIME_HAS_RFC3339_VALIDATOR:
return validate_rfc3339(instance)
return bool(validate_rfc3339(instance))

if DATETIME_HAS_STRICT_RFC3339:
return strict_rfc3339.validate_rfc3339(instance)
return bool(strict_rfc3339.validate_rfc3339(instance))

if DATETIME_HAS_ISODATE:
return isodate.parse_datetime(instance)
return bool(isodate.parse_datetime(instance))

return True


def is_date(instance):
def is_date(instance: Any) -> bool:
if not isinstance(instance, (bytes, str)):
return False

if isinstance(instance, bytes):
instance = instance.decode()

return datetime.strptime(instance, "%Y-%m-%d")
return bool(datetime.strptime(instance, "%Y-%m-%d"))


def is_uuid(instance):
def is_uuid(instance: Any) -> bool:
if not isinstance(instance, (bytes, str)):
return False

Expand All @@ -104,41 +110,43 @@ def is_uuid(instance):
return str(UUID(instance)).lower() == instance.lower()


def is_password(instance):
def is_password(instance: Any) -> bool:
return True


class OASFormatChecker(FormatChecker):
class OASFormatChecker(FormatChecker): # type: ignore

checkers = {
'int32': (is_int32, ()),
'int64': (is_int64, ()),
'float': (is_float, ()),
'double': (is_double, ()),
'byte': (is_byte, (binascii.Error, TypeError)),
'binary': (is_binary, ()),
'date': (is_date, (ValueError, )),
'date-time': (is_datetime, DATETIME_RAISES),
'password': (is_password, ()),
"int32": (is_int32, ()),
"int64": (is_int64, ()),
"float": (is_float, ()),
"double": (is_double, ()),
"byte": (is_byte, (binascii.Error, TypeError)),
"binary": (is_binary, ()),
"date": (is_date, (ValueError,)),
"date-time": (is_datetime, DATETIME_RAISES),
"password": (is_password, ()),
# non standard
'uuid': (is_uuid, (AttributeError, ValueError)),
"uuid": (is_uuid, (AttributeError, ValueError)),
}

def check(self, instance, format):
def check(self, instance: Any, format: str) -> Any:
if format not in self.checkers:
raise FormatError(
"Format checker for %r format not found" % (format, ))
f"Format checker for {format!r} format not found"
)

func, raises = self.checkers[format]
result, cause = None, None
try:
result = func(instance)
except raises as e:
except raises as e: # type: ignore
cause = e

if not result:
raise FormatError(
"%r is not a %r" % (instance, format), cause=cause,
f"{instance!r} is not a {format!r}",
cause=cause,
)
return result

Expand Down
27 changes: 16 additions & 11 deletions openapi_schema_validator/_types.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
from jsonschema._types import (
TypeChecker, is_array, is_bool, is_integer,
is_object, is_number, draft202012_type_checker,
)
from typing import Any

from jsonschema._types import TypeChecker
from jsonschema._types import draft202012_type_checker
from jsonschema._types import is_array
from jsonschema._types import is_bool
from jsonschema._types import is_integer
from jsonschema._types import is_number
from jsonschema._types import is_object


def is_string(checker, instance):
def is_string(checker: TypeChecker, instance: Any) -> bool:
return isinstance(instance, (str, bytes))


oas30_type_checker = TypeChecker(
{
u"string": is_string,
u"number": is_number,
u"integer": is_integer,
u"boolean": is_bool,
u"array": is_array,
u"object": is_object,
"string": is_string,
"number": is_number,
"integer": is_integer,
"boolean": is_bool,
"array": is_array,
"object": is_object,
},
)
oas31_type_checker = draft202012_type_checker

0 comments on commit 22b12e7

Please sign in to comment.