Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

more work on Scaffold base class #3933

Merged
merged 5 commits into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 4 additions & 79 deletions src/flask/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
from .globals import g
from .globals import request
from .globals import session
from .helpers import find_package
from .helpers import get_debug_flag
from .helpers import get_env
from .helpers import get_flashed_messages
Expand All @@ -40,6 +39,7 @@
from .logging import create_logger
from .scaffold import _endpoint_from_view_func
from .scaffold import _sentinel
from .scaffold import find_package
from .scaffold import Scaffold
from .scaffold import setupmethod
from .sessions import SecureCookieSessionInterface
Expand Down Expand Up @@ -346,21 +346,6 @@ class Flask(Scaffold):
#: .. versionadded:: 0.8
session_interface = SecureCookieSessionInterface()

# TODO remove the next three attrs when Sphinx :inherited-members: works
# https://github.com/sphinx-doc/sphinx/issues/741

#: The name of the package or module that this app belongs to. Do not
#: change this once it is set by the constructor.
import_name = None

#: Location of the template files to be added to the template lookup.
#: ``None`` if templates should not be added.
template_folder = None

#: Absolute path to the package on the filesystem. Used to look up
#: resources contained in the package.
root_path = None

def __init__(
self,
import_name,
Expand Down Expand Up @@ -1017,58 +1002,6 @@ def add_url_rule(
provide_automatic_options=None,
**options,
):
"""Connects a URL rule. Works exactly like the :meth:`route`
decorator. If a view_func is provided it will be registered with the
endpoint.

Basically this example::

@app.route('/')
def index():
pass

Is equivalent to the following::

def index():
pass
app.add_url_rule('/', 'index', index)

If the view_func is not provided you will need to connect the endpoint
to a view function like so::

app.view_functions['index'] = index

Internally :meth:`route` invokes :meth:`add_url_rule` so if you want
to customize the behavior via subclassing you only need to change
this method.

For more information refer to :ref:`url-route-registrations`.

.. versionchanged:: 0.2
`view_func` parameter added.

.. versionchanged:: 0.6
``OPTIONS`` is added automatically as method.

:param rule: the URL rule as string
:param endpoint: the endpoint for the registered URL rule. Flask
itself assumes the name of the view function as
endpoint
:param view_func: the function to call when serving a request to the
provided endpoint
:param provide_automatic_options: controls whether the ``OPTIONS``
method should be added automatically. This can also be controlled
by setting the ``view_func.provide_automatic_options = False``
before adding the rule.
:param options: the options to be forwarded to the underlying
:class:`~werkzeug.routing.Rule` object. A change
to Werkzeug is handling of method options. methods
is a list of methods this rule should be limited
to (``GET``, ``POST`` etc.). By default a rule
just listens for ``GET`` (and implicitly ``HEAD``).
Starting with Flask 0.6, ``OPTIONS`` is implicitly
added and handled by the standard request handling.
"""
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
options["endpoint"] = endpoint
Expand Down Expand Up @@ -1417,12 +1350,6 @@ def handle_exception(self, e):
always receive the ``InternalServerError``. The original
unhandled exception is available as ``e.original_exception``.

.. note::
Prior to Werkzeug 1.0.0, ``InternalServerError`` will not
always have an ``original_exception`` attribute. Use
``getattr(e, "original_exception", None)`` to simulate the
behavior for compatibility.

.. versionchanged:: 1.1.0
Always passes the ``InternalServerError`` instance to the
handler, setting ``original_exception`` to the unhandled
Expand Down Expand Up @@ -2023,9 +1950,7 @@ def wsgi_app(self, environ, start_response):

def __call__(self, environ, start_response):
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
WSGI application. This calls :meth:`wsgi_app`, which can be
wrapped to apply middleware.
"""
return self.wsgi_app(environ, start_response)

def __repr__(self):
return f"<{type(self).__name__} {self.name!r}>"
116 changes: 48 additions & 68 deletions src/flask/blueprints.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,6 @@ class Blueprint(Scaffold):

See :doc:`/blueprints` for more information.

.. versionchanged:: 1.1.0
Blueprints have a ``cli`` group to register nested CLI commands.
The ``cli_group`` parameter controls the name of the group under
the ``flask`` command.

.. versionadded:: 0.7

:param name: The name of the blueprint. Will be prepended to each
endpoint name.
:param import_name: The name of the blueprint package, usually
Expand All @@ -121,37 +114,29 @@ class Blueprint(Scaffold):
default.
:param url_defaults: A dict of default values that blueprint routes
will receive by default.
:param root_path: By default, the blueprint will automatically set this
based on ``import_name``. In certain situations this automatic
detection can fail, so the path can be specified manually
instead.
:param root_path: By default, the blueprint will automatically set
this based on ``import_name``. In certain situations this
automatic detection can fail, so the path can be specified
manually instead.

.. versionchanged:: 1.1.0
Blueprints have a ``cli`` group to register nested CLI commands.
The ``cli_group`` parameter controls the name of the group under
the ``flask`` command.

.. versionadded:: 0.7
"""

warn_on_modifications = False
_got_registered_once = False

#: Blueprint local JSON encoder class to use.
#: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_encoder`.
#: Blueprint local JSON encoder class to use. Set to ``None`` to use
#: the app's :class:`~flask.Flask.json_encoder`.
json_encoder = None
#: Blueprint local JSON decoder class to use.
#: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_decoder`.
#: Blueprint local JSON decoder class to use. Set to ``None`` to use
#: the app's :class:`~flask.Flask.json_decoder`.
json_decoder = None

# TODO remove the next three attrs when Sphinx :inherited-members: works
# https://github.com/sphinx-doc/sphinx/issues/741

#: The name of the package or module that this app belongs to. Do not
#: change this once it is set by the constructor.
import_name = None

#: Location of the template files to be added to the template lookup.
#: ``None`` if templates should not be added.
template_folder = None

#: Absolute path to the package on the filesystem. Used to look up
#: resources contained in the package.
root_path = None

def __init__(
self,
name,
Expand All @@ -176,8 +161,10 @@ def __init__(
self.url_prefix = url_prefix
self.subdomain = subdomain
self.deferred_functions = []

if url_defaults is None:
url_defaults = {}

self.url_values_defaults = url_defaults
self.cli_group = cli_group

Expand All @@ -195,9 +182,9 @@ def record(self, func):

warn(
Warning(
"The blueprint was already registered once "
"but is getting modified now. These changes "
"will not show up."
"The blueprint was already registered once but is"
" getting modified now. These changes will not show"
" up."
)
)
self.deferred_functions.append(func)
Expand All @@ -223,12 +210,13 @@ def make_setup_state(self, app, options, first_registration=False):
return BlueprintSetupState(self, app, options, first_registration)

def register(self, app, options, first_registration=False):
"""Called by :meth:`Flask.register_blueprint` to register all views
and callbacks registered on the blueprint with the application. Creates
a :class:`.BlueprintSetupState` and calls each :meth:`record` callback
with it.
"""Called by :meth:`Flask.register_blueprint` to register all
views and callbacks registered on the blueprint with the
application. Creates a :class:`.BlueprintSetupState` and calls
each :meth:`record` callbackwith it.

:param app: The application this blueprint is being registered with.
:param app: The application this blueprint is being registered
with.
:param options: Keyword arguments forwarded from
:meth:`~Flask.register_blueprint`.
:param first_registration: Whether this is the first time this
Expand All @@ -244,44 +232,36 @@ def register(self, app, options, first_registration=False):
endpoint="static",
)

# Merge app and self dictionaries.
def merge_dict_lists(self_dict, app_dict):
"""Merges self_dict into app_dict. Replaces None keys with self.name.
Values of dict must be lists.
"""
for key, values in self_dict.items():
key = self.name if key is None else f"{self.name}.{key}"
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.
Values of dict must be dict.
"""
for key, value in self_dict.items():
key = self.name if key is None else f"{self.name}.{key}"
app_dict[key] = value

app.view_functions.update(self.view_functions)

merge_dict_lists(self.before_request_funcs, app.before_request_funcs)
merge_dict_lists(self.after_request_funcs, app.after_request_funcs)
merge_dict_lists(self.teardown_request_funcs, app.teardown_request_funcs)
merge_dict_lists(self.url_default_functions, app.url_default_functions)
merge_dict_lists(self.url_value_preprocessors, app.url_value_preprocessors)
merge_dict_lists(
self.template_context_processors, app.template_context_processors
)
# Merge blueprint data into parent.
if first_registration:

merge_dict_nested(self.error_handler_spec, app.error_handler_spec)
def extend(bp_dict, parent_dict):
for key, values in bp_dict.items():
key = self.name if key is None else f"{self.name}.{key}"
parent_dict[key].extend(values)

def update(bp_dict, parent_dict):
for key, value in bp_dict.items():
key = self.name if key is None else f"{self.name}.{key}"
parent_dict[key] = value

app.view_functions.update(self.view_functions)
extend(self.before_request_funcs, app.before_request_funcs)
extend(self.after_request_funcs, app.after_request_funcs)
extend(self.teardown_request_funcs, app.teardown_request_funcs)
extend(self.url_default_functions, app.url_default_functions)
extend(self.url_value_preprocessors, app.url_value_preprocessors)
extend(self.template_context_processors, app.template_context_processors)
update(self.error_handler_spec, app.error_handler_spec)

for deferred in self.deferred_functions:
deferred(state)

cli_resolved_group = options.get("cli_group", self.cli_group)

if not self.cli.commands:
return

cli_resolved_group = options.get("cli_group", self.cli_group)

if cli_resolved_group is None:
app.cli.commands.update(self.cli.commands)
elif cli_resolved_group is _sentinel:
Expand Down
Loading