| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 | 
							- import json
 
- import pathlib
 
- import ipaddress
 
- import sqlalchemy
 
- class config_error(TypeError):
 
-     pass
 
- class settings:
 
-     """
 
-     This class is responsible for handling settings in app. It read config 
 
-     file from specified path, and handle options from it. When any config
 
-     option is specified wrong, for example bad formated IP address, then
 
-     config_error will be raised.
 
-     """
 
-     def __init__(self, path: pathlib.Path) -> None:
 
-         """
 
-         This function initialize settings, it require path to parse it as 
 
-         config file, and reading parameters from it.
 
-         Parameters:
 
-             path (pathlib.Path): Path of config file
 
-         """
 
-         if not path.is_file():
 
-             raise FileNotFoundError("Config JSON not found.")
 
-         with path.open() as config:
 
-             try:
 
-                 self.__source = json.loads(config.read())
 
-             except:
 
-                 raise config_error("Syntax error in config file.")
 
-     @property 
 
-     def app_name(self) -> str:
 
-         """ It return provider app name. """
 
-         
 
-         return self.__get("provider.app_name", str) or "Key App"
 
-     @property
 
-     def app_description(self) -> str:
 
-         """ It return description of app given by provider. """
 
-         return self.__get("provider.description", str) or "Secure keys store."
 
-     @property
 
-     def organization_name(self) -> str:
 
-         """ This return name of organization which own app. """
 
-         return self.__get("provider.organization_name", str) or "cx-org"
 
-     @property
 
-     def database(self) -> str:
 
-         """
 
-         This return URL of the database, to build database connector. It
 
-         parse all options, that mean user could give all parameters one
 
-         by one, or URL as string.
 
-         Returns:
 
-             (str): URL of the database connection 
 
-         """
 
-         username = self.__get("database.username", str) or None
 
-         password = self.__get("database.password", str) or None
 
-         port = self.__get("database.port", int) or None
 
-         host = self.__get("database.host", str) or None
 
-         engine = self.__get("database.engine", str) or None
 
-         database = self.__get("database.name", str) or None
 
-         url = self.__get("database.url", str) or None
 
-         if url is not None:
 
-             check = username or None
 
-             check = check or password or None
 
-             check = check or port or None
 
-             check = check or host or None
 
-             check = check or engine or None
 
-             check = check or database or None
 
-             if check is None:
 
-                 return url
 
-             error = "When database URL config parameter is in use, any other "
 
-             error = error + "database config can not being specified. Decide "
 
-             error = error + "which method to setting database connection use."
 
-             raise config_rror(error)
 
-         
 
-         engine = engine or "sqlite"
 
-         database = database or "database.sqlite3"
 
-         url = sqlalchemy.URL(
 
-             engine,
 
-             username = username,
 
-             password = password,
 
-             host = host,
 
-             database = database,
 
-             port = port,
 
-             query = None
 
-         )
 
-         return str(url)
 
-     @property
 
-     def host(self) -> str:
 
-         """
 
-         This return host to listen on. When in config is not valid ip address
 
-         v4 or v6, then raise error.
 
-         Returns:
 
-             (str): Host to app listen on
 
-         """
 
-         host = self.__get("listen.ip", str) or "0.0.0.0"
 
-         try:
 
-             ipaddress.ip_address(host)
 
-         except:
 
-             raise config_error("IP address is in invald form.")
 
-         return host
 
-     @property
 
-     def port(self) -> int:
 
-         """
 
-         This return port, or raise exception when port is failed.
 
-         Returns:
 
-             (int): Number of port to listen on
 
-         """
 
-         port = self.__get("listen.port", int) or 8000
 
-         if not self.__is_port(port):
 
-             raise config_error("Port is not in valid range.")
 
-         return port
 
-     @property
 
-     def address(self) -> str:
 
-         """
 
-         This is address of app to make server listen on. 
 
-         Returns:
 
-             (str): Address of app to listen on
 
-         """
 
-         port = self.port
 
-         address = self.host
 
-         ip_type = ipaddress.ip_address(host)
 
-         if type(ip_type) is ipaddress.IPv4Address:
 
-             return "http://" + address + ":" + port
 
-             
 
-         return "http://[" + address + "]:" + port
 
-     def __is_port(self, port: int) -> bool:
 
-         """
 
-         This function check that given number is proof port.
 
-         Parameters:
 
-             port (int): Port number to check
 
-         Returns:
 
-             (bool): True when this is valid port, false when not
 
-         """
 
-         return port >= 0 and port <= 65535
 
-     def __get(
 
-         self, 
 
-         name: str, 
 
-         typed: type | None = None
 
-     ) -> str | int | float | bool | None:
 
-         """
 
-         This function load parameter, from source config file. It could 
 
-         check type of loaded parameter, and raise error, if type is not
 
-         valid. When typed parameter is not provided, then it skip 
 
-         check of parameter type. When item not exists in config file, then
 
-         return None. It could be used with "or". Config file has parts, to 
 
-         navigate in parts use ".". For example: ["x"]["y"]["z"] => "x.y.z".
 
-         
 
-         Example:
 
-             param = self.__get("default.param", str) or "default"
 
-             
 
-         Parameters:
 
-             name (str): Name of the parameter, with dot "." notation
 
-             typed (type | None): Type of returned parameter, None to skip 
 
-                                  checking. Default = None
 
-         
 
-         Returns:
 
-             (str | int | float | bool | None): Value of parameter, of None
 
-                                                when not exists
 
-         """
 
-         parts = name.split(".")
 
-         result = self.__source
 
-         for part in parts:
 
-             if not part in result:
 
-                 return None
 
-             result = result[part]
 
-         if typed is not None and type(result) is not typed:
 
-             error = ""
 
-             error = error + "Error, type of " + name + " "
 
-             error = error + "must be in " + str(type(default)) + " type, "
 
-             error = error + "but it is in " + str(type(result)) + "type."
 
-             error = error + "Change it in config file."
 
-             raise config_error(error)
 
-         return result
 
 
  |