Skip to content

Commit

Permalink
environ: handle case insensitive env vars on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
lazka committed Dec 1, 2016
1 parent 2605bad commit 32381df
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 4 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* :func:`print_`: Don't ignore ``flush`` in Windows redirect mode
* :obj:`argv`: Forwards changes to `sys.argv` :bug:`2`
* :obj:`environ`: Forwards changes to `os.environ` :bug:`2`
* :obj:`environ`: Handle case insensitive env vars on Windows
* :func:`fsn2text`: Add a ``strict`` mode


Expand Down
16 changes: 12 additions & 4 deletions senf/_environ.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import collections

from ._compat import text_type, PY2
from ._fsnative import path2fsn, is_win, _fsn2legacy
from ._fsnative import path2fsn, is_win, _fsn2legacy, fsnative
from . import _winapi as winapi


Expand Down Expand Up @@ -105,6 +105,7 @@ def read_windows_environ():
key, value = entry.split(u"=", 1)
except ValueError:
continue
key = _norm_key(key)
dict_[key] = value

status = winapi.FreeEnvironmentStringsW(res)
Expand All @@ -114,6 +115,13 @@ def read_windows_environ():
return dict_


def _norm_key(key):
assert isinstance(key, fsnative)
if is_win:
key = key.upper()
return key


class Environ(collections.MutableMapping):
"""Dict[`fsnative`, `fsnative`]: Like `os.environ` but contains unicode
keys and values under Windows + Python 2.
Expand All @@ -132,11 +140,11 @@ def __init__(self):
self._env = env

def __getitem__(self, key):
key = path2fsn(key)
key = _norm_key(path2fsn(key))
return self._env[key]

def __setitem__(self, key, value):
key = path2fsn(key)
key = _norm_key(path2fsn(key))
value = path2fsn(value)

if is_win and PY2:
Expand All @@ -157,7 +165,7 @@ def __setitem__(self, key, value):
raise ValueError

def __delitem__(self, key):
key = path2fsn(key)
key = _norm_key(path2fsn(key))

if is_win and PY2:
try:
Expand Down
37 changes: 37 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ def preserve_environ(environ=environ):
environ[key] = value


environ_case_sensitive = True
with preserve_environ():
os.environ.pop("senf", None)
os.environ["SENF"] = "foo"
environ_case_sensitive = not ("senf" in os.environ)


@contextlib.contextmanager
def preserve_argv(argv=argv):
old = argv[:]
Expand Down Expand Up @@ -706,6 +713,21 @@ def test_environ():
repr(environ)


def test_environ_case():
if not environ_case_sensitive:
with preserve_environ():
environ.pop("foo", None)
environ["FoO"] = "bla"
assert environ["foo"] == "bla"
assert sorted(os.environ.keys()) == sorted(environ.keys())
else:
with preserve_environ():
environ["foo"] = "1"
environ["FOO"] = "2"
assert environ["foo"] != environ["FOO"]
assert sorted(os.environ.keys()) == sorted(environ.keys())


@pytest.mark.skipif(os.name != "nt", reason="win only")
def test_environ_mirror():
with preserve_environ():
Expand Down Expand Up @@ -881,6 +903,21 @@ def test_expandvars():
assert expandvars(u"%ä%") == u"%ä%"


def test_expandvars_case():
if not environ_case_sensitive:
with preserve_environ():
environ.pop("foo", None)
environ["FOO"] = "bar"
assert expandvars("$foo") == "bar"
environ["FOo"] = "baz"
assert expandvars("$fOO") == "baz"
else:
with preserve_environ():
environ.pop("foo", None)
environ["FOO"] = "bar"
assert expandvars("$foo") == "$foo"


def test_python_handling_broken_utf16():
# Create a file with an invalid utf-16 name.
# Mainly to see how Python handles it
Expand Down

0 comments on commit 32381df

Please sign in to comment.