Pass argument to Dash object

emher
Level 3
Pass argument to Dash object

I have just upgraded to dataiku 9.0, and I am trying out the new Dash integration. It's great! But since the Dash object is instantiated by dataiku, I am not sure how to pass arguments to the constructor, e.g. like this

 

app = dash.Dash(external_stylesheets=["some_stylesheet.css"], prevent_initial_callbacks=True)

 

What is the recommended way? ๐Ÿ™‚

0 Kudos
7 Replies
HenriC
Dataiker

Hi @emher,

We are glad you like the new release ๐Ÿ™‚

The new Dash Webapp has been built for simple use cases running natively on our platform. To achieve what you want to do, you can use the following workaround:

app.config.external_stylesheets =["some_stylesheet.css"]
app.config.prevent_initial_callbacks = True

However, some of the attributes can only be set through the constructor, and there are unfortunately no workaround to set them once the object has been initialised:

[
   "name",
   "assets_folder",
   "assets_url_path",
   "eager_loading",
   "url_base_pathname",
   "routes_pathname_prefix",
   "requests_pathname_prefix",
   "serve_locally",
   "compress",
]

 

Do not hesitate if you have more question about it!

Have a nice day,

Henri

0 Kudos
emher
Level 3
Author

Hi @HenriC ,

thank you for the answer! Unfortunately, one of the properties that I need to modify is the asserts folder (I didn't include it in my initial question to keep the example as simple as possible). Is there a reason why you don't allow editing these properties?

In Dataiku 8.0 I was able to do it simply by binding the Dash app to the flask webserver of a "normal" web app. It required some hacking though, so I would prefer moving to the new native integration, which is much cleaner. But I need to be able to load my own assets ๐Ÿ™‚

0 Kudos
HenriC
Dataiker

Hi!

The reason is quite simple. To make our app working out of the box, we intensiate `app = Dash(...)` just before running the user code. Once this app is created and linked to the flask server, well it can't be unregistered from it.

The list of fields comes from Dash source code: dash.py:335. This list describes all fields that can't be set outside the constructor, and is unfortunately not up to us. Does it make sense?

My guess would be for you to do as you were doing in DSS 8.0. We know it is not optimal and we will take your request in consideration for the coming releases ๐Ÿ™‚

 

0 Kudos
emher
Level 3
Author

Would it be possible to move that line,

`app = Dash(...)`

into the user code? If you make it part of the templates, it shouldn't make it more complex for "non-power user", and it would unleash the full power of Dash for "power users"?

emher
Level 3
Author

I have now figured out a hack. So in dataiku 9.0, all Dash apps are linked to the same assets directory. The idea of the hack is to create a subdirectory within this director, and load only assets from this directory. The resulting syntax is,

 

import my_dash_app
from dash_extensions.dataiku import bind_assets_folder

assets_folder = os.path.join(os.path.dirname(my_dash_app.__file__), "assets")
bind_assets_folder(app, "my_dash_app", assets_folder)

 

where my_dash_app is the module in which the app is defined, and where the app assets are located in an "assets" folder next to this file (which is the common pattern for dash applications). I think this more-or-less OK, but the hack itself is pretty ugly,

 

def bind_assets_folder(app, app_id, assets_folder):
    import os
    import re
    from distutils.dir_util import copy_tree
    # Create assets container folder for the app.
    dst_assets_folder = os.path.join(app.config.assets_folder, app_id)
    os.makedirs(dst_assets_folder, exist_ok=True)
    # Copy assets.
    copy_tree(assets_folder, dst_assets_folder)
    
    def _walk_assets_directory(self):
        walk_dir = os.path.join(self.config.assets_folder, app_id)  # EMHER: Use application sub dir
        slash_splitter = re.compile(r"[\\/]+")
        ignore_str = self.config.assets_ignore
        ignore_filter = re.compile(ignore_str) if ignore_str else None

        for current, _, files in sorted(os.walk(walk_dir)):
            if current == walk_dir:
                base = ""
            else:
                s = current.replace(walk_dir, "").lstrip("\\").lstrip("/")
                splitted = slash_splitter.split(s)
                if len(splitted) > 1:
                    base = "/".join(slash_splitter.split(s))
                else:
                    base = splitted[0]
            base = os.path.join(base, app_id)  # EMHER: Use application sub dir
            if ignore_filter:
                files_gen = (x for x in files if not ignore_filter.search(x))
            else:
                files_gen = files

            for f in sorted(files_gen):
                path = "/".join([base, f]) if base else f

                full = os.path.join(current, f)

                if f.endswith("js"):
                    self.scripts.append_script(self._add_assets_resource(path, full))
                elif f.endswith("css"):
                    self.css.append_css(self._add_assets_resource(path, full))
                elif f == "favicon.ico":
                    self._favicon = path

    app._walk_assets_directory = lambda x=app: _walk_assets_directory(app)

 

0 Kudos
williamwirono
Level 2

Hi @emher,

I am confused on what you meant by my_dash_app. What is that referring to? In addition, where are you placing this piece of code:

import my_dash_app
from dash_extensions.dataiku import bind_assets_folder

assets_folder = os.path.join(os.path.dirname(my_dash_app.__file__), "assets")
bind_assets_folder(app, "my_dash_app", assets_folder)

Is it on the Dash webapp script?

Similarly, where are you placing the CSS stuff in the /assets folder. Did you create an assets folder in the Code Library?

Thanks in advance!

0 Kudos
emher
Level 3
Author

@HenriC As a side note, many of the features of [dash-extensions](https://github.com/thedirtyfew/dash-extensions) relies on the app object construction being part of the user code. So to be able to utilize these tools (without excessive hacking), moving the app construction into user code would also be a great benefit ๐Ÿ™‚