Работа с переменными окружения в Python: os.environ, os.environb и кроссплатформенные аспекты
Как читать, изменять и синхронизировать переменные окружения в Python с помощью os.environ и os.environb, обрабатывать различия кодировок на разных платформах и передавать окружение дочерним процессам.
В этом материале
Короткий ответ
os.environ представляет собой подобный словарю снимок окружения процесса, сделанный при запуске интерпретатора. Изменение его содержимого автоматически вызывает setenv или unsetenv, но вызов os.putenv не обновляет отображение. В Unix os.environb предоставляет представление в байтах, синхронизированное с os.environ. Кодирование ключей и значений зависит от кодировки файловой системы или режима UTF-8. os.reload_environ (начиная с Python 3.14) обновляет кэш после внешних изменений, но не является потокобезопасным. Дочерние процессы получают окружение через параметр env функций spawn*/exec*, который должен содержать строковые ключи и значения.
Чтение переменных окружения через os.environ и os.getenv
os.environ - это объект отображения, представляющий окружение процесса. Доступ к ключу, которого не существует, вызывает исключение KeyError, тогда как os.environ.get(key, default) возвращает значение по умолчанию вместо этого. os.getenv(key, default=None) - это вспомогательная обертка, которая также возвращает None, когда переменная отсутствует. Критическое различие заключается между переменной, которая не установлена (возвращает None), и переменной, установленной в пустую строку (возвращает ''). Использование логической истинности результата смешивает эти два состояния, что может молча маскировать ошибки конфигурации в производственных развертываниях.
Согласно исходной документации, environ - это отображение, где и ключи, и значения являются строками. На платформах, поддерживающих доступ к окружению в виде байтов, представление в байтах доступно через os.environb и os.getenvb. Для большинства кода приложений достаточно os.environ или os.getenv, так как интерпретатор автоматически выполняет декодирование из кодировки файловой системы или режима UTF-8.
import os
# Distinguish absent from empty
value = os.environ.get("APP_MODE")
if value is None:
print("variable not set")
else:
print("value:", repr(value))Практический совет
Когда необходимо определить, отсутствует ли переменная действительно или она установлена как пустая строка, используйте os.environ.get(name) и проверяйте результат на None, а не полагайтесь на логическую истинность, поскольку пустая строка является ложной, но семантически отличается от несуществующей переменной.
Изменение и удаление переменных через os.environ
Присваивание os.environ[key] вызывает системную функцию setenv. Удаление ключа вызывает unsetenv. Методы pop() и clear() также вызывают unsetenv для каждого удаленного элемента. Эта автоматическая синхронизация является причиной того, что документация рекомендует изменять os.environ, а не вызывать os.putenv напрямую, потому что putenv записывает в массив C-уровня environ без обновления Python-картирования, оставляя os.environ устаревшим.
На некоторых платформах, включая FreeBSD и macOS, многократные вызовы setenv или putenv могут вызывать утечки памяти в среде выполнения C. Это проблема уровня платформы, а не ошибка Python, но это означает, что долгоживущие процессы, которые часто изменяют переменные окружения, должны минимизировать количество создаваемых ими различных ключей.
import os
os.environ["APP_MODE"] = "production" # calls setenv
os.environ.pop("APP_MODE", None) # calls unsetenv if presentСинхронизация os.environ и os.environb
os.environb - это версия в байтах, доступная только тогда, когда os.supports_bytes_environ равен True, что верно на платформах Unix. Два отображения находятся в синхронизации: изменение environb обновляет environ и наоборот. Это означает, что вы можете записать значение в байтах в environb и прочитать декодированную строку из environ, или наоборот. На Windows supports_bytes_environ равен False, и environb не существует, поэтому код, ориентированный на обе платформы, должен защищать доступ этим флагом.
Синхронизация использует кодировку файловой системы и ее обработчик ошибок для преобразования между байтами и строкой. Если значение в байтах содержит последовательности, которые нельзя декодировать в текущей кодировке, surrogateescape производит одиночные суррогаты в представлении str. Обратное преобразование через оба представления сохраняет исходные байты.
import os
if os.supports_bytes_environ:
os.environb[b"RAW_KEY"] = b"\xff\xfe"
print(os.environ["RAW_KEY"]) # decoded via fsencode/fsdecodeКодировки: режим UTF-8, fsencode/fsdecode и getenvb
Когда активен режим Python UTF-8 Mode (включается через -X utf8 или PYTHONUTF8=1, или автоматически, когда локаль равна C или POSIX), переменные окружения декодируются с использованием UTF-8 независимо от системной локали. Вне режима UTF-8 декодирование управляется кодировкой файловой системы. os.fsencode и os.fsdecode явно открывают эти преобразования и являются рекомендуемым способом обработки путей в виде байтов, которые могут появляться в значениях переменных окружения.
os.getenvb возвращает сырые байты переменной окружения без шага декодирования. Это полезно, когда необходимо просмотреть или переслать значения, которые могут содержать байты, недопустимые в текущей кодировке, или при написании переносимого кода, который должен избегать неявного цикла декодирования-затем-повторного кодирования.
import os, sys
print("filesystem encoding:", sys.getfilesystemencoding())
print("utf8 mode:", sys.flags.utf8_mode)
# Raw bytes access (Unix only)
if os.supports_bytes_environ:
raw = os.getenvb(b"LANG")
print("LANG bytes:", raw)Кэш окружения и os.reload_environ
Документация явно указывает: os.environ и os.environb представляют собой кэш переменных окружения на момент запуска Python. Изменения, внесенные вне интерпретатора, или через os.putenv и os.unsetenv, не отражаются в отображении. os.reload_environ, добавленный в Python 3.14, обновляет оба отображения из текущего окружения процесса. До версии 3.14 не существовало стандартного механизма для повторной синхронизации кэша после внешнего изменения.
Это имеет значение в сценариях, когда родительский процесс или обработчик сигналов изменяет окружение через API уровня C, или когда библиотека вызывает putenv без использования os.environ. Без reload_environ последующие чтения возвращают устаревшие значения.
import os
# After external modification of the process environment:
os.reload_environ() # Python 3.14+
print(os.environ.get("EXTERNALLY_SET_VAR"))Ошибки и потокобезопасность при работе с окружением
Все функции модуля os вызывают OSError или его подкласс, когда аргументы имеют правильные типы, но отвергаются операционной системой. Для операций с окружением это редко, поскольку setenv и unsetenv успешны для допустимых строк, но недопустимые суррогатные символы в ключах или значениях могут вызвать UnicodeEncodeError до достижения системного вызова.
os.reload_environ содержит явное предупреждение о том, что он не является потокобезопасным. Чтение из os.environ, os.environb или вызов os.getenv во время перезагрузки может вернуть пустой результат. В многопоточных приложениях сериализуйте доступ к reload_environ или избегайте его полностью, управляя состоянием окружения только через мутации os.environ.
import os
try:
os.environ["BAD_KEY"] = "value\ud800" # lone surrogate
except UnicodeEncodeError as e:
print("encoding error:", e)Переносимость между платформами: os.name, ограничения платформы и PATH
os.name возвращает 'posix' на системах, подобных Unix, и 'nt' на Windows. На WebAssembly, Android и iOS большая часть модуля os недоступна или ведет себя иначе: такие API процессов, как fork, execve и spawn отсутствуют, а getuid и getpid могут быть заглушками. Код, который должен работать на этих целевых платформах, должен проверять os.name и os.supports_bytes_environ перед полаганиемся на поведение, специфичное для платформы.
os.get_exec_path возвращает список директорий, просматриваемых для исполняемых файлов, читая переменную PATH из предоставленного словаря env или из os.environ по умолчанию. Это программный эквивалент поиска PATH оболочки и используется внутренне вариантами p-вариантов функций spawn и exec.
import os
print("os.name:", os.name)
print("supports_bytes_environ:", os.supports_bytes_environ)
print("exec paths:", os.get_exec_path())Передача окружения дочерним процессам через spawn и exec
Варианты spawn*e и exec*e принимают параметр env, который полностью заменяет окружение дочернего процесса, а не наследует его от родителя. Ключи и значения в этом отображении должны быть строками; недопустимые типы приводят к тому, что функция завершается со значением возврата 127. Когда env предоставлен, поиск PATH для исполняемого файла использует новое окружение, а не родительский os.environ.
Это различие между наследованием (spawnl, spawnv, execl, execv) и заменой (spawnle, spawnlpe, spawnve, spawnvpe, execle, execvpe) существенно для песочницы дочерних процессов или внедрения конфигурации без загрязнения окружения родителя.
import os
child_env = dict(os.environ)
child_env["APP_MODE"] = "production"
# spawnvpe replaces the child environment entirely
status = os.spawnvpe(os.P_WAIT, "cp", ["cp", "index.html", "/dev/null"], child_env)
print("exit status:", status)Что проверить
- os.environ.get возвращает None для отсутствующих переменных и '' для переменных, установленных в пустую строку; они различимы только проверкой идентичности против None.
- os.environ[key] = value вызывает setenv; del os.environ[key] вызывает unsetenv; os.putenv не обновляет os.environ.
- os.environb существует только тогда, когда os.supports_bytes_environ равен True (Unix); изменение одного отображения обновляет другое.
- Режим UTF-8 принудительно декодирует переменные окружения в UTF-8 независимо от локали; проверьте sys.flags.utf8_mode.
- os.reload_environ доступен начиная с Python 3.14 и не является потокобезопасным; одновременные чтения могут вернуть пустые результаты.
- Параметр env в spawn*e и exec*e должен содержать строковые ключи и значения; недопустимые записи вызывают возврат значения 127.
- На WebAssembly, Android и iOS функции os, связанные с процессом, недоступны или заглушены.
Границы применения
os.environb и os.getenvb доступны только в Unix (управляется флагом supports_bytes_environ). os.reload_environ требует Python 3.14 или новее и явно не является потокобезопасным. На FreeBSD и macOS частые вызовы setenv могут вызывать утечки памяти на уровне C. Режим UTF-8 можно включить только при запуске интерпретатора и нельзя переключать во время выполнения. Функции spawn/exec недоступны на WebAssembly, Android и iOS.