Hi All. I’m writing because I’ve been working with dash over the past couple of months to develop (and eventually deploy - hopefully) a multi-page dashboard to Heroku. I’ve followed through all tutorials structured my app as recommended in “structuring a multi-page app” tutorial (and I’ve also successfully deployed one-page apps to Heroku without an issue). Anyway, I’m writing because I’m trying to deploy to Heroku and I open the page, I get an internal server error and when I check the heroku logs I get the following error:
AttributeError: 'NoneType' object has no attribute 'traverse'
Please note the error logs are modified anywhere you might expect an website sounding url because plotly isn’t letting me post links because I recently joined and they think I might be a spammer.
(dash_apps) RTI-102626:app_base cinkpen$ heroku logs
2018-01-20T18:14:30.757858+00:00 app[web.1]: Failed to find application: 'app'
2018-01-20T18:14:30.759669+00:00 app[web.1]: [2018-01-20 18:14:30 +0000] [8] [INFO] Worker exiting (pid: 8)
2018-01-20T18:14:30.879541+00:00 app[web.1]: Traceback (most recent call last):
2018-01-20T18:14:30.879852+00:00 app[web.1]: self.sleep()
2018-01-20T18:14:30.880053+00:00 app[web.1]: ready = select.select([self.PIPE[0]], [], [], 1.0)
2018-01-20T18:14:30.879855+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/arbiter.py", line 359, in sleep
2018-01-20T18:14:30.879554+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/arbiter.py", line 209, in run
2018-01-20T18:14:30.880213+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/arbiter.py", line 524, in reap_workers
2018-01-20T18:14:30.880199+00:00 app[web.1]: self.reap_workers()
2018-01-20T18:14:30.880057+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/arbiter.py", line 244, in handle_chld
2018-01-20T18:14:30.880429+00:00 app[web.1]: raise HaltServer(reason, self.WORKER_BOOT_ERROR)
2018-01-20T18:14:30.880465+00:00 app[web.1]: gunicorn.errors.HaltServer: <HaltServer 'Worker failed to boot.' 3>
2018-01-20T18:14:30.880466+00:00 app[web.1]:
2018-01-20T18:14:30.880467+00:00 app[web.1]: During handling of the above exception, another exception occurred:
2018-01-20T18:14:30.880474+00:00 app[web.1]: Traceback (most recent call last):
2018-01-20T18:14:30.880468+00:00 app[web.1]:
2018-01-20T18:14:30.880476+00:00 app[web.1]: File "/app/.heroku/python/bin/gunicorn", line 11, in <module>
2018-01-20T18:14:30.880572+00:00 app[web.1]: sys.exit(run())
2018-01-20T18:14:30.880573+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 74, in run
2018-01-20T18:14:30.880683+00:00 app[web.1]: WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]").run()
2018-01-20T18:14:30.880685+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/app/base.py", line 203, in run
2018-01-20T18:14:30.880825+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/app/base.py", line 72, in run
2018-01-20T18:14:30.880928+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/arbiter.py", line 231, in run
2018-01-20T18:14:30.881064+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/arbiter.py", line 344, in halt
2018-01-20T18:14:30.880823+00:00 app[web.1]: super(Application, self).run()
2018-01-20T18:14:30.880925+00:00 app[web.1]: Arbiter(self).run()
2018-01-20T18:14:30.881061+00:00 app[web.1]: self.halt(reason=inst.reason, exit_status=inst.exit_status)
2018-01-20T18:14:30.881240+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/arbiter.py", line 393, in stop
2018-01-20T18:14:30.881419+00:00 app[web.1]: time.sleep(0.1)
2018-01-20T18:14:30.881421+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/arbiter.py", line 244, in handle_chld
2018-01-20T18:14:30.881235+00:00 app[web.1]: self.stop()
2018-01-20T18:14:30.881559+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/arbiter.py", line 527, in reap_workers
2018-01-20T18:14:30.881769+00:00 app[web.1]: raise HaltServer(reason, self.APP_LOAD_ERROR)
2018-01-20T18:14:30.881556+00:00 app[web.1]: self.reap_workers()
2018-01-20T18:14:30.881785+00:00 app[web.1]: gunicorn.errors.HaltServer: <HaltServer 'App failed to load.' 4>
2018-01-20T18:14:30.961512+00:00 heroku[web.1]: State changed from up to crashed
2018-01-20T18:14:30.954592+00:00 heroku[web.1]: Process exited with status 1
2018-01-20T18:32:45.000000+00:00 app[api]: Build started by user cinkpen_at_someplace
2018-01-20T18:33:16.189288+00:00 heroku[web.1]: State changed from crashed to starting
2018-01-20T18:33:15.925286+00:00 app[api]: Release v7 created by user cinkpen_at_someplace
2018-01-20T18:33:15.925286+00:00 app[api]: Deploy 12a3da55 by user cinkpen_at_someplace
2018-01-20T18:32:45.000000+00:00 app[api]: Build succeeded
2018-01-20T18:33:28.504702+00:00 heroku[web.1]: Starting process with command `gunicorn app:server`
2018-01-20T18:33:30.600967+00:00 app[web.1]: [2018-01-20 18:33:30 +0000] [4] [INFO] Starting gunicorn 19.7.1
2018-01-20T18:33:30.601453+00:00 app[web.1]: [2018-01-20 18:33:30 +0000] [4] [INFO] Listening at: hteeteeeepeecolon-slash-slash0.0.0.0:28512 (4)
2018-01-20T18:33:30.601551+00:00 app[web.1]: [2018-01-20 18:33:30 +0000] [4] [INFO] Using worker: sync
2018-01-20T18:33:30.605174+00:00 app[web.1]: [2018-01-20 18:33:30 +0000] [8] [INFO] Booting worker with pid: 8
2018-01-20T18:33:30.615251+00:00 app[web.1]: [2018-01-20 18:33:30 +0000] [9] [INFO] Booting worker with pid: 9
2018-01-20T18:33:31.269528+00:00 heroku[web.1]: State changed from starting to up
2018-01-20T18:33:37.261611+00:00 heroku[router]: at=info method=GET path="/" host=fakenamequickdashdotsomething request_id=c7df2261-3b6b-470a-8eb4-8225920bf2fb fwd="161.69.123.11" dyno=web.1 connect=0ms service=6ms status=500 bytes=456 protocol=hteeteeeepees
2018-01-20T18:33:37.260685+00:00 app[web.1]: [2018-01-20 18:33:37,259] ERROR in app: Exception on / [GET]
2018-01-20T18:33:37.260710+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1982, in wsgi_app
2018-01-20T18:33:37.260705+00:00 app[web.1]: Traceback (most recent call last):
2018-01-20T18:33:37.260711+00:00 app[web.1]: response = self.full_dispatch_request()
2018-01-20T18:33:37.260713+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1607, in full_dispatch_request
2018-01-20T18:33:37.260714+00:00 app[web.1]: self.try_trigger_before_first_request_functions()
2018-01-20T18:33:37.260716+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1654, in try_trigger_before_first_request_functions
2018-01-20T18:33:37.260718+00:00 app[web.1]: func()
2018-01-20T18:33:37.260719+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/dash/dash.py", line 554, in _setup_server
2018-01-20T18:33:37.260721+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/dash/dash.py", line 244, in _generate_scripts_html
2018-01-20T18:33:37.260722+00:00 app[web.1]: self.scripts.get_all_scripts() +
2018-01-20T18:33:37.260720+00:00 app[web.1]: self._generate_scripts_html()
2018-01-20T18:33:37.260723+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/dash/resources.py", line 123, in get_all_scripts
2018-01-20T18:33:37.260725+00:00 app[web.1]: return self._resources.get_all_resources()
2018-01-20T18:33:37.260726+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/dash/resources.py", line 56, in get_all_resources
2018-01-20T18:33:37.260727+00:00 app[web.1]: self.get_inferred_resources() + self._resources
2018-01-20T18:33:37.260728+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/dash/resources.py", line 84, in get_inferred_resources
2018-01-20T18:33:37.260730+00:00 app[web.1]: for t in layout.traverse():
2018-01-20T18:33:37.260736+00:00 app[web.1]: AttributeError: 'NoneType' object has no attribute 'traverse'
2018-01-20T18:33:37.508440+00:00 app[web.1]: [2018-01-20 18:33:37,507] ERROR in app: Exception on /favicon.ico [GET]
2018-01-20T18:33:37.261361+00:00 app[web.1]: 10.35.194.85 - - [20/Jan/2018:18:33:37 +0000] "GET / hteeteeeepee/1.1" 500 291 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
2018-01-20T18:33:37.508443+00:00 app[web.1]: Traceback (most recent call last):
2018-01-20T18:33:37.508445+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1982, in wsgi_app
2018-01-20T18:33:37.508452+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/dash/dash.py", line 554, in _setup_server
2018-01-20T18:33:37.508446+00:00 app[web.1]: response = self.full_dispatch_request()
2018-01-20T18:33:37.508447+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1607, in full_dispatch_request
2018-01-20T18:33:37.508448+00:00 app[web.1]: self.try_trigger_before_first_request_functions()
2018-01-20T18:33:37.508450+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1654, in try_trigger_before_first_request_functions
2018-01-20T18:33:37.508459+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/dash/resources.py", line 56, in get_all_resources
2018-01-20T18:33:37.509234+00:00 app[web.1]: 10.35.194.85 - - [20/Jan/2018:18:33:37 +0000] "GET /favicon.ico hteeteeeepee/1.1" 500 291 "hteeteeeepeescolon-slash-slashfakenamequickdashdotherokuapp/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
2018-01-20T18:33:37.508453+00:00 app[web.1]: self._generate_scripts_html()
2018-01-20T18:33:37.508460+00:00 app[web.1]: self.get_inferred_resources() + self._resources
2018-01-20T18:33:37.508451+00:00 app[web.1]: func()
2018-01-20T18:33:37.508458+00:00 app[web.1]: return self._resources.get_all_resources()
2018-01-20T18:33:37.508468+00:00 app[web.1]: AttributeError: 'NoneType' object has no attribute 'traverse'
2018-01-20T18:33:37.508457+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/dash/resources.py", line 123, in get_all_scripts
2018-01-20T18:33:37.508455+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/dash/dash.py", line 244, in _generate_scripts_html
2018-01-20T18:33:37.508463+00:00 app[web.1]: for t in layout.traverse():
2018-01-20T18:33:37.508456+00:00 app[web.1]: self.scripts.get_all_scripts() +
2018-01-20T18:33:37.508461+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/dash/resources.py", line 84, in get_inferred_resources
2018-01-20T18:33:37.510592+00:00 heroku[router]: at=info method=GET path="/favicon.ico" host=fakename request_id=f27f69a1-6739-4c6d-97d8-c6cb637e9a0e fwd="161.69.123.11" dyno=web.1 connect=0ms service=5ms status=500 bytes=456 protocol=hteeteeeepees
So I can see it’s saying it’s failing to load the app there, most likely because the app.py in my file doesn’t actually have a layout (that’s in index.py described below).
I’ve structured the app as follows:
|- multi_app
|- apps
| |- global_page.py
| |- region_page.py
| |- department_page.py
| |- municipal_page.py
| |- pycache
|- app.py
|- index.py
|- init.py
|- pycache
|- Datafile1.csv
|- Datafile2.csv
|- Procfile
|- requirements.txt
|- password_text.py
The app words perfectly well locally. Here is the pertinent code for the files:
app.py
import dash
import pandas as pd
import datetime
# bring in the data
# Activities Data prep
df = pd.read_csv('Actividades_Data_2018-01-12.csv')
df['date'] = pd.to_datetime(df['fecha_de_inicio']).dt.strftime('%m/%Y')
df['count'] = 1
#print(municipio)
# Pillars Data prep
pf = pd.read_csv('test_item_pruebas.csv')
pf['month_year'] = pd.to_datetime(pf['date']).dt.strftime('%m/%Y')
pf['count'] = 1
# Get current date for axes
now = datetime.datetime.now()
# Define Link Stylesheet Dictionary
link_dict = {'margin': 5, 'color': '#fff', 'padding': '8px', 'text-decoration': 'none', 'font-size': '150%', 'text-transform': 'uppercase'}
title_link_dict = {'margin': 0, 'color': '#fff', 'padding': 0, 'text-decoration': 'none', 'font-size': '100%'}
list_style = {'list-style': 'none', 'display': 'inline-block', 'float': 'right'}
title_style = {'list-style': 'none', 'color': '#fff', 'margin': 5, 'padding': '0px', 'text-decoration': 'none', 'font-size': '200%'}
app = dash.Dash()
server = app.server
app.config.supress_callback_exceptions = True
index.py*
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import dash_auth
from app import app
from apps import global_page, region_page, department_page, municipality_page, four0four_page
from test import VALID_USERNAME_PASSWORD_PAIRS
auth = dash_auth.BasicAuth(
app,
VALID_USERNAME_PASSWORD_PAIRS
)
# App Layout
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
html.Div(id='page-content')
])
@app.callback(Output('page-content', 'children'),
[Input('url', 'pathname')])
def display_page(pathname):
if pathname == '/':
return global_page.layout
elif pathname == '/apps/global_page':
return global_page.layout
elif pathname == '/apps/region_page':
return region_page.layout
elif pathname == '/apps/department_page':
return department_page.layout
elif pathname == '/apps/municipality_page':
return municipality_page.layout
else:
return four0four_page.layout
# style the app with CSS
app.css.append_css({"external_url": "hteeteeeepeescolon-slash-slashcodepen.io/chriddyp/pen/bWLwgPdotcss"})
if __name__ == '__main__':
app.run_server(debug=True)
an example of one of the apps with just the imports - global.py
I’m only showing an example of what I’m importing just for brevity but each app has 6 different dash apps and they all use a different aggregation of the data, so I return the layouts/callbacks from each (e.g. global_page). I have no issue rendering it locally.
# Global Page for QuickDash
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd
from dash.dependencies import Input, Output
import datetime
from app import app, df, pf, now, link_dict, title_style, list_style, title_link_dict
Procfile
web: gunicorn app:server
password_text.py
VALID_USERNAME_PASSWORD_PAIRS = [["fake_user", "password"]]
requirements.txt
appnope==0.1.0
certifi==2017.7.27.1
chardet==3.0.4
click==6.7
dash==0.18.3
dash-auth==0.0.9
dash-core-components==0.15.0rc1
dash-html-components==0.7.0
dash-renderer==0.11.0
decorator==4.1.2
Flask==0.12.2
Flask-Compress==1.4.0
Flask-SeaSurf==0.2.2
idna==2.6
ipython==6.2.1
ipython-genutils==0.2.0
itsdangerous==0.24
jedi==0.11.0
Jinja2==2.9.6
jsonschema==2.6.0
jupyter-core==4.3.0
loremipsum==1.0.5
MarkupSafe==1.0
nbformat==4.4.0
numpy==1.13.1
pandas==0.20.3
parso==0.1.0
pexpect==4.3.0
pickleshare==0.7.4
plotly==2.0.15
prompt-toolkit==1.0.15
ptyprocess==0.5.2
Pygments==2.2.0
python-dateutil==2.6.1
pytz==2017.2
requests==2.18.4
simplegeneric==0.8.1
six==1.11.0
traitlets==4.3.2
urllib3==1.22
wcwidth==0.1.7
Werkzeug==0.12.2
gunicorn==19.7.1
So initially I’m thinking the reason is won’t load is because I know Heroku looks for an “app.py” file and runs that and if it’s doing that, it’s not going to get the layout/logic (which is in index), but if I just give these files different names (and fix the import names correctly) by switching to index.py to app.py and app.py to base_app.py, I get a different error (posted below).
error when switching names to make index.py now have the name of app.py and app.py have the name base_app.py (with the imports sorted out properly for the file names)
(dash_apps) RTI-102626:app_base cinkpen$ git add .
(dash_apps) RTI-102626:app_base cinkpen$ git commit -m "testing base_app nonsense"
[master 7568ad7] testing base_app nonsense
7 files changed, 6 insertions(+), 6 deletions(-)
rename index.py => app.py (97%)
rename app.py => base_app.py (100%)
(dash_apps) RTI-102626:app_base cinkpen$ git push heroku master
Enter passphrase for key '/Users/cinkpen/.ssh/id_rsa':
Enter passphrase for key '/Users/cinkpen/.ssh/id_rsa':
Counting objects: 9, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 1.27 KiB | 0 bytes/s, done.
Total 9 (delta 6), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Python app detected
remote: -----> Installing requirements with pip
remote:
remote: -----> Discovering process types
remote: Procfile declares types -> web
remote:
remote: -----> Compressing...
remote: Done: 96.6M
remote: -----> Launching...
remote: Released v8
remote: hteeteeeepeescolon-slash-slashfakenamequickdashdotherokuapp/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To heroku:fakenamequickdashdotgit
12a3da5..7568ad7 master -> master
(dash_apps) RTI-102626:app_base cinkpen$ heroku ps:scale web=1
Scaling dynos... done, now running web at 1:Free
Then when I load up the app on heroku, it gives me an application error with the following logs:
(dash_apps) RTI-102626:app_base cinkpen$ heroku logs
2018-01-20T22:59:54.513314+00:00 app[web.1]: Failed to find application: 'app'
2018-01-20T22:59:54.517704+00:00 app[web.1]: [2018-01-20 22:59:54 +0000] [9] [INFO] Worker exiting (pid: 9)
2018-01-20T22:59:54.748030+00:00 app[web.1]: Failed to find application: 'app'
2018-01-20T22:59:54.752363+00:00 app[web.1]: [2018-01-20 22:59:54 +0000] [8] [INFO] Worker exiting (pid: 8)
2018-01-20T22:59:54.865229+00:00 app[web.1]: [2018-01-20 22:59:54 +0000] [4] [INFO] Shutting down: Master
2018-01-20T22:59:54.865366+00:00 app[web.1]: [2018-01-20 22:59:54 +0000] [4] [INFO] Reason: App failed to load.
2018-01-20T22:59:54.965234+00:00 heroku[web.1]: State changed from up to crashed
2018-01-20T22:59:54.951614+00:00 heroku[web.1]: Process exited with status 4
2018-01-20T23:01:04.178613+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=fakenamequickdashdotherokuapp request_id=8df411e9-471f-4947-9d1f-79d3f275e9c9 fwd="161.69.123.11" dyno= connect= service= status=503 bytes= protocol=hteeteeeepees
2018-01-20T23:01:04.428823+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/favicon.ico" host=fakenamequickdashdotherokuapp request_id=d6a0e17d-a08e-4a13-ac28-586c8669667c fwd="161.69.123.11" dyno= connect= service= status=503 bytes= protocol=hteeteeeepees
I’ve also just moved the stuff from index into app.py to see what happens if I just load it up like that and then the app doesn’t render the pages “region_page.py”, “department_page.py”, and “municipal_page.py” and when I do it that way I sort out the issues with the circular dependencies.
So I’m just wondering if you can provide some feedback on how to deploy a multi-page app structured like the one on the dash tutorial (which I know is also a multi-page app) to Heroku.
Thanks!