[系統互動篇] 如何從環境變數,讀取與設定參數 - tsungjung411/python-study GitHub Wiki

環境變數存取

印出所有環境變數

import os
os.environ

讀取一個環境變數

import os
print(os.environ['HOME'])
print(os.getenv('HOME'))

執行結果:

/home/tj

參考資料

組態物件

from __future__ import annotations
import os

class CaseInsensitiveEnvConfig(dict):
    '''
    Provide case-insensitive environment variable keys, and its value.

    source
    ------
    - key: case insensitive
    - value: case sensitive

    usage
    -----
    env = CaseInsensitiveEnvConfig()
    env.HOME
    '''
    __INSTANCE = None

    def __init__(self, env: dict = None):
        if not env:
            env = os.environ
        
        # convert all input keys to uppercase
        # - key: case insensitive
        # - value: case sensitive
        input_keys = {}
        for key, value in sorted(env.items()):
            key = key.strip()
            if type(value) is str:
                value = value.strip()

            upper_key = key.upper()
            self[upper_key] = value

    @staticmethod
    def get_instance(force_reload: bool = False) -> CaseInsensitiveEnvConfig:
        '''
        parameter
        ---------
        force_update: force update the global instance to a newer one
        '''
        if CaseInsensitiveEnvConfig.__INSTANCE is None or force_reload:
            CaseInsensitiveEnvConfig.__INSTANCE = CaseInsensitiveEnvConfig()
        return CaseInsensitiveEnvConfig.__INSTANCE


class BaseConfig(dict):

    def __init__(self, force_reload: bool = False):
        env_config = CaseInsensitiveEnvConfig.get_instance(force_reload)

        # collect the object's ancestors
        ancestor_class_list = []

        # exclude BaseConfig itself and BaseConfig's ancestors
        for __class__ in self.__class__.mro():
            if __class__ is BaseConfig:
                break
            ancestor_class_list.append(__class__)

        # From the upper-level ancestors to the lower-level children,
        # sequentially visit classes and dynamically configure 
        # attributes for the target object
        for __class__ in reversed(ancestor_class_list):
            for attr, value in __class__.__dict__.items():

                # [1] only public attributes are allowed
                # [2] only class attributes are allowed
                if not attr.startswith('_') and not callable(value):
                    upper_attr = attr.upper()
                    new_value = env_config.get(upper_attr, value)

                    # add the key-value pair for the dict
                    self[attr] = new_value
                    # set the name-value pair for the object
                    setattr(self, attr, new_value)


class MyConfig1(BaseConfig):
    NAME1: str = 'TJ1'
    FULL_NAME: str = 'TJ Tsai'

class MyConfig2(BaseConfig):
    name2: str = 'tj2'
    First_name: str = 'TJ'
    last_name = 'Tsai'

class MyProfile(MyConfig1, MyConfig2):
    pass

mp = MyProfile()
print('mp:', mp)
print('NAME1:', mp.NAME1)
print('name2:', mp.name2)
print('First_name:', mp.First_name)
print('last_name:', mp.last_name)
print('FULL_NAME:', mp.FULL_NAME)

執行結果:

mp: {'name2': 'tj2', 'First_name': 'TJ', 'last_name': 'Tsai', 'NAME1': 'TJ1', 'FULL_NAME': 'TJ Tsai'}
NAME1: TJ1
name2: tj2
First_name: TJ
last_name: Tsai
FULL_NAME: TJ Tsai