Skip to content

Commit

Permalink
Merge pull request #444 from plotly/component-suites-registration
Browse files Browse the repository at this point in the history
Register component suites by importing.
  • Loading branch information
T4rk1n committed Nov 6, 2018
2 parents 1c94fd2 + b731b6e commit 1663fea
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 45 deletions.
3 changes: 2 additions & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ confidence=
disable=fixme,
missing-docstring,
invalid-name,
too-many-lines
too-many-lines,
old-style-class
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.29.0 - 2018-11-06
## Added
- Added component namespaces registry, collect the resources needed by component library when they are imported instead of crawling the layout. [#444](https://github.com/plotly/dash/pull/444)

## 0.28.7 - 2018-11-05
## Fixed
- Component generation now uses the same prop name black list in all supported Python versions. Closes [#361](https://github.com/plotly/dash/issues/361). [#450](https://github.com/plotly/dash/pull/450)
Expand Down
45 changes: 45 additions & 0 deletions dash/development/base_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,53 @@
import copy
import os
import inspect
import abc
import sys
import six

from ._all_keywords import kwlist


# pylint: disable=no-init,too-few-public-methods
class ComponentRegistry:
"""Holds a registry of the namespaces used by components."""

registry = set()
__dist_cache = {}

@classmethod
def get_resources(cls, resource_name):
cached = cls.__dist_cache.get(resource_name)

if cached:
return cached

cls.__dist_cache[resource_name] = resources = []

for module_name in cls.registry:
module = sys.modules[module_name]
resources.extend(getattr(module, resource_name, []))

return resources


class ComponentMeta(abc.ABCMeta):

# pylint: disable=arguments-differ
def __new__(mcs, name, bases, attributes):
component = abc.ABCMeta.__new__(mcs, name, bases, attributes)
module = attributes['__module__'].split('.')[0]
if name == 'Component' or module == 'builtins':
# Don't do the base component
# and the components loaded dynamically by load_component
# as it doesn't have the namespace.
return component

ComponentRegistry.registry.add(module)

return component


def is_number(s):
try:
float(s)
Expand Down Expand Up @@ -53,6 +97,7 @@ def wrapper(*args, **kwargs):
return wrapper


@six.add_metaclass(ComponentMeta)
class Component(collections.MutableMapping):
class _UNDEFINED(object):
def __repr__(self):
Expand Down
3 changes: 3 additions & 0 deletions dash/development/component_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
from .base_component import generate_class
from .base_component import generate_class_file
from .base_component import ComponentRegistry


def _get_metadata(metadata_path):
Expand Down Expand Up @@ -30,6 +31,8 @@ def load_components(metadata_path,
`type`, `valid_kwargs`, and `setup`.
"""

# Register the component lib for index include.
ComponentRegistry.registry.add(namespace)
components = []

data = _get_metadata(metadata_path)
Expand Down
53 changes: 10 additions & 43 deletions dash/resources.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from copy import copy
import json
import warnings
import os

from .development.base_component import Component
from .development.base_component import ComponentRegistry


# pylint: disable=old-style-class
Expand All @@ -12,6 +11,7 @@ def __init__(self, resource_name, layout):
self._resources = []
self.resource_name = resource_name
self.layout = layout
self._resources_cache = []

def append_resource(self, resource):
self._resources.append(resource)
Expand Down Expand Up @@ -59,45 +59,18 @@ def _filter_resources(self, all_resources, dev_bundles=False):
return filtered_resources

def get_all_resources(self, dev_bundles=False):
all_resources = []
if self.config.infer_from_layout:
all_resources = (
self.get_inferred_resources() + self._resources
)
else:
all_resources = self._resources
if self._resources_cache:
return self._resources_cache

return self._filter_resources(all_resources, dev_bundles)
all_resources = ComponentRegistry.get_resources(self.resource_name)
all_resources.extend(self._resources)

def get_inferred_resources(self):
namespaces = []
resources = []
layout = self.layout
self._resources_cache = res = \
self._filter_resources(all_resources, dev_bundles)
return res

def extract_resource_from_component(component):
# pylint: disable=protected-access
if (isinstance(component, Component) and
component._namespace not in namespaces):

namespaces.append(component._namespace)

if hasattr(component, self.resource_name):

component_resources = copy(
getattr(component, self.resource_name)
)
for r in component_resources:
r['namespace'] = component._namespace
resources.extend(component_resources)

extract_resource_from_component(layout)
for t in layout.traverse():
extract_resource_from_component(t)
return resources


class Css:
# pylint: disable=old-style-class
class Css: # pylint: disable=old-style-class
def __init__(self, layout=None):
self._resources = Resources('_css_dist', layout)
self._resources.config = self.config
Expand All @@ -111,9 +84,6 @@ def append_css(self, stylesheet):
def get_all_css(self):
return self._resources.get_all_resources()

def get_inferred_css_dist(self):
return self._resources.get_inferred_resources()

# pylint: disable=old-style-class, no-init, too-few-public-methods
class config:
infer_from_layout = True
Expand All @@ -134,9 +104,6 @@ def append_script(self, script):
def get_all_scripts(self, dev_bundles=False):
return self._resources.get_all_resources(dev_bundles)

def get_inferred_scripts(self):
return self._resources.get_inferred_resources()

# pylint: disable=old-style-class, no-init, too-few-public-methods
class config:
infer_from_layout = True
Expand Down
2 changes: 1 addition & 1 deletion dash/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.28.7'
__version__ = '0.29.0'
24 changes: 24 additions & 0 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,3 +531,27 @@ def create_layout():

self.startServer(app)
time.sleep(0.5)

def test_late_component_register(self):
app = dash.Dash()

app.layout = html.Div([
html.Button('Click me to put a dcc ', id='btn-insert'),
html.Div(id='output')
])

@app.callback(Output('output', 'children'),
[Input('btn-insert', 'n_clicks')])
def update_output(value):
if value is None:
raise PreventUpdate

return dcc.Input(id='inserted-input')

self.startServer(app)

btn = self.wait_for_element_by_css_selector('#btn-insert')
btn.click()
time.sleep(1)

self.wait_for_element_by_css_selector('#inserted-input')

0 comments on commit 1663fea

Please sign in to comment.