Skip to content

Commit b78262e

Browse files
committed
Add configs from init or env, add pathname configs tests.
1 parent cc2b065 commit b78262e

File tree

4 files changed

+231
-11
lines changed

4 files changed

+231
-11
lines changed

Diff for: dash/_configs.py

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import os
2+
3+
# noinspection PyCompatibility
4+
from . import exceptions
5+
from ._utils import AttributeDict
6+
7+
8+
def env_configs():
9+
"""
10+
Configs from the environ.
11+
12+
:return: A dict with the dash environ vars
13+
"""
14+
return AttributeDict({x: os.getenv(x, os.getenv(x.lower())) for x in (
15+
'DASH_APP_NAME',
16+
'DASH_URL_BASE_PATHNAME',
17+
'DASH_ROUTES_PATHNAME_PREFIX',
18+
'DASH_REQUESTS_PATHNAME_PREFIX',
19+
'DASH_SUPPRESS_CALLBACK_EXCEPTIONS',
20+
'DASH_ASSETS_EXTERNAL_PATH',
21+
'DASH_INCLUDE_ASSETS_FILES'
22+
)})
23+
24+
25+
def choose_config(config_name, init, env, default=None):
26+
if init is not None:
27+
return init
28+
29+
env_value = env.get('DASH_{}'.format(config_name.upper()))
30+
if env_value is None:
31+
return default
32+
return env_value
33+
34+
35+
def pathname_configs(url_base_pathname=None,
36+
routes_pathname_prefix=None,
37+
requests_pathname_prefix=None,
38+
environ_configs=None):
39+
_pathname_config_error_message = '''
40+
{} This is ambiguous.
41+
To fix this, set `routes_pathname_prefix` instead of `url_base_pathname`.
42+
43+
Note that `requests_pathname_prefix` is the prefix for the AJAX calls that
44+
originate from the client (the web browser) and `routes_pathname_prefix` is
45+
the prefix for the API routes on the backend (this flask server).
46+
`url_base_pathname` will set `requests_pathname_prefix` and
47+
`routes_pathname_prefix` to the same value.
48+
If you need these to be different values then you should set
49+
`requests_pathname_prefix` and `routes_pathname_prefix`,
50+
not `url_base_pathname`.
51+
'''
52+
environ_configs = environ_configs or env_configs()
53+
54+
url_base_pathname = choose_config('url_base_pathname',
55+
url_base_pathname,
56+
environ_configs)
57+
58+
routes_pathname_prefix = choose_config('routes_pathname_prefix',
59+
routes_pathname_prefix,
60+
environ_configs)
61+
62+
requests_pathname_prefix = choose_config('requests_pathname_prefix',
63+
requests_pathname_prefix,
64+
environ_configs)
65+
66+
if url_base_pathname is not None and requests_pathname_prefix is not None:
67+
raise exceptions.InvalidConfig(
68+
_pathname_config_error_message.format(
69+
'You supplied `url_base_pathname` and '
70+
'`requests_pathname_prefix`.'
71+
)
72+
)
73+
elif url_base_pathname is not None and routes_pathname_prefix is not None:
74+
raise exceptions.InvalidConfig(
75+
_pathname_config_error_message.format(
76+
'You supplied `url_base_pathname` and '
77+
'`routes_pathname_prefix`.')
78+
)
79+
elif url_base_pathname is not None and routes_pathname_prefix is None:
80+
routes_pathname_prefix = url_base_pathname
81+
elif routes_pathname_prefix is None:
82+
routes_pathname_prefix = '/'
83+
84+
if not routes_pathname_prefix.startswith('/'):
85+
raise exceptions.InvalidConfig(
86+
'`routes_pathname_prefix` needs to start with `/`')
87+
if not routes_pathname_prefix.endswith('/'):
88+
raise exceptions.InvalidConfig(
89+
'`routes_pathname_prefix` needs to end with `/`')
90+
91+
app_name = environ_configs.DASH_APP_NAME
92+
93+
if not requests_pathname_prefix and app_name:
94+
requests_pathname_prefix = '/' + app_name + routes_pathname_prefix
95+
elif requests_pathname_prefix is None:
96+
requests_pathname_prefix = routes_pathname_prefix
97+
98+
if not requests_pathname_prefix.endswith(routes_pathname_prefix):
99+
raise exceptions.InvalidConfig(
100+
'`requests_pathname_prefix` needs to ends with '
101+
'`routes_pathname_prefix`.'
102+
)
103+
104+
return url_base_pathname, routes_pathname_prefix, requests_pathname_prefix

Diff for: dash/dash.py

+30-11
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
from ._utils import AttributeDict as _AttributeDict
2525
from ._utils import interpolate_str as _interpolate
2626
from ._utils import format_tag as _format_tag
27+
from . import _configs
28+
2729

2830
_default_index = '''
2931
<!DOCTYPE html>
@@ -71,14 +73,17 @@ def __init__(
7173
static_folder='static',
7274
assets_folder=None,
7375
assets_url_path='/assets',
74-
include_assets_files=True,
75-
url_base_pathname='/',
76-
requests_pathname_prefix='',
76+
assets_external_path=None,
77+
include_assets_files=None,
78+
url_base_pathname=None,
79+
requests_pathname_prefix=None,
80+
routes_pathname_prefix=None,
7781
compress=True,
7882
meta_tags=None,
7983
index_string=_default_index,
8084
external_scripts=None,
8185
external_stylesheets=None,
86+
suppress_callback_exceptions=None,
8287
**kwargs):
8388

8489
# pylint-disable: too-many-instance-attributes
@@ -102,16 +107,30 @@ def __init__(
102107
static_folder=self._assets_folder,
103108
static_url_path=assets_url_path))
104109

110+
env_configs = _configs.env_configs()
111+
112+
url_base_pathname, routes_pathname_prefix, requests_pathname_prefix = \
113+
_configs.pathname_configs(
114+
url_base_pathname,
115+
routes_pathname_prefix,
116+
requests_pathname_prefix,
117+
environ_configs=env_configs)
118+
105119
self.url_base_pathname = url_base_pathname
106120
self.config = _AttributeDict({
107-
'suppress_callback_exceptions': False,
108-
'routes_pathname_prefix': url_base_pathname,
109-
'requests_pathname_prefix': requests_pathname_prefix or os.getenv(
110-
'DASH_REQUESTS_PATHNAME_PREFIX',
111-
'/{}'.format(os.environ['DASH_APP_NAME'])
112-
if 'DASH_APP_NAME' in os.environ else '') + url_base_pathname,
113-
'include_assets_files': include_assets_files,
114-
'assets_external_path': '',
121+
'suppress_callback_exceptions': _configs.choose_config(
122+
'suppress_callback_exceptions',
123+
suppress_callback_exceptions, env_configs, False
124+
),
125+
'routes_pathname_prefix': routes_pathname_prefix,
126+
'requests_pathname_prefix': requests_pathname_prefix,
127+
'include_assets_files': _configs.choose_config(
128+
'include_assets_files',
129+
include_assets_files,
130+
env_configs,
131+
True),
132+
'assets_external_path': _configs.choose_config(
133+
'assets_external_path', assets_external_path, env_configs, ''),
115134
})
116135

117136
# list of dependencies

Diff for: dash/exceptions.py

+4
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,7 @@ class PreventUpdate(CallbackException):
5252

5353
class InvalidCallbackReturnValue(CallbackException):
5454
pass
55+
56+
57+
class InvalidConfig(DashException):
58+
pass

Diff for: tests/test_configs.py

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import unittest
2+
# noinspection PyProtectedMember
3+
from dash import _configs
4+
from dash import exceptions as _exc
5+
import os
6+
7+
8+
class MyTestCase(unittest.TestCase):
9+
10+
def setUp(self):
11+
environ = _configs.env_configs()
12+
13+
for k in environ.keys():
14+
if k in os.environ:
15+
os.environ.pop(k)
16+
17+
def test_valid_pathname_prefix_init(self):
18+
_, routes, req = _configs.pathname_configs()
19+
20+
self.assertEqual('/', routes)
21+
self.assertEqual('/', req)
22+
23+
_, routes, req = _configs.pathname_configs(
24+
routes_pathname_prefix='/dash/')
25+
26+
self.assertEqual('/dash/', req)
27+
28+
_, routes, req = _configs.pathname_configs(
29+
requests_pathname_prefix='/my-dash-app/',
30+
)
31+
32+
self.assertEqual(routes, '/')
33+
self.assertEqual(req, '/my-dash-app/')
34+
35+
_, routes, req = _configs.pathname_configs(
36+
routes_pathname_prefix='/dash/',
37+
requests_pathname_prefix='/my-dash-app/dash/'
38+
)
39+
40+
self.assertEqual('/dash/', routes)
41+
self.assertEqual('/my-dash-app/dash/', req)
42+
43+
def test_invalid_pathname_prefix(self):
44+
with self.assertRaises(_exc.InvalidConfig) as context:
45+
_, _, _ = _configs.pathname_configs('/my-path', '/another-path')
46+
47+
self.assertTrue('url_base_pathname' in str(context.exception))
48+
49+
with self.assertRaises(_exc.InvalidConfig) as context:
50+
_, _, _ = _configs.pathname_configs(
51+
url_base_pathname='/invalid',
52+
routes_pathname_prefix='/invalid')
53+
54+
self.assertTrue(str(context.exception).split('.')[0]
55+
.endswith('`routes_pathname_prefix`'))
56+
57+
with self.assertRaises(_exc.InvalidConfig) as context:
58+
_, _, _ = _configs.pathname_configs(
59+
url_base_pathname='/my-path',
60+
requests_pathname_prefix='/another-path')
61+
62+
self.assertTrue(str(context.exception).split('.')[0]
63+
.endswith('`requests_pathname_prefix`'))
64+
65+
with self.assertRaises(_exc.InvalidConfig) as context:
66+
_, _, _ = _configs.pathname_configs('my-path')
67+
68+
self.assertTrue('start with `/`' in str(context.exception))
69+
70+
with self.assertRaises(_exc.InvalidConfig) as context:
71+
_, _, _ = _configs.pathname_configs('/my-path')
72+
73+
self.assertTrue('end with `/`' in str(context.exception))
74+
75+
def test_pathname_prefix_from_environ_app_name(self):
76+
os.environ['DASH_APP_NAME'] = 'my-dash-app'
77+
_, routes, req = _configs.pathname_configs()
78+
self.assertEqual('/my-dash-app/', req)
79+
self.assertEqual('/', routes)
80+
81+
def test_pathname_prefix_environ_routes(self):
82+
os.environ['DASH_ROUTES_PATHNAME_PREFIX'] = '/routes/'
83+
_, routes, req = _configs.pathname_configs()
84+
self.assertEqual('/routes/', routes)
85+
86+
def test_pathname_prefix_environ_requests(self):
87+
os.environ['DASH_REQUESTS_PATHNAME_PREFIX'] = '/requests/'
88+
_, routes, req = _configs.pathname_configs()
89+
self.assertEqual('/requests/', req)
90+
91+
92+
if __name__ == '__main__':
93+
unittest.main()

0 commit comments

Comments
 (0)