Skip to content
Snippets Groups Projects

Class Config with descriptor, special initialisation, basic heritage attempt - failure

  • Clone with SSH
  • Clone with HTTPS
  • Embed
  • Share
    The snippet can be accessed without any authentication.
    Authored by Françoise Conil
    descriptors_sms_config_with_init_and_heritage-1-ko.py 4.66 KiB
    """
    https://docs.python.org/3.9/howto/descriptor.html#complete-practical-example
    
    Bon, une mise en place naïve de l'héritage ne se passe pas très bien car ce
    sont des attributs de classe et on doit les initialiser via la classe dans
    laquelle ils sont définis.
    """
    
    import logging
    import os
    
    logging.basicConfig(level=logging.DEBUG)
    
    
    class ConfigEntry:
        """Stores information about a config entry"""
    
        def __init__(self, env=None, doc=None, default=None):
            """Initialize parameter's informations.
    
            :param env: Name of environment variable for the parameter, defaults to None
            :type env: str, optional
            :param doc: Description of the parameter, defaults to None
            :type doc: str, optional
            :param default: Default value for the parameter, defaults to None
            :type default: No default type, optional
            """
            self.env = env
            self.doc = doc
            self.default = default
    
        def __set_name__(self, owner, name):
            """Called at the time the owning class "owner" is created.
            The descriptor has been assigned to "name".
    
            https://docs.python.org/3/howto/descriptor.html#automatic-name-notification
            https://docs.python.org/3/reference/datamodel.html#object.__set_name__
    
            :param owner: Owning class
            :type owner: Config class
            :param name: Name assigned to this descriptor
            :type name: str
            """
            self.public_name = name
            self.private_name = f"_{name}"
            if self.env is None:
                self.env = name.upper()
    
        def __get__(self, obj, objtype=None):
            value = getattr(obj, self.private_name)
            logging.debug("Accessing %r giving %r", self.public_name, value)
            return value
    
        def __set__(self, obj, value):
            logging.debug("Updating %r to %r", self.public_name, value)
            setattr(obj, self.private_name, value)
    
        def set_initial_value(self, obj):
            logging.debug("Set initial value for %r", self.public_name)
            self.__set__(obj, os.environ.get(self.env, self.default))
    
    
    class ConfigGeneral:
        """Manage configuration parameters, get values from environment
        variables or default value. Value can be changed easily thanks
        to descriptors lookup."""
    
        def __init__(self):
            logging.debug("ConfigGeneral.__init__ called")
    
            self.config_keys = []
    
            # Try to write something easier to read
            cls = type(self)
            cls_dict = cls.__dict__
    
            logging.debug("cls.__name__=%s, vars(cls)=%s", cls.__name__, vars(cls))
            logging.debug("cls.__mro__=%s", cls.__mro__)
            for p in vars(cls):
                if isinstance(cls_dict[p], ConfigEntry):
                    cls_dict[p].set_initial_value(self)
                    self.config_keys.append(p)
    
        def print_config(self):
            logging.info("Class parameters")
            for k in self.config_keys:
                logging.info("{%s} -> {%s}", k, type(self).__dict__[k].__get__(self))
    
    
    class ConfigHTTP(ConfigGeneral):
        """Contains configuration information for http_fetcher"""
    
        # HTTP parameters
        name = ConfigEntry(
            env="HTTP_FETCHER_NAME", doc="Name of the acquisition", default=None
        )
        mode = ConfigEntry(env="HTTP_FETCHER_MODE", doc="http method", default="GET")
        url = ConfigEntry(env="HTTP_FETCHER_URL", doc="URL to fetch", default=None)
        delay = ConfigEntry(
            env="HTTP_FETCHER_DELAY",
            doc="Delay between requests in seconds",
            default=3600,
        )
        format = ConfigEntry(env="HTTP_FETCHER_FORMAT", doc="File format", default="json")
        maxitems = ConfigEntry(
            env="HTTP_FETCHER_MAXITEMS", doc="Max number of fetches", default=-1
        )
    
    
    class ConfigHTTPFetcher(ConfigHTTP):
        """Contains configuration information for http_fetcher (HTTP and
        RabbitMQ data)."""
    
        # RabbitMQ parameters
        broker = ConfigEntry(
            env="RABBITMQ_BROKER", doc="Hostname or IP for rabbitmq broker", default=None
        )
        queue = ConfigEntry(
            env="RABBITMQ_QUEUE",
            doc="Name of the destination queue for fetched data",
            default=None,
        )
    
    
    if __name__ == "__main__":
        # logging.debug(f"vars(ConfigHTTP) = {vars(ConfigHTTP)}")
        # logging.debug(f"vars(ConfigHTTPFetcher) = {vars(ConfigHTTPFetcher)}")
    
        # KeyError : "mode" est une variable ConfigHTTP, pas de ConfigHTTPFetcher
        # logging.debug(
        #     f"vars(vars(ConfigHTTPFetcher)['mode']) = {vars(vars(ConfigHTTPFetcher)['mode'])}"  # noqa E501
        # )
        # logging.debug(f"vars(vars(ConfigHTTP)['mode']) = {vars(vars(ConfigHTTP)['mode'])}")     # noqa E501
    
        c1 = ConfigHTTPFetcher()
    
        c1.print_config()
        logging.debug(f"vars(c1) = {vars(c1)}")
    
        c1.format = "xml"
        logging.debug(f"vars(c1) = {vars(c1)}")
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Finish editing this message first!
    Please register or to comment