Исправлена ​​ошибка импорта при использовании environmentB в Python - PullRequest
0 голосов
/ 11 сентября 2018
from os import environb as environ

При вводе вышеуказанной команды в Windows Anaconda python 3.6 при установке я получаю эту ошибку:

ImportError: cannot import name 'environb'.

Кажется, это потому, что

environb is only available if supports_bytes_environ is True.

и

supports_bytes_environ является ложным в Windows.

Есть ли способ обойти это?

ref: https://docs.python.org/3/library/os.html#os.environb

В частности, я обнаружил эту ошибку в библиотеке сценариев GRASS GIS Python .

Ответы [ 2 ]

0 голосов
/ 27 сентября 2018

Прежде всего: вам , вероятно, вообще не нужно 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, чтобы они могли это исправить в будущем выпуске.

0 голосов
/ 26 сентября 2018

В Windows модуль os не имеет атрибута environb, поэтому вы не сможете его загрузить. Тем не менее, вы можете добавить его вручную:

Сначала загрузите os в глобальное пространство имен:

import os

Перегрузите его в модуль os. Это изменит модуль, который уже загружен:

os.environb = {bytes(k, encoding='utf-8'): bytes(env, encoding='utf8') for k, env in os.environ.items()}

Теперь, если вы запустите from os import environb as environ, python увидит, что os уже импортирован, и не будет пытаться загрузить его снова.

environ[b'PATH']

>>> b'C:\\Windows\\System32'

Если вам также необходимо установить переменные окружения, вы можете обеспечить двустороннее сопоставление с помощью следующего:

class Environb(object):
    def __init__(self):
        pass
    def __getitem__(self, item):
        return bytes(os.environ[item.decode('utf8')], encoding='utf-8')
    def __setitem__(self, key, item):
        os.environ[key.decode('utf8')] = item.decode('utf8')

os.environb = Environb()

os.environb[b'FOO'] = b'BAR'
print(os.environ['FOO']

>>> 'BAR'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...