-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
[WIP] Add hooks #3029
base: dash-3.0
Are you sure you want to change the base?
[WIP] Add hooks #3029
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some questions - happy to chat
|
||
def layout(func): | ||
""" | ||
Run a function when serving the layout, the return value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like it saves the function func
but doesn't run it - is the comment incorrect?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it is only run when
serving the layout, the docstring are placeholder, we can improve the wording.
|
||
def wrap(func): | ||
_name = name or func.__name__ | ||
_ns["routes"].append((_name, func, methods)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm - so some of the _ns
entries are functions and some are tuples, so the structure of entries in _ns["key"]
depends on the value of "key"
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _ns
just saves the info for the different hooks, it isn't meant for public usage. For routes/callbacks it needs more arguments so it's saved in a tuple for easy unpacking.
return wrap | ||
|
||
|
||
def error(func: _t.Callable[[Exception], _t.Any]): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're declaring the type of func
here but you don't declare types in the previous registration functions - declare them all for consistency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is some issue with cyclic dependencies with the other types, I'll see what I can do.
""" | ||
|
||
def wrap(func): | ||
_ns["callback"].append((list(args), dict(kwargs), func)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again, it looks like the structure of values stored in _ns
depends on the key, which feels like it should be documented somewhere in this file to help the next person reading the code.
|
||
|
||
class HooksManager: | ||
_registered = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add a comment explaining what _registered
is for - in particular, why is it a class-level variable instead of an instance variable?
|
||
for dist in importlib.metadata.distributions(): | ||
for entry in dist.entry_points: | ||
if entry.group != "dash": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm - hard-coded string embedded in the file - define as a constant at the top of the file to make it easier to find? and a comment here explaining what this filtering is doing would be welcome - I don't really understand it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This just a filter on the entry_points
of a setup.py
, the entry_points are used to add cli and other functionalities to python packages. I usually don't define a variable if it's only used one time.
@@ -573,6 +573,8 @@ def __init__( # pylint: disable=too-many-statements | |||
for plugin in plugins: | |||
plugin.plug(self) | |||
|
|||
self._setup_hooks() | |||
|
|||
# tracks internally if a function already handled at least one request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need/want this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's the setup that load the plugins and add the setup/callback/error hooks.
self._hooks = HooksManager | ||
self._hooks.register_setuptools() | ||
|
||
for setup in self._hooks.get_hooks("setup"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this where the difference in structure between the things that are stored in different _ns
entries shows up? this loop is getting one value per list item; the loop below is getting three.
Add
dash.hooks
system to enhance created apps with functionality. These hooks can be automatically added to apps without imports by adding adash
entry toentry_points
insetup.py
.Hooks:
layout
: wraps the layout before serving. Take the layout as first argument and the return value is used as the new layout.setup
: Called when aDash.__init__
is called, used to get a reference to the app.callback
: Add a callback to all the apps. Same signature asdash.callback
.route
: Add a route to the flask server, the route will be prefixed withroutes_pathname_prefix
.error
: Add a global error handler to be used with callbacks.