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 6d78075 commit c35af01
Show file tree
Hide file tree
Showing 21 changed files with 800 additions and 511 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/python-test.yml
Expand Up @@ -57,5 +57,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
@@ -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]
1 change: 1 addition & 0 deletions MANIFEST.in
@@ -1,5 +1,6 @@
include README.md
include requirements.txt
include requirements_dev.txt
include openapi_spec_validator/py.typed
include openapi_spec_validator/resources/schemas/*/*
include LICENSE
56 changes: 27 additions & 29 deletions openapi_spec_validator/__init__.py
@@ -1,62 +1,60 @@
# -*- coding: utf-8 -*-
from jsonschema_spec.handlers import default_handlers

from openapi_spec_validator.shortcuts import validate_spec_detect_factory
from openapi_spec_validator.shortcuts import validate_spec_url_detect_factory
from openapi_spec_validator.shortcuts import validate_spec_factory
from openapi_spec_validator.shortcuts import validate_spec_url_detect_factory
from openapi_spec_validator.shortcuts import validate_spec_url_factory
from openapi_spec_validator.validation import openapi_v2_spec_validator
from openapi_spec_validator.validation import openapi_v3_spec_validator
from openapi_spec_validator.validation import openapi_v30_spec_validator
from openapi_spec_validator.validation import openapi_v31_spec_validator

__author__ = 'Artur Maciag'
__email__ = 'maciag.artur@gmail.com'
__version__ = '0.5.0a3'
__url__ = 'https://github.com/p1c2u/openapi-spec-validator'
__license__ = 'Apache License, Version 2.0'
__author__ = "Artur Maciag"
__email__ = "maciag.artur@gmail.com"
__version__ = "0.5.0a3"
__url__ = "https://github.com/p1c2u/openapi-spec-validator"
__license__ = "Apache License, Version 2.0"

__all__ = [
'openapi_v2_spec_validator',
'openapi_v3_spec_validator',
'openapi_v30_spec_validator',
'openapi_v31_spec_validator',
'validate_v2_spec',
'validate_v3_spec',
'validate_v30_spec',
'validate_v31_spec',
'validate_spec',
'validate_v2_spec_url',
'validate_v3_spec_url',
'validate_v30_spec_url',
'validate_v31_spec_url',
'validate_spec_url',
"openapi_v2_spec_validator",
"openapi_v3_spec_validator",
"openapi_v30_spec_validator",
"openapi_v31_spec_validator",
"validate_v2_spec",
"validate_v3_spec",
"validate_v30_spec",
"validate_v31_spec",
"validate_spec",
"validate_v2_spec_url",
"validate_v3_spec_url",
"validate_v30_spec_url",
"validate_v31_spec_url",
"validate_spec_url",
]

# shortcuts
validate_spec = validate_spec_detect_factory({
validate_spec = validate_spec_detect_factory(
{
("swagger", "2.0"): openapi_v2_spec_validator,
("openapi", "3.0"): openapi_v30_spec_validator,
("openapi", "3.1"): openapi_v31_spec_validator,
},
)
validate_spec_url = validate_spec_url_detect_factory({
validate_spec_url = validate_spec_url_detect_factory(
{
("swagger", "2.0"): openapi_v2_spec_validator,
("openapi", "3.0"): openapi_v30_spec_validator,
("openapi", "3.1"): openapi_v31_spec_validator,
},
)
validate_v2_spec = validate_spec_factory(openapi_v2_spec_validator)
validate_v2_spec_url = validate_spec_url_factory(
openapi_v2_spec_validator)
validate_v2_spec_url = validate_spec_url_factory(openapi_v2_spec_validator)

validate_v30_spec = validate_spec_factory(openapi_v30_spec_validator)
validate_v30_spec_url = validate_spec_url_factory(
openapi_v30_spec_validator)
validate_v30_spec_url = validate_spec_url_factory(openapi_v30_spec_validator)

validate_v31_spec = validate_spec_factory(openapi_v31_spec_validator)
validate_v31_spec_url = validate_spec_url_factory(
openapi_v31_spec_validator)
validate_v31_spec_url = validate_spec_url_factory(openapi_v31_spec_validator)

# aliases to the latest v3 version
validate_v3_spec = validate_v31_spec
Expand Down
57 changes: 29 additions & 28 deletions openapi_spec_validator/__main__.py
@@ -1,25 +1,26 @@
from argparse import ArgumentParser
import logging
import argparse
import sys
from typing import Optional
from typing import Sequence

from jsonschema.exceptions import best_match
from jsonschema.exceptions import ValidationError

from openapi_spec_validator import (
openapi_v2_spec_validator,
openapi_v30_spec_validator,
openapi_v31_spec_validator,
)
from openapi_spec_validator.validation.exceptions import ValidationError
from openapi_spec_validator.readers import read_from_stdin, read_from_filename
from openapi_spec_validator import openapi_v2_spec_validator
from openapi_spec_validator import openapi_v30_spec_validator
from openapi_spec_validator import openapi_v31_spec_validator
from openapi_spec_validator.readers import read_from_filename
from openapi_spec_validator.readers import read_from_stdin

logger = logging.getLogger(__name__)
logging.basicConfig(
format='%(asctime)s %(levelname)s %(name)s %(message)s',
level=logging.WARNING
format="%(asctime)s %(levelname)s %(name)s %(message)s",
level=logging.WARNING,
)


def print_validationerror(exc, errors="best-match"):
def print_validationerror(exc: ValidationError, errors: str = "best-match") -> None:
print("# Validation Error\n")
print(exc)
if exc.cause:
Expand All @@ -35,14 +36,14 @@ def print_validationerror(exc, errors="best-match"):
print("## " + str(best_match(exc.context)))
if len(exc.context) > 1:
print(
"\n({} more subschemas errors,".format(len(exc.context) - 1),
f"\n({len(exc.context) - 1} more subschemas errors,",
"use --errors=all to see them.)",
)


def main(args=None):
parser = argparse.ArgumentParser()
parser.add_argument('filename', help="Absolute or relative path to file")
def main(args: Optional[Sequence[str]] = None) -> None:
parser = ArgumentParser()
parser.add_argument("filename", help="Absolute or relative path to file")
parser.add_argument(
"--errors",
choices=("best-match", "all"),
Expand All @@ -51,46 +52,46 @@ def main(args=None):
"""use "all" to get all subschema errors.""",
)
parser.add_argument(
'--schema',
"--schema",
help="OpenAPI schema (default: 3.1.0)",
type=str,
choices=['2.0', '3.0.0', '3.1.0'],
default='3.1.0'
choices=["2.0", "3.0.0", "3.1.0"],
default="3.1.0",
)
args = parser.parse_args(args)
args_parsed = parser.parse_args(args)

# choose source
reader = read_from_filename
if args.filename in ['-', '/-']:
if args_parsed.filename in ["-", "/-"]:
reader = read_from_stdin

# read source
try:
spec, spec_url = reader(args.filename)
spec, spec_url = reader(args_parsed.filename)
except Exception as exc:
print(exc)
sys.exit(1)

# choose the validator
validators = {
'2.0': openapi_v2_spec_validator,
'3.0.0': openapi_v30_spec_validator,
'3.1.0': openapi_v31_spec_validator,
"2.0": openapi_v2_spec_validator,
"3.0.0": openapi_v30_spec_validator,
"3.1.0": openapi_v31_spec_validator,
}
validator = validators[args.schema]
validator = validators[args_parsed.schema]

# validate
try:
validator.validate(spec, spec_url=spec_url)
except ValidationError as exc:
print_validationerror(exc, args.errors)
print_validationerror(exc, args_parsed.errors)
sys.exit(1)
except Exception as exc:
print(exc)
sys.exit(2)
else:
print('OK')
print("OK")


if __name__ == '__main__':
if __name__ == "__main__":
main()
Empty file added openapi_spec_validator/py.typed
Empty file.
25 changes: 15 additions & 10 deletions openapi_spec_validator/readers.py
@@ -1,18 +1,23 @@
import os
import pathlib
from os import path
from pathlib import Path
import sys
from typing import Any
from typing import Hashable
from typing import Mapping
from typing import Tuple

from jsonschema_spec.handlers import file_handler, all_urls_handler
from jsonschema_spec.handlers import all_urls_handler
from jsonschema_spec.handlers import file_handler


def read_from_stdin(filename):
return file_handler(sys.stdin), ''
def read_from_stdin(filename: str) -> Tuple[Mapping[Hashable, Any], str]:
return file_handler(sys.stdin), ""


def read_from_filename(filename):
if not os.path.isfile(filename):
raise IOError("No such file: {0}".format(filename))
def read_from_filename(filename: str) -> Tuple[Mapping[Hashable, Any], str]:
if not path.isfile(filename):
raise OSError(f"No such file: {filename}")

filename = os.path.abspath(filename)
uri = pathlib.Path(filename).as_uri()
filename = path.abspath(filename)
uri = Path(filename).as_uri()
return all_urls_handler(uri), uri
6 changes: 3 additions & 3 deletions openapi_spec_validator/schemas/__init__.py
Expand Up @@ -3,9 +3,9 @@

__all__ = ["schema_v2", "schema_v3", "schema_v30", "schema_v31"]

schema_v2, _ = get_schema('2.0')
schema_v30, _ = get_schema('3.0')
schema_v31, _ = get_schema('3.1')
schema_v2, _ = get_schema("2.0")
schema_v30, _ = get_schema("3.0")
schema_v31, _ = get_schema("3.1")

# alias to the latest v3 version
schema_v3 = schema_v31
11 changes: 7 additions & 4 deletions openapi_spec_validator/schemas/utils.py
@@ -1,14 +1,17 @@
"""OpenAIP spec validator schemas utils module."""
from os import path
from typing import Any
from typing import Hashable
from typing import Mapping
from typing import Tuple

import importlib_resources

from jsonschema_spec.readers import FilePathReader


def get_schema(version):
schema_path = 'resources/schemas/v{0}/schema.json'.format(version)
ref = importlib_resources.files('openapi_spec_validator') / schema_path
def get_schema(version: str) -> Tuple[Mapping[Hashable, Any], str]:
schema_path = f"resources/schemas/v{version}/schema.json"
ref = importlib_resources.files("openapi_spec_validator") / schema_path
with importlib_resources.as_file(ref) as resource_path:
schema_path_full = path.join(path.dirname(__file__), resource_path)
return FilePathReader(schema_path_full).read()
41 changes: 25 additions & 16 deletions openapi_spec_validator/shortcuts.py
@@ -1,41 +1,50 @@
"""OpenAPI spec validator shortcuts module."""
import urllib.parse
from typing import Any
from typing import Callable
from typing import Hashable
from typing import Mapping
from typing import Tuple

from jsonschema_spec.handlers import all_urls_handler

from openapi_spec_validator.exceptions import ValidatorDetectError
from openapi_spec_validator.validation.validators import SpecValidator


def detect_validator(choices, spec):
def detect_validator(choices: Mapping[Tuple[str, str], SpecValidator], spec: Mapping[Hashable, Any]) -> SpecValidator:
for (key, value), validator in choices.items():
if key in spec and spec[key].startswith(value):
return validator
raise ValidatorDetectError("Spec schema version not detected")


def validate_spec_detect_factory(choices):
def validate(spec, spec_url=''):
validator_class = detect_validator(choices, spec)
return validator_class.validate(spec, spec_url=spec_url)
def validate_spec_detect_factory(choices: Mapping[Tuple[str, str], SpecValidator]) -> Callable[[Mapping[Hashable, Any], str], None]:
def validate(spec: Mapping[Hashable, Any], spec_url: str = "") -> None:
validator = detect_validator(choices, spec)
return validator.validate(spec, spec_url=spec_url)

return validate


def validate_spec_factory(validator_class):
def validate(spec, spec_url=''):
return validator_class.validate(spec, spec_url=spec_url)
def validate_spec_factory(validator: SpecValidator) -> Callable[[Mapping[Hashable, Any], str], None]:
def validate(spec: Mapping[Hashable, Any], spec_url: str = "") -> None:
return validator.validate(spec, spec_url=spec_url)

return validate


def validate_spec_url_detect_factory(choices):
def validate(spec_url):
def validate_spec_url_detect_factory(choices: Mapping[Tuple[str, str], SpecValidator]) -> Callable[[str], None]:
def validate(spec_url: str) -> None:
spec = all_urls_handler(spec_url)
validator_class = detect_validator(choices, spec)
return validator_class.validate(spec, spec_url=spec_url)
validator = detect_validator(choices, spec)
return validator.validate(spec, spec_url=spec_url)

return validate


def validate_spec_url_factory(validator_class):
def validate(spec_url):
def validate_spec_url_factory(validator: SpecValidator) -> Callable[[str], None]:
def validate(spec_url: str) -> None:
spec = all_urls_handler(spec_url)
return validator_class.validate(spec, spec_url=spec_url)
return validator.validate(spec, spec_url=spec_url)

return validate

0 comments on commit c35af01

Please sign in to comment.