Прежде всего: вам , вероятно, вообще не нужно os.environb
.
Я расскажу, почему нет, когда вы разрабатываете программное обеспечение Python, и в конце,Также рассмотрим GRASS GIS и как правильно исправить этот проект.
Почему вам не нужен этот объект
В системах, отличных от Windows, отображение os.environb
необходимо только в том случае, если вам когда-либо понадобитсядля доступа к необработанным двоичным данным из среды, без их декодирования в Unicode в соответствии с текущей локалью.Возможно, вы захотите получить такой доступ, потому что локаль может быть неправильной, или вы хотите передать двоичные данные из переменной среды в вашу программу без необходимости перекодировать их с помощью локали и обработчика ошибок surrogateescape
или передатьданные в другой кодировке для другой программы, опять же без необходимости сначала принудительно создавать декодированную строку surrogateescape
.(Я скрываю тот факт, что в POSIX вы не можете использовать пустые значения в переменных окружения , но это не имеет значения).
В Windows вам это не нужно, потому что в этой ОС переменные окружения уже передаются в Python как данные Unicode .Это также означает, что среда Windows не может быть легко использована для передачи двоичных данных ;вы не можете передавать данные с другой кодировкой для дочерних процессов, и вы не можете принимать двоичные данные из среды, не предварительно связав эти данные в какой-либо двоичной текстовой кодировке, такой как Base64.os.environb
не годится для Windows!
Итак, если вы создаете кроссплатформенное программное обеспечение, вы должны использовать os.environ
и требовать, чтобы локаль была правильно настроена, и не беспокоиться о os.environb
.
Вместо этого используйте защитный код
Иногда требуется доступ к данным в двоичной среде?Тогда следующим вариантом может быть защитный код для атрибута, отсутствующего с защитой ImportError
, и просто принять, что он отсутствует:
try:
from os import environb
except ImportError:
environb = None
# ...
if environb is not None:
# ... it is available, use it
Полная замена os.environb
Последний вариантв том случае, когда какая-либо третья сторона ожидает, что в любом случае будет доступен os.environb
, и вы не можете это изменить, или если у вас есть большая кодовая база, которую будет сложно обновить, это создать объект os.environb
только дляWindows.
Это не так сложно;просто закодируйте данные из оригинального os.environ
по мере необходимости и снова декодируйте их при установке новых ключей или значений.Объект os.environ
для POSIX уже делает то же самое, за исключением другого направления, поэтому мы можем повторно использовать ту же инфраструктуру:
import os
try:
os.environb
except AttributeError:
# Windows has no os.environb, create one that maps to os.environ._data
import sys
_encoding = sys.getfilesystemencoding() # echos POSIX
# note the inversion, we *decode* where encoding is expected, and vice versa
def _encode(value, _encoding=_encoding):
if not isinstance(value, bytes):
raise TypeError("bytes expected, not %s" % type(value).__name__)
return value.decode(_encoding, 'surrogateescape')
def _decode(value, _encoding=_encoding):
return value.encode(_encoding, 'surrogateescape')
# reuse the Unicode mapping, putenv and unsetenv references from os.environ
# but map binary data to unicode on setting, unicode to binary on getting
os.environb = os._Environ(
os.environ._data,
_encode, _decode, _encode, _decode,
os.environ.putenv, os.environ.unsetenv)
del _encoding, _encode, _decode
Это создает тот же тип объекта отображения, который полностью поддерживаетполучение и установка переменных среды, и изменения этого объекта будут видны в os.environ
, и наоборот:
>>> os.environ
environ({'FOO': 'bar baz', 'SPAM': 'håm'})
>>> os.environb
environ({b'FOO': b'bar baz', b'SPAM': b'h\xc3\xa5m'})
>>> os.environb[b'eric'] = 'Îdlé'.encode('utf8')
>>> os.environ
environ({'FOO': 'bar baz', 'SPAM': 'håm', 'eric': 'Îdlé'})
>>> del os.environ['FOO']
>>> os.environb
environ({b'SPAM': b'h\xc3\xa5m', b'eric': b'\xc3\x8edl\xc3\xa9'})
GRASS GIS конкретно
В комментариях вы упоминаетеВы пытаетесь заставить GRASS GIS работать.Этот проект просто сделал неправильный выбор, чтобы установить переменную среды как bytes
на Python 2 и Python 3, и имеет проблемы не только для Windows, но и для всех платформ, которые требуют адресации.
Они попробуйте использовать os.environb
в качестве замены os.environ
, а затем используйте простой метод цитирования, чтобы сгенерировать значение из sys.argv
.В то же время тот же модуль использует os.environ
для всех остальных потребностей переменных среды.
В верхней части lib/python/script/core.py
они используют
# python3
# ...
from os import environb as environ
, а затем сохраняют одну переменнуюв этом отображении (в определении функции def parser():
):
cmdline = [basename(encode(sys.argv[0]))]
cmdline += [b'"' + encode(arg) + b'"' for arg in sys.argv[1:]]
environ[b'CMDLINE'] = b' '.join(cmdline)
b'"' + encode(arg) + b'"'
- наивный метод для цитирования значений, чтобы избежать проблем с подоболочками, но он не будет обрабатывать встроенные кавычки.
нет причин , чтобы это было байтовое значение.sys.argv
- это список строк Unicode в Python 3, байтовых строк в Python 2. Это соответствует типам данных os.environ
в любой версии Python, поэтому данные должны обрабатываться как str
типов в любой версии Python.
Для цитирования значений по отношению к интерпретации оболочки Python имеет функцию shlex.quote()
, которая доступна как pipes.quotes()
как для Python 2, так и для Python 3.
Таким образом, эту проблему можно полностью избежать, сделав несколько изменений в этом файле (трассировка возврата при ошибке импорта os.environb
скажет вам, где он находится на вашем компьютере):
# 1. at the top, add an import
import pipes
# 2. remove the `from os import environb as environ` line altogether
# 3. in def parse(), use
cmdline = [basename(sys.argv[0])]
cmdline += (pipes.quote(a) for a in sys.argv[1:])
os.environ['CMDLINE'] = ' '.join(cmdline)
Я сообщаю об этом в проект GRASS GIS, чтобы они могли это исправить в будущем выпуске.