Profile picture

[Python] 환경 변수를 읽어오는 여러 가지 방법

JaehyoJJAng2023년 06월 06일

◾️ python-dotenv

라이브러리 설치

pip install python-dotenv

예제

from dotenv import load_dotenv
import os

def get_environment(env_file:str)-> None:
    # Load env_file
    load_dotenv(dotenv_path=env_file)

    # Get env
    env1 : str = os.getnev('env1')
    env2 : str = os.getnev('env2')

env_file

env1=TEST1
env2=TEST2

◾️ django-environ

라이브러리 설치

pip install django-environ

예제

import environ

def get_environment(env_file)-> environ.Env:
    env : environ.Env = environ.Env(DEBUG=(bool,False))
    env.read_env(env_file)
    return env

env : environ.Env = get_environment(env_file='.secret.env')

env1  : str = env('env1')
env2  : str = env('env2')

env_file

env1=TEST1
env2=TEST2

◾️ SettingsConfigDict

라이브러리 설치

pip install pydantic-settings

예제에 사용되는 환경변수 파일: .env

IP=192.168.219.162
PORT=25575

예제

from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file='.env', env_file_encoding='utf-8')
    IP: str
    PORT: int

settings : Settings = Settings()
print(settings.IP) # 출력 192.168.219.162
print(settings.PORT) # 출력 25575

image


2개의 파일을 로드해야하는 경우

from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(
        env_file=('.env', '.env.prod'),
        env_file_encoding='utf-8')

키워드 인수 override를 사용하여 인스턴스화 키워드 인수인 model_config에 전달하면 pydantic에 파일을 전혀 로드하지 않도록(클래스에 파일이 설정되어 있더라도) 지시할 수 있다.

settings = Settings(_env_file=None)

또한, 각 필드의 속성을 지정 할 수도 있다.

from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file='.env', env_file_encoding='utf-8')
    IP : str
    PORT : int
    PASSWORD : str | None = Field(default=None)
settings : Settings = Settings()
print(settings.IP)
print(settings.PORT)

Field 클래스를 사용하여 필드를 정의하면 Pydantic이 해당 필드에 대해 유효성을 검사하고 필드에 할당된 값의 유형을 강제할 수 있습니다.


◾️ os 내장 모듈

▪️ os.environ

운영체제에 설정되어 있는 모든 환경 변수는 os 모듈의 environ이라는 속성을 통해서 접근 가능하다.

os.environ 속성은 마치 파이썬 내장 자료구조인 사전(dictionary)과 같이 사용할 수 있다.

간단한 예시로, HOME이라는 환경 벼수에 저장되어 있는 값은 다음과 같이 읽어올 수 있다.

value: str = os.environ['HOME']
print(value)
'/home/server01'

하지만 설정해준 적 없는 환경 변수를 읽어오라고 한다면 어떻게 될까?

value: str = os.environ['error']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<frozen os>", line 679, in __getitem__
KeyError: 'API_KEY'

다음과 같이 KeyError가 발생하게 된다.


우리가 파이썬을 사용할 때 사전(dictionary)에 존재하지 않는 키를 넘길 경우 KeyError가 발생하는 경우와 같은 이치라고 생각하면 된다.

따라서 코드를 작성할 때 환경 변수가 존재하지 않는 상황에 대해서도 적당한 예외처리를 해주어야 한다.

이 때, 호출하고자 하는 환경 변수가 없을 때, 오류를 발생시키는 대신 None을 반환받고 싶은 경우에는 사전과 동일하게 get() 메서드를 사용하면 된다.

value: str | None = os.environ.get('API_KEY') is None
print(type(value))
True

get() 메서드의 두 번째 인자로 기본 값을 넘길 수도 있다. 꼭 설정해주지 않아도 되는 환경 변수에 유용하게 사용된다.

value: str = os.environ.get('API_KEY','aaabbbccc')
print(value)
'aaabbbccc'

▪️ os.getenv

os 모듈의 getenv() 함수를 사용하면 조금 더 간결한 코드로 os.environ.get() 함수와 동일한 효과를 얻을 수 있다.

>>> os.getenv('API_KEY') is None
True

>>> os.getenv('API_KEY', 'aaabbbccc')
'aaabbbccc'

configparser

Property 파일처럼 사용할 수 있는 Configparser 모듈에 대해서 알아보자.


파일 생성 (config.ini)

ini 파일을 생성하자. 파일 내용에는 각 섹션이라고 하는 큰 분류 값이 있으며, 그 안에는 Key-Value 형태의 데이터로 구성되어 있다.
보통 섹션 Key 값은 대문자로 입력을 하고 세부내용은 소문자로 입력을 한다. 아래 내용을 참고해보자

[DEFAULT]
config = 0
textfile = config.ini

[TEST]
test = 0.5

[CONFIG]
id = None
age = 150

configparser - Get

configparser을 정의하고 파일을 불러오면 내용을 key 값으로 조회가 가능하다

import configparser

# ConfigParser 객체 생성
properties: configparser.ConfigParser = configparser.ConfigParser() 

# 파일 읽기
properties.read('config.ini') 

# DEFAULT 섹션 선택
default: configparser.SectionProxy = properties['DEFAULT']

# TEST 섹션 선택
test: configparser.SectionProxy = properties['TEST']

# CONFIG 섹션 선택
config: configparser.SectionProxy = properties['CONFIG']

# Key-Value 조회
print(default['textfile'])
print(test['test'])
print(config['id'])

결과는 아래와 같다

$ python3 main.py
config.ini
0.5
None

물론 가져온 값은 항상 문자열 형태이다. "1" 이라는 값을 숫자로 사용하기 위해서는 값을 가져온 다음에 형변환 하는 방법이 있을 수 있겠지만, 값을 가져올 때 아예 int 값으로 형변환 시켜서 가져오게끔 할 수도 있다.

print(test.getint('test'))

이 외에 getboolean() , getfloat() 사용이 가능하다


configparser - Set

이제 값을 조회해봤으니 값을 넣는 방법에 대해서 알아보자.
해당 객체에 섹션, key, value 값 입력이 필요하다.


다음은 값을 추가하는 내용입니다.

properties.set("DEFAULT", "addkey", "configset")

default = properties["DEFAULT"] 
default.setdefault("abc", "a")

print( default["addkey"] )
print( default["abc"] )

이렇게 저장하면 코드 내에서 다시 불러올 수 있게된다. 하지만 ini 파일에 추가가 되지는 않는다. 파일에도 저장하기 위해서는 with context manager를 사용하여 파일에 쓰면 된다.


configparser - Save

불러온 값을 수정/추가 하게되면 새로 파일을 써야 기록이 된다

with open(filepath,'w') as fp:
    properties.write(fp)

filepath 는 실제 파일 경로이다. 여기서는 config.ini이 되겠다.
이렇게 하면 ini 파일 형태로 기록을 한다.
이때 섹션값을 대문자로, 세부내용은 소문자로 기록이 됩니다.


Loading script...