Package: service-configurator
Service ConfiguratorService Configurator is a python library created for managing settings and secrets for your service.InstallationUse package manager pip to install service-configurator.pipinstallservice-configuratorUsageCreating settings classesAll settings classes should derive from BaseSettings or its subclass.fromconfiguratorimportBaseSettings,Integer,StringclassMySettings(BaseSettings):user_id=Integer()password=String()config=MySettings()config.user_id=10config.password='123'print(f'User:{config.user_id}logged with password:{config.password}')Settings class could have other settings objects as members.fromconfiguratorimportBaseSettings,IntegerclassMySettings(BaseSettings):var1=Integer()classMySettings2(BaseSettings):my_settings=MySettings()var2=Integer()Inheriting from another settings class is also possible. Subclass treats parent's settings as its own.fromconfiguratorimportBaseSettings,IntegerclassParentSettings(BaseSettings):var1=Integer()classMySettings(ParentSettings):var2=Integer()equalsfromconfiguratorimportBaseSettings,IntegerclassMySettings(BaseSettings):var1=Integer()var2=Integer()Available settings fieldsAll implemented settings fields are presented below.fromconfiguratorimportBaseSettings,Integer,PositiveInteger,String,Email,Boolean,Float,UrlclassMySettings(BaseSettings):var1=Integer()var2=PositiveInteger()var3=String()var4=Email()var5=Boolean()var6=Float()var7=Url()Remember to always include parentheses () when creating settings fields.Optional fields and default valuesSetting fields can be marked as optional using required parameter. In addition to that default value can be set, the
default value is returned if field wasn't modified. When these arguments are not provided field is required and it's
default value is field type specific.fromconfiguratorimportBaseSettings,IntegerclassMySettings(BaseSettings):var=Integer(required=False,default=50)Importing settingsYou can import settings using:python dict objectjson fileyaml filefromconfiguratorimportBaseSettings,IntegerclassMySettings(BaseSettings):var=Integer()config=MySettings()config.from_dict({'var':12})config.from_json('file.json')config.from_yaml('file.yaml')When importing from python dict object you can use partial_update argument if you don't want to provide all required
values, which could be useful when unit testing. By default, an exception is thrown if required values are missing. This
option is not accessible for json and yaml imports.fromconfiguratorimportBaseSettings,IntegerclassMySettings(BaseSettings):var1=Integer()var2=Integer()config=MySettings()config.from_dict({'var1':12},partial_update=True)Exporting settingsSimilar to import you have few export options:python dict objectjson fileyaml filefromconfiguratorimportBaseSettings,IntegerclassMySettings(BaseSettings):var=Integer()config=MySettings()config.var=10config.to_dict()# {'var': 10}config.to_json('file.json')config.to_yaml('file.yaml')Yaml files are recommended option for storing your configuration.Generating template filesTemplate/skeleton config file can be simply generated by creating a new instance of a settings class and using export
method.fromconfiguratorimportBaseSettings,IntegerclassMySettings(BaseSettings):var=Integer()config=MySettings()config.to_json('file.json.skel')config.to_yaml('file.yaml.skel')Setting and getting single fieldIf you want get or set single attribute you can access is as normal class member.fromconfiguratorimportBaseSettings,IntegerclassMySettings(BaseSettings):var=Integer()config=MySettings()config.var=4print(config.var)# prints 4Private settings fieldsIf you add a non-field member to your config class, it won't be exported or imported. However you can still use it as a
normal class member.fromconfiguratorimportBaseSettings,IntegerclassMySettings(BaseSettings):var=Integer(default=5)multiplier=2defmultiply(self):returnself.var*self.multiplierconfig=MySettings()print(config.multiply())# prints 10config.to_dict()# {'var': 5}Handling exceptionsConfigurator throws following exceptions when something goes wrong:ValidationError - provided value didn't pass validation checks for the fieldSettingsError - a generic Settings error, read an error message for more informationfromconfiguratorimportBaseSettings,PositiveInteger,ValidationError,SettingsErrorclassMySettings(BaseSettings):var=PositiveInteger()config=MySettings()try:config.var=-1exceptValidationErrorasex:# ValidationError -1 can't be assigned to PositiveIntegerprint(f'Validation error:{ex}')try:config.from_dict({})exceptSettingsErrorasex:# SettingsError missing required field 'var'print(f'Settings error:{ex}')Exceptions shouldn't pass silently.Utility classesFor commonly used sets of settings utility classes were implemented to avoid unnecessary code repetition in multiple
services.All currently implemented utility classes are presented below. Check docs for class destiny.fromconfiguratorimportBaseSettingsfromconfigurator.utilsimportOracleConnectorSettings,BoxSettingsclassMySettings(BaseSettings):oracle_db=OracleConnectorSettings()box=BoxSettings()Complete examplefromconfiguratorimportBaseSettings,String,Emailfromconfigurator.utilsimportOracleConnectorSettingsclassMySettings(BaseSettings):api_key=String()report_email=Email(required=False)oracle_db=OracleConnectorSettings()config=MySettings()config.from_yaml('config.yml')config.yamlapi_key:'123qwerty'oracle_db:host:'http://localhost'password:'pass123'port:1521sid:'db2'user:'admin'report_email:'report@example.com'ContributingCreating new settings fieldsAll fields classes are located in thefields.pyfile.Every field should be inherited fromFieldclass or its subclass.All fields should implementdefault,type, andvalidate, unless it's implemented in parent class and changes
aren't neededExample field classes:# implemented in fields.pyclassString(Field):"""Class for string type fields."""type_=strdefault=''classEmail(String):"""Class for email fields."""defvalidate(self,value:str)->str:"""Validates email using simple regex."""value=super().validate(value)regex=re.compile(r'^\S+@\S+\.\S+$')ifregex.fullmatch(value)isNone:raiseValidationError('Provided string is not a valid email.')returnvalueCreating new utility settingsCommon sets of parameters used in many services shouldn't be copy-pasted. Instead, a new utility class should be created
in autils.py. Create the utility class as a normal settings class.# implemented in utils.pyclassOracleConnectorSettings(BaseSettings):"""Setting required for connection to oracle database."""host=Url()port=Integer()user=String()password=String()sid=String()defget_connection_url(self):"""Get connection url for sql alchemy"""returnf"oracle://{self.user}:{self.password}@{self.host}:{self.port}/{self.sid}"Additional things to considerEvery class and function should be documented. In addition to that run pydoc3 to generate html documentation after
modifications.pdoc--html-odocsconfiguratorTest coverage of this package is 100% try your best to not lower it.Update__all__in__init__.pyif needed