diff -r e281a57d5b29 Doc/library/urllib.request.rst --- a/Doc/library/urllib.request.rst Tue Apr 19 08:53:14 2016 +0200 +++ b/Doc/library/urllib.request.rst Thu Apr 21 10:52:52 2016 +0200 @@ -166,6 +166,8 @@ in a case insensitive approach, for all operating systems first, and when it cannot find it, looks for proxy information from Mac OSX System Configuration for Mac OS X and Windows Systems Registry for Windows. + If both lowercase and uppercase environment variables exist (and disagree), + lowercase is preferred. The following classes are provided: diff -r e281a57d5b29 Lib/test/test_urllib.py --- a/Lib/test/test_urllib.py Tue Apr 19 08:53:14 2016 +0200 +++ b/Lib/test/test_urllib.py Thu Apr 21 10:52:52 2016 +0200 @@ -229,6 +229,27 @@ self.env.set('NO_PROXY', 'localhost, anotherdomain.com, newdomain.com') self.assertTrue(urllib.request.proxy_bypass_environment('anotherdomain.com')) +class ProxyTests_withOrderedEnv(unittest.TestCase): + + def setUp(self): + # we need to test conditions, where variable order is significant + self._environ = os.environ + # monkey patch os.environ + os.environ = self.env = collections.OrderedDict() + + def tearDown(self): + # Restore all proxy related env vars + os.environ = self._environ + + def test_getproxies_environment_prefer_lowercase(self): + os.environ['http_proxy'] = '' + os.environ['HTTP_PROXY'] = 'http://somewhere:3128' + proxies = urllib.request.getproxies_environment() + print(os.environ) + print(proxies) + self.assertEqual('', proxies['http']) + + class urlopen_HttpTests(unittest.TestCase, FakeHTTPMixin, FakeFTPMixin): """Test urlopen() opening a fake http connection.""" diff -r e281a57d5b29 Lib/urllib/request.py --- a/Lib/urllib/request.py Tue Apr 19 08:53:14 2016 +0200 +++ b/Lib/urllib/request.py Thu Apr 21 10:52:52 2016 +0200 @@ -2455,19 +2455,33 @@ """ proxies = {} + # in order to prefer lowercase variables, process environment in + # two passes: first matches any, second pass matches lowercase only for name, value in os.environ.items(): name = name.lower() if value and name[-6:] == '_proxy': proxies[name[:-6]] = value + for name, value in os.environ.items(): + if name[-6:] == '_proxy': + name = name.lower() + if value: + proxies[name[:-6]] = value + else: + proxies.pop(name[:-6], None) return proxies -def proxy_bypass_environment(host): +def proxy_bypass_environment(host, proxies): """Test if proxies should not be used for a particular host. - Checks the environment for a variable named no_proxy, which should - be a list of DNS suffixes separated by commas, or '*' for all hosts. + Checks the proxy dict for the value of no_proxy, which should + be a list of comma separated DNS suffixes, or '*' for all hosts. + """ - no_proxy = os.environ.get('no_proxy', '') or os.environ.get('NO_PROXY', '') + # don't bypass, if no_proxy isn't specified + try: + no_proxy = proxies['no'] + except AttributeError: + return 0 # '*' is special case for always bypass if no_proxy == '*': return 1 @@ -2562,8 +2576,15 @@ def proxy_bypass(host): - if getproxies_environment(): - return proxy_bypass_environment(host) + """Return True, if host should be bypassed. + + Checks proxy settings gathered from the environment, if specified, + or from the MacOSX framework SystemConfiguration. + + """ + proxies = getproxies_environment() + if proxies: + return proxy_bypass_environment(host, proxies) else: return proxy_bypass_macosx_sysconf(host) @@ -2677,14 +2698,15 @@ return 0 def proxy_bypass(host): - """Return a dictionary of scheme -> proxy server URL mappings. - - Returns settings gathered from the environment, if specified, + """Return True, if host should be bypassed. + + Checks proxy settings gathered from the environment, if specified, or the registry. """ - if getproxies_environment(): - return proxy_bypass_environment(host) + proxies = getproxies_environment() + if proxies: + return proxy_bypass_environment(host, proxies) else: return proxy_bypass_registry(host)