From fd62210f583e90764be6e8d9f295f37f33a65e48 Mon Sep 17 00:00:00 2001 From: pgjones Date: Sun, 21 Feb 2021 12:55:30 +0000 Subject: [PATCH 1/2] Utilise defaultdicts This code originates from the Python 2.4 supporting version of Flask, with defaultdicts being added in 2.5. Using defaultdict makes the intentional usage clearer, and slightly simplifies the code. --- src/flask/app.py | 12 ++++++------ src/flask/blueprints.py | 2 +- src/flask/scaffold.py | 30 ++++++++++++++++-------------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/flask/app.py b/src/flask/app.py index 87058495..7fc79546 100644 --- a/src/flask/app.py +++ b/src/flask/app.py @@ -1292,7 +1292,7 @@ class Flask(Scaffold): (request.blueprint, None), (None, None), ): - handler_map = self.error_handler_spec.setdefault(name, {}).get(c) + handler_map = self.error_handler_spec[name][c] if not handler_map: continue @@ -1753,10 +1753,10 @@ class Flask(Scaffold): .. versionadded:: 0.7 """ - funcs = self.url_default_functions.get(None, ()) + funcs = self.url_default_functions[None] if "." in endpoint: bp = endpoint.rsplit(".", 1)[0] - funcs = chain(funcs, self.url_default_functions.get(bp, ())) + funcs = chain(funcs, self.url_default_functions[bp]) for func in funcs: func(endpoint, values) @@ -1794,13 +1794,13 @@ class Flask(Scaffold): bp = _request_ctx_stack.top.request.blueprint - funcs = self.url_value_preprocessors.get(None, ()) + funcs = self.url_value_preprocessors[None] if bp is not None and bp in self.url_value_preprocessors: funcs = chain(funcs, self.url_value_preprocessors[bp]) for func in funcs: func(request.endpoint, request.view_args) - funcs = self.before_request_funcs.get(None, ()) + funcs = self.before_request_funcs[None] if bp is not None and bp in self.before_request_funcs: funcs = chain(funcs, self.before_request_funcs[bp]) for func in funcs: @@ -1857,7 +1857,7 @@ class Flask(Scaffold): """ if exc is _sentinel: exc = sys.exc_info()[1] - funcs = reversed(self.teardown_request_funcs.get(None, ())) + funcs = reversed(self.teardown_request_funcs[None]) bp = _request_ctx_stack.top.request.blueprint if bp is not None and bp in self.teardown_request_funcs: funcs = chain(funcs, reversed(self.teardown_request_funcs[bp])) diff --git a/src/flask/blueprints.py b/src/flask/blueprints.py index 4842ace8..fbfa6c6d 100644 --- a/src/flask/blueprints.py +++ b/src/flask/blueprints.py @@ -251,7 +251,7 @@ class Blueprint(Scaffold): """ for key, values in self_dict.items(): key = self.name if key is None else f"{self.name}.{key}" - app_dict.setdefault(key, []).extend(values) + app_dict[key].extend(values) def merge_dict_nested(self_dict, app_dict): """Merges self_dict into app_dict. Replaces None keys with self.name. diff --git a/src/flask/scaffold.py b/src/flask/scaffold.py index d1c24bd1..aa89fdef 100644 --- a/src/flask/scaffold.py +++ b/src/flask/scaffold.py @@ -1,3 +1,4 @@ +from collections import defaultdict from functools import update_wrapper from werkzeug.exceptions import default_exceptions @@ -86,21 +87,21 @@ class Scaffold(_PackageBoundObject): #: #: To register an error handler, use the :meth:`errorhandler` #: decorator. - self.error_handler_spec = {} + self.error_handler_spec = defaultdict(lambda: defaultdict(dict)) #: A dictionary with lists of functions that will be called at the #: beginning of each request. The key of the dictionary is the name of #: the blueprint this function is active for, or ``None`` for all #: requests. To register a function, use the :meth:`before_request` #: decorator. - self.before_request_funcs = {} + self.before_request_funcs = defaultdict(list) #: A dictionary with lists of functions that should be called after #: each request. The key of the dictionary is the name of the blueprint #: this function is active for, ``None`` for all requests. This can for #: example be used to close database connections. To register a function #: here, use the :meth:`after_request` decorator. - self.after_request_funcs = {} + self.after_request_funcs = defaultdict(list) #: A dictionary with lists of functions that are called after #: each request, even if an exception has occurred. The key of the @@ -112,7 +113,7 @@ class Scaffold(_PackageBoundObject): #: :meth:`teardown_request` decorator. #: #: .. versionadded:: 0.7 - self.teardown_request_funcs = {} + self.teardown_request_funcs = defaultdict(list) #: A dictionary with list of functions that are called without argument #: to populate the template context. The key of the dictionary is the @@ -120,7 +121,9 @@ class Scaffold(_PackageBoundObject): #: requests. Each returns a dictionary that the template context is #: updated with. To register a function here, use the #: :meth:`context_processor` decorator. - self.template_context_processors = {None: [_default_template_ctx_processor]} + self.template_context_processors = defaultdict( + list, {None: [_default_template_ctx_processor]} + ) #: A dictionary with lists of functions that are called before the #: :attr:`before_request_funcs` functions. The key of the dictionary is @@ -129,7 +132,7 @@ class Scaffold(_PackageBoundObject): #: :meth:`url_value_preprocessor`. #: #: .. versionadded:: 0.7 - self.url_value_preprocessors = {} + self.url_value_preprocessors = defaultdict(list) #: A dictionary with lists of functions that can be used as URL value #: preprocessors. The key ``None`` here is used for application wide @@ -141,7 +144,7 @@ class Scaffold(_PackageBoundObject): #: automatically again that were removed that way. #: #: .. versionadded:: 0.7 - self.url_default_functions = {} + self.url_default_functions = defaultdict(list) def _is_setup_finished(self): raise NotImplementedError @@ -258,7 +261,7 @@ class Scaffold(_PackageBoundObject): non-None value, the value is handled as if it was the return value from the view, and further request handling is stopped. """ - self.before_request_funcs.setdefault(None, []).append(f) + self.before_request_funcs[None].append(f) return f @setupmethod @@ -272,7 +275,7 @@ class Scaffold(_PackageBoundObject): As of Flask 0.7 this function might not be executed at the end of the request in case an unhandled exception occurred. """ - self.after_request_funcs.setdefault(None, []).append(f) + self.after_request_funcs[None].append(f) return f @setupmethod @@ -311,7 +314,7 @@ class Scaffold(_PackageBoundObject): debugger can still access it. This behavior can be controlled by the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable. """ - self.teardown_request_funcs.setdefault(None, []).append(f) + self.teardown_request_funcs[None].append(f) return f @setupmethod @@ -334,7 +337,7 @@ class Scaffold(_PackageBoundObject): The function is passed the endpoint name and values dict. The return value is ignored. """ - self.url_value_preprocessors.setdefault(None, []).append(f) + self.url_value_preprocessors[None].append(f) return f @setupmethod @@ -343,7 +346,7 @@ class Scaffold(_PackageBoundObject): application. It's called with the endpoint and values and should update the values passed in place. """ - self.url_default_functions.setdefault(None, []).append(f) + self.url_default_functions[None].append(f) return f @setupmethod @@ -416,8 +419,7 @@ class Scaffold(_PackageBoundObject): " instead." ) - handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {}) - handlers[exc_class] = f + self.error_handler_spec[key][code][exc_class] = f @staticmethod def _get_exc_class_and_code(exc_class_or_code): From 83d358d2c43c79b7d7fe5365e079447296c65eaf Mon Sep 17 00:00:00 2001 From: pgjones Date: Tue, 23 Feb 2021 20:19:53 +0000 Subject: [PATCH 2/2] remove _blueprint_order, dicts are ordered This code originates from supporting Python 2.4. Dicts are ordered in supported Pythons as of 3.6. An OrderedDict could be used to indicate that order matters, but is not since we don't rely on the implementation differences. --- src/flask/app.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/flask/app.py b/src/flask/app.py index 7fc79546..496732ec 100644 --- a/src/flask/app.py +++ b/src/flask/app.py @@ -430,13 +430,13 @@ class Flask(Scaffold): #: .. versionadded:: 0.11 self.shell_context_processors = [] - #: all the attached blueprints in a dictionary by name. Blueprints - #: can be attached multiple times so this dictionary does not tell - #: you how often they got attached. + #: Maps registered blueprint names to blueprint objects. The + #: dict retains the order the blueprints were registered in. + #: Blueprints can be registered multiple times, this dict does + #: not track how often they were attached. #: #: .. versionadded:: 0.7 self.blueprints = {} - self._blueprint_order = [] #: a place where extensions can store application specific state. For #: example this is where an extension could store database engines and @@ -997,7 +997,6 @@ class Flask(Scaffold): ) else: self.blueprints[blueprint.name] = blueprint - self._blueprint_order.append(blueprint) first_registration = True blueprint.register(self, options, first_registration) @@ -1007,7 +1006,7 @@ class Flask(Scaffold): .. versionadded:: 0.11 """ - return iter(self._blueprint_order) + return self.blueprints.values() @setupmethod def add_url_rule(