text: # -*- coding: utf-8 -*-
  # Copyright 2018-2019 Streamlit Inc.
  #
  # 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.
  
  """This class stores a key-value pair for the config system."""
  
  # Python 2/3 compatibility
  from __future__ import print_function, division, unicode_literals, absolute_import
  from streamlit.compatibility import setup_2_3_shims
  
  setup_2_3_shims(globals())
  
  import datetime
  import re
  import textwrap
  
  from streamlit.logger import get_logger
  
  LOGGER = get_logger(__name__)
  
  
  class ConfigOption(object):
      '''Stores a Streamlit configuration option.
  
      A configuration option, like 'browser.serverPort', which indicates which port
      to use when connecting to the proxy. There are two ways to create a
      ConfigOption:
  
      Simple ConfigOptions are created as follows:
  
          ConfigOption('browser.serverPort',
              description = 'Connect to the proxy at this port.',
              default_val = 8501)
  
      More complex config options resolve thier values at runtime as follows:
  
          @ConfigOption('browser.serverPort')
          def _proxy_port():
              """Connect to the proxy at this port.
  
              Defaults to 8501.
              """
              return 8501
  
      NOTE: For complex config options, the function is called each time the
      option.value is evaluated!
  
      Attributes
      ----------
      key : str
          The fully qualified section.name
      value
          The value for this option. If this is a a complex config option then
          the callback is called EACH TIME value is evaluated.
      section : str
          The section of this option. Example: 'global'.
      name : str
          See __init__.
      description : str
          See __init__.
      where_defined : str
          Indicates which file set this config option.
          ConfigOption.DEFAULT_DEFINITION means this file.
      visibility : {'visible', 'hidden', 'obfuscated'}
          See __init__.
      deprecated: bool
          See __init__.
      deprecation_text : str or None
          See __init__.
      expiration_date : str or None
          See __init__.
      replaced_by : str or None
          See __init__.
      '''
  
      # This is a special value for ConfigOption.where_defined which indicates
      # that the option default was not overridden.
      DEFAULT_DEFINITION = "<default>"
  
      def __init__(
          self,
          key,
          description=None,
          default_val=None,
          visibility="visible",
          deprecated=False,
          deprecation_text=None,
          expiration_date=None,
          replaced_by=None,
          config_getter=None,
      ):
          """Create a ConfigOption with the given name.
  
          Parameters
          ----------
          key : str
              Should be of the form "section.optionName"
          description : str
              Like a comment for the config option.
          default_val : anything
              The value for this config option.
          visibility : {'visible', 'hidden', 'obfuscated'}
              Whether this option should be shown to users.
          deprecated: bool
              Whether this config option is deprecated.
          deprecation_text : str or None
              Required if deprecated == True. Set this to a string explaining
              what to use instead.
          expiration_date : str or None
              Required if deprecated == True. set this to the date at which it
              will no longer be accepted. Format: 'YYYY-MM-DD'.
          replaced_by : str or None
              If this is option has been deprecated in favor or another option,
              set this to the path to the new option. Example:
              'server.runOnSave'. If this is set, the 'deprecated' option
              will automatically be set to True, and deprecation_text will have a
              meaningful default (unless you override it).
          config_getter : callable or None
              Required if replaced_by != None. Should be set to
              config.get_option.
          """
          # Parse out the section and name.
          self.key = key
          key_format = r"(?P<section>\_?[a-z][a-z0-9]*)\.(?P<name>[a-z][a-zA-Z0-9]*)$"
          match = re.match(key_format, self.key)
          assert match, 'Key "%s" has invalid format.' % self.key
          self.section, self.name = match.group("section"), match.group("name")
  
          self.description = description
  
          self.visibility = visibility
          self.default_val = default_val
          self.deprecated = deprecated
          self.replaced_by = replaced_by
          self._get_val_func = None
          self.where_defined = ConfigOption.DEFAULT_DEFINITION
  
          if self.replaced_by:
              self.deprecated = True
              if deprecation_text is None:
                  deprecation_text = "Replaced by %s." % self.replaced_by
  
          if self.deprecated:
              assert expiration_date, "expiration_date is required for deprecated items"
              assert deprecation_text, "deprecation_text is required for deprecated items"
              self.expiration_date = expiration_date
              self.deprecation_text = textwrap.dedent(deprecation_text)
  
          self.set_value(default_val)
  
      def __call__(self, get_val_func):
          """Assign a function to compute the value for this option.
  
          This method is called when ConfigOption is used as a decorator.
  
          Parameters
          ----------
          get_val_func : function
              A function which will be called to get the value of this parameter.
              We will use its docString as the description.
  
          Returns
          -------
          ConfigOption
              Returns self, which makes testing easier. See config_test.py.
  
          """
          assert (
              get_val_func.__doc__
          ), "Complex config options require doc strings for their description."
          self.description = get_val_func.__doc__
          self._get_val_func = get_val_func
          return self
  
      @property
      def value(self):
          """Get the value of this config option."""
          return self._get_val_func()
  
      def set_value(self, value, where_defined=None):
          """Set the value of this option.
  
          Parameters
          ----------
          value
              The new value for this parameter.
          where_defined : str
              New value to remember where this parameter was set.
  
          """
          self._get_val_func = lambda: value
  
          if where_defined is None:
              self.where_defined = ConfigOption.DEFAULT_DEFINITION
          else:
              self.where_defined = where_defined
  
          if self.deprecated and self.where_defined != ConfigOption.DEFAULT_DEFINITION:
  
              details = {
                  "key": self.key,
                  "file": self.where_defined,
                  "explanation": self.deprecation_text,
                  "date": self.expiration_date,
              }
  
              if self.is_expired():
                  raise DeprecationError(
                      textwrap.dedent(
                          """
                      ════════════════════════════════════════════════
                      %(key)s IS NO LONGER SUPPORTED.
  
                      %(explanation)s
  
                      Please update %(file)s.
                      ════════════════════════════════════════════════
                      """
                      )
                      % details
                  )
              else:
                  LOGGER.warning(
                      textwrap.dedent(
                          """
                      ════════════════════════════════════════════════
                      %(key)s IS DEPRECATED.
                      %(explanation)s
  
                      This option will be removed on or after %(date)s.
  
                      Please update %(file)s.
                      ════════════════════════════════════════════════
                      """
                      )
                      % details
                  )
  
      def is_expired(self):
          """Returns true if expiration_date is in the past."""
          if not self.deprecated:
              return False
  
          expiration_date = _parse_yyyymmdd_str(self.expiration_date)
          now = datetime.datetime.now()
          return now > expiration_date
  
  
  def _parse_yyyymmdd_str(date_str):
      return datetime.datetime(*[int(token) for token in date_str.split("-")])
  
  
  class Error(Exception):
      pass
  
  
  class DeprecationError(Error):
      pass
  