diff --git a/src/flask/app.py b/src/flask/app.py index 87058495..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( @@ -1292,7 +1291,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 +1752,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 +1793,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 +1856,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):