python-3.xflaskiis-10plotly-dashwfastcgi

plotly-dash on iis with flask and wfastcgi


I've configured IIS with python and wfastcgi.py, getting it working, mostly. When trying with simple stuff, it returns the expected content.

My issue now is with getting flask and plotly/dash working under IIS, using attribute routing.

My web.config is as follows:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <add name="Python FastCGI" path="*" verb="*" modules="FastCgiModule" scriptProcessor="C:\Python36\python.exe|C:\Python36\Scripts\wfastcgi.py" resourceType="Unspecified" requireAccess="Script" />
    </handlers>
  </system.webServer>

  <appSettings>
    <!-- Required settings -->
    <add key="WSGI_HANDLER" value="index.wsgi_app" />
    <add key="PYTHONPATH" value="C:/inetpub/wwwroot/python/venv/Scripts/python36.zip;C:/inetpub/wwwroot/python/venv/DLLs;C:/inetpub/wwwroot/python/venv/lib;C:/inetpub/wwwroot/python/venv/Scripts;c:/python36/Lib', 'c:/python36/DLLs;C:/inetpub/wwwroot/python/venv;C:/inetpub/wwwroot/python/venv/lib/site-packages" />

    <!-- Optional settings -->
    <add key="WSGI_LOG" value="C:\temp\my_app.log" />
    <add key="WSGI_RESTART_FILE_REGEX" value=".*((\.py)|(\.config))$" />
    <add key="APPINSIGHTS_INSTRUMENTATIONKEY" value="__instrumentation_key__" />
    <add key="WSGI_PTVSD_SECRET" value="__secret_code__" />
    <add key="WSGI_PTVSD_ADDRESS" value="ipaddress:port" />
  </appSettings>
</configuration>

I've created an index.py file and when I use

from flask import Flask
from SimpleDash1 import app as sd1
from WebAppExampleA import app as waea

app = Flask(__name__)

@app.route("/")
def hello():
    response = ["Hello, world!\n"]
    return (line.encode("utf-8") for line in response)

def wsgi_app(environ,start_response):
    start_response('200 OK', [('Content-type', 'text/html'), ('Content-encoding', 'utf-8')])
    return hello()

if __name__ == "__main__":
    app.run()

It works fine, but it doesnt exactly serve up my plotly app. To try and load my plotly app, I've used this instead:

from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html

from app import app
from apps import web_example_a


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 == '/apps/web_example_a':
         return web_example_a.layout
    #elif pathname == '/apps/app2':
    #     return app2.layout
    else:
        return '404'

if __name__ == '__main__':
    app.run_server(debug=True)

But now I'm stumped as to what entrypoint I need to configure as my WSGI_HANDLER in web.config. If I try to change the WSGI_HANDLER to "index.display_page" I get an error saying:

Error occurred:

Traceback (most recent call last): File "C:\Python36\Scripts\wfastcgi.py", line 849, in main for part in result: File "C:\python36\lib\site-packages\applicationinsights\requests\WSGIApplication.py", line 70, in call for part in self._wsgi_application(environ, status_interceptor): File "C:\python36\lib\site-packages\dash\dash.py", line 498, in add_context output_value = func(*args, **kwargs) TypeError: display_page() takes 1 positional argument but 2 were given

StdOut:

StdErr: C:\python36\lib\site-packages\plotly\tools.py:103: UserWarning:

Looks like you don't have 'read-write' permission to your 'home' ('~') directory or to our '~/.plotly' directory. That means plotly's python api can't setup local configuration files. No problem though! You'll just have to sign-in using 'plotly.plotly.sign_in()'. For help with that: 'help(plotly.plotly.sign_in)'. Questions? Visit https://support.plot.ly


Solution

  • The WSGI entry point for Dash apps is the Flask instance attached to the Dash instance. That means you want to point your WSGI handler at app.server (where app is the Dash instance).

    WSGI servers often look for an attribute application when passed a module as an entry point. So a common thing to do is create a an entry-point file wsgi.py which in your case would simply have this:

    from index import app
    
    application = app.server