[Python-checkins] r80198 - in python/branches/py3k: Lib/urllib/request.py Misc/NEWS Modules/_scproxy.c setup.py

ronald.oussoren python-checkins at python.org
Sun Apr 18 22:46:11 CEST 2010


Author: ronald.oussoren
Date: Sun Apr 18 22:46:11 2010
New Revision: 80198

Log:
For for issue #7154: Port the code that uses
the SystemConfiguration framework to detect the
proxy settings on OSX from the trunk to python 3.2


Added:
   python/branches/py3k/Modules/_scproxy.c   (contents, props changed)
Modified:
   python/branches/py3k/Lib/urllib/request.py
   python/branches/py3k/Misc/NEWS
   python/branches/py3k/setup.py

Modified: python/branches/py3k/Lib/urllib/request.py
==============================================================================
--- python/branches/py3k/Lib/urllib/request.py	(original)
+++ python/branches/py3k/Lib/urllib/request.py	Sun Apr 18 22:46:11 2010
@@ -2140,44 +2140,82 @@
 
 
 if sys.platform == 'darwin':
-    def getproxies_internetconfig():
-        """Return a dictionary of scheme -> proxy server URL mappings.
+    from _scproxy import _get_proxy_settings, _get_proxies
 
-        By convention the mac uses Internet Config to store
-        proxies.  An HTTP proxy, for instance, is stored under
-        the HttpProxy key.
+    def proxy_bypass_macosx_sysconf(host):
+        """
+        Return True iff this host shouldn't be accessed using a proxy
 
+        This function uses the MacOSX framework SystemConfiguration
+        to fetch the proxy information.
         """
-        try:
-            import ic
-        except ImportError:
-            return {}
+        import re
+        import socket
+        from fnmatch import fnmatch
+
+        hostonly, port = splitport(host)
+
+        def ip2num(ipAddr):
+            parts = ipAddr.split('.')
+            parts = map(int, parts)
+            if len(parts) != 4:
+                parts = (parts + [0, 0, 0, 0])[:4]
+            return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3]
+
+        proxy_settings = _get_proxy_settings()
+
+        # Check for simple host names:
+        if '.' not in host:
+            if proxy_settings['exclude_simple']:
+                return True
+
+        hostIP = None
+
+        for value in proxy_settings.get('exceptions', ()):
+            # Items in the list are strings like these: *.local, 169.254/16
+            if not value: continue
+
+            m = re.match(r"(\d+(?:\.\d+)*)(/\d+)?", value)
+            if m is not None:
+                if hostIP is None:
+                    try:
+                        hostIP = socket.gethostbyname(hostonly)
+                        hostIP = ip2num(hostIP)
+                    except socket.error:
+                        continue
+
+                base = ip2num(m.group(1))
+                mask = int(m.group(2)[1:])
+                mask = 32 - mask
+
+                if (hostIP >> mask) == (base >> mask):
+                    return True
+
+            elif fnmatch(host, value):
+                return True
+
+        return False
+
+
+    def getproxies_macosx_sysconf():
+        """Return a dictionary of scheme -> proxy server URL mappings.
+
+        This function uses the MacOSX framework SystemConfiguration
+        to fetch the proxy information.
+        """
+        return _get_proxies()
+
 
-        try:
-            config = ic.IC()
-        except ic.error:
-            return {}
-        proxies = {}
-        # HTTP:
-        if 'UseHTTPProxy' in config and config['UseHTTPProxy']:
-            try:
-                value = config['HTTPProxyHost']
-            except ic.error:
-                pass
-            else:
-                proxies['http'] = 'http://%s' % value
-        # FTP: XXX To be done.
-        # Gopher: XXX To be done.
-        return proxies
 
     def proxy_bypass(host):
         if getproxies_environment():
             return proxy_bypass_environment(host)
         else:
-            return 0
+            return proxy_bypass_macosx_sysconf(host)
 
     def getproxies():
-        return getproxies_environment() or getproxies_internetconfig()
+        return getproxies_environment() or getproxies_macosx_sysconf()
+
 
 elif os.name == 'nt':
     def getproxies_registry():

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Sun Apr 18 22:46:11 2010
@@ -318,6 +318,9 @@
 Library
 -------
 
+- Issue #7154: urllib.request can now detect the proxy settings on OSX 10.6
+  (as long as the user didn't specify 'automatic proxy configuration').
+
 - Issue #3817: ftplib.FTP.abort() method now considers 225 a valid response
   code as stated in RFC-959 at chapter 5.4.
 

Added: python/branches/py3k/Modules/_scproxy.c
==============================================================================
--- (empty file)
+++ python/branches/py3k/Modules/_scproxy.c	Sun Apr 18 22:46:11 2010
@@ -0,0 +1,261 @@
+/*
+ * Helper method for urllib to fetch the proxy configuration settings
+ * using the SystemConfiguration framework.
+ */
+#include <Python.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+
+static int32_t 
+cfnum_to_int32(CFNumberRef num)
+{
+	int32_t result;
+
+	CFNumberGetValue(num, kCFNumberSInt32Type, &result);
+	return result;
+}
+
+static PyObject*
+cfstring_to_pystring(CFStringRef ref)
+{
+	const char* s; 
+
+	s = CFStringGetCStringPtr(ref, kCFStringEncodingUTF8);
+	if (s) {
+		return PyUnicode_DecodeUTF8(
+				s, strlen(s), NULL);
+
+	} else {
+		CFIndex len = CFStringGetLength(ref);
+		Boolean ok;
+		PyObject* result;
+		char* buf;
+		
+		buf = PyMem_Malloc(len*4);
+		if (buf == NULL) {
+			PyErr_NoMemory();
+			return NULL;
+		}
+
+		ok = CFStringGetCString(ref, 
+				buf, len * 4,
+				kCFStringEncodingUTF8);
+		if (!ok) {
+			PyMem_Free(buf);
+			return NULL;
+		} else {
+			result = PyUnicode_DecodeUTF8(
+					buf, strlen(buf), NULL);
+			PyMem_Free(buf);
+		}
+		return result;
+	}
+}
+
+
+static PyObject*
+get_proxy_settings(PyObject* mod __attribute__((__unused__)))
+{
+	CFDictionaryRef proxyDict = NULL;
+	CFNumberRef aNum = NULL;
+	CFArrayRef anArray = NULL;
+	PyObject* result = NULL;
+	PyObject* v;
+	int r;
+
+	proxyDict = SCDynamicStoreCopyProxies(NULL);
+	if (!proxyDict) {
+		Py_INCREF(Py_None);
+		return Py_None;
+	}
+
+	result = PyDict_New();
+	if (result == NULL) goto error;
+
+	if (&kSCPropNetProxiesExcludeSimpleHostnames != NULL) {
+		aNum = CFDictionaryGetValue(proxyDict, 
+			kSCPropNetProxiesExcludeSimpleHostnames);
+		if (aNum == NULL) {
+			v = PyBool_FromLong(1);
+		} else {
+			v = PyBool_FromLong(cfnum_to_int32(aNum));
+		}
+	}  else {
+		v = PyBool_FromLong(1);
+	}
+
+	if (v == NULL) goto error;
+
+	r = PyDict_SetItemString(result, "exclude_simple", v);
+	Py_DECREF(v); v = NULL;
+	if (r == -1) goto error;
+
+	anArray = CFDictionaryGetValue(proxyDict, 
+			kSCPropNetProxiesExceptionsList);
+	if (anArray != NULL) {
+		CFIndex len = CFArrayGetCount(anArray);
+		CFIndex i;
+		v = PyTuple_New(len);
+		if (v == NULL) goto error;
+
+		r = PyDict_SetItemString(result, "exceptions", v);
+		Py_DECREF(v);
+		if (r == -1) goto error;
+
+		for (i = 0; i < len; i++) {
+			CFStringRef aString = NULL;
+
+			aString = CFArrayGetValueAtIndex(anArray, i);
+			if (aString == NULL) {
+				PyTuple_SetItem(v, i, Py_None);
+				Py_INCREF(Py_None);
+			} else {
+				PyObject* t = cfstring_to_pystring(aString);
+				if (!t) {
+					PyTuple_SetItem(v, i, Py_None);
+					Py_INCREF(Py_None);
+				} else {
+					PyTuple_SetItem(v, i, t);
+				}
+			}
+		}
+	}
+
+	CFRelease(proxyDict);
+	return result;
+
+error:
+	if (proxyDict)  CFRelease(proxyDict);
+	Py_XDECREF(result);
+	return NULL;
+}
+
+static int
+set_proxy(PyObject* proxies, char* proto, CFDictionaryRef proxyDict,
+		CFStringRef enabledKey, 
+		CFStringRef hostKey, CFStringRef portKey)
+{
+	CFNumberRef aNum;
+
+	aNum = CFDictionaryGetValue(proxyDict, enabledKey);
+	if (aNum && cfnum_to_int32(aNum)) {
+		CFStringRef hostString;
+
+		hostString = CFDictionaryGetValue(proxyDict, hostKey);
+		aNum = CFDictionaryGetValue(proxyDict, portKey);
+
+		if (hostString) {
+			int r;
+			PyObject* h = cfstring_to_pystring(hostString);
+			PyObject* v;
+			if (h) {
+				if (aNum) {
+					int32_t port = cfnum_to_int32(aNum);
+					v = PyUnicode_FromFormat("http://%U:%ld",
+						h, (long)port);
+				} else {
+					v = PyUnicode_FromFormat("http://%U", h);
+				}
+				Py_DECREF(h);
+				if (!v) return -1;
+				r = PyDict_SetItemString(proxies, proto,
+					v);
+				Py_DECREF(v);
+				return r;
+			}
+		}
+
+	}
+	return 0;
+}
+
+
+
+static PyObject*
+get_proxies(PyObject* mod __attribute__((__unused__)))
+{
+	PyObject* result = NULL;
+	int r;
+	CFDictionaryRef proxyDict = NULL;
+
+	proxyDict = SCDynamicStoreCopyProxies(NULL);
+	if (proxyDict == NULL) {
+		return PyDict_New();
+	}
+
+	result = PyDict_New();
+	if (result == NULL) goto error;
+
+	r = set_proxy(result, "http", proxyDict,
+		kSCPropNetProxiesHTTPEnable,
+		kSCPropNetProxiesHTTPProxy,
+		kSCPropNetProxiesHTTPPort);
+	if (r == -1) goto error;
+	r = set_proxy(result, "https", proxyDict,
+		kSCPropNetProxiesHTTPSEnable,
+		kSCPropNetProxiesHTTPSProxy,
+		kSCPropNetProxiesHTTPSPort);
+	if (r == -1) goto error;
+	r = set_proxy(result, "ftp", proxyDict,
+		kSCPropNetProxiesFTPEnable,
+		kSCPropNetProxiesFTPProxy,
+		kSCPropNetProxiesFTPPort);
+	if (r == -1) goto error;
+	r = set_proxy(result, "gopher", proxyDict,
+		kSCPropNetProxiesGopherEnable,
+		kSCPropNetProxiesGopherProxy,
+		kSCPropNetProxiesGopherPort);
+	if (r == -1) goto error;
+
+	CFRelease(proxyDict);
+	return result;
+error:
+	if (proxyDict)  CFRelease(proxyDict);
+	Py_XDECREF(result);
+	return NULL;
+}
+
+static PyMethodDef mod_methods[] = {
+	{
+		"_get_proxy_settings",
+		(PyCFunction)get_proxy_settings,
+		METH_NOARGS,
+		NULL,
+	},
+	{
+		"_get_proxies",
+		(PyCFunction)get_proxies,
+		METH_NOARGS,
+		NULL,
+	},
+	{ 0, 0, 0, 0 }
+};
+
+
+
+static struct PyModuleDef mod_module = {
+	PyModuleDef_HEAD_INIT,
+	"_scproxy",
+	NULL,
+	-1,
+	mod_methods,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+PyObject* 
+PyInit__scproxy(void)
+{
+	return PyModule_Create(&mod_module);
+}
+
+#ifdef __cplusplus
+}
+#endif
+

Modified: python/branches/py3k/setup.py
==============================================================================
--- python/branches/py3k/setup.py	(original)
+++ python/branches/py3k/setup.py	Sun Apr 18 22:46:11 2010
@@ -1238,6 +1238,12 @@
                        Extension('_gestalt', ['_gestalt.c'],
                        extra_link_args=['-framework', 'Carbon'])
                        )
+            exts.append(
+                       Extension('_scproxy', ['_scproxy.c'],
+                       extra_link_args=[
+                           '-framework', 'SystemConfiguration',
+                           '-framework', 'CoreFoundation',
+                        ]))
 
         self.extensions.extend(exts)
 


More information about the Python-checkins mailing list