修复在python中使用environb导入错误

use*_*827 9 python anaconda

from os import environb as environ
Run Code Online (Sandbox Code Playgroud)

在Windows Anaconda python 3.6安装中键入以上命令时,我收到此错误:

ImportError: cannot import name 'environb'.

这似乎是因为,

environb is only available if supports_bytes_environ is True.

supports_bytes_environ 在Windows上为False.

有办法解决这个问题吗?

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

具体来说,我在GRASS GIS Python脚本库中发现了这个错误.

Mar*_*ers 5

首先:您可能根本不需要os.environb

我将介绍为什么在开发Python软件时不这样做,最后还介绍GRASS GIS以及如何正确修复该项目。

为什么您不需要此对象

在非Windows系统上,os.environb仅当您需要从环境访问原始二进制数据时才需要映射,而不必根据当前语言环境将其解码为Unicode。您可能希望拥有该访问权限,因为语言环境可能是错误的,或者您想要将二进制数据从环境变量传递到程序中,而不必使用语言环境和surrogateescape错误处理程序对其进行重新编码,也不必在与另一个程序的编码方式不同,同样无需先强制创建surrogateescape解码后的字符串。(我掩盖了一个事实,在POSIX中,您不能在环境变量中使用null,但这无关紧要)。

在Windows上,您不需要这样做,因为在该OS上,环境变量已作为Unicode数据传递给Python 。这也意味着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
Run Code Online (Sandbox Code Playgroud)

完全更换os.environb

对于某些情况,如果某些第三方os.environb仍然期望可用并且您无法更改它,或者您的代码库很大而又很难更新,则最后一种选择os.environb是为Windows 创建对象。

这并不难;只需os.environ根据需要对原始数据进行编码,然后在设置新键或值时再次对其进行解码。os.environPOSIX 的对象已经做过同样的事情,除了另一个方向,因此我们可以重复使用相同的基础结构:

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
Run Code Online (Sandbox Code Playgroud)

这将创建相同类型的映射对象,该对象完全支持获取和设置环境变量,并且该对象的更改将在中显示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'})
Run Code Online (Sandbox Code Playgroud)

专门用于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
Run Code Online (Sandbox Code Playgroud)

然后在该映射中(在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)
Run Code Online (Sandbox Code Playgroud)

b'"' + encode(arg) + b'"'是一种天真的报价方式,可以避免子外壳出现问题,但是这种方式无法处理嵌入式报价。

没有理由为这是一个字节的值。sys.argv是Python 3上Unicode字符串的列表,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)
Run Code Online (Sandbox Code Playgroud)

我正在将此报告给GRASS GIS项目,以便他们可以为将来的版本进行修复。


And*_*ndy 1

在 Windows 上,该os模块没有 属性environb,因此您将无法加载它。但是,您可以手动添加:

首先,加载os到全局命名空间:

import os
Run Code Online (Sandbox Code Playgroud)

将其重载到os模块中。这将修改已加载的模块:

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

现在,如果您运行from os import environb as environ,python 将看到它os已经导入并且不会尝试再次加载它。

environ[b'PATH']

>>> b'C:\\Windows\\System32'
Run Code Online (Sandbox Code Playgroud)

如果您还需要能够设置环境变量,则可以使用以下命令提供双向映射:

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'
Run Code Online (Sandbox Code Playgroud)