From f6708648dfe1e6162c426b8bdfdd2c3784523e98 Mon Sep 17 00:00:00 2001 From: Manuel Stausberg Date: Mon, 26 Sep 2022 17:07:02 +0200 Subject: [PATCH 01/10] update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1fa4136f..e4a1fac7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.pyc dist/ .tox +.venv \ No newline at end of file From aef0ee80c2e062e6b8f3c5d2e346092c708449ad Mon Sep 17 00:00:00 2001 From: Manuel Stausberg Date: Mon, 26 Sep 2022 17:07:14 +0200 Subject: [PATCH 02/10] add type hints --- src/kubernetes_validate/utils.py | 63 +++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/src/kubernetes_validate/utils.py b/src/kubernetes_validate/utils.py index 820067a0..a7d5ea2d 100644 --- a/src/kubernetes_validate/utils.py +++ b/src/kubernetes_validate/utils.py @@ -1,27 +1,29 @@ from __future__ import print_function -from distutils.version import LooseVersion import json -import jsonschema import os import platform -import pkg_resources import re import sys +from distutils.version import LooseVersion +from typing import Dict, Generator, List + +import jsonschema +import pkg_resources import yaml from kubernetes_validate.version import __version__ class ValidationError(jsonschema.ValidationError): - def __init__(self, caught, version): + def __init__(self, caught: Exception, version: str): self.version = version for attr, value in caught.__dict__.items(): self.__dict__[attr] = value class SchemaNotFoundError(Exception): - def __init__(self, kind, version, api_version): + def __init__(self, kind: str, version: str, api_version: str): self.kind = kind self.version = version self.api_version = api_version @@ -30,33 +32,33 @@ def __init__(self, kind, version, api_version): class VersionNotSupportedError(Exception): - def __init__(self, version): + def __init__(self, version: str): self.version = version self.message = ("kubernetes-validate does not support version %s" % self.version) class InvalidSchemaError(Exception): - def __init__(self, message): + def __init__(self, message: str): self.message = message -def all_versions(): +def all_versions() -> List[str]: schemas = pkg_resources.resource_listdir('kubernetes_validate', '/kubernetes-json-schema') version_regex = re.compile(r'^v([^-]*).*') return sorted([version_regex.sub(r"\1", schema) for schema in schemas if version_regex.match(schema)], key=LooseVersion) -def major_minor(version): +def major_minor(version: str) -> str: version_regex = re.compile(r'^(\d+\.\d+).*') return version_regex.sub(r"\1", version) -def latest_version(): +def latest_version() -> str: return all_versions()[-1] -def validate(data, desired_version, strict=False): +def validate(data, desired_version: str, strict=False) -> str: # strip initial v from version (I keep forgetting, so other people will too) if desired_version.startswith('v'): desired_version = desired_version[1:] @@ -84,7 +86,7 @@ def validate(data, desired_version, strict=False): api_version=data['apiVersion']) try: schema = json.load(f) - except json.JsonDecodeError: + except json.decoder.JSONDecodeError: raise InvalidSchemaError("Couldn't parse schema %s" % schema_file) finally: f.close() @@ -102,31 +104,38 @@ def validate(data, desired_version, strict=False): return major_minor(version) except jsonschema.ValidationError as e: raise ValidationError(e, version=major_minor(version)) - except jsonschema.exceptions.RefResolutionError: + except jsonschema.RefResolutionError: raise -def kn(resource): +def kn(resource: dict) -> str: return "%s/%s" % (resource["kind"].lower(), resource["metadata"]["name"]) -def validate_resource(resource, filename, version, strict, quiet, no_warn): +def validate_resource( + resource: dict, + filename: str, + version: str, + strict: bool, + quiet: bool, + no_warn: bool, +) -> int: try: validated_version = validate(resource, version, strict) if not quiet: print("INFO %s passed for resource %s against version %s" % - (filename, kn(resource), validated_version)) + (filename, kn(resource), validated_version)) except ValidationError as e: path = '.'.join([str(item) for item in e.path]) print("ERROR %s did not validate for resource %s against version %s: %s: %s" % - (filename, kn(resource), e.version, path, e.message)) + (filename, kn(resource), e.version, path, e.message)) return 1 except (SchemaNotFoundError, InvalidSchemaError) as e: if not no_warn: print("WARN %s %s" % (filename, e.message)) except VersionNotSupportedError: print("FATAL kubernetes-validate %s does not support kubernetes version %s" % - (__version__, version)) + (__version__, version)) return 2 except Exception as e: print("ERROR %s could not be validated: %s" % (filename, str(e))) @@ -134,7 +143,8 @@ def validate_resource(resource, filename, version, strict, quiet, no_warn): return 0 -def construct_value(load, node): + +def construct_value(load, node: yaml.ScalarNode) -> Generator[str, None, None]: if not isinstance(node, yaml.ScalarNode): raise yaml.constructor.ConstructorError( "while constructing a value", @@ -144,7 +154,7 @@ def construct_value(load, node): yield str(node.value) -def resources_from_file(filename): +def resources_from_file(filename: str) -> List[Dict]: # Handle nodes that start with '=' # See https://github.com/yaml/pyyaml/issues/89 yaml.SafeLoader.add_constructor(u'tag:yaml.org,2002:value', construct_value) @@ -166,8 +176,17 @@ def resources_from_file(filename): return data -def validate_file(filename, version, strict, quiet, no_warn): +def validate_file( + filename: str, version: str, strict: bool, quiet: bool, no_warn: bool +) -> int: rc = 0 for resource in resources_from_file(filename): - rc |= validate_resource(resource, filename, version, strict, quiet, no_warn) + rc |= validate_resource( + resource=resource, + filename=filename, + version=version, + strict=strict, + quiet=quiet, + no_warn=no_warn, + ) return rc From 1b694c3053aba890aa1b151dc405d075f4455d78 Mon Sep 17 00:00:00 2001 From: Manuel Stausberg Date: Sun, 2 Oct 2022 14:05:33 +0200 Subject: [PATCH 03/10] make indentation adhere to pep8 --- src/kubernetes_validate/utils.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/kubernetes_validate/utils.py b/src/kubernetes_validate/utils.py index a7d5ea2d..5f25d702 100644 --- a/src/kubernetes_validate/utils.py +++ b/src/kubernetes_validate/utils.py @@ -124,18 +124,18 @@ def validate_resource( validated_version = validate(resource, version, strict) if not quiet: print("INFO %s passed for resource %s against version %s" % - (filename, kn(resource), validated_version)) + (filename, kn(resource), validated_version)) except ValidationError as e: path = '.'.join([str(item) for item in e.path]) print("ERROR %s did not validate for resource %s against version %s: %s: %s" % - (filename, kn(resource), e.version, path, e.message)) + (filename, kn(resource), e.version, path, e.message)) return 1 except (SchemaNotFoundError, InvalidSchemaError) as e: if not no_warn: print("WARN %s %s" % (filename, e.message)) except VersionNotSupportedError: print("FATAL kubernetes-validate %s does not support kubernetes version %s" % - (__version__, version)) + (__version__, version)) return 2 except Exception as e: print("ERROR %s could not be validated: %s" % (filename, str(e))) @@ -143,7 +143,6 @@ def validate_resource( return 0 - def construct_value(load, node: yaml.ScalarNode) -> Generator[str, None, None]: if not isinstance(node, yaml.ScalarNode): raise yaml.constructor.ConstructorError( From 74c51ab864ab69c3bdab70e0875d9b15918c6c0f Mon Sep 17 00:00:00 2001 From: Manuel Stausberg Date: Sun, 9 Oct 2022 17:36:38 +0200 Subject: [PATCH 04/10] add some more type hints --- src/kubernetes_validate/__main__.py | 2 +- src/kubernetes_validate/utils.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/kubernetes_validate/__main__.py b/src/kubernetes_validate/__main__.py index 12855120..6bc4fa8f 100644 --- a/src/kubernetes_validate/__main__.py +++ b/src/kubernetes_validate/__main__.py @@ -10,7 +10,7 @@ from kubernetes_validate.version import __version__ -def main(): +def main() -> int: parser = argparse.ArgumentParser(description='validate a kubernetes resource definition') parser.add_argument('-k', '--kubernetes-version', action='append', help='version of kubernetes against which to validate. Defaults to %s' % diff --git a/src/kubernetes_validate/utils.py b/src/kubernetes_validate/utils.py index 5f25d702..f677e73c 100644 --- a/src/kubernetes_validate/utils.py +++ b/src/kubernetes_validate/utils.py @@ -6,7 +6,7 @@ import re import sys from distutils.version import LooseVersion -from typing import Dict, Generator, List +from typing import Any, Dict, Generator, List import jsonschema import pkg_resources @@ -58,7 +58,7 @@ def latest_version() -> str: return all_versions()[-1] -def validate(data, desired_version: str, strict=False) -> str: +def validate(data: Dict[str, Any], desired_version: str, strict: bool=False) -> str: # strip initial v from version (I keep forgetting, so other people will too) if desired_version.startswith('v'): desired_version = desired_version[1:] @@ -143,7 +143,7 @@ def validate_resource( return 0 -def construct_value(load, node: yaml.ScalarNode) -> Generator[str, None, None]: +def construct_value(load: yaml.Loader, node: yaml.ScalarNode) -> Generator[str, None, None]: if not isinstance(node, yaml.ScalarNode): raise yaml.constructor.ConstructorError( "while constructing a value", From 1cd07d50f864ea5e202cad6e63d9bed4a3a51cac Mon Sep 17 00:00:00 2001 From: Manuel Stausberg Date: Sun, 9 Oct 2022 17:38:09 +0200 Subject: [PATCH 05/10] drop python 2.7 support --- tox.ini | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tox.ini b/tox.ini index 5ed91679..f1a3b413 100644 --- a/tox.ini +++ b/tox.ini @@ -1,22 +1,23 @@ [tox] minversion = 1.6 -envlist = py{27,39}-{pytest,flake8} +envlist = py{38,39,310}-{pytest,flake8} [gh-actions] python = - 2.7: py27 + 3.8: py38 3.9: py39 + 3.10: py310 [testenv] -[testenv:py{27,39}-pytest] +[testenv:py{38,39,310}-pytest] deps = -rtest-deps.txt commands = pytest passenv = HOME recreate = False -[testenv:py{27,39}-flake8] +[testenv:py{38,39,310}-flake8] platform = linux|darwin deps = flake8 commands = python -m flake8 src From d6a713730feb951a154c02329f3280f027239f9f Mon Sep 17 00:00:00 2001 From: Manuel Stausberg Date: Sun, 9 Oct 2022 17:41:07 +0200 Subject: [PATCH 06/10] add mypy --- test-deps.txt | 4 ++++ tox.ini | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/test-deps.txt b/test-deps.txt index da4bfde5..16b02ee0 100644 --- a/test-deps.txt +++ b/test-deps.txt @@ -3,3 +3,7 @@ pytest pep8-naming tox wheel +mypy +types-jsonschema +types-setuptools +types-PyYAML diff --git a/tox.ini b/tox.ini index f1a3b413..a238aa37 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion = 1.6 -envlist = py{38,39,310}-{pytest,flake8} +envlist = py{38,39,310}-{pytest,flake8},mypy [gh-actions] python = @@ -23,3 +23,11 @@ deps = flake8 commands = python -m flake8 src usedevelop = True recreate = False + +[testenv:mypy] +deps = -rtest-deps.txt +commands = mypy src \ + --disallow-untyped-calls \ + --disallow-untyped-defs \ + --disallow-incomplete-defs +recreate = False From 51c73c0bcdc425b4b6bc664a033ef79ad56bb75b Mon Sep 17 00:00:00 2001 From: Manuel Stausberg Date: Sun, 9 Oct 2022 17:51:25 +0200 Subject: [PATCH 07/10] fix whitespace --- src/kubernetes_validate/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kubernetes_validate/utils.py b/src/kubernetes_validate/utils.py index f677e73c..4da989d0 100644 --- a/src/kubernetes_validate/utils.py +++ b/src/kubernetes_validate/utils.py @@ -58,7 +58,7 @@ def latest_version() -> str: return all_versions()[-1] -def validate(data: Dict[str, Any], desired_version: str, strict: bool=False) -> str: +def validate(data: Dict[str, Any], desired_version: str, strict: bool = False) -> str: # strip initial v from version (I keep forgetting, so other people will too) if desired_version.startswith('v'): desired_version = desired_version[1:] From cd93c65c147c47b61891b0d01aadbbf4ce572932 Mon Sep 17 00:00:00 2001 From: Manuel Stausberg Date: Sun, 9 Oct 2022 17:56:33 +0200 Subject: [PATCH 08/10] make python versions for gh-actions match those for tox --- .github/workflows/tox.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tox.yaml b/.github/workflows/tox.yaml index 7147fd66..16bb159e 100644 --- a/.github/workflows/tox.yaml +++ b/.github/workflows/tox.yaml @@ -10,7 +10,7 @@ jobs: strategy: matrix: platform: [ubuntu-latest, windows-latest] - python-version: [2.7, 3.9] + python-version: [3.8, 3.9, 3.10] steps: - uses: actions/checkout@v1 From 80f20d788ca3dd73df69badb830632a03f88e756 Mon Sep 17 00:00:00 2001 From: Manuel Stausberg Date: Sun, 9 Oct 2022 18:01:45 +0200 Subject: [PATCH 09/10] fix gh-actions ("python-version" float -> str) --- .github/workflows/tox.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tox.yaml b/.github/workflows/tox.yaml index 16bb159e..a3de57e7 100644 --- a/.github/workflows/tox.yaml +++ b/.github/workflows/tox.yaml @@ -10,7 +10,7 @@ jobs: strategy: matrix: platform: [ubuntu-latest, windows-latest] - python-version: [3.8, 3.9, 3.10] + python-version: ["3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v1 From 59f60a2e049a57b2094ec2e12df571f005da63de Mon Sep 17 00:00:00 2001 From: Manuel Stausberg Date: Sun, 9 Oct 2022 18:19:11 +0200 Subject: [PATCH 10/10] add mypy to tox gh-actions --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index a238aa37..3aa34966 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,7 @@ envlist = py{38,39,310}-{pytest,flake8},mypy python = 3.8: py38 3.9: py39 - 3.10: py310 + 3.10: py310, mypy [testenv]