web-dev-qa-db-ja.com

ファイルを使用して環境変数をキャストして入力する

すべてのプロジェクトで、最初にすべての環境変数をロードし、 dotenv-safeアプローチ に従って_.env.example_ファイルで記述されているように、予想されるすべてのキーが存在することを確認します。

ただし、env変数は文字列であり、Pythonコード内で使用する場合は常に手動でキャストする必要があります。これは迷惑でエラーが発生しやすくなります。以下の情報を使用したいと思います。 _.env.example_ファイルを使用して環境変数をキャストし、Python入力サポートをmy IDE(VS Code)で取得します。どうすればよいですか?

env.example

_PORT: int
SSL: boolean
_

Pythonの理想的な動作

_# Set the env in some way (doesn't matter)
import os
os.environment["SSL"] = "0"
os.environment["PORT"] = "99999"

env = type_env()
if not env["SSL"]: # <-- I'd like this to be cast to boolean and typed as a boolean
    print("Connecting w/o SSL!")
if 65535 < env["PORT"]:  # <-- I'd like this to be cast to int and typed as an int
    print("Invalid port!")
_

このコード例では、booleanintfloat、およびstrのみがサポートされていると想定すると、type_env()関数はどのようになりますか?

例に示すように、キャストを行うことはそれほど難しくありません。 https://stackoverflow.com/a/11781375/1452257 ですが、タイピングサポートで動作させる方法はわかりません。

3
pir

変数のタイプを明示的に指定するか、_type_env_関数に実際の値からタイプを推測させる2つのオプションがあります。他のコメンターは、明示的な型を使用する方法の例をすでに提供しています。使用する必要がある変数の数に応じて、個人的にはPORT = int(os.getenv("PORT", 5555))またはdataclassアプローチを使用します。

ただし、タイプを明示的に指定すると、多少のオーバーヘッドが発生します。これが私の推論方法です。それはmypyに正確なタイプを知らせません、それらはすべてAnyになります。

_import os
from distutils.util import strtobool
from typing import Dict, Any

os.environ["SSL"] = "0"
os.environ["PORT"] = "99999"


def type_env() -> Dict[str, Any]:
    d: Dict[str, Any] = dict(os.environ)
    for key in d:
        try:
            d[key] = bool(strtobool(d[key]))
            continue
        except ValueError:
            pass
        try:
            d[key] = int(d[key])
            continue
        except ValueError:
            pass
        try:
            d[key] = float(d[key])
            continue
        except ValueError:
            pass
    return d


env = type_env()
print(type(env["SSL"]))
print(type(env["PORT"]))

if not env["SSL"]:  # <-- I'd like this to be cast to boolean and typed as a boolean
    print("Connecting w/o SSL!")
if 65535 < env["PORT"]:  # <-- I'd like this to be cast to int and typed as an int
    print("Invalid port!")
_
0
  • Env.exampleがyaml形式であると想定します(少なくとも、あなたが書いたものは有効なyamlです)
  • そして、PyYamlがインストールされていると仮定します(pip install pyyaml

...次に、次のコードが機能します。

# do this or anything else to make a dict from your env.example
import yaml
example=yaml.safe_load("""
PORT: int
SSL: bool
""")

# the missing implementation
def type_env():
    env={}
    for k, v in os.environ.items():
        t=example.get(k)
        if t == "bool":
            env[k] = v.lower() not in ["false", "no", "0", ""] # whatever you want to consider as False
            # or env[k] = v.lower() in ["true", "yes", "1"] # whatever you want to consider as True
        Elif t == "int":
            env[k] = int(v)
        Elif t == "float":
            env[k] = float(v)
        else:
            env[k] = v
    return env

# From now on your code (exactly your code, except amending os.environment to os.environ)

# Set the env in some way (doesn't matter)
import os
os.environ["SSL"] = "0"
os.environ["PORT"] = "9999"

env = type_env()
if not env["SSL"]: # <-- I'd like this to be cast to boolean and typed as a boolean
    print("Connecting w/o SSL!")
if 65535 < env["PORT"]:  # <-- I'd like this to be cast to int and typed as an int
    print("Invalid port!")

0
yaccob