Patchwork D8347: encoding: use special dictionary type for env variables on Windows

login
register
mail settings
Submitter phabricator
Date March 30, 2020, 1:30 a.m.
Message ID <differential-rev-PHID-DREV-iu7w2fyewhztmxnewpiw-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/45945/
State New
Headers show

Comments

phabricator - March 30, 2020, 1:30 a.m.
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Environment variables on Windows are case insensitive and Python
  internally uses a special dict type that normalizes all keys to
  uppercase.
  
  Our custom bytes-based environment variable dict on Windows was
  not aware of this, leading to failures when looking up lower case
  environment variables (such as `http_proxy`).
  
  This commit introduces a custom dict type that normalizes keys
  to uppercase on Windows. test-http-proxy.t passes after this
  change, as a lookup of b'http_proxy' now succeeds.
  
  It's worth noting that Python's behavior with regards to
  normalizing all environment variables to uppercase is buggy.
  See https://bugs.python.org/issue28824. I preserved Python's
  behavior for compatibility.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D8347

AFFECTED FILES
  mercurial/encoding.py

CHANGE DETAILS




To: indygreg, #hg-reviewers
Cc: mercurial-devel
phabricator - April 17, 2020, 6:31 p.m.
marmoute added a comment.
marmoute accepted this revision.


  Urg, this make me sad, but seems a reasonable way to move forward.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D8347/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D8347

To: indygreg, #hg-reviewers, marmoute
Cc: marmoute, mercurial-devel

Patch

diff --git a/mercurial/encoding.py b/mercurial/encoding.py
--- a/mercurial/encoding.py
+++ b/mercurial/encoding.py
@@ -290,6 +290,42 @@ 
         for k, v in os.environ.items()  # re-exports
     }
 
+# Environment variables are normalized to uppercase on Windows. So wrap
+# in a custom type to emulate what the standard library does.
+if pycompat.ispy3 and pycompat.iswindows:
+
+    class UppercaseDict(dict):
+        def __init__(self, values):
+            super(UppercaseDict, self).__init__()
+            for k, v in values.items():
+                self[k] = v
+
+        def __setitem__(self, key, value):
+            super(UppercaseDict, self).__setitem__(key.upper(), value)
+
+        def __getitem__(self, key):
+            return super(UppercaseDict, self).__getitem__(key.upper())
+
+        def __delitem__(self, key):
+            super(UppercaseDict, self).__delitem__(key.upper())
+
+        def __contains__(self, key):
+            return super(UppercaseDict, self).__contains__(key.upper())
+
+        def get(self, key, *args):
+            return super(UppercaseDict, self).get(key.upper(), *args)
+
+        def pop(self, key, *args):
+            return super(UppercaseDict, self).pop(key.upper(), *args)
+
+        def update(self):
+            raise NotImplementedError
+
+        def setdefault(self, key, *args):
+            return super(UppercaseDict, self).setdefault(key.upper(), *args)
+
+    environ = UppercaseDict(environ)
+
 if pycompat.ispy3:
     # os.getcwd() on Python 3 returns string, but it has os.getcwdb() which
     # returns bytes.