From 59ff08c43eb9724a053fd5abe7fe534cf15764ff Mon Sep 17 00:00:00 2001 From: HitLuca Date: Thu, 3 Mar 2022 14:34:35 +0100 Subject: [PATCH] :building_construction: added type hints to most classes using mokeytype --- flask_cors/core.py | 41 ++++++++------ flask_cors/decorator.py | 3 +- flask_cors/extension.py | 9 ++- tests/base_test.py | 4 +- tests/core/test_override_headers.py | 4 +- tests/decorator/test_allow_headers.py | 14 ++--- tests/decorator/test_credentials.py | 8 +-- tests/decorator/test_duplicate_headers.py | 4 +- .../decorator/test_exception_interception.py | 23 ++++---- tests/decorator/test_expose_headers.py | 6 +- tests/decorator/test_max_age.py | 8 +-- tests/decorator/test_methods.py | 6 +- tests/decorator/test_options.py | 8 +-- tests/decorator/test_origins.py | 30 +++++----- tests/decorator/test_vary_header.py | 10 ++-- tests/decorator/test_w3.py | 8 +-- tests/extension/test_app_extension.py | 56 +++++++++---------- 17 files changed, 126 insertions(+), 116 deletions(-) diff --git a/flask_cors/core.py b/flask_cors/core.py index 93c7529..ce3f233 100644 --- a/flask_cors/core.py +++ b/flask_cors/core.py @@ -7,6 +7,7 @@ :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ +from difflib import Match import re import logging try: @@ -18,7 +19,11 @@ from datetime import timedelta from six import string_types from flask import request, current_app -from werkzeug.datastructures import Headers, MultiDict +from werkzeug.datastructures import EnvironHeaders, Headers, MultiDict +from flask.app import Flask +from flask.wrappers import Response +from typing import Any, Dict, List, Optional, Pattern, Set, Union +from werkzeug.local import LocalProxy LOG = logging.getLogger(__name__) @@ -63,7 +68,7 @@ always_send=True) -def parse_resources(resources): +def parse_resources(resources: Any) -> List[Any]: if isinstance(resources, dict): # To make the API more consistent with the decorator, allow a # resource of '*', which is not actually a valid regexp. @@ -95,7 +100,7 @@ def pattern_length(pair): raise ValueError("Unexpected value for resources argument.") -def get_regexp_pattern(regexp): +def get_regexp_pattern(regexp: Union[Pattern, str]) -> str: """ Helper that returns regexp pattern from given value. @@ -110,7 +115,7 @@ def get_regexp_pattern(regexp): return str(regexp) -def get_cors_origins(options, request_origin): +def get_cors_origins(options: Dict[str, Any], request_origin: Optional[str]) -> Optional[List[str]]: origins = options.get('origins') wildcard = r'.*' in origins @@ -158,7 +163,7 @@ def get_cors_origins(options, request_origin): return None -def get_allow_headers(options, acl_request_headers): +def get_allow_headers(options: Dict[str, Optional[Union[str, List[str], bool, int]]], acl_request_headers: Optional[str]) -> Optional[str]: if acl_request_headers: request_headers = [h.strip() for h in acl_request_headers.split(',')] @@ -173,7 +178,7 @@ def get_allow_headers(options, acl_request_headers): return None -def get_cors_headers(options, request_headers, request_method): +def get_cors_headers(options: Dict[str, Any], request_headers: EnvironHeaders, request_method: str) -> MultiDict: origins_to_set = get_cors_origins(options, request_headers.get('Origin')) headers = MultiDict() @@ -221,7 +226,7 @@ def get_cors_headers(options, request_headers, request_method): return MultiDict((k, v) for k, v in headers.items() if v) -def set_cors_headers(resp, options): +def set_cors_headers(resp: Response, options: Dict[str, Any]) -> Response: """ Performs the actual evaluation of Flask-CORS options and actually modifies the response object. @@ -251,7 +256,7 @@ def set_cors_headers(resp, options): return resp -def probably_regex(maybe_regex): +def probably_regex(maybe_regex: Union[Pattern, str]) -> bool: if isinstance(maybe_regex, RegexObject): return True else: @@ -260,7 +265,7 @@ def probably_regex(maybe_regex): # for if this string is in fact a regex. return any((c in maybe_regex for c in common_regex_chars)) -def re_fix(reg): +def re_fix(reg: Union[Pattern, str]) -> Union[Pattern, str]: """ Replace the invalid regex r'*' with the valid, wildcard regex r'/.*' to enable the CORS app extension to have a more user friendly api. @@ -268,11 +273,11 @@ def re_fix(reg): return r'.*' if reg == r'*' else reg -def try_match_any(inst, patterns): +def try_match_any(inst: str, patterns: List[Union[str, Pattern]]) -> bool: return any(try_match(inst, pattern) for pattern in patterns) -def try_match(request_origin, maybe_regex): +def try_match(request_origin: str, maybe_regex: Union[Pattern, str]) -> Optional[Union[Match, bool]]: """Safely attempts to match a pattern or string to a request origin.""" if isinstance(maybe_regex, RegexObject): return re.match(maybe_regex, request_origin) @@ -285,7 +290,7 @@ def try_match(request_origin, maybe_regex): return request_origin == maybe_regex -def get_cors_options(appInstance, *dicts): +def get_cors_options(appInstance: Union[LocalProxy, Flask], *dicts) -> Dict[str, Any]: """ Compute CORS options for an application by combining the DEFAULT_OPTIONS, the app's configuration-specified options and any dictionaries passed. The @@ -300,7 +305,7 @@ def get_cors_options(appInstance, *dicts): return serialize_options(options) -def get_app_kwarg_dict(appInstance=None): +def get_app_kwarg_dict(appInstance: Optional[Union[LocalProxy, Flask]]=None) -> Dict[Any, Any]: """Returns the dictionary of CORS specific app configurations.""" app = (appInstance or current_app) @@ -314,7 +319,7 @@ def get_app_kwarg_dict(appInstance=None): } -def flexible_str(obj): +def flexible_str(obj: Optional[Union[List[str], str]]) -> Optional[str]: """ A more flexible str function which intelligently handles stringifying strings, lists and other iterables. The results are lexographically sorted @@ -330,13 +335,13 @@ def flexible_str(obj): return str(obj) -def serialize_option(options_dict, key, upper=False): +def serialize_option(options_dict: Dict[str, Any], key: str, upper: bool=False) -> None: if key in options_dict: value = flexible_str(options_dict[key]) options_dict[key] = value.upper() if upper else value -def ensure_iterable(inst): +def ensure_iterable(inst: Union[List[str], Pattern, str, Set[str]]) -> Union[List[str], List[Pattern], Set[str]]: """ Wraps scalars or string types as a list, or returns the iterable instance. """ @@ -347,11 +352,11 @@ def ensure_iterable(inst): else: return inst -def sanitize_regex_param(param): +def sanitize_regex_param(param: Union[List[str], Set[str], Pattern, str]) -> List[Union[str, Pattern]]: return [re_fix(x) for x in ensure_iterable(param)] -def serialize_options(opts): +def serialize_options(opts: Dict[str, Any]) -> Dict[str, Any]: """ A helper method to serialize and processes the options dictionary. """ diff --git a/flask_cors/decorator.py b/flask_cors/decorator.py index 4a529e7..e0e071d 100644 --- a/flask_cors/decorator.py +++ b/flask_cors/decorator.py @@ -12,10 +12,11 @@ from functools import update_wrapper from flask import make_response, request, current_app from .core import * +from typing import Callable LOG = logging.getLogger(__name__) -def cross_origin(*args, **kwargs): +def cross_origin(*args, **kwargs) -> Callable: """ This function is the decorator which is used to wrap a Flask route with. In the simplest case, simply use the default parameters to allow all diff --git a/flask_cors/extension.py b/flask_cors/extension.py index 7695aad..b1dc4c7 100644 --- a/flask_cors/extension.py +++ b/flask_cors/extension.py @@ -10,6 +10,9 @@ """ from flask import request from .core import * +from flask.app import Flask +from typing import Any, Callable, List, Optional + try: from urllib.parse import unquote_plus except ImportError: @@ -131,12 +134,12 @@ class CORS(object): :type vary_header: bool """ - def __init__(self, app=None, **kwargs): + def __init__(self, app: Optional[Flask]=None, **kwargs) -> None: self._options = kwargs if app is not None: self.init_app(app, **kwargs) - def init_app(self, app, **kwargs): + def init_app(self, app: Flask, **kwargs) -> None: # The resources and options may be specified in the App Config, the CORS constructor # or the kwargs to the call to init_app. options = get_cors_options(app, self._options, kwargs) @@ -175,7 +178,7 @@ def wrapped_function(*args, **kwargs): app.handle_user_exception = _after_request_decorator( app.handle_user_exception) -def make_after_request_function(resources): +def make_after_request_function(resources: List[Any]) -> Callable: def cors_after_request(resp): # If CORS headers are set in a view decorator, pass if resp.headers is not None and resp.headers.get(ACL_ORIGIN): diff --git a/tests/base_test.py b/tests/base_test.py index 1b4ced3..b795e72 100644 --- a/tests/base_test.py +++ b/tests/base_test.py @@ -16,7 +16,7 @@ class FlaskCorsTestCase(unittest.TestCase): - def shortDescription(self): + def shortDescription(self) -> str: """ Get's the one liner description to be displayed. Source: @@ -37,7 +37,7 @@ def iter_responses(self, path, verbs=['get', 'head', 'options'], **kwargs): for verb in verbs: yield self._request(verb.lower(), path, **kwargs) - def _request(self, verb, *args, **kwargs): + def _request(self, verb: str, *args, **kwargs): _origin = kwargs.pop('origin', None) headers = kwargs.pop('headers', {}) if _origin: diff --git a/tests/core/test_override_headers.py b/tests/core/test_override_headers.py index f76a1cd..4be259b 100644 --- a/tests/core/test_override_headers.py +++ b/tests/core/test_override_headers.py @@ -13,7 +13,7 @@ from flask_cors.core import * class ResponseHeadersOverrideTestCaseIntegration(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) CORS(self.app) @@ -22,7 +22,7 @@ def index(): response = Response(headers={"custom": "dictionary"}) return 'Welcome' - def test_override_headers(self): + def test_override_headers(self) -> None: ''' Ensure we work even if response.headers is set to something other than a MultiDict. ''' diff --git a/tests/decorator/test_allow_headers.py b/tests/decorator/test_allow_headers.py index 6ad3a00..9b623eb 100644 --- a/tests/decorator/test_allow_headers.py +++ b/tests/decorator/test_allow_headers.py @@ -13,7 +13,7 @@ from flask_cors.core import * class AllowHeadersTestCaseIntegration(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) @self.app.route('/test_default') @@ -32,19 +32,19 @@ def test_allow_headers(): def test_allow_headers_regex(): return 'Welcome!' - def test_default(self): + def test_default(self) -> None: for resp in self.iter_responses('/test_default'): self.assertTrue(resp.headers.get(ACL_ALLOW_HEADERS) is None, "Default should have no allowed headers") - def test_allow_headers_no_request_headers(self): + def test_allow_headers_no_request_headers(self) -> None: ''' No ACL_REQUEST_HEADERS sent, ACL_ALLOW_HEADERS should be empty ''' resp = self.preflight('/test_allow_headers', origin='www.example.com') self.assertEqual(resp.headers.get(ACL_ALLOW_HEADERS), None) - def test_allow_headers_with_request_headers(self): + def test_allow_headers_with_request_headers(self) -> None: ''' If there is an Access-Control-Request-Method header in the request and Access-Control-Request-Method is allowed for cross origin @@ -58,7 +58,7 @@ def test_allow_headers_with_request_headers(self): self.assertEqual(resp.headers.get(ACL_ALLOW_HEADERS), 'X-Example-Header-A') - def test_allow_headers_with_request_headers_case_insensitive(self): + def test_allow_headers_with_request_headers_case_insensitive(self) -> None: ''' HTTP headers are case insensitive. We should respect that and match regardless of case, returning the casing sent by @@ -70,7 +70,7 @@ def test_allow_headers_with_request_headers_case_insensitive(self): self.assertEqual(resp.headers.get(ACL_ALLOW_HEADERS), 'X-Example-header-a') - def test_allow_headers_with_unmatched_request_headers(self): + def test_allow_headers_with_unmatched_request_headers(self) -> None: ''' If every element in the Access-Control-Request-Headers is not an allowed header, then the matching headers should be returned. @@ -87,7 +87,7 @@ def test_allow_headers_with_unmatched_request_headers(self): self.assertEqual(resp.headers.get(ACL_ALLOW_HEADERS), 'X-Example-Header-A') - def test_allow_headers_regex(self): + def test_allow_headers_regex(self) -> None: ''' If every element in the Access-Control-Request-Headers is not an allowed header, then the matching headers should be returned. diff --git a/tests/decorator/test_credentials.py b/tests/decorator/test_credentials.py index f1bafd4..670f1fc 100644 --- a/tests/decorator/test_credentials.py +++ b/tests/decorator/test_credentials.py @@ -17,7 +17,7 @@ class SupportsCredentialsCase(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) @self.app.route('/test_credentials_supported') @@ -35,14 +35,14 @@ def test_credentials_unsupported(): def test_default(): return 'Open!' - def test_credentials_supported(self): + def test_credentials_supported(self) -> None: ''' The specified route should return the Access-Control-Allow-Credentials header. ''' resp = self.get('/test_credentials_supported', origin='www.example.com') self.assertEqual(resp.headers.get(ACL_CREDENTIALS), 'true') - def test_default(self): + def test_default(self) -> None: ''' The default behavior should be to disallow credentials. ''' resp = self.get('/test_default', origin='www.example.com') @@ -51,7 +51,7 @@ def test_default(self): resp = self.get('/test_default') self.assertFalse(ACL_CREDENTIALS in resp.headers) - def test_credentials_unsupported(self): + def test_credentials_unsupported(self) -> None: ''' The default behavior should be to disallow credentials. ''' resp = self.get('/test_credentials_unsupported', origin='www.example.com') diff --git a/tests/decorator/test_duplicate_headers.py b/tests/decorator/test_duplicate_headers.py index 4f266ac..187be13 100644 --- a/tests/decorator/test_duplicate_headers.py +++ b/tests/decorator/test_duplicate_headers.py @@ -14,7 +14,7 @@ class AllowsMultipleHeaderEntries(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) @self.app.route('/test_multiple_set_cookie_headers') @@ -25,7 +25,7 @@ def test_multiple_set_cookie_headers(): resp.headers.add('set-cookie', 'bar') return resp - def test_multiple_set_cookie_headers(self): + def test_multiple_set_cookie_headers(self) -> None: resp = self.get('/test_multiple_set_cookie_headers') self.assertEqual(len(resp.headers.getlist('set-cookie')), 2) diff --git a/tests/decorator/test_exception_interception.py b/tests/decorator/test_exception_interception.py index fe42d3d..9d3d7ca 100644 --- a/tests/decorator/test_exception_interception.py +++ b/tests/decorator/test_exception_interception.py @@ -16,9 +16,10 @@ from flask import Flask, abort from flask_cors import * from flask_cors.core import * +from flask.app import Flask -def add_routes(app): +def add_routes(app: Flask) -> None: @app.route('/test_no_acl_abort_404') @app.route('/test_acl_abort_404') def test_acl_abort_404(): @@ -39,14 +40,14 @@ def test_no_acl_uncaught_exception_500(): class ExceptionInterceptionDefaultTestCase(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) CORS(self.app, resources={ r'/test_acl*': {}, }) add_routes(self.app) - def test_acl_abort_404(self): + def test_acl_abort_404(self) -> None: ''' HTTP Responses generated by calling abort are handled identically to normal responses, and should be wrapped by CORS headers if thep @@ -57,7 +58,7 @@ def test_acl_abort_404(self): self.assertEqual(resp.status_code, 404) self.assertTrue(ACL_ORIGIN in resp.headers) - def test_no_acl_abort_404(self): + def test_no_acl_abort_404(self) -> None: ''' HTTP Responses generated by calling abort are handled identically to normal responses, and should be wrapped by CORS headers if thep @@ -67,7 +68,7 @@ def test_no_acl_abort_404(self): self.assertEqual(resp.status_code, 404) self.assertFalse(ACL_ORIGIN in resp.headers) - def test_acl_abort_500(self): + def test_acl_abort_500(self) -> None: ''' HTTP Responses generated by calling abort are handled identically to normal responses, and should be wrapped by CORS headers if thep @@ -77,7 +78,7 @@ def test_acl_abort_500(self): self.assertEqual(resp.status_code, 500) self.assertTrue(ACL_ORIGIN in resp.headers) - def test_no_acl_abort_500(self): + def test_no_acl_abort_500(self) -> None: ''' HTTP Responses generated by calling abort are handled identically to normal responses, and should be wrapped by CORS headers if thep @@ -87,7 +88,7 @@ def test_no_acl_abort_500(self): self.assertEqual(resp.status_code, 500) self.assertFalse(ACL_ORIGIN in resp.headers) - def test_acl_uncaught_exception_500(self): + def test_acl_uncaught_exception_500(self) -> None: ''' Uncaught exceptions will trigger Flask's internal exception handler, and should have ACL headers only if intercept_exceptions @@ -100,7 +101,7 @@ def test_acl_uncaught_exception_500(self): self.assertEqual(resp.status_code, 500) self.assertTrue(ACL_ORIGIN in resp.headers) - def test_no_acl_uncaught_exception_500(self): + def test_no_acl_uncaught_exception_500(self) -> None: ''' Uncaught exceptions will trigger Flask's internal exception handler, and should have ACL headers only if intercept_exceptions @@ -113,7 +114,7 @@ def test_no_acl_uncaught_exception_500(self): self.assertEqual(resp.status_code, 500) self.assertFalse(ACL_ORIGIN in resp.headers) - def test_acl_exception_with_error_handler(self): + def test_acl_exception_with_error_handler(self) -> None: ''' If a 500 handler is setup by the user, responses should have CORS matching rules applied, regardless of whether or not @@ -155,7 +156,7 @@ def get_with_origins(path): class NoExceptionInterceptionTestCase(ExceptionInterceptionDefaultTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) CORS(self.app, intercept_exceptions=False, @@ -164,7 +165,7 @@ def setUp(self): }) add_routes(self.app) - def test_acl_exception_with_error_handler(self): + def test_acl_exception_with_error_handler(self) -> None: ''' If a 500 handler is setup by the user, responses should have CORS matching rules applied, regardless of whether or not diff --git a/tests/decorator/test_expose_headers.py b/tests/decorator/test_expose_headers.py index bd07887..be50080 100644 --- a/tests/decorator/test_expose_headers.py +++ b/tests/decorator/test_expose_headers.py @@ -14,7 +14,7 @@ class ExposeHeadersTestCase(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) @self.app.route('/test_default') @@ -27,12 +27,12 @@ def test_default(): def test_override(): return 'Welcome!' - def test_default(self): + def test_default(self) -> None: for resp in self.iter_responses('/test_default', origin='www.example.com'): self.assertTrue(resp.headers.get(ACL_EXPOSE_HEADERS) is None, "No Access-Control-Expose-Headers by default") - def test_override(self): + def test_override(self) -> None: ''' The specified headers should be returned in the ACL_EXPOSE_HEADERS and correctly serialized if it is a list. ''' diff --git a/tests/decorator/test_max_age.py b/tests/decorator/test_max_age.py index 7140e47..356d7c5 100644 --- a/tests/decorator/test_max_age.py +++ b/tests/decorator/test_max_age.py @@ -17,7 +17,7 @@ class MaxAgeTestCase(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) @self.app.route('/defaults') @@ -35,20 +35,20 @@ def test_string(): def test_time_delta(): return 'Open!' - def test_defaults(self): + def test_defaults(self) -> None: ''' By default, no max-age headers should be returned ''' for resp in self.iter_responses('/defaults', origin='www.example.com'): self.assertFalse(ACL_MAX_AGE in resp.headers) - def test_string(self): + def test_string(self) -> None: ''' If the methods parameter is defined, always return the allowed methods defined by the user. ''' resp = self.preflight('/test_string', origin='www.example.com') self.assertEqual(resp.headers.get(ACL_MAX_AGE), '600') - def test_time_delta(self): + def test_time_delta(self) -> None: ''' If the methods parameter is defined, always return the allowed methods defined by the user. ''' diff --git a/tests/decorator/test_methods.py b/tests/decorator/test_methods.py index b49a2c9..fb20802 100644 --- a/tests/decorator/test_methods.py +++ b/tests/decorator/test_methods.py @@ -17,7 +17,7 @@ class MethodsCase(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) @self.app.route('/defaults') @@ -30,7 +30,7 @@ def defaults(): def test_get(): return 'Only allow POST' - def test_defaults(self): + def test_defaults(self) -> None: ''' Access-Control-Allow-Methods headers should only be returned if the client makes an OPTIONS request. ''' @@ -41,7 +41,7 @@ def test_defaults(self): for method in ALL_METHODS: self.assertTrue(method in res.headers.get(ACL_METHODS)) - def test_methods_defined(self): + def test_methods_defined(self) -> None: ''' If the methods parameter is defined, it should override the default methods defined by the user. ''' diff --git a/tests/decorator/test_options.py b/tests/decorator/test_options.py index 849117e..734a029 100644 --- a/tests/decorator/test_options.py +++ b/tests/decorator/test_options.py @@ -17,7 +17,7 @@ class OptionsTestCase(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) @self.app.route('/test_default') @@ -35,7 +35,7 @@ def test_no_options_and_not_auto(): def test_options_and_not_auto(): return 'Welcome!' - def test_defaults(self): + def test_defaults(self) -> None: ''' The default behavior should automatically provide OPTIONS and return CORS headers. @@ -48,7 +48,7 @@ def test_defaults(self): self.assertEqual(resp.status_code, 200) self.assertTrue(ACL_ORIGIN in resp.headers) - def test_no_options_and_not_auto(self): + def test_no_options_and_not_auto(self) -> None: ''' If automatic_options is False, and the view func does not provide OPTIONS, then Flask's default handling will occur, and no CORS @@ -62,7 +62,7 @@ def test_no_options_and_not_auto(self): self.assertEqual(resp.status_code, 200) self.assertFalse(ACL_ORIGIN in resp.headers) - def test_options_and_not_auto(self): + def test_options_and_not_auto(self) -> None: ''' If OPTIONS is in methods, and automatic_options is False, the view function must return a response. diff --git a/tests/decorator/test_origins.py b/tests/decorator/test_origins.py index 5e7e20a..fd59611 100644 --- a/tests/decorator/test_origins.py +++ b/tests/decorator/test_origins.py @@ -18,7 +18,7 @@ letters = 'abcdefghijklmnopqrstuvwxyz' # string.letters is not PY3 compatible class OriginsTestCase(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) @self.app.route('/') @@ -81,14 +81,14 @@ def test_regex_mixed_list(): def test_multiple_protocols(): return '' - def test_defaults_no_origin(self): + def test_defaults_no_origin(self) -> None: ''' If there is no Origin header in the request, the Access-Control-Allow-Origin header should be '*' by default. ''' for resp in self.iter_responses('/'): self.assertEqual(resp.headers.get(ACL_ORIGIN), '*') - def test_defaults_with_origin(self): + def test_defaults_with_origin(self) -> None: ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be included. ''' @@ -96,7 +96,7 @@ def test_defaults_with_origin(self): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), 'http://example.com') - def test_always_send_no_wildcard(self): + def test_always_send_no_wildcard(self) -> None: ''' If send_wildcard=False, but the there is '*' in the allowed origins, we should send it anyways. @@ -105,13 +105,13 @@ def test_always_send_no_wildcard(self): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), '*') - def test_always_send_no_wildcard_origins(self): + def test_always_send_no_wildcard_origins(self) -> None: for resp in self.iter_responses('/'): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), '*') - def test_send_wildcard_with_origin(self): + def test_send_wildcard_with_origin(self) -> None: ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be included. ''' @@ -119,21 +119,21 @@ def test_send_wildcard_with_origin(self): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), '*') - def test_list_serialized(self): + def test_list_serialized(self) -> None: ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed. ''' resp = self.get('/test_list', origin='http://bar.com') self.assertEqual(resp.headers.get(ACL_ORIGIN),'http://bar.com') - def test_string_serialized(self): + def test_string_serialized(self) -> None: ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed back. ''' resp = self.get('/test_string', origin='http://foo.com') self.assertEqual(resp.headers.get(ACL_ORIGIN), 'http://foo.com') - def test_set_serialized(self): + def test_set_serialized(self) -> None: ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed back. ''' @@ -143,25 +143,25 @@ def test_set_serialized(self): # Order is not guaranteed self.assertEqual(allowed, 'http://bar.com') - def test_not_matching_origins(self): + def test_not_matching_origins(self) -> None: for resp in self.iter_responses('/test_list',origin="http://bazz.com"): self.assertFalse(ACL_ORIGIN in resp.headers) - def test_subdomain_regex(self): + def test_subdomain_regex(self) -> None: for sub in letters: domain = "http://%s.example.com" % sub for resp in self.iter_responses('/test_subdomain_regex', headers={'origin': domain}): self.assertEqual(domain, resp.headers.get(ACL_ORIGIN)) - def test_compiled_subdomain_regex(self): + def test_compiled_subdomain_regex(self) -> None: for sub in letters: domain = "http://%s.example.com" % sub for resp in self.iter_responses('/test_compiled_subdomain_regex', headers={'origin': domain}): self.assertEqual(domain, resp.headers.get(ACL_ORIGIN)) - def test_regex_list(self): + def test_regex_list(self) -> None: for parent in 'example.com', 'otherexample.com': for sub in letters: domain = "http://{}.{}.com".format(sub, parent) @@ -169,7 +169,7 @@ def test_regex_list(self): headers={'origin': domain}): self.assertEqual(domain, resp.headers.get(ACL_ORIGIN)) - def test_regex_mixed_list(self): + def test_regex_mixed_list(self) -> None: ''' Tests the corner case occurs when the send_always setting is True and no Origin header in the request, it is not possible to match @@ -193,7 +193,7 @@ def test_regex_mixed_list(self): self.assertEqual("http://example.com", self.get('/test_regex_mixed_list', origin='http://example.com').headers.get(ACL_ORIGIN)) - def test_multiple_protocols(self): + def test_multiple_protocols(self) -> None: import logging logging.getLogger('flask_cors').level = logging.DEBUG resp = self.get('test_multiple_protocols', origin='https://example.com') diff --git a/tests/decorator/test_vary_header.py b/tests/decorator/test_vary_header.py index b6cfa59..ad18752 100644 --- a/tests/decorator/test_vary_header.py +++ b/tests/decorator/test_vary_header.py @@ -17,7 +17,7 @@ class VaryHeaderTestCase(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) @self.app.route('/') @@ -41,7 +41,7 @@ def test_existing_vary_headers(): return Response('', status=200, headers={'Vary': 'Accept-Encoding'}) - def test_default(self): + def test_default(self) -> None: ''' By default, allow all domains, which means the Vary:Origin header should be set. @@ -49,7 +49,7 @@ def test_default(self): for resp in self.iter_responses('/', origin="http://foo.com"): self.assertTrue('Vary' in resp.headers) - def test_consistent_origin(self): + def test_consistent_origin(self) -> None: ''' If the Access-Control-Allow-Origin header will change dynamically, the Vary:Origin header should be set. @@ -57,7 +57,7 @@ def test_consistent_origin(self): for resp in self.iter_responses('/test_consistent_origin', origin="http://foo.com"): self.assertFalse('Vary' in resp.headers) - def test_varying_origin(self): + def test_varying_origin(self) -> None: ''' Resources that wish to enable themselves to be shared with multiple Origins but do not respond uniformly with "*" must in practice generate the Access-Control-Allow-Origin header @@ -73,7 +73,7 @@ def test_varying_origin(self): self.assertHasACLOrigin(resp) self.assertEqual(resp.headers.get('Vary'), 'Origin') - def test_consistent_origin_concat(self): + def test_consistent_origin_concat(self) -> None: ''' If Flask-Cors adds a Vary header and there is already a Vary header set, the headers should be combined and comma-separated. diff --git a/tests/decorator/test_w3.py b/tests/decorator/test_w3.py index 11ead12..44eae2b 100644 --- a/tests/decorator/test_w3.py +++ b/tests/decorator/test_w3.py @@ -16,7 +16,7 @@ class OriginsW3TestCase(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) @self.app.route('/') @@ -38,7 +38,7 @@ def noWildcard(): ''' return 'Welcome!' - def test_wildcard_origin_header(self): + def test_wildcard_origin_header(self) -> None: ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed back. ''' @@ -50,14 +50,14 @@ def test_wildcard_origin_header(self): example_origin ) - def test_wildcard_no_origin_header(self): + def test_wildcard_no_origin_header(self) -> None: ''' If there is no Origin header in the request, the Access-Control-Allow-Origin header should not be included. ''' for resp in self.iter_responses('/'): self.assertTrue(ACL_ORIGIN not in resp.headers) - def test_wildcard_default_origins(self): + def test_wildcard_default_origins(self) -> None: ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed back. ''' diff --git a/tests/extension/test_app_extension.py b/tests/extension/test_app_extension.py index 3e5b79f..2e2b9bd 100644 --- a/tests/extension/test_app_extension.py +++ b/tests/extension/test_app_extension.py @@ -19,7 +19,7 @@ letters = 'abcdefghijklmnopqrstuvwxyz' # string.letters is not PY3 compatible class AppExtensionRegexp(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) CORS(self.app, resources={ r'/test_list': {'origins': ["http://foo.com", "http://bar.com"]}, @@ -65,14 +65,14 @@ def test_string(): def test_set(): return 'Welcome!' - def test_defaults_no_origin(self): + def test_defaults_no_origin(self) -> None: ''' If there is no Origin header in the request, by default the '*' should be sent ''' for resp in self.iter_responses('/test_defaults'): self.assertEqual(resp.headers.get(ACL_ORIGIN), '*') - def test_defaults_with_origin(self): + def test_defaults_with_origin(self) -> None: ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be included. ''' @@ -80,7 +80,7 @@ def test_defaults_with_origin(self): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), 'http://example.com') - def test_send_wildcard_with_origin(self): + def test_send_wildcard_with_origin(self) -> None: ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be included. ''' @@ -88,21 +88,21 @@ def test_send_wildcard_with_origin(self): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), '*') - def test_list_serialized(self): + def test_list_serialized(self) -> None: ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed. ''' resp = self.get('/test_list', origin='http://bar.com') self.assertEqual(resp.headers.get(ACL_ORIGIN),'http://bar.com') - def test_string_serialized(self): + def test_string_serialized(self) -> None: ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed back. ''' resp = self.get('/test_string', origin='http://foo.com') self.assertEqual(resp.headers.get(ACL_ORIGIN), 'http://foo.com') - def test_set_serialized(self): + def test_set_serialized(self) -> None: ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed back. ''' @@ -112,18 +112,18 @@ def test_set_serialized(self): # Order is not guaranteed self.assertEqual(allowed, 'http://bar.com') - def test_not_matching_origins(self): + def test_not_matching_origins(self) -> None: for resp in self.iter_responses('/test_list',origin="http://bazz.com"): self.assertFalse(ACL_ORIGIN in resp.headers) - def test_subdomain_regex(self): + def test_subdomain_regex(self) -> None: for sub in letters: domain = "http://%s.example.com" % sub for resp in self.iter_responses('/test_subdomain_regex', headers={'origin': domain}): self.assertEqual(domain, resp.headers.get(ACL_ORIGIN)) - def test_compiled_subdomain_regex(self): + def test_compiled_subdomain_regex(self) -> None: for sub in [1, 100, 200]: domain = "http://example%s.com" % sub for resp in self.iter_responses('/test_compiled_subdomain_regex', @@ -133,7 +133,7 @@ def test_compiled_subdomain_regex(self): headers={'origin': "http://examplea.com"}): self.assertEqual(None, resp.headers.get(ACL_ORIGIN)) - def test_regex_list(self): + def test_regex_list(self) -> None: for parent in 'example.com', 'otherexample.com': for sub in letters: domain = "http://{}.{}.com".format(sub, parent) @@ -141,7 +141,7 @@ def test_regex_list(self): headers={'origin': domain}): self.assertEqual(domain, resp.headers.get(ACL_ORIGIN)) - def test_regex_mixed_list(self): + def test_regex_mixed_list(self) -> None: ''' Tests the corner case occurs when the send_always setting is True and no Origin header in the request, it is not possible to match @@ -167,7 +167,7 @@ def test_regex_mixed_list(self): class AppExtensionList(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) CORS(self.app, resources=[r'/test_exposed', r'/test_other_exposed'], origins=['http://foo.com', 'http://bar.com']) @@ -184,24 +184,24 @@ def exposed1(): def exposed2(): return 'Welcome!' - def test_exposed(self): + def test_exposed(self) -> None: for resp in self.iter_responses('/test_exposed', origin='http://foo.com'): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN),'http://foo.com') - def test_other_exposed(self): + def test_other_exposed(self) -> None: for resp in self.iter_responses('/test_other_exposed', origin='http://bar.com'): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), 'http://bar.com') - def test_unexposed(self): + def test_unexposed(self) -> None: for resp in self.iter_responses('/test_unexposed', origin='http://foo.com'): self.assertEqual(resp.status_code, 200) self.assertFalse(ACL_ORIGIN in resp.headers) class AppExtensionString(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) CORS(self.app, resources=r'/api/*', allow_headers='Content-Type', @@ -229,7 +229,7 @@ def index(): def foo_txt(): return 'Welcome' - def test_exposed(self): + def test_exposed(self) -> None: for path in '/api/v1/foo', '/api/v1/bar': for resp in self.iter_responses(path, origin='http://bar.com'): self.assertEqual(resp.status_code, 200) @@ -241,13 +241,13 @@ def test_exposed(self): self.assertFalse(ACL_ORIGIN in resp.headers) self.assertFalse(ACL_EXPOSE_HEADERS in resp.headers) - def test_unexposed(self): + def test_unexposed(self) -> None: for resp in self.iter_responses('/', origin='http://bar.com'): self.assertEqual(resp.status_code, 200) self.assertFalse(ACL_ORIGIN in resp.headers) self.assertFalse(ACL_EXPOSE_HEADERS in resp.headers) - def test_override(self): + def test_override(self) -> None: for resp in self.iter_responses('/api/v1/special', origin='http://foo.com'): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), 'http://foo.com') @@ -261,7 +261,7 @@ def test_override(self): class AppExtensionError(FlaskCorsTestCase): - def test_value_error(self): + def test_value_error(self) -> None: try: app = Flask(__name__) CORS(app, resources=5) @@ -271,7 +271,7 @@ def test_value_error(self): class AppExtensionDefault(FlaskCorsTestCase): - def test_default(self): + def test_default(self) -> None: ''' By default match all. ''' @@ -289,7 +289,7 @@ def index(): class AppExtensionExampleApp(FlaskCorsTestCase): - def setUp(self): + def setUp(self) -> None: self.app = Flask(__name__) CORS(self.app, resources={ r'/api/*': {'origins': ['http://blah.com', 'http://foo.bar']} @@ -307,14 +307,14 @@ def test_wildcard(): def test_exact_match(): return '' - def test_index(self): + def test_index(self) -> None: ''' If regex does not match, do not set CORS ''' for resp in self.iter_responses('/', origin='http://foo.bar'): self.assertFalse(ACL_ORIGIN in resp.headers) - def test_wildcard(self): + def test_wildcard(self) -> None: ''' Match anything matching the path /api/* with an origin of 'http://blah.com' or 'http://foo.bar' @@ -324,7 +324,7 @@ def test_wildcard(self): self.assertTrue(ACL_ORIGIN in resp.headers) self.assertEqual(origin, resp.headers.get(ACL_ORIGIN)) - def test_exact_match(self): + def test_exact_match(self) -> None: ''' Match anything matching the path /api/* with an origin of 'http://blah.com' or 'http://foo.bar' @@ -336,7 +336,7 @@ def test_exact_match(self): class AppExtensionCompiledRegexp(FlaskCorsTestCase): - def test_compiled_regex(self): + def test_compiled_regex(self) -> None: ''' Ensure we do not error if the user sepcifies an bad regular expression. @@ -361,7 +361,7 @@ def example(): class AppExtensionBadRegexp(FlaskCorsTestCase): - def test_value_error(self): + def test_value_error(self) -> None: ''' Ensure we do not error if the user sepcifies an bad regular expression.