Skip to content

Commit

Permalink
add mypy type hints to all non-test code + update mypy config (#300)
Browse files Browse the repository at this point in the history
* begin adding type hints

* fixes

* more hints

* 1

* 1

* 1

* type hint marshmallow converters

* 1

* revert

* change import to hopefully make compatible with older python

* more fixes for old python

* fix for old marshmallow

* generator v2

* v3 generator

* swagger objects

* more typing

* add a few missing hints

* add typed

* minor fixes

* update type + rerun black

* more type hints

* finish adding type hints

* fix tests

* address comments

* fix

* minor fix

* fix typo
  • Loading branch information
dekim24 authored Feb 8, 2024
1 parent d4ebdb0 commit 697e14f
Show file tree
Hide file tree
Showing 27 changed files with 961 additions and 497 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repos:
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.4.1
rev: v1.8.0
hooks:
- id: mypy
- repo: https://github.com/Lucas-C/pre-commit-hooks-markup
Expand Down
6 changes: 3 additions & 3 deletions flask_rebar/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@

from flask_rebar.validation import ResponseSchema, RequestSchema

from flask_rebar.swagger_generation import (
from flask_rebar.swagger_generation.swagger_generator_v2 import SwaggerV2Generator
from flask_rebar.swagger_generation.swagger_generator_v3 import SwaggerV3Generator
from flask_rebar.swagger_generation.swagger_objects import (
ExternalDocumentation,
SwaggerV2Generator,
SwaggerV3Generator,
Tag,
Server,
ServerVariable,
Expand Down
2 changes: 1 addition & 1 deletion flask_rebar/authenticators/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Authenticator:
extend this class.
"""

def authenticate(self):
def authenticate(self) -> None:
"""
Implementations of :class:`Authenticator` should override this method.
Expand Down
14 changes: 8 additions & 6 deletions flask_rebar/authenticators/header_api_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
:copyright: Copyright 2018 PlanGrid, Inc., see AUTHORS.
:license: MIT, see LICENSE for details.
"""
from typing import Dict

from flask import request, g
from hmac import compare_digest

from flask_rebar import errors, messages
from flask_rebar.authenticators.base import Authenticator


def get_authenticated_app_name():
def get_authenticated_app_name() -> str:
return g.authenticated_app_name


Expand All @@ -39,16 +41,16 @@ class HeaderApiKeyAuthenticator(Authenticator):
# This is the default name, if someone doesn't need about this feature.
DEFAULT_APP_NAME = "default"

def __init__(self, header, name="sharedSecret"):
def __init__(self, header: str, name: str = "sharedSecret") -> None:
self.header = header
self.keys = {}
self.keys: Dict[str, str] = {}
self.name = name

@property
def authenticated_app_name(self):
def authenticated_app_name(self) -> str:
return get_authenticated_app_name()

def register_key(self, key, app_name=DEFAULT_APP_NAME):
def register_key(self, key: str, app_name: str = DEFAULT_APP_NAME) -> None:
"""
Register a client application's shared secret.
Expand All @@ -60,7 +62,7 @@ def register_key(self, key, app_name=DEFAULT_APP_NAME):
"""
self.keys[key] = app_name

def authenticate(self):
def authenticate(self) -> None:
if self.header not in request.headers:
raise errors.Unauthorized(messages.missing_auth_token)

Expand Down
17 changes: 12 additions & 5 deletions flask_rebar/compat.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
from typing import Any
from typing import Dict

import marshmallow
from marshmallow.fields import Field
from marshmallow.schema import Schema

from flask import current_app
from flask_rebar.validation import filter_dump_only, RequireOnDumpMixin


def set_data_key(field, key):
def set_data_key(field: Field, key: str) -> Field:
field.data_key = key
return field


def get_data_key(field):
def get_data_key(field: Field) -> str:
if field.data_key:
return field.data_key
if field.name is None:
raise ValueError("Field name cannot be None")
return field.name


def load(schema, data):
def load(schema: Schema, data: Dict[str, Any]) -> Dict[str, Any]:
return schema.load(data)


def dump(schema, data):
def dump(schema: Schema, data: Dict[str, Any]) -> Dict[str, Any]:
"""
Our wrapper for Schema.dump that includes optional validation.
Note that as of Flask-Rebar 2.x (hence Marshmallow 3.x), Marshmallow's default behavior is to NOT validate on dump
Expand Down Expand Up @@ -54,6 +61,6 @@ def dump(schema, data):
return result


def exclude_unknown_fields(schema):
def exclude_unknown_fields(schema: Schema) -> Schema:
schema.unknown = marshmallow.EXCLUDE
return schema
8 changes: 7 additions & 1 deletion flask_rebar/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
:copyright: Copyright 2018 PlanGrid, Inc., see AUTHORS.
:license: MIT, see LICENSE for details.
"""
from flask_rebar.messages import ErrorMessage
from typing import Any, Dict, Optional, Union


class HttpJsonError(Exception):
Expand All @@ -30,7 +32,11 @@ class HttpJsonError(Exception):
default_message: str
http_status_code: int

def __init__(self, msg=None, additional_data=None):
def __init__(
self,
msg: Optional[Union[str, ErrorMessage]] = None,
additional_data: Optional[Dict[str, Any]] = None,
) -> None:
self.error_message = msg or self.default_message
self.additional_data = additional_data
super().__init__(self.error_message)
Expand Down
6 changes: 3 additions & 3 deletions flask_rebar/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,21 +65,21 @@ class ErrorCode:
)


def required_field_missing(field_name):
def required_field_missing(field_name: str) -> ErrorMessage:
return ErrorMessage(
f"Required field missing: {field_name}",
ErrorCode.REQUIRED_FIELD_MISSING,
)


def required_field_empty(field_name):
def required_field_empty(field_name: str) -> ErrorMessage:
return ErrorMessage(
f"Value for required field cannot be None: {field_name}",
ErrorCode.REQUIRED_FIELD_EMPTY,
)


def unsupported_fields(field_names):
def unsupported_fields(field_names: str) -> ErrorMessage:
return ErrorMessage(
"Unexpected field: {}".format(",".join(field_names)),
ErrorCode.UNSUPPORTED_FIELDS,
Expand Down
Empty file added flask_rebar/py.typed
Empty file.
Loading

0 comments on commit 697e14f

Please sign in to comment.