diff --git a/Lib/collections/abc.py b/Lib/collections/abc.py --- a/Lib/collections/abc.py +++ b/Lib/collections/abc.py @@ -7,6 +7,7 @@ """ from abc import ABCMeta, abstractmethod +import os import sys __all__ = ["Hashable", "Iterable", "Iterator", @@ -588,6 +589,7 @@ return default MutableMapping.register(dict) +MutableMapping.register(os._Environ) ### SEQUENCES ### diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -631,9 +631,30 @@ # Change environ to automatically call putenv(), unsetenv if they exist. -from collections.abc import MutableMapping -class _Environ(MutableMapping): +class _EnvironMeta(type): + # one-shot wonder: replaces itself with type when resolving baseclass + # See issue #19218. + def __new__(meta, name, bases, ns): + bases = (type('_FakeMutableMapping', (object,), {'__slots__': ()}),) + return type.__new__(meta, name, bases, ns) + def _resolve_lazy_baseclass(cls): + from collections.abc import MutableMapping + cls.__class__ = type('_EnvironMetaReplaced', (type,), {}) + cls.__bases__ = (MutableMapping,) + del cls.__getattr__ + del cls.get + def __getattribute__(cls, name): + if name in ('__class__', '__bases__'): + cls._resolve_lazy_baseclass() + return getattr(cls, name) + return type.__getattribute__(cls, name) + def __getattr__(cls, name): + cls._resolve_lazy_baseclass() + return getattr(cls, name) + + +class _Environ(metaclass=_EnvironMeta): def __init__(self, data, encodekey, decodekey, encodevalue, decodevalue, putenv, unsetenv): self.encodekey = encodekey self.decodekey = decodekey @@ -686,6 +707,20 @@ self[key] = value return self[key] + # For issue #19218 (removed when baseclass is resolved). + def __getattr__(self, name): + self.__class__._resolve_lazy_baseclass() + return getattr(self, name) + + # For issue #19218 (removed when baseclass is resolved). + # Implemented for the sake of the site module. + def get(self, name, default=None): + try: + return self[name] + except KeyError: + return default + + try: _putenv = putenv except NameError: