Skip to content
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

[BUG] Dash 2.1.0 incompatible with Apache wsgi #1928

Closed
pfbuxton opened this issue Feb 15, 2022 · 5 comments
Closed

[BUG] Dash 2.1.0 incompatible with Apache wsgi #1928

pfbuxton opened this issue Feb 15, 2022 · 5 comments

Comments

@pfbuxton
Copy link

In summary I am using Apache as the web-server, but the importing of app causes the error:
'Read-only: can only be set in the Dash constructor or during init_app()'

This seems to be similar to #1907. All works fine with dash 2.0.0, but breaks with 2.1.0.

Detailed explanation:

Following https://dash.plotly.com/urls we have:

- app.py
- pages
   |-- __init__.py
   |-- page1.py
   |-- page2.py

We also have the modwsgi python file, /var/www/path_to_site/site_name.wsgi, which Apache is set-up to use, this is what is causing the error:

import sys
sys.path.insert(0, "/var/www/path_to_site/")
from site_name.app import server as application

and the Apache settings /etc/httpd/sites-enabled/site_name.conf

<VirtualHost *:80>
    ServerName server_name

    WSGIDaemonProcess site_name home=/var/www/path_to_site processes=4 threads=12 python-home=/var/www/path_to_site/venv

    WSGIScriptAlias /st40_phys_viewer /var/www/path_to_site/site_name.wsgi

    <Directory /var/www/path_to_site>
        WSGIProcessGroup site_name
        WSGIApplicationGroup %{GLOBAL}
        Order allow,deny
        Allow from all
    </Directory>

    <Directory /var/www/path_to_site/assets>
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>
@pfbuxton pfbuxton reopened this Feb 15, 2022
@pfbuxton
Copy link
Author

I have found a workaround... but it's not ideal.

Modify the following file: /PATH_TO_YOUR_PYTHON_ENVIROMENT/site-packages/dash/_utils.py lines 157-158
Original:

    def __setitem__(self, key, val):
        if key in self.__dict__.get("_read_only", {}):
            raise AttributeError(self._read_only[key], key)

        final_msg = self.__dict__.get("_final")
        if final_msg and key not in self:
            raise AttributeError(final_msg, key)

        return super().__setitem__(key, val)

Modified:

    def __setitem__(self, key, val):
        #if key in self.__dict__.get("_read_only", {}):
        #    raise AttributeError(self._read_only[key], key)

        final_msg = self.__dict__.get("_final")
        if final_msg and key not in self:
            raise AttributeError(final_msg, key)

        return super().__setitem__(key, val)

other suggestions plotly/jupyter-dash#75 are to do this:

del app.config._read_only["requests_pathname_prefix"]

however, for me this did not work.

@alexcjohnson
Copy link
Collaborator

@pfbuxton are you able to share the full traceback you're seeing? These attributes are read-only for a reason: if they're modified late without being extremely careful about it the app can break in some very confusing ways.

Obviously jupyter-dash needed to do this anyway - and plotly/jupyter-dash#76 fixes it to work with Dash 2.1 - but before we go about extending that pattern elsewhere it would be nice to understand what's going on in your case.

@pfbuxton
Copy link
Author

Hi @alexcjohnson,

Sorry for the delay. I needed to create a virtual OS to do some of the testing. We are using CentOS 7.5.

Attached are the relevant files (both settings and errors):
FILES.zip

To start the Apache example you can do:
sudo /sbin/service httpd restart

Here is the error log, /etc/httpd/logs/error_log:

[Mon Jul 11 14:44:33.473341 2022] [wsgi:error] [pid 20737] hello1
[Mon Jul 11 14:44:33.473416 2022] [wsgi:error] [pid 20737] /var/www/example/venv/bin/python
[Mon Jul 11 14:44:33.722478 2022] [wsgi:error] [pid 20737] hello2
[Mon Jul 11 14:44:34.996686 2022] [wsgi:error] [pid 20737] [remote 10.99.1.36:51432] mod_wsgi (pid=20737): Failed to exec Python script file '/var/www/example/example_wsgi.wsgi'.
[Mon Jul 11 14:44:34.996737 2022] [wsgi:error] [pid 20737] [remote 10.99.1.36:51432] mod_wsgi (pid=20737): Exception occurred processing WSGI script '/var/www/example/example_wsgi.wsgi'.
[Mon Jul 11 14:44:34.997070 2022] [wsgi:error] [pid 20737] [remote 10.99.1.36:51432] Traceback (most recent call last):
[Mon Jul 11 14:44:34.997097 2022] [wsgi:error] [pid 20737] [remote 10.99.1.36:51432]   File "/var/www/example/example_wsgi.wsgi", line 9, in <module>
[Mon Jul 11 14:44:34.997100 2022] [wsgi:error] [pid 20737] [remote 10.99.1.36:51432]     from minimal_example import app
[Mon Jul 11 14:44:34.997113 2022] [wsgi:error] [pid 20737] [remote 10.99.1.36:51432]   File "/var/www/example/minimal_example.py", line 12, in <module>
[Mon Jul 11 14:44:34.997116 2022] [wsgi:error] [pid 20737] [remote 10.99.1.36:51432]     requests_pathname_prefix = '/st40_phys_viewer/'
[Mon Jul 11 14:44:34.997120 2022] [wsgi:error] [pid 20737] [remote 10.99.1.36:51432]   File "/var/www/example/venv/lib64/python3.6/site-packages/dash/_utils.py", line 124, in update
[Mon Jul 11 14:44:34.997123 2022] [wsgi:error] [pid 20737] [remote 10.99.1.36:51432]     self[k] = v
[Mon Jul 11 14:44:34.997127 2022] [wsgi:error] [pid 20737] [remote 10.99.1.36:51432]   File "/var/www/example/venv/lib64/python3.6/site-packages/dash/_utils.py", line 113, in __setitem__
[Mon Jul 11 14:44:34.997129 2022] [wsgi:error] [pid 20737] [remote 10.99.1.36:51432]     raise AttributeError(self._read_only[key], key)
[Mon Jul 11 14:44:34.997143 2022] [wsgi:error] [pid 20737] [remote 10.99.1.36:51432] AttributeError: ('Read-only: can only be set in the Dash constructor or during init_app()', 'routes_pathname_prefix')

Thanks,
Peter

@alexcjohnson
Copy link
Collaborator

I see, thanks. Is there a reason this can't all be moved into the constructor though? Also if you're setting routes_pathname_prefix and requests_pathname_prefix to the same thing ('/example/'), I don't believe there's any purpose setting url_base_pathname to something else. And while we're at it, a few of the other settings you're using here are now the defaults as of Dash 2.0: compress=False and serve_locally=True. And app.enable_dev_tools(debug=False) I believe is a noop. So I'd think you can rewrite this block in minimal_example.py:

app = Dash(__name__, url_base_pathname='/', compress=False)
app.config.update(dict(
    routes_pathname_prefix = '/example/', 
    requests_pathname_prefix = '/example/'
))
app.enable_dev_tools(debug=False)

# Only use local css and javascripts
app.css.config.serve_locally = True
app.scripts.config.serve_locally = True

app.config.suppress_callback_exceptions = True
app.title = 'example'

as:

app = Dash(
    __name__,
    url_base_pathname='/example/',
    suppress_callback_exceptions=True,
    title='example'
)

Does that work?

@pfbuxton
Copy link
Author

Hi @alexcjohnson,

Your suggestion partially fixed this issue.

With my apache settings (i.e. the /etc/httpd/sites-enabled/site_name.conf file) I needed to have routes_pathname_prefix, and requests_pathname_prefix set using app.config.update. If they were all set during the initial construction Dash complained that they weren't the same as url_base_pathname!

Anyway the solution is to change the scope in the wsgi file form:

    WSGIScriptAlias /st40_phys_viewer /var/www/path_to_site/site_name.wsgi

to:

    WSGIScriptAlias / /var/www/path_to_site/site_name.wsgi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants