From 73484303e3f15b779da53497dcfad4fd1f957fcc Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Fri, 30 Aug 2024 12:50:22 +0100 Subject: [PATCH 1/3] Add runtime type checking in more places --- conftest.py | 2 +- src/mock_vws/_flask_server/healthcheck.py | 3 +++ src/mock_vws/_flask_server/target_manager.py | 6 +++++- src/mock_vws/_flask_server/vwq.py | 1 + src/mock_vws/_flask_server/vws.py | 2 ++ src/mock_vws/_requests_mock_server/decorators.py | 2 ++ .../mock_web_services_api.py | 2 ++ src/mock_vws/_services_validators/exceptions.py | 16 ++++++++++++++++ src/mock_vws/target.py | 2 ++ src/mock_vws/target_manager.py | 1 - src/mock_vws/target_raters.py | 4 ++++ 11 files changed, 38 insertions(+), 3 deletions(-) diff --git a/conftest.py b/conftest.py index e8f679b7..f7b14d1d 100644 --- a/conftest.py +++ b/conftest.py @@ -32,8 +32,8 @@ def pytest_collection_modifyitems(items: list[pytest.Item]) -> None: ).pytest() -@pytest.hookimpl(optionalhook=True) @beartype +@pytest.hookimpl(optionalhook=True) def pytest_set_filtered_exceptions() -> tuple[type[Exception], ...]: """ Return exceptions to retry on. diff --git a/src/mock_vws/_flask_server/healthcheck.py b/src/mock_vws/_flask_server/healthcheck.py index c4513fbf..a20f3a8c 100644 --- a/src/mock_vws/_flask_server/healthcheck.py +++ b/src/mock_vws/_flask_server/healthcheck.py @@ -7,7 +7,10 @@ import sys from http import HTTPStatus +from beartype import beartype + +@beartype def flask_app_healthy(port: int) -> bool: """ Check if the Flask app is healthy. diff --git a/src/mock_vws/_flask_server/target_manager.py b/src/mock_vws/_flask_server/target_manager.py index 5ba64629..89bb7321 100644 --- a/src/mock_vws/_flask_server/target_manager.py +++ b/src/mock_vws/_flask_server/target_manager.py @@ -59,6 +59,7 @@ class TargetManagerSettings(BaseSettings): "/databases/", methods=[HTTPMethod.DELETE], ) +@beartype def delete_database(database_name: str) -> Response: """ Delete a database. @@ -92,6 +93,7 @@ def get_databases() -> Response: @TARGET_MANAGER_FLASK_APP.route("/databases", methods=[HTTPMethod.POST]) +@beartype def create_database() -> Response: """ Create a new database. @@ -177,6 +179,7 @@ def create_database() -> Response: "/databases//targets", methods=[HTTPMethod.POST], ) +@beartype def create_target(database_name: str) -> Response: """ Create a new target in a given database. @@ -212,8 +215,9 @@ def create_target(database_name: str) -> Response: @TARGET_MANAGER_FLASK_APP.route( "/databases//targets/", - methods=[HTTPMethod.DELETE], + methods={HTTPMethod.DELETE}, ) +@beartype def delete_target(database_name: str, target_id: str) -> Response: """ Delete a target. diff --git a/src/mock_vws/_flask_server/vwq.py b/src/mock_vws/_flask_server/vwq.py index 567b62ee..9ab8a34e 100644 --- a/src/mock_vws/_flask_server/vwq.py +++ b/src/mock_vws/_flask_server/vwq.py @@ -76,6 +76,7 @@ def get_all_databases() -> set[VuforiaDatabase]: @CLOUDRECO_FLASK_APP.before_request +@beartype def set_terminate_wsgi_input() -> None: """ We set ``wsgi.input_terminated`` to ``True`` when going through diff --git a/src/mock_vws/_flask_server/vws.py b/src/mock_vws/_flask_server/vws.py index 4d6f28d3..ba81f0a1 100644 --- a/src/mock_vws/_flask_server/vws.py +++ b/src/mock_vws/_flask_server/vws.py @@ -151,6 +151,7 @@ def handle_exceptions(exc: ValidatorError) -> Response: @VWS_FLASK_APP.route("/targets", methods=[HTTPMethod.POST]) +@beartype def add_target() -> Response: """ Add a target. @@ -334,6 +335,7 @@ def delete_target(target_id: str) -> Response: @VWS_FLASK_APP.route("/summary", methods=[HTTPMethod.GET]) +@beartype def database_summary() -> Response: """ Get a database summary report. diff --git a/src/mock_vws/_requests_mock_server/decorators.py b/src/mock_vws/_requests_mock_server/decorators.py index 1c856e9d..cab3bd48 100644 --- a/src/mock_vws/_requests_mock_server/decorators.py +++ b/src/mock_vws/_requests_mock_server/decorators.py @@ -8,6 +8,7 @@ from urllib.parse import urljoin, urlparse import requests +from beartype import beartype from responses import RequestsMock from mock_vws.database import VuforiaDatabase @@ -28,6 +29,7 @@ _BRISQUE_TRACKING_RATER = BrisqueTargetTrackingRater() +@beartype class MockVWS(ContextDecorator): """ Route requests to Vuforia's Web Service APIs to fakes of those APIs. diff --git a/src/mock_vws/_requests_mock_server/mock_web_services_api.py b/src/mock_vws/_requests_mock_server/mock_web_services_api.py index bcd6bfb2..8040a834 100644 --- a/src/mock_vws/_requests_mock_server/mock_web_services_api.py +++ b/src/mock_vws/_requests_mock_server/mock_web_services_api.py @@ -59,6 +59,7 @@ def route( A decorator which takes methods and makes them recognizable as routes. """ + @beartype def decorator( method: Callable[..., _ResponseType], ) -> Callable[..., _ResponseType]: @@ -82,6 +83,7 @@ def decorator( return decorator +@beartype def _body_bytes(request: PreparedRequest) -> bytes: """ Return the body of a request as bytes. diff --git a/src/mock_vws/_services_validators/exceptions.py b/src/mock_vws/_services_validators/exceptions.py index 8fc8fc4b..1d8bdf48 100644 --- a/src/mock_vws/_services_validators/exceptions.py +++ b/src/mock_vws/_services_validators/exceptions.py @@ -8,6 +8,8 @@ from http import HTTPStatus from pathlib import Path +from beartype import beartype + from mock_vws._constants import ResultCodes from mock_vws._mock_common import json_dump @@ -28,6 +30,7 @@ class UnknownTargetError(ValidatorError): 'UnknownTarget'. """ + @beartype def __init__(self) -> None: """ Attributes: @@ -67,6 +70,7 @@ class ProjectInactiveError(ValidatorError): 'ProjectInactive'. """ + @beartype def __init__(self) -> None: """ Attributes: @@ -106,6 +110,7 @@ class AuthenticationFailureError(ValidatorError): 'AuthenticationFailure'. """ + @beartype def __init__(self) -> None: """ Attributes: @@ -144,6 +149,7 @@ class FailError(ValidatorError): Exception raised when Vuforia returns a response with a result code 'Fail'. """ + @beartype def __init__(self, *, status_code: HTTPStatus) -> None: """ Attributes: @@ -222,6 +228,7 @@ class TargetNameExistError(ValidatorError): 'TargetNameExist'. """ + @beartype def __init__(self) -> None: """ Attributes: @@ -263,6 +270,7 @@ class OopsErrorOccurredResponseError(ValidatorError): This has been seen to happen when the given name includes a bad character. """ + @beartype def __init__(self) -> None: """ Attributes: @@ -302,6 +310,7 @@ class BadImageError(ValidatorError): 'BadImage'. """ + @beartype def __init__(self) -> None: """ Attributes: @@ -341,6 +350,7 @@ class ImageTooLargeError(ValidatorError): 'ImageTooLarge'. """ + @beartype def __init__(self) -> None: """ Attributes: @@ -380,6 +390,7 @@ class RequestTimeTooSkewedError(ValidatorError): 'RequestTimeTooSkewed'. """ + @beartype def __init__(self) -> None: """ Attributes: @@ -419,6 +430,7 @@ class ContentLengthHeaderTooLargeError(ValidatorError): """ # We skip coverage here as running a test to cover this is very slow. + @beartype def __init__(self) -> None: # pragma: no cover """ Attributes: @@ -449,6 +461,7 @@ class ContentLengthHeaderNotIntError(ValidatorError): Exception raised when the given content length header is not an integer. """ + @beartype def __init__(self) -> None: """ Attributes: @@ -488,6 +501,7 @@ class UnnecessaryRequestBodyError(ValidatorError): Exception raised when a request body is given but not necessary. """ + @beartype def __init__(self) -> None: """ Attributes: @@ -518,6 +532,7 @@ class TargetStatusNotSuccessError(ValidatorError): success status. """ + @beartype def __init__(self) -> None: """ Attributes: @@ -556,6 +571,7 @@ class TargetStatusProcessingError(ValidatorError): Exception raised when trying to delete a target which is processing. """ + @beartype def __init__(self) -> None: """ Attributes: diff --git a/src/mock_vws/target.py b/src/mock_vws/target.py index d1185bac..6ffd0256 100644 --- a/src/mock_vws/target.py +++ b/src/mock_vws/target.py @@ -39,6 +39,7 @@ class TargetDict(TypedDict): tracking_rating: int +@beartype def _random_hex() -> str: """ Return a random hex value. @@ -46,6 +47,7 @@ def _random_hex() -> str: return uuid.uuid4().hex +@beartype def _time_now() -> datetime.datetime: """ Return the current time in the GMT time zone. diff --git a/src/mock_vws/target_manager.py b/src/mock_vws/target_manager.py index 75ed7486..3635aefb 100644 --- a/src/mock_vws/target_manager.py +++ b/src/mock_vws/target_manager.py @@ -85,7 +85,6 @@ def add_database(self, database: VuforiaDatabase) -> None: @property @beartype - @beartype def databases(self) -> set[VuforiaDatabase]: """ All cloud databases. diff --git a/src/mock_vws/target_raters.py b/src/mock_vws/target_raters.py index d0012e36..5b34c42a 100644 --- a/src/mock_vws/target_raters.py +++ b/src/mock_vws/target_raters.py @@ -62,6 +62,7 @@ def __call__(self, image_content: bytes) -> int: class RandomTargetTrackingRater: """A rater which returns a random number.""" + @beartype def __call__(self, image_content: bytes) -> int: """ A random target tracking rating. @@ -76,6 +77,7 @@ def __call__(self, image_content: bytes) -> int: class HardcodedTargetTrackingRater: """A rater which returns a hardcoded number.""" + @beartype def __init__(self, rating: int) -> None: """ Args: @@ -83,6 +85,7 @@ def __init__(self, rating: int) -> None: """ self._rating = rating + @beartype def __call__(self, image_content: bytes) -> int: """ A random target tracking rating. @@ -97,6 +100,7 @@ def __call__(self, image_content: bytes) -> int: class BrisqueTargetTrackingRater: """A rater which returns a rating based on a BRISQUE score.""" + @beartype def __call__(self, image_content: bytes) -> int: """ A rating based on a BRISQUE score. From 0706a3ef6b34115dc43c0f2a3ae7010538750d3e Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Fri, 30 Aug 2024 13:21:56 +0100 Subject: [PATCH 2/3] Use beartype on classes --- src/mock_vws/_constants.py | 4 +++ src/mock_vws/_flask_server/target_manager.py | 2 ++ src/mock_vws/_flask_server/vwq.py | 2 ++ src/mock_vws/_flask_server/vws.py | 2 ++ src/mock_vws/_query_validators/exceptions.py | 24 +++++++++++++++ .../_requests_mock_server/decorators.py | 6 ++-- .../mock_web_query_api.py | 4 +++ .../mock_web_services_api.py | 3 +- .../_services_validators/exceptions.py | 30 ++++++++++--------- src/mock_vws/database.py | 8 +---- src/mock_vws/image_matchers.py | 4 +-- src/mock_vws/states.py | 3 ++ src/mock_vws/target.py | 5 +--- src/mock_vws/target_manager.py | 4 --- src/mock_vws/target_raters.py | 7 ++--- 15 files changed, 68 insertions(+), 40 deletions(-) diff --git a/src/mock_vws/_constants.py b/src/mock_vws/_constants.py index 84b22380..dc964520 100644 --- a/src/mock_vws/_constants.py +++ b/src/mock_vws/_constants.py @@ -4,7 +4,10 @@ from enum import Enum +from beartype import beartype + +@beartype class ResultCodes(Enum): """ Constants representing various VWS result codes. @@ -37,6 +40,7 @@ class ResultCodes(Enum): TOO_MANY_REQUESTS = "TooManyRequests" +@beartype class TargetStatuses(Enum): """ Constants representing VWS target statuses. diff --git a/src/mock_vws/_flask_server/target_manager.py b/src/mock_vws/_flask_server/target_manager.py index 89bb7321..83afda03 100644 --- a/src/mock_vws/_flask_server/target_manager.py +++ b/src/mock_vws/_flask_server/target_manager.py @@ -30,6 +30,7 @@ TARGET_MANAGER = TargetManager() +@beartype class _TargetRaterChoice(StrEnum): """Target rater choices.""" @@ -48,6 +49,7 @@ def to_target_rater(self) -> TargetTrackingRater: return rater +@beartype class TargetManagerSettings(BaseSettings): """Settings for the Target Manager Flask app.""" diff --git a/src/mock_vws/_flask_server/vwq.py b/src/mock_vws/_flask_server/vwq.py index 9ab8a34e..f0b7394c 100644 --- a/src/mock_vws/_flask_server/vwq.py +++ b/src/mock_vws/_flask_server/vwq.py @@ -32,6 +32,7 @@ CLOUDRECO_FLASK_APP.config["PROPAGATE_EXCEPTIONS"] = True +@beartype class _ImageMatcherChoice(StrEnum): """Image matcher choices.""" @@ -49,6 +50,7 @@ def to_image_matcher(self) -> ImageMatcher: return matcher +@beartype class VWQSettings(BaseSettings): """Settings for the VWQ Flask app.""" diff --git a/src/mock_vws/_flask_server/vws.py b/src/mock_vws/_flask_server/vws.py index ba81f0a1..d5913f47 100644 --- a/src/mock_vws/_flask_server/vws.py +++ b/src/mock_vws/_flask_server/vws.py @@ -46,6 +46,7 @@ _LOGGER = logging.getLogger(name=__name__) +@beartype class _ImageMatcherChoice(StrEnum): """Image matcher choices.""" @@ -63,6 +64,7 @@ def to_image_matcher(self) -> ImageMatcher: return matcher +@beartype class VWSSettings(BaseSettings): """Settings for the VWS Flask app.""" diff --git a/src/mock_vws/_query_validators/exceptions.py b/src/mock_vws/_query_validators/exceptions.py index 48e8fb71..8a39d468 100644 --- a/src/mock_vws/_query_validators/exceptions.py +++ b/src/mock_vws/_query_validators/exceptions.py @@ -7,10 +7,13 @@ import uuid from http import HTTPStatus +from beartype import beartype + from mock_vws._constants import ResultCodes from mock_vws._mock_common import json_dump +@beartype class ValidatorError(Exception): """ A base class for exceptions thrown from mock Vuforia cloud recognition @@ -22,6 +25,7 @@ class ValidatorError(Exception): headers: dict[str, str] +@beartype class DateHeaderNotGivenError(ValidatorError): """ Exception raised when a date header is not given. @@ -52,6 +56,7 @@ def __init__(self) -> None: } +@beartype class DateFormatNotValidError(ValidatorError): """ Exception raised when the date format is not valid. @@ -83,6 +88,7 @@ def __init__(self) -> None: } +@beartype class RequestTimeTooSkewedError(ValidatorError): """ Exception raised when Vuforia returns a response with a result code @@ -118,6 +124,7 @@ def __init__(self) -> None: } +@beartype class BadImageError(ValidatorError): """ Exception raised when Vuforia returns a response with a result code @@ -160,6 +167,7 @@ def __init__(self) -> None: } +@beartype class AuthenticationFailureError(ValidatorError): """ Exception raised when Vuforia returns a response with a result code @@ -202,6 +210,7 @@ def __init__(self) -> None: } +@beartype class AuthenticationFailureGoodFormattingError(ValidatorError): """ Exception raised when Vuforia returns a response with a result code @@ -239,6 +248,7 @@ def __init__(self) -> None: } +@beartype class ImageNotGivenError(ValidatorError): """ Exception raised when an image is not given. @@ -270,6 +280,7 @@ def __init__(self) -> None: } +@beartype class AuthHeaderMissingError(ValidatorError): """ Exception raised when an auth header is not given. @@ -302,6 +313,7 @@ def __init__(self) -> None: } +@beartype class MalformedAuthHeaderError(ValidatorError): """ Exception raised when an auth header is not given. @@ -335,6 +347,7 @@ def __init__(self) -> None: } +@beartype class UnknownParametersError(ValidatorError): """ Exception raised when unknown parameters are given. @@ -366,6 +379,7 @@ def __init__(self) -> None: } +@beartype class InactiveProjectError(ValidatorError): """ Exception raised when Vuforia returns a response with a result code @@ -407,6 +421,7 @@ def __init__(self) -> None: } +@beartype class InvalidMaxNumResultsError(ValidatorError): """ Exception raised when an invalid value is given as the @@ -443,6 +458,7 @@ def __init__(self, given_value: str) -> None: } +@beartype class MaxNumResultsOutOfRangeError(ValidatorError): """ Exception raised when an integer value is given as the "max_num_results" @@ -479,6 +495,7 @@ def __init__(self, given_value: str) -> None: } +@beartype class InvalidIncludeTargetDataError(ValidatorError): """ Exception raised when an invalid value is given as the @@ -517,6 +534,7 @@ def __init__(self, given_value: str) -> None: } +@beartype class UnsupportedMediaTypeError(ValidatorError): """ Exception raised when no boundary is found for multipart data. @@ -547,6 +565,7 @@ def __init__(self) -> None: } +@beartype class InvalidAcceptHeaderError(ValidatorError): """ Exception raised when there is an invalid accept header given. @@ -577,6 +596,7 @@ def __init__(self) -> None: } +@beartype class NoBoundaryFoundError(ValidatorError): """ Exception raised when an invalid media type is given. @@ -611,6 +631,7 @@ def __init__(self) -> None: } +@beartype class ContentLengthHeaderTooLargeError(ValidatorError): """ Exception raised when the given content length header is too large. @@ -634,6 +655,7 @@ def __init__(self) -> None: # pragma: no cover } +@beartype class ContentLengthHeaderNotIntError(ValidatorError): """ Exception raised when the given content length header is not an integer. @@ -656,6 +678,7 @@ def __init__(self) -> None: } +@beartype class RequestEntityTooLargeError(ValidatorError): """ Exception raised when the given image file size is too large. @@ -699,6 +722,7 @@ def __init__(self) -> None: # pragma: no cover } +@beartype class NoContentTypeError(ValidatorError): """ Exception raised when a content type is either not given or is empty. diff --git a/src/mock_vws/_requests_mock_server/decorators.py b/src/mock_vws/_requests_mock_server/decorators.py index cab3bd48..69f6c149 100644 --- a/src/mock_vws/_requests_mock_server/decorators.py +++ b/src/mock_vws/_requests_mock_server/decorators.py @@ -8,7 +8,7 @@ from urllib.parse import urljoin, urlparse import requests -from beartype import beartype +from beartype import BeartypeConf, beartype from responses import RequestsMock from mock_vws.database import VuforiaDatabase @@ -29,7 +29,7 @@ _BRISQUE_TRACKING_RATER = BrisqueTargetTrackingRater() -@beartype +@beartype(conf=BeartypeConf(is_pep484_tower=True)) class MockVWS(ContextDecorator): """ Route requests to Vuforia's Web Service APIs to fakes of those APIs. @@ -88,7 +88,7 @@ def __init__( self._mock_vws_api = MockVuforiaWebServicesAPI( target_manager=self._target_manager, - processing_time_seconds=processing_time_seconds, + processing_time_seconds=float(processing_time_seconds), duplicate_match_checker=duplicate_match_checker, target_tracking_rater=target_tracking_rater, ) diff --git a/src/mock_vws/_requests_mock_server/mock_web_query_api.py b/src/mock_vws/_requests_mock_server/mock_web_query_api.py index 13ba6cef..cc6871b8 100644 --- a/src/mock_vws/_requests_mock_server/mock_web_query_api.py +++ b/src/mock_vws/_requests_mock_server/mock_web_query_api.py @@ -9,6 +9,7 @@ from collections.abc import Callable from http import HTTPMethod, HTTPStatus +from beartype import beartype from requests.models import PreparedRequest from mock_vws._mock_common import Route @@ -27,6 +28,7 @@ _ResponseType = tuple[int, dict[str, str], str] +@beartype def route( path_pattern: str, http_methods: set[str], @@ -66,6 +68,7 @@ def decorator( return decorator +@beartype def _body_bytes(request: PreparedRequest) -> bytes: """ Return the body of a request as bytes. @@ -77,6 +80,7 @@ def _body_bytes(request: PreparedRequest) -> bytes: return request.body +@beartype class MockVuforiaWebQueryAPI: """ A fake implementation of the Vuforia Web Query API. diff --git a/src/mock_vws/_requests_mock_server/mock_web_services_api.py b/src/mock_vws/_requests_mock_server/mock_web_services_api.py index 8040a834..36936c03 100644 --- a/src/mock_vws/_requests_mock_server/mock_web_services_api.py +++ b/src/mock_vws/_requests_mock_server/mock_web_services_api.py @@ -98,7 +98,7 @@ def _body_bytes(request: PreparedRequest) -> bytes: return request.body -@beartype +@beartype(conf=BeartypeConf(is_pep484_tower=True)) class MockVuforiaWebServicesAPI: """ A fake implementation of the Vuforia Web Services API. @@ -106,7 +106,6 @@ class MockVuforiaWebServicesAPI: This implementation is tied to the implementation of ``responses``. """ - @beartype(conf=BeartypeConf(is_pep484_tower=True)) def __init__( self, *, diff --git a/src/mock_vws/_services_validators/exceptions.py b/src/mock_vws/_services_validators/exceptions.py index 1d8bdf48..f528f1bf 100644 --- a/src/mock_vws/_services_validators/exceptions.py +++ b/src/mock_vws/_services_validators/exceptions.py @@ -14,6 +14,7 @@ from mock_vws._mock_common import json_dump +@beartype class ValidatorError(Exception): """ A base class for exceptions thrown from mock Vuforia services endpoints. @@ -24,13 +25,13 @@ class ValidatorError(Exception): headers: dict[str, str] +@beartype class UnknownTargetError(ValidatorError): """ Exception raised when Vuforia returns a response with a result code 'UnknownTarget'. """ - @beartype def __init__(self) -> None: """ Attributes: @@ -64,13 +65,13 @@ def __init__(self) -> None: } +@beartype class ProjectInactiveError(ValidatorError): """ Exception raised when Vuforia returns a response with a result code 'ProjectInactive'. """ - @beartype def __init__(self) -> None: """ Attributes: @@ -104,13 +105,13 @@ def __init__(self) -> None: } +@beartype class AuthenticationFailureError(ValidatorError): """ Exception raised when Vuforia returns a response with a result code 'AuthenticationFailure'. """ - @beartype def __init__(self) -> None: """ Attributes: @@ -144,12 +145,12 @@ def __init__(self) -> None: } +@beartype class FailError(ValidatorError): """ Exception raised when Vuforia returns a response with a result code 'Fail'. """ - @beartype def __init__(self, *, status_code: HTTPStatus) -> None: """ Attributes: @@ -183,6 +184,7 @@ def __init__(self, *, status_code: HTTPStatus) -> None: } +@beartype class MetadataTooLargeError(ValidatorError): """ Exception raised when Vuforia returns a response with a result code @@ -222,13 +224,13 @@ def __init__(self) -> None: } +@beartype class TargetNameExistError(ValidatorError): """ Exception raised when Vuforia returns a response with a result code 'TargetNameExist'. """ - @beartype def __init__(self) -> None: """ Attributes: @@ -262,6 +264,7 @@ def __init__(self) -> None: } +@beartype class OopsErrorOccurredResponseError(ValidatorError): """ Exception raised when VWS returns an HTML page which says "Oops, an error @@ -270,7 +273,6 @@ class OopsErrorOccurredResponseError(ValidatorError): This has been seen to happen when the given name includes a bad character. """ - @beartype def __init__(self) -> None: """ Attributes: @@ -304,13 +306,13 @@ def __init__(self) -> None: } +@beartype class BadImageError(ValidatorError): """ Exception raised when Vuforia returns a response with a result code 'BadImage'. """ - @beartype def __init__(self) -> None: """ Attributes: @@ -344,13 +346,13 @@ def __init__(self) -> None: } +@beartype class ImageTooLargeError(ValidatorError): """ Exception raised when Vuforia returns a response with a result code 'ImageTooLarge'. """ - @beartype def __init__(self) -> None: """ Attributes: @@ -384,13 +386,13 @@ def __init__(self) -> None: } +@beartype class RequestTimeTooSkewedError(ValidatorError): """ Exception raised when Vuforia returns a response with a result code 'RequestTimeTooSkewed'. """ - @beartype def __init__(self) -> None: """ Attributes: @@ -424,13 +426,13 @@ def __init__(self) -> None: } +@beartype class ContentLengthHeaderTooLargeError(ValidatorError): """ Exception raised when the given content length header is too large. """ # We skip coverage here as running a test to cover this is very slow. - @beartype def __init__(self) -> None: # pragma: no cover """ Attributes: @@ -456,12 +458,12 @@ def __init__(self) -> None: # pragma: no cover } +@beartype class ContentLengthHeaderNotIntError(ValidatorError): """ Exception raised when the given content length header is not an integer. """ - @beartype def __init__(self) -> None: """ Attributes: @@ -496,12 +498,12 @@ def __init__(self) -> None: } +@beartype class UnnecessaryRequestBodyError(ValidatorError): """ Exception raised when a request body is given but not necessary. """ - @beartype def __init__(self) -> None: """ Attributes: @@ -526,13 +528,13 @@ def __init__(self) -> None: } +@beartype class TargetStatusNotSuccessError(ValidatorError): """ Exception raised when trying to update a target that does not have a success status. """ - @beartype def __init__(self) -> None: """ Attributes: @@ -566,12 +568,12 @@ def __init__(self) -> None: } +@beartype class TargetStatusProcessingError(ValidatorError): """ Exception raised when trying to delete a target which is processing. """ - @beartype def __init__(self) -> None: """ Attributes: diff --git a/src/mock_vws/database.py b/src/mock_vws/database.py index 75f68edb..153c8f58 100644 --- a/src/mock_vws/database.py +++ b/src/mock_vws/database.py @@ -13,6 +13,7 @@ from mock_vws.target import Target, TargetDict +@beartype class DatabaseDict(TypedDict): """ A dictionary type which represents a database. @@ -76,7 +77,6 @@ class VuforiaDatabase: total_recos: int = 0 target_quota: int = 1000 - @beartype def to_dict(self) -> DatabaseDict: """ Dump a target to a dictionary which can be loaded as JSON. @@ -92,7 +92,6 @@ def to_dict(self) -> DatabaseDict: "targets": targets, } - @beartype def get_target(self, target_id: str) -> Target: """ Return a target from the database with the given ID. @@ -121,7 +120,6 @@ def from_dict(cls, database_dict: DatabaseDict) -> Self: ) @property - @beartype def not_deleted_targets(self) -> set[Target]: """ All targets which have not been deleted. @@ -129,7 +127,6 @@ def not_deleted_targets(self) -> set[Target]: return {target for target in self.targets if not target.delete_date} @property - @beartype def active_targets(self) -> set[Target]: """ All active targets. @@ -142,7 +139,6 @@ def active_targets(self) -> set[Target]: } @property - @beartype def inactive_targets(self) -> set[Target]: """ All inactive targets. @@ -155,7 +151,6 @@ def inactive_targets(self) -> set[Target]: } @property - @beartype def failed_targets(self) -> set[Target]: """ All failed targets. @@ -167,7 +162,6 @@ def failed_targets(self) -> set[Target]: } @property - @beartype def processing_targets(self) -> set[Target]: """ All processing targets. diff --git a/src/mock_vws/image_matchers.py b/src/mock_vws/image_matchers.py index 7b1c834d..5768ad8b 100644 --- a/src/mock_vws/image_matchers.py +++ b/src/mock_vws/image_matchers.py @@ -33,10 +33,10 @@ def __call__( ... # pylint: disable=unnecessary-ellipsis +@beartype class ExactMatcher: """A matcher which returns whether two images are exactly equal.""" - @beartype def __call__( self, first_image_content: bytes, @@ -52,10 +52,10 @@ def __call__( return bool(first_image_content == second_image_content) +@beartype class StructuralSimilarityMatcher: """A matcher which returns whether two images are similar using SSIM.""" - @beartype def __call__( self, first_image_content: bytes, diff --git a/src/mock_vws/states.py b/src/mock_vws/states.py index 53654d67..c3f55b01 100644 --- a/src/mock_vws/states.py +++ b/src/mock_vws/states.py @@ -4,7 +4,10 @@ from enum import StrEnum, auto +from beartype import beartype + +@beartype class States(StrEnum): """ Constants representing various web service states. diff --git a/src/mock_vws/target.py b/src/mock_vws/target.py index 6ffd0256..3a95fad5 100644 --- a/src/mock_vws/target.py +++ b/src/mock_vws/target.py @@ -56,6 +56,7 @@ def _time_now() -> datetime.datetime: return datetime.datetime.now(tz=gmt) +@beartype @dataclass(frozen=True, eq=True) class Target: """ @@ -80,7 +81,6 @@ class Target: upload_date: datetime.datetime = field(default_factory=_time_now) @property - @beartype def _post_processing_status(self) -> TargetStatuses: """ Return the status of the target, or what it will be when processing is @@ -104,7 +104,6 @@ def _post_processing_status(self) -> TargetStatuses: return TargetStatuses.FAILED @property - @beartype def status(self) -> str: """ Return the status of the target. @@ -130,13 +129,11 @@ def status(self) -> str: return self._post_processing_status.value @property - @beartype def _post_processing_target_rating(self) -> int: """The rating of the target after processing.""" return self.target_tracking_rater(image_content=self.image_value) @property - @beartype def tracking_rating(self) -> int: """ Return the tracking rating of the target recognition image. diff --git a/src/mock_vws/target_manager.py b/src/mock_vws/target_manager.py index 3635aefb..fb8543c2 100644 --- a/src/mock_vws/target_manager.py +++ b/src/mock_vws/target_manager.py @@ -13,14 +13,12 @@ class TargetManager: A target manager as per https://developer.vuforia.com/target-manager. """ - @beartype def __init__(self) -> None: """ Create a target manager with no databases. """ self._databases: set[VuforiaDatabase] = set() - @beartype def remove_database(self, database: VuforiaDatabase) -> None: """ Remove a cloud database. @@ -33,7 +31,6 @@ def remove_database(self, database: VuforiaDatabase) -> None: """ self._databases.remove(database) - @beartype def add_database(self, database: VuforiaDatabase) -> None: """ Add a cloud database. @@ -84,7 +81,6 @@ def add_database(self, database: VuforiaDatabase) -> None: self._databases.add(database) @property - @beartype def databases(self) -> set[VuforiaDatabase]: """ All cloud databases. diff --git a/src/mock_vws/target_raters.py b/src/mock_vws/target_raters.py index 5b34c42a..dc7542f4 100644 --- a/src/mock_vws/target_raters.py +++ b/src/mock_vws/target_raters.py @@ -59,10 +59,10 @@ def __call__(self, image_content: bytes) -> int: ... # pylint: disable=unnecessary-ellipsis +@beartype class RandomTargetTrackingRater: """A rater which returns a random number.""" - @beartype def __call__(self, image_content: bytes) -> int: """ A random target tracking rating. @@ -74,10 +74,10 @@ def __call__(self, image_content: bytes) -> int: return secrets.randbelow(exclusive_upper_bound=6) +@beartype class HardcodedTargetTrackingRater: """A rater which returns a hardcoded number.""" - @beartype def __init__(self, rating: int) -> None: """ Args: @@ -85,7 +85,6 @@ def __init__(self, rating: int) -> None: """ self._rating = rating - @beartype def __call__(self, image_content: bytes) -> int: """ A random target tracking rating. @@ -97,10 +96,10 @@ def __call__(self, image_content: bytes) -> int: return self._rating +@beartype class BrisqueTargetTrackingRater: """A rater which returns a rating based on a BRISQUE score.""" - @beartype def __call__(self, image_content: bytes) -> int: """ A rating based on a BRISQUE score. From 715c189568a918ddd8f1bd66918cc5f4b9b95a64 Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Fri, 30 Aug 2024 14:04:55 +0100 Subject: [PATCH 3/3] Use tower in more places --- src/mock_vws/target.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mock_vws/target.py b/src/mock_vws/target.py index 3a95fad5..67419ec8 100644 --- a/src/mock_vws/target.py +++ b/src/mock_vws/target.py @@ -11,7 +11,7 @@ from typing import Self, TypedDict from zoneinfo import ZoneInfo -from beartype import beartype +from beartype import BeartypeConf, beartype from PIL import Image, ImageStat from mock_vws._constants import TargetStatuses @@ -56,7 +56,7 @@ def _time_now() -> datetime.datetime: return datetime.datetime.now(tz=gmt) -@beartype +@beartype(conf=BeartypeConf(is_pep484_tower=True)) @dataclass(frozen=True, eq=True) class Target: """