diff options
Diffstat (limited to 'venv/lib/python3.9/site-packages/streamlit/config.py')
-rw-r--r-- | venv/lib/python3.9/site-packages/streamlit/config.py | 1265 |
1 files changed, 1265 insertions, 0 deletions
diff --git a/venv/lib/python3.9/site-packages/streamlit/config.py b/venv/lib/python3.9/site-packages/streamlit/config.py new file mode 100644 index 00000000..4b9f67b3 --- /dev/null +++ b/venv/lib/python3.9/site-packages/streamlit/config.py @@ -0,0 +1,1265 @@ +# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Loads the configuration data.""" + +import copy +import os +import secrets +import threading +from collections import OrderedDict +from typing import Any, Callable, Dict, Optional, cast + +import toml +from blinker import Signal + +from streamlit import config_util, development, env_util, file_util, util +from streamlit.config_option import ConfigOption +from streamlit.errors import StreamlitAPIException + +# Config System Global State # + +# Descriptions of each of the possible config sections. +# (We use OrderedDict to make the order in which sections are declared in this +# file be the same order as the sections appear with `streamlit config show`) +_section_descriptions: Dict[str, str] = OrderedDict( + _test="Special test section just used for unit tests." +) + +# Ensures that we don't try to get or set config options when config.toml files +# change so are re-parsed. +_config_lock = threading.RLock() + +# Stores config options with their default values (or None if they don't have +# a default) before they are updated with values from config.toml files, flags +# to `streamlit run`, etc. Note that this and _config_options below are +# OrderedDicts to ensure stable ordering when printed using +# `streamlit config show`. +_config_options_template: Dict[str, ConfigOption] = OrderedDict() + +# Stores the current state of config options. +_config_options: Optional[Dict[str, ConfigOption]] = None + + +# Indicates that a config option was defined by the user. +_USER_DEFINED = "<user defined>" + +# Indicates that a config option was defined either in an environment variable +# or via command-line flag. +_DEFINED_BY_FLAG = "command-line argument or environment variable" + + +def set_option(key: str, value: Any, where_defined: str = _USER_DEFINED) -> None: + """Set config option. + + Run `streamlit config show` in the terminal to see all available options. + + This is an internal API. The public `st.set_option` API is implemented + in `set_user_option`. + + Parameters + ---------- + key : str + The config option key of the form "section.optionName". To see all + available options, run `streamlit config show` on a terminal. + + value + The new value to assign to this config option. + + where_defined : str + Tells the config system where this was set. + """ + with _config_lock: + # Ensure that our config files have been parsed. + get_config_options() + _set_option(key, value, where_defined) + + +def set_user_option(key: str, value: Any) -> None: + """Set config option. + + Currently, only the following config options can be set within the script itself: + * client.caching + * client.displayEnabled + * deprecation.* + + Calling with any other options will raise StreamlitAPIException. + + Run `streamlit config show` in the terminal to see all available options. + + Parameters + ---------- + key : str + The config option key of the form "section.optionName". To see all + available options, run `streamlit config show` on a terminal. + + value + The new value to assign to this config option. + + """ + try: + opt = _config_options_template[key] + except KeyError as ke: + raise StreamlitAPIException( + "Unrecognized config option: {key}".format(key=key) + ) from ke + if opt.scriptable: + set_option(key, value) + return + + raise StreamlitAPIException( + "{key} cannot be set on the fly. Set as command line option, e.g. streamlit run script.py --{key}, or in config.toml instead.".format( + key=key + ) + ) + + +def get_option(key: str) -> Any: + """Return the current value of a given Streamlit config option. + + Run `streamlit config show` in the terminal to see all available options. + + Parameters + ---------- + key : str + The config option key of the form "section.optionName". To see all + available options, run `streamlit config show` on a terminal. + """ + with _config_lock: + config_options = get_config_options() + + if key not in config_options: + raise RuntimeError('Config key "%s" not defined.' % key) + return config_options[key].value + + +def get_options_for_section(section: str) -> Dict[str, Any]: + """Get all of the config options for the given section. + + Run `streamlit config show` in the terminal to see all available options. + + Parameters + ---------- + section : str + The name of the config section to fetch options for. + + Returns + ------- + Dict[str, Any] + A dict mapping the names of the options in the given section (without + the section name as a prefix) to their values. + """ + with _config_lock: + config_options = get_config_options() + + options_for_section = {} + for option in config_options.values(): + if option.section == section: + options_for_section[option.name] = option.value + return options_for_section + + +def _create_section(section: str, description: str) -> None: + """Create a config section and store it globally in this module.""" + assert section not in _section_descriptions, ( + 'Cannot define section "%s" twice.' % section + ) + _section_descriptions[section] = description + + +def _create_option( + key: str, + description: Optional[str] = None, + default_val: Optional[Any] = None, + scriptable: bool = False, + visibility: str = "visible", + deprecated: bool = False, + deprecation_text: Optional[str] = None, + expiration_date: Optional[str] = None, + replaced_by: Optional[str] = None, + type_: type = str, +) -> ConfigOption: + '''Create a ConfigOption and store it globally in this module. + + There are two ways to create a ConfigOption: + + (1) Simple, constant config options are created as follows: + + _create_option('section.optionName', + description = 'Put the description here.', + default_val = 12345) + + (2) More complex, programmable config options use decorator syntax to + resolve their values at runtime: + + @_create_option('section.optionName') + def _section_option_name(): + """Put the description here.""" + return 12345 + + To achieve this sugar, _create_option() returns a *callable object* of type + ConfigObject, which then decorates the function. + + NOTE: ConfigObjects call their evaluation functions *every time* the option + is requested. To prevent this, use the `streamlit.util.memoize` decorator as + follows: + + @_create_option('section.memoizedOptionName') + @util.memoize + def _section_memoized_option_name(): + """Put the description here.""" + + (This function is only called once.) + """ + return 12345 + + ''' + option = ConfigOption( + key, + description=description, + default_val=default_val, + scriptable=scriptable, + visibility=visibility, + deprecated=deprecated, + deprecation_text=deprecation_text, + expiration_date=expiration_date, + replaced_by=replaced_by, + type_=type_, + ) + assert ( + option.section in _section_descriptions + ), 'Section "%s" must be one of %s.' % ( + option.section, + ", ".join(_section_descriptions.keys()), + ) + assert key not in _config_options_template, 'Cannot define option "%s" twice.' % key + _config_options_template[key] = option + return option + + +def _delete_option(key: str) -> None: + """Remove a ConfigOption by key from the global store. + + Only for use in testing. + """ + try: + del _config_options_template[key] + del cast(Dict[str, ConfigOption], _config_options)[key] + except Exception: + # We don't care if the option already doesn't exist. + pass + + +# Config Section: Global # + +_create_section("global", "Global options that apply across all of Streamlit.") + +_create_option( + "global.disableWatchdogWarning", + description=""" + By default, Streamlit checks if the Python watchdog module is available + and, if not, prints a warning asking for you to install it. The watchdog + module is not required, but highly recommended. It improves Streamlit's + ability to detect changes to files in your filesystem. + + If you'd like to turn off this warning, set this to True. + """, + default_val=False, + type_=bool, +) + + +_create_option( + "global.showWarningOnDirectExecution", + description=""" + If True, will show a warning when you run a Streamlit-enabled script + via "python my_script.py". + """, + default_val=True, + type_=bool, +) + + +@_create_option("global.developmentMode", visibility="hidden", type_=bool) +def _global_development_mode() -> bool: + """Are we in development mode. + + This option defaults to True if and only if Streamlit wasn't installed + normally. + """ + return ( + not env_util.is_pex() + and "site-packages" not in __file__ + and "dist-packages" not in __file__ + and "__pypackages__" not in __file__ + ) + + +_create_option( + "global.logLevel", + description="""Level of logging: 'error', 'warning', 'info', or 'debug'. + + Default: 'info' + """, + deprecated=True, + deprecation_text="global.logLevel has been replaced with logger.level", + expiration_date="2020-11-30", + replaced_by="logger.level", +) + +_create_option( + "global.unitTest", + description="Are we in a unit test?", + visibility="hidden", + default_val=False, + type_=bool, +) + +_create_option( + "global.suppressDeprecationWarnings", + description="Hide deprecation warnings in the streamlit app.", + visibility="hidden", + default_val=False, + type_=bool, +) + +_create_option( + "global.minCachedMessageSize", + description="""Only cache ForwardMsgs that are greater than or equal to + this minimum.""", + visibility="hidden", + default_val=10 * 1e3, + type_=float, +) # 10k + +_create_option( + "global.maxCachedMessageAge", + description="""Expire cached ForwardMsgs whose age is greater than this + value. A message's age is defined by how many times its script has + finished running since the message has been accessed.""", + visibility="hidden", + default_val=2, + type_=int, +) + +_create_option( + "global.dataFrameSerialization", + description=""" + DataFrame serialization. + + Acceptable values: + - 'legacy': Serialize DataFrames using Streamlit's custom format. Slow + but battle-tested. + - 'arrow': Serialize DataFrames using Apache Arrow. Much faster and versatile.""", + default_val="arrow", + type_=str, +) + + +# Config Section: Logger # +_create_section("logger", "Settings to customize Streamlit log messages.") + + +@_create_option("logger.level", type_=str) +def _logger_log_level() -> str: + """Level of logging: 'error', 'warning', 'info', or 'debug'. + + Default: 'info' + """ + if get_option("global.logLevel"): + return str(get_option("global.logLevel")) + elif get_option("global.developmentMode"): + return "debug" + else: + return "info" + + +@_create_option("logger.messageFormat", type_=str) +def _logger_message_format() -> str: + """String format for logging messages. If logger.datetimeFormat is set, + logger messages will default to `%(asctime)s.%(msecs)03d %(message)s`. See + [Python's documentation](https://docs.python.org/2.6/library/logging.html#formatter-objects) + for available attributes. + + Default: "%(asctime)s %(message)s" + """ + if get_option("global.developmentMode"): + from streamlit.logger import DEFAULT_LOG_MESSAGE + + return DEFAULT_LOG_MESSAGE + else: + return "%(asctime)s %(message)s" + + +_create_option( + "logger.enableRich", + description=""" + Controls whether uncaught app exceptions are logged via the rich library. + + If True and if rich is installed, exception tracebacks will be logged with syntax highlighting and formatting. + Rich tracebacks are easier to read and show more code than standard Python tracebacks. + + If set to False, the default Python traceback formatting will be used.""", + default_val=False, + visibility="hidden", + type_=bool, + scriptable=True, +) + +# Config Section: Client # + +_create_section("client", "Settings for scripts that use Streamlit.") + +_create_option( + "client.caching", + description="Whether to enable st.cache.", + default_val=True, + type_=bool, + scriptable=True, +) + +_create_option( + "client.displayEnabled", + description="""If false, makes your Streamlit script not draw to a + Streamlit app.""", + default_val=True, + type_=bool, + scriptable=True, +) + +_create_option( + "client.showErrorDetails", + description=""" + Controls whether uncaught app exceptions and deprecation warnings + are displayed in the browser. By default, this is set to True and + Streamlit displays app exceptions and associated tracebacks, and + deprecation warnings, in the browser. + + If set to False, an exception or deprecation warning will result in + a generic message being shown in the browser, and exceptions, tracebacks, + and deprecation warnings will be printed to the console only.""", + default_val=True, + type_=bool, + scriptable=True, +) + +# Config Section: Runner # + +_create_section("runner", "Settings for how Streamlit executes your script") + +_create_option( + "runner.magicEnabled", + description=""" + Allows you to type a variable or string by itself in a single line of + Python code to write it to the app. + """, + default_val=True, + type_=bool, +) + +_create_option( + "runner.installTracer", + description=""" + Install a Python tracer to allow you to stop or pause your script at + any point and introspect it. As a side-effect, this slows down your + script's execution. + """, + default_val=False, + type_=bool, +) + +_create_option( + "runner.fixMatplotlib", + description=""" + Sets the MPLBACKEND environment variable to Agg inside Streamlit to + prevent Python crashing. + """, + default_val=True, + type_=bool, +) + +_create_option( + "runner.postScriptGC", + description=""" + Run the Python Garbage Collector after each script execution. This + can help avoid excess memory use in Streamlit apps, but could + introduce delay in rerunning the app script for high-memory-use + applications. + """, + default_val=True, + type_=bool, +) + +_create_option( + "runner.fastReruns", + description=""" + Handle script rerun requests immediately, rather than waiting for script + execution to reach a yield point. This makes Streamlit much more + responsive to user interaction, but it can lead to race conditions in + apps that mutate session_state data outside of explicit session_state + assignment statements. + """, + default_val=True, + type_=bool, +) + +_create_option( + "runner.enforceSerializableSessionState", + description=""" + Raise an exception after adding unserializable data to Session State. + Some execution environments may require serializing all data in Session + State, so it may be useful to detect incompatibility during development, + or when the execution environment will stop supporting it in the future. + """, + default_val=False, + type_=bool, +) + +# Config Section: Server # + +_create_section("server", "Settings for the Streamlit server") + +_create_option( + "server.folderWatchBlacklist", + description="""List of folders that should not be watched for changes. This + impacts both "Run on Save" and @st.cache. + + Relative paths will be taken as relative to the current working directory. + + Example: ['/home/user1/env', 'relative/path/to/folder'] + """, + default_val=[], +) + +_create_option( + "server.fileWatcherType", + description=""" + Change the type of file watcher used by Streamlit, or turn it off + completely. + + Allowed values: + * "auto" : Streamlit will attempt to use the watchdog module, and + falls back to polling if watchdog is not available. + * "watchdog" : Force Streamlit to use the watchdog module. + * "poll" : Force Streamlit to always use polling. + * "none" : Streamlit will not watch files. + """, + default_val="auto", + type_=str, +) + + +@_create_option("server.cookieSecret", type_=str) +@util.memoize +def _server_cookie_secret() -> str: + """Symmetric key used to produce signed cookies. If deploying on multiple replicas, this should + be set to the same value across all replicas to ensure they all share the same secret. + + Default: randomly generated secret key. + """ + return secrets.token_hex() + + +@_create_option("server.headless", type_=bool) +def _server_headless() -> bool: + """If false, will attempt to open a browser window on start. + + Default: false unless (1) we are on a Linux box where DISPLAY is unset, or + (2) we are running in the Streamlit Atom plugin. + """ + if env_util.IS_LINUX_OR_BSD and not os.getenv("DISPLAY"): + # We're running in Linux and DISPLAY is unset + return True + + if os.getenv("IS_RUNNING_IN_STREAMLIT_EDITOR_PLUGIN") is not None: + # We're running within the Streamlit Atom plugin + return True + + return False + + +_create_option( + "server.runOnSave", + description=""" + Automatically rerun script when the file is modified on disk. + """, + default_val=False, + type_=bool, +) + +_create_option( + "server.allowRunOnSave", + description=""" + Allows users to automatically rerun when app is updated. + """, + visibility="hidden", + default_val=True, + type_=bool, +) + + +@_create_option("server.address") +def _server_address() -> Optional[str]: + """The address where the server will listen for client and browser + connections. Use this if you want to bind the server to a specific address. + If set, the server will only be accessible from this address, and not from + any aliases (like localhost). + + Default: (unset) + """ + return None + + +_create_option( + "server.port", + description=""" + The port where the server will listen for browser connections.""", + default_val=8501, + type_=int, +) + +_create_option( + "server.scriptHealthCheckEnabled", + visibility="hidden", + description=""" + Flag for enabling the script health check endpoint. It used for checking if + a script loads successfully. On success, the endpoint will return a 200 + HTTP status code. On failure, the endpoint will return a 503 HTTP status code. + + Note: This is an experimental Streamlit internal API. The API is subject + to change anytime so this should be used at your own risk + """, + default_val=False, + type_=bool, +) + +_create_option( + "server.baseUrlPath", + description=""" + The base path for the URL where Streamlit should be served from. + """, + default_val="", + type_=str, +) + +# TODO: Rename to server.enableCorsProtection. +_create_option( + "server.enableCORS", + description=""" + Enables support for Cross-Origin Resource Sharing (CORS) protection, for added security. + + Due to conflicts between CORS and XSRF, if `server.enableXsrfProtection` is on and + `server.enableCORS` is off at the same time, we will prioritize `server.enableXsrfProtection`. + """, + default_val=True, + type_=bool, +) + + +_create_option( + "server.enableXsrfProtection", + description=""" + Enables support for Cross-Site Request Forgery (XSRF) protection, for added security. + + Due to conflicts between CORS and XSRF, if `server.enableXsrfProtection` is on and + `server.enableCORS` is off at the same time, we will prioritize `server.enableXsrfProtection`. + """, + default_val=True, + type_=bool, +) + +_create_option( + "server.maxUploadSize", + description=""" + Max size, in megabytes, for files uploaded with the file_uploader. + """, + default_val=200, # If this default is changed, please also update the docstring for `DeltaGenerator.file_uploader`. + type_=int, +) + +_create_option( + "server.maxMessageSize", + description=""" + Max size, in megabytes, of messages that can be sent via the WebSocket connection. + """, + default_val=200, + type_=int, +) + +_create_option( + "server.enableWebsocketCompression", + description=""" + Enables support for websocket compression. + """, + default_val=False, + type_=bool, +) + +_create_option( + "server.enableStaticServing", + description=""" + Enable serving files from a `static` directory in the running app's directory. + """, + default_val=False, + type_=bool, +) + +# Config Section: Browser # + +_create_section("browser", "Configuration of non-UI browser options.") + + +_create_option( + "browser.serverAddress", + description=""" + Internet address where users should point their browsers in order to + connect to the app. Can be IP address or DNS name and path. + + This is used to: + - Set the correct URL for CORS and XSRF protection purposes. + - Show the URL on the terminal + - Open the browser + """, + default_val="localhost", + type_=str, +) + + +_create_option( + "browser.gatherUsageStats", + description=""" + Whether to send usage statistics to Streamlit. + """, + default_val=True, + type_=bool, +) + + +@_create_option("browser.serverPort", type_=int) +def _browser_server_port() -> int: + """Port where users should point their browsers in order to connect to the + app. + + This is used to: + - Set the correct URL for CORS and XSRF protection purposes. + - Show the URL on the terminal + - Open the browser + + Default: whatever value is set in server.port. + """ + return int(get_option("server.port")) + + +_SSL_PRODUCTION_WARNING = [ + "DO NOT USE THIS OPTION IN A PRODUCTION ENVIRONMENT. It has not gone through " + "security audits or performance tests. For the production environment, " + "we recommend performing SSL termination by the load balancer or the reverse proxy." +] + +_create_option( + "server.sslCertFile", + description=( + f""" + Server certificate file for connecting via HTTPS. + Must be set at the same time as "server.sslKeyFile". + + {_SSL_PRODUCTION_WARNING} + """ + ), +) + +_create_option( + "server.sslKeyFile", + description=( + f""" + Cryptographic key file for connecting via HTTPS. + Must be set at the same time as "server.sslCertFile". + + {_SSL_PRODUCTION_WARNING} + """ + ), +) + +# Config Section: UI # + +# NOTE: We currently hide the ui config section in the `streamlit config show` +# output as all of its options are hidden. If a non-hidden option is eventually +# added, the section should be unhidden by removing it from the `SKIP_SECTIONS` +# set in config_util.show_config. +_create_section("ui", "Configuration of UI elements displayed in the browser.") + +_create_option( + "ui.hideTopBar", + description=""" + Flag to hide most of the UI elements found at the top of a Streamlit app. + + NOTE: This does *not* hide the hamburger menu in the top-right of an app. + """, + default_val=False, + type_=bool, + visibility="hidden", +) + +_create_option( + "ui.hideSidebarNav", + description="Flag to hide the sidebar page navigation component.", + default_val=False, + type_=bool, + visibility="hidden", +) + + +# Config Section: Mapbox # + +_create_section("mapbox", "Mapbox configuration that is being used by DeckGL.") + +_create_option( + "mapbox.token", + description="""Configure Streamlit to use a custom Mapbox + token for elements like st.pydeck_chart and st.map. + To get a token for yourself, create an account at + https://mapbox.com. It's free (for moderate usage levels)!""", + default_val="", +) + + +# Config Section: deprecations + +_create_section("deprecation", "Configuration to show or hide deprecation warnings.") + +_create_option( + "deprecation.showfileUploaderEncoding", + description="Set to false to disable the deprecation warning for the file uploader encoding.", + default_val=True, + scriptable=True, + type_=bool, + expiration_date="2021-01-06", +) + +_create_option( + "deprecation.showImageFormat", + description="Set to false to disable the deprecation warning for the image format parameter.", + default_val=True, + scriptable=True, + type_=bool, + deprecated=True, + deprecation_text="The format parameter for st.image has been removed.", + expiration_date="2021-03-24", +) + +_create_option( + "deprecation.showPyplotGlobalUse", + description="Set to false to disable the deprecation warning for using the global pyplot instance.", + default_val=True, + scriptable=True, + type_=bool, +) + + +# Config Section: Custom Theme # + +_create_section("theme", "Settings to define a custom theme for your Streamlit app.") + +_create_option( + "theme.base", + description="""The preset Streamlit theme that your custom theme inherits from. + One of "light" or "dark".""", +) + +_create_option( + "theme.primaryColor", + description="Primary accent color for interactive elements.", +) + +_create_option( + "theme.backgroundColor", + description="Background color for the main content area.", +) + +_create_option( + "theme.secondaryBackgroundColor", + description="Background color used for the sidebar and most interactive widgets.", +) + +_create_option( + "theme.textColor", + description="Color used for almost all text.", +) + +_create_option( + "theme.font", + description=""" + Font family for all text in the app, except code blocks. One of "sans serif", + "serif", or "monospace". + """, +) + + +def get_where_defined(key: str) -> str: + """Indicate where (e.g. in which file) this option was defined. + + Parameters + ---------- + key : str + The config option key of the form "section.optionName" + + """ + with _config_lock: + config_options = get_config_options() + + if key not in config_options: + raise RuntimeError('Config key "%s" not defined.' % key) + return config_options[key].where_defined + + +def _is_unset(option_name: str) -> bool: + """Check if a given option has not been set by the user. + + Parameters + ---------- + option_name : str + The option to check + + + Returns + ------- + bool + True if the option has not been set by the user. + + """ + return get_where_defined(option_name) == ConfigOption.DEFAULT_DEFINITION + + +def is_manually_set(option_name: str) -> bool: + """Check if a given option was actually defined by the user. + + Parameters + ---------- + option_name : str + The option to check + + + Returns + ------- + bool + True if the option has been set by the user. + + """ + return get_where_defined(option_name) not in ( + ConfigOption.DEFAULT_DEFINITION, + ConfigOption.STREAMLIT_DEFINITION, + ) + + +def show_config() -> None: + """Print all config options to the terminal.""" + with _config_lock: + config_util.show_config( + _section_descriptions, cast(Dict[str, ConfigOption], _config_options) + ) + + +# Load Config Files # + + +def _set_option(key: str, value: Any, where_defined: str) -> None: + """Set a config option by key / value pair. + + This function assumes that the _config_options dictionary has already been + populated and thus should only be used within this file and by tests. + + Parameters + ---------- + key : str + The key of the option, like "logger.level". + value + The value of the option. + where_defined : str + Tells the config system where this was set. + + """ + assert ( + _config_options is not None + ), "_config_options should always be populated here." + if key not in _config_options: + # Import logger locally to prevent circular references + from streamlit.logger import get_logger + + LOGGER = get_logger(__name__) + + LOGGER.warning( + f'"{key}" is not a valid config option. If you previously had this config option set, it may have been removed.' + ) + + else: + _config_options[key].set_value(value, where_defined) + + +def _update_config_with_toml(raw_toml: str, where_defined: str) -> None: + """Update the config system by parsing this string. + + This should only be called from get_config_options. + + Parameters + ---------- + raw_toml : str + The TOML file to parse to update the config values. + where_defined : str + Tells the config system where this was set. + + """ + parsed_config_file = toml.loads(raw_toml) + + for section, options in parsed_config_file.items(): + for name, value in options.items(): + value = _maybe_read_env_variable(value) + _set_option(f"{section}.{name}", value, where_defined) + + +def _maybe_read_env_variable(value: Any) -> Any: + """If value is "env:foo", return value of environment variable "foo". + + If value is not in the shape above, returns the value right back. + + Parameters + ---------- + value : any + The value to check + + Returns + ------- + any + Either returns value right back, or the value of the environment + variable. + + """ + if isinstance(value, str) and value.startswith("env:"): + var_name = value[len("env:") :] + env_var = os.environ.get(var_name) + + if env_var is None: + # Import logger locally to prevent circular references + from streamlit.logger import get_logger + + LOGGER = get_logger(__name__) + + LOGGER.error("No environment variable called %s" % var_name) + else: + return _maybe_convert_to_number(env_var) + + return value + + +def _maybe_convert_to_number(v: Any) -> Any: + """Convert v to int or float, or leave it as is.""" + try: + return int(v) + except Exception: + pass + + try: + return float(v) + except Exception: + pass + + return v + + +# Allow outside modules to wait for the config file to be parsed before doing +# something. +_on_config_parsed = Signal(doc="Emitted when the config file is parsed.") + +CONFIG_FILENAMES = [ + file_util.get_streamlit_file_path("config.toml"), + file_util.get_project_streamlit_file_path("config.toml"), +] + + +def get_config_options( + force_reparse=False, options_from_flags: Optional[Dict[str, Any]] = None +) -> Dict[str, ConfigOption]: + """Create and return a dict mapping config option names to their values, + returning a cached dict if possible. + + Config option values are sourced from the following locations. Values + set in locations further down the list overwrite those set earlier. + 1. default values defined in this file + 2. the global `~/.streamlit/config.toml` file + 3. per-project `$CWD/.streamlit/config.toml` files + 4. environment variables such as `STREAMLIT_SERVER_PORT` + 5. command line flags passed to `streamlit run` + + Parameters + ---------- + force_reparse : bool + Force config files to be parsed so that we pick up any changes to them. + + options_from_flags : Optional[Dict[str, any] + Config options that we received via CLI flag. + + Returns + ------- + Dict[str, ConfigOption] + An ordered dict that maps config option names to their values. + """ + global _config_options + + if not options_from_flags: + options_from_flags = {} + + # Avoid grabbing the lock in the case where there's nothing for us to do. + config_options = _config_options + if config_options and not force_reparse: + return config_options + + with _config_lock: + # Short-circuit if config files were parsed while we were waiting on + # the lock. + if _config_options and not force_reparse: + return _config_options + + old_options = _config_options + _config_options = copy.deepcopy(_config_options_template) + + # Values set in files later in the CONFIG_FILENAMES list overwrite those + # set earlier. + for filename in CONFIG_FILENAMES: + if not os.path.exists(filename): + continue + + with open(filename, "r", encoding="utf-8") as input: + file_contents = input.read() + + _update_config_with_toml(file_contents, filename) + + for opt_name, opt_val in options_from_flags.items(): + _set_option(opt_name, opt_val, _DEFINED_BY_FLAG) + + if old_options and config_util.server_option_changed( + old_options, _config_options + ): + # Import logger locally to prevent circular references. + from streamlit.logger import get_logger + + LOGGER = get_logger(__name__) + LOGGER.warning( + "An update to the [server] config option section was detected." + " To have these changes be reflected, please restart streamlit." + ) + + _on_config_parsed.send() + return _config_options + + +def _check_conflicts() -> None: + # Node-related conflicts + + # When using the Node server, we must always connect to 8501 (this is + # hard-coded in JS). Otherwise, the browser would decide what port to + # connect to based on window.location.port, which in dev is going to + # be (3000) + + # Import logger locally to prevent circular references + from streamlit.logger import get_logger + + LOGGER = get_logger(__name__) + + if get_option("global.developmentMode"): + assert _is_unset( + "server.port" + ), "server.port does not work when global.developmentMode is true." + + assert _is_unset("browser.serverPort"), ( + "browser.serverPort does not work when global.developmentMode is " "true." + ) + + # XSRF conflicts + if get_option("server.enableXsrfProtection"): + if not get_option("server.enableCORS") or get_option("global.developmentMode"): + LOGGER.warning( + """ +Warning: the config option 'server.enableCORS=false' is not compatible with 'server.enableXsrfProtection=true'. +As a result, 'server.enableCORS' is being overridden to 'true'. + +More information: +In order to protect against CSRF attacks, we send a cookie with each request. +To do so, we must specify allowable origins, which places a restriction on +cross-origin resource sharing. + +If cross origin resource sharing is required, please disable server.enableXsrfProtection. + """ + ) + + +def _set_development_mode() -> None: + development.is_development_mode = get_option("global.developmentMode") + + +def on_config_parsed( + func: Callable[[], None], force_connect=False, lock=False +) -> Callable[[], bool]: + """Wait for the config file to be parsed then call func. + + If the config file has already been parsed, just calls func immediately + unless force_connect is set. + + Parameters + ---------- + func : Callable[[], None] + A function to run on config parse. + + force_connect : bool + Wait until the next config file parse to run func, even if config files + have already been parsed. + + lock : bool + If set, grab _config_lock before running func. + + Returns + ------- + Callable[[], bool] + A function that the caller can use to deregister func. + """ + # We need to use the same receiver when we connect or disconnect on the + # Signal. If we don't do this, then the registered receiver won't be released + # leading to a memory leak because the Signal will keep a reference of the + # callable argument. When the callable argument is an object method, then + # the reference to that object won't be released. + receiver = lambda _: func_with_lock() + + def disconnect(): + return _on_config_parsed.disconnect(receiver) + + def func_with_lock(): + if lock: + with _config_lock: + func() + else: + func() + + if force_connect or not _config_options: + # weak=False so that we have control of when the on_config_parsed + # callback is deregistered. + _on_config_parsed.connect(receiver, weak=False) + else: + func_with_lock() + + return disconnect + + +# Run _check_conflicts only once the config file is parsed in order to avoid +# loops. We also need to grab the lock when running _check_conflicts since it +# may edit config options based on the values of other config options. +on_config_parsed(_check_conflicts, lock=True) +on_config_parsed(_set_development_mode) |