| 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
|