8000 os.environ type signature partially incorrect in python 2 · Issue #872 · python/typeshed · GitHub
[go: up one dir, main page]

Skip to content
os.environ type signature partially incorrect in python 2 #872
Closed
@lucaswiman

Description

@lucaswiman

The following code is valid and idiomatic Python 2 code, but does not typecheck under mypy --py2:

from __future__ import unicode_literals
import os

os.environ['key'] = 'value'

It yields the error:

os_environ.py:4: error: Invalid index type "unicode" for "MutableMapping"
os_environ.py:4: error: Incompatible types in assignment (expression has type "unicode", target has type "str")

From a bit of poking at it in the repl, it seems that os.environ is a dict-like object which functions a bit like cStringIO: it accepts either str or unicode objects, but throws a runtime encoding error when passed a unicode object which cannot be encoded to the ascii codec:

>>> os.environ[u'💣'] = u'🔥'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/lucaswiman/.pyenv/versions/2.7/lib/python2.7/os.py", line 473, in __setitem__
    putenv(key, item)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

In #869, I suggested replacing the type signature with MutableMapping[Union[str, unicode], Union[str, unicode]], to which @JukkaL replied:

This is potentially problematic, since this may break a lot of existing code using os.environ that assumes that it only has str keys and values (and there are other potential issues as well).

I think the type I suggested there is accurate, since annoyingly, these unicode objects' type information does seem to be retained and actually can break code at runtime that assumes it's only getting str:

>>> import io
>>> os.environ[u'key'] = u'value'
>>> set(map(type, os.environ.values()))
set([<type 'str'>, <type 'unicode'>])
>>> io.BytesIO(os.environ['key'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'unicode' does not have the buffer interface

That said, if there is interest in something more backwards-compatible with previously annotated code, we could allow assignment of str key/values, but assume the actual contents of the mapping are str. I think the following update to os/__init__.pyi would achieve this effect, and might be a good compromise:

class _Environ(MutableMapping[str, str]):
    def __setitem__(self, key:Union[str, unicode], value:Union[str, unicode]) -> None: ...
    def copy(self) -> Dict[str, str]: ...

environ = ...  # type: _Environ

Please advise, and I'd be happy to submit a pull request once a decision has been reached.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0