Skip to content

Commit 7121cb6

Browse files
committed
chore: Shell2HTTP is now a subclass of Flask.Blueprint
1 parent 103ddfb commit 7121cb6

File tree

1 file changed

+56
-60
lines changed

1 file changed

+56
-60
lines changed

flask_shell2http/base_entrypoint.py

Lines changed: 56 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import Callable, Dict, List, Any
44

55
# web imports
6+
from flask import Blueprint
67
from flask_executor import Executor
78
from flask_executor.futures import Future
89

@@ -14,61 +15,56 @@
1415
logger = get_logger()
1516

1617

17-
class Shell2HTTP(object):
18+
class Shell2HTTP(Blueprint):
1819
"""
19-
Flask-Shell2HTTP base entrypoint class.
20-
The only public API available to users.
20+
Flask-Shell2HTTP's base entrypoint. This is the only public API available to users.
21+
22+
This is a subclass of `Flask.Blueprint` so it accepts
23+
all the same arguments and functionality of a generic ``Flask.Blueprint`` instance..
2124
2225
Attributes:
23-
app: Flask application instance.
24-
executor: Flask-Executor instance
25-
base_url_prefix (str): base prefix to apply to endpoints. Defaults to "/".
26+
executor: ``flask_executor.Executor`` instance
27+
import_name: The name of the blueprint package, usually
28+
``__name__``. This helps locate the ``root_path`` for the
29+
blueprint.
30+
static_folder: A folder with static files that should be
31+
served by the blueprint's static route. The path is relative to
32+
the blueprint's root path. Blueprint static files are disabled
33+
by default.
34+
static_url_path: The url to serve static files from.
35+
Defaults to ``static_folder``. If the blueprint does not have
36+
a ``url_prefix``, the app's static route will take precedence,
37+
and the blueprint's static files won't be accessible.
38+
template_folder: A folder with templates that should be added
39+
to the app's template search path. The path is relative to the
40+
blueprint's root path. Blueprint templates are disabled by
41+
default. Blueprint templates have a lower precedence than those
42+
in the app's templates folder.
43+
url_prefix: A path to prepend to all of the blueprint's URLs,
44+
to make them distinct from the rest of the app's routes.
45+
subdomain: A subdomain that blueprint routes will match on by
46+
default.
47+
url_defaults: A dict of default values that blueprint routes
48+
will receive by default.
49+
root_path: By default, the blueprint will automatically this
50+
based on ``import_name``. In certain situations this automatic
51+
detection can fail, so the path can be specified manually
52+
instead.
2653
2754
Example::
2855
2956
app = Flask(__name__)
3057
executor = Executor(app)
31-
shell2http = Shell2HTTP(app=app, executor=executor, base_url_prefix="/tasks/")
58+
shell2http = Shell2HTTP(executor, 'tasks', __name__, url_prefix="/tasks")
3259
"""
3360

34-
__commands: "OrderedDict[str, str]" = OrderedDict()
35-
__url_prefix: str = "/"
36-
37-
def __init__(
38-
self, app=None, executor: Executor = None, base_url_prefix: str = "/"
39-
) -> None:
40-
self.__url_prefix = base_url_prefix
41-
if app and executor:
42-
self.init_app(app, executor)
43-
44-
def init_app(self, app, executor: Executor) -> None:
45-
"""
46-
For use with Flask's `Application Factory`_ method.
47-
48-
Example::
49-
50-
executor = Executor()
51-
shell2http = Shell2HTTP(base_url_prefix="/commands/")
52-
app = Flask(__name__)
53-
executor.init_app(app)
54-
shell2http.init_app(app=app, executor=executor)
55-
56-
.. _Application Factory:
57-
https://flask.palletsprojects.com/en/1.1.x/patterns/appfactories/
58-
"""
59-
self.app = app
60-
self.__executor: Executor = executor
61-
self.__init_extension()
62-
63-
def __init_extension(self) -> None:
64-
"""
65-
Adds the Shell2HTTP() instance to `app.extensions` list.
66-
For internal use only.
67-
"""
68-
if not hasattr(self.app, "extensions"):
69-
self.app.extensions = dict()
61+
__commands: "OrderedDict[str, str]"
62+
__executor: Executor
7063

71-
self.app.extensions["shell2http"] = self
64+
def __init__(self, executor: Executor, *args, **kwargs):
65+
self.__commands = OrderedDict()
66+
self.__executor = executor
67+
super().__init__(*args, **kwargs)
7268

7369
def register_command(
7470
self,
@@ -78,11 +74,14 @@ def register_command(
7874
decorators: List = [],
7975
) -> None:
8076
"""
81-
Function to map a shell command to an endpoint.
77+
Function to map a shell command to an endpoint or route.
78+
79+
This internally registers the route via the ``Blueprint.add_url_rule`` method
80+
so you can enjoy all the same features and powers of a blueprint instance.
8281
8382
Args:
8483
endpoint (str):
85-
- your command would live here: ``/{base_url_prefix}/{endpoint}``
84+
- your command would live here: ``/{url_prefix}/{endpoint}``
8685
command_name (str):
8786
- The base command which can be executed from the given endpoint.
8887
- If ``command_name='echo'``, then all arguments passed
@@ -115,15 +114,15 @@ def my_callback_fn(context: dict, future: Future) -> None:
115114
decorators=[],
116115
)
117116
"""
118-
uri: str = self.__construct_route(endpoint)
119117
# make sure the given endpoint is not already registered
120-
cmd_already_exists = self.__commands.get(uri)
118+
cmd_already_exists = self.__commands.get(endpoint)
121119
if cmd_already_exists:
122-
logger.error(
120+
err_msg = (
123121
"Failed to register since given endpoint: "
124122
f"'{endpoint}' already maps to command: '{cmd_already_exists}'."
125123
)
126-
return None
124+
logger.error(err_msg)
125+
raise AssertionError(err_msg)
127126

128127
# else, add new URL rule
129128
view_func = Shell2HttpAPI.as_view(
@@ -136,12 +135,15 @@ def my_callback_fn(context: dict, future: Future) -> None:
136135
for dec in decorators:
137136
view_func = dec(view_func)
138137
# register URL rule
139-
self.app.add_url_rule(
140-
uri,
138+
self.add_url_rule(
139+
endpoint,
141140
view_func=view_func,
142141
)
143-
self.__commands.update({uri: command_name})
144-
logger.info(f"New endpoint: '{uri}' registered for command: '{command_name}'.")
142+
self.__commands.update({endpoint: command_name})
143+
logger.info(
144+
f"New endpoint: '{self.url_prefix}/{endpoint}' "
145+
f"registered for command: '{command_name}'."
146+
)
145147

146148
def get_registered_commands(self) -> "OrderedDict[str, str]":
147149
"""
@@ -153,9 +155,3 @@ def get_registered_commands(self) -> "OrderedDict[str, str]":
153155
i.e. mapping of registered commands and their URLs.
154156
"""
155157
return self.__commands
156-
157-
def __construct_route(self, endpoint: str) -> str:
158-
"""
159-
For internal use only.
160-
"""
161-
return self.__url_prefix + endpoint

0 commit comments

Comments
 (0)