آنچه در این مقاله میخوانید [پنهانسازی]
برای جلوگیری از قرار دادن کلیدها و رمزها داخل کد، از فایل .env و کتابخانه python-dotenv در پایتون استفاده کنید: فایل .env را بسازید، آن را به .gitignore اضافه کنید، سپس با load_dotenv بارگذاری کنید تا مقادیر به os.environ منتقل شوند و کلیدها در مخزن کد قرار نگیرند.
مسیر سریع اجرا (Quickstart)
- نصب بسته:
pip install python-dotenv - ساخت فایل .env در ریشه پروژه:
# .env DEBUG=true API_KEY=your_real_api_key_here DB_HOST=localhost DB_PORT=5432 - افزودن به .gitignore:
.env .env.* - بارگذاری در ابتدای entrypoint برنامه:
import os from dotenv import load_dotenv, find_dotenv # محیط واقعی (مثلا CI/سرور) باید بر .env اولویت داشته باشد load_dotenv(find_dotenv(), override=False) API_KEY = os.getenv("API_KEY") if not API_KEY: raise RuntimeError("API_KEY is missing") DEBUG = os.getenv("DEBUG", "false").lower() in {"1", "true", "yes", "on"} print("DEBUG:", DEBUG) - اجرا و بررسی: اگر DEBUG چاپ شد و در نبود API_KEY خطا گرفتید، پیکربندی درست است.
الگوی پیشنهادی برای توسعه و تولید
به طور معمول .env برای توسعه محلی و تست استفاده میشود. در محیط تولید، مقداردهی از طریق متغیرهای محیطی سیستم، سرویس ابری یا ارکستریتور انجام میگیرد. با override=False، اگر در تولید متغیرها از قبل ست شده باشند، مقادیر .env جایگزینشان نمیشوند. در محیط توسعه میتوانید override را روشن کنید تا فایل .env مقدم باشد.
یک مثال عملی: تنظیمات امن با dataclass
در مثال زیر ساختار خوانا و قابل اعتبارسنجی برای تنظیمات میسازیم. اگر با dataclass آشنا نیستید، راهنمای کامل آن در کدیتی موجود است: آموزش dataclass در پایتون برای مدلهای داده تمیز.
from dataclasses import dataclass
from typing import Iterable
import os
from dotenv import load_dotenv, find_dotenv
# بارگذاری زودهنگام
load_dotenv(find_dotenv(usecwd=True), override=False, encoding="utf-8")
def require(name: str) -> str:
value = os.getenv(name)
if not value:
raise ValueError(f"{name} is required")
return value
def to_bool(value: str, default: bool = False) -> bool:
if value is None:
return default
return str(value).strip().lower() in {"1", "true", "yes", "on", "y"}
def to_int(value: str, default: int) -> int:
try:
return int(value) if value is not None else default
except ValueError:
raise ValueError(f"Expected integer, got: {value!r}")
@dataclass(frozen=True)
class Settings:
api_key: str
db_host: str
db_port: int
debug: bool
@classmethod
def from_env(cls) -> "Settings":
return cls(
api_key=require("API_KEY"),
db_host=os.getenv("DB_HOST", "localhost"),
db_port=to_int(os.getenv("DB_PORT"), 5432),
debug=to_bool(os.getenv("DEBUG"), default=False),
)
def validate_required(names: Iterable[str]) -> None:
missing = [n for n in names if not os.getenv(n)]
if missing:
raise RuntimeError(f"Missing required envs: {', '.join(missing)}")
if __name__ == "__main__":
validate_required(["API_KEY"])
settings = Settings.from_env()
print("DB:", settings.db_host, settings.db_port)روش بررسی: با خالی کردن API_KEY باید خطا ببینید. با مقداردهی DB_PORT غیرعددی باید خطای معتبر دریافت کنید. این رفتارها نشان میدهند اعتبارسنجی شما کار میکند.
ساخت DSN، توسعه محلی و تولید
گاهی از چند متغیر برای ساخت یک آدرس اتصال (DSN) استفاده میشود. python-dotenv از جایگذاری متغیرها پشتیبانی میکند.
# .env
DB_USER=app
DB_PASS=s3cret
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mydb
DATABASE_URL=postgresql://${DB_USER}:${DB_PASS}@${DB_HOST}:${DB_PORT}/${DB_NAME}
from dotenv import load_dotenv
import os
load_dotenv()
dsn = os.getenv("DATABASE_URL")
if not dsn:
raise RuntimeError("DATABASE_URL is missing")
print(dsn)در تولید، توصیه میشود DATABASE_URL را مستقیما از متغیرهای محیطی واقعی تأمین کنید تا وابسته به فایل .env نباشید.
dotenv_values یا load_dotenv؟
load_dotenv مقادیر را وارد os.environ میکند و تمام کدهای استاندارد پایتون (و کتابخانهها) میتوانند به آن دسترسی داشته باشند. در مقابل، dotenv_values فقط مقادیر را به صورت دیکشنری برمیگرداند و محیط فرآیند را تغییر نمیدهد:
from dotenv import dotenv_values
cfg = dotenv_values() # {'API_KEY': '...'}
print(cfg.get("API_KEY"))اگر با کتابخانههای دیگری کار میکنید که تنظیمات را از os.environ میخوانند (مانند درایورهای پایگاه داده یا فریمورکها)، از load_dotenv استفاده کنید.
چند محیط (.env.local، .env.test و …)
میتوانید برای محیطهای مختلف فایلهای جداگانه داشته باشید و به ترتیب بارگذاری کنید. ترتیب را شفاف و کنترل شده نگه دارید:
from pathlib import Path
from dotenv import load_dotenv
root = Path(__file__).resolve().parent
# پایه
load_dotenv(root / ".env", override=False)
# اختصاصی دستگاه توسعه (نادیدهگرفتهشده در گیت)
load_dotenv(root / ".env.local", override=True)
# تست خودکار
load_dotenv(root / ".env.test", override=True)قاعده ساده: محیط واقعی (متغیرهای OS/CI) باید در نهایت قابل غلبه توسط فایلها نباشند؛ بنابراین برای تولید معمولا همان بارگذاری پایه با override=False کافی است یا کلا بارگذاری را غیرفعال میکنید.
نکات امنیتی و خطاهای رایج
- .env را هرگز در مخزن گیت قرار ندهید. الگوهای .gitignore بالا را اضافه کنید.
- از چاپ مستقیم رمزها در لاگ بپرهیزید. اگر نیاز به عیبیابی است، مقدار را ماسک کنید:
def mask(s: str, show: int = 4) -> str: if not s: return "" return s[:show] + "..." + ("*" * max(0, len(s) - show - 3)) print("API_KEY:", mask(os.getenv("API_KEY", "")))برای لاگ نویسی حرفهایتر میتوانید از Loguru استفاده کنید: کتابخانه Loguru؛ راهنمای کامل لاگ نویسی در پایتون.
- اگر کلید چندخطی (مثل گواهی) دارید، آن را در .env با کاراکترهای \n ذخیره کنید و هنگام خواندن جایگزین کنید:
PRIVATE_KEY = os.getenv("PRIVATE_KEY", "").replace("\\n", "\n") - فاصلههای اضافی و کوتیشنها را مدیریت کنید. قالب استاندارد
KEY=valueاست. برای مقادیر شامل فاصله از کوتیشن استفاده کنید:TITLE="My App Dev". - مقداردهی نوعی را جدی بگیرید: بولین و عدد را به درستی تبدیل کنید تا خطاهای منطقی رخ ندهد.
تست پیکربندی بدون افشای رمز
در تستها متغیرهای محیطی را تزریق کنید؛ به .env وابسته نشوید.
# test_settings.py
from myapp.config import Settings
def test_settings_from_env(monkeypatch):
monkeypatch.setenv("API_KEY", "testkey")
monkeypatch.setenv("DB_PORT", "1234")
s = Settings.from_env()
assert s.api_key == "testkey"
assert s.db_port == 1234این کار تستها را مستقل و قابل تکرار میکند.
Docker و Kubernetes چه؟
python-dotenv برای توسعه محلی عالی است؛ اما در کانتینرها بهتر است رمزها را با مکانیسمهای بومی تزریق کنید (Docker secrets، Kubernetes Secrets یا متغیرهای محیطی استقرار). برای دیدن الگوهای امن در این فضا به راهنمای مدیریت Secret در Docker مراجعه کنید.
پیکربندی CLI یا ابزارهای کوچک
در ابزارهای خط فرمان که با ورودیهای مختلف اجرا میشوند، بارگذاری اولیه و اعتبارسنجی سریع اهمیت دارد. اگر قصد ساخت CLI دارید، آشنایی با Typer مفید است: آموزش Typer در پایتون: ساخت ابزار خط فرمان حرفهای.
عیبیابی سریع
- load_dotenv را خیلی دیر صدا زدهاید؟ آن را در ابتدای entrypoint اجرا کنید، قبل از importهایی که env میخوانند.
- فایل .env پیدا نمیشود؟ از find_dotenv(usecwd=True) یا مسیر صریح با Path استفاده کنید.
- مقادیر .env اعمال نمیشوند؟ بررسی کنید override=False باشد و آیا متغیرها از قبل در محیط ست شدهاند.
- کاراکترهای غیرلاتین بههمریختهاند؟ پارامتر encoding=”utf-8″ را به load_dotenv بدهید.
گام بعدی
الگوی بالا را در پروژه اعمال کنید: .env را بسازید، .gitignore را بهروز کنید، بارگذاری زودهنگام و اعتبارسنجی داشته باشید، و در تولید از متغیرهای واقعی محیط یا Secret Manager استفاده کنید. با همین گامهای ساده، ریسک افشای کلیدها به طور محسوسی کاهش مییابد و پیکربندی پروژه شما قابل اتکا میشود.







