Commits

Ronald Oussoren  committed 21e8569

* Finish OC_PythonNumber (but: still no unittests for this)
* Add some documentation to _pycoder

  • Participants
  • Parent commits 5ecd0b9
  • Branches pyobjc-ancient

Comments (0)

Files changed (2)

File pyobjc-core/Lib/objc/_pycoder.py

 NOTE: this only works with a keyed archiver, not with a plain archiver. It 
 should be easy enough to change this later on if needed.
 
-FIXME: encoding for lists and tuples is far from optimal
-FIXME: need versioning
+A minor problem with NSCoding support is that NSCoding restores
+graphs recusively while Pickle does so depth-first (more of less). 
+This can cause problems when the object state contains the
+object itself, which is why we need a 'setValue' callback for the
+load_* functions below.
 """
 import objc
 from types import *
                     cls.__name__, str(err)), sys.exc_info()[2]
 
             
+        # We now have the object, but haven't set the correct
+        # state yet.  Tell the bridge about this value right
+        # away, that's needed because `value` might be part
+        # of the object state which we'll retrieve next.
         setValue(value)
 
         state = coder.decodeObjectForKey_(kSTATE)
 
     def load_reduce(coder, setValue):
         func = coder.decodeObjectForKey_(kFUNC)
-
-        # XXX: a problem: ``args`` might contain
-        # the object we want to recover (either
-        # directly or somewhere in the object graph)
         args = coder.decodeObjectForKey_(kARGS)
 
         value = func(*args)
+
+        # We now have the object, but haven't set the correct
+        # state yet.  Tell the bridge about this value right
+        # away, that's needed because `value` might be part
+        # of the object state which we'll retrieve next.
         setValue(value)
 
         listitems = coder.decodeObjectForKey_(kLIST)

File pyobjc-core/Modules/objc/OC_PythonNumber.m

 
 -(NSDecimal)decimalValue
 {
-	/* FIXME */
-	[NSException raise:NSInvalidArgumentException
-	              format:@"Cannot convert python number to NSDecimal"];
+	NSDecimal result;
+	NSDecimalNumber* num;
+
+	unsigned long long mantissa = 0;
+	unsigned short exponent = 0;
+	BOOL negative = NO;
+
+	PyObjC_BEGIN_WITH_GIL
+		if (PyInt_Check(value)) {
+			long lng = PyInt_AsLong(value);
+			if (lng < 0) {
+				mantissa = -lng;
+				exponent = 0;
+				negative = YES;
+			} else {
+				mantissa = lng;
+				exponent = 0;
+				negative = NO;
+			}
+
+		} else if (PyLong_Check(value)) {
+			mantissa = PyLong_AsUnsignedLongLong(value);
+			if (PyErr_Occurred()) {
+				long long lng;
+				PyErr_Clear();
+				lng = PyLong_AsLongLong(value);
+				if (PyErr_Occurred()) {
+					PyObjC_GIL_FORWARD_EXC();
+				}
+
+				if (lng < 0) {
+					mantissa = -lng;
+					exponent = 0;
+					negative = YES;
+				} else {
+					mantissa = lng;
+					exponent = 0;
+					negative = NO;
+				}
+			} else {
+				exponent = 0;
+				negative = NO;
+			}
+
+		} else if (PyFloat_Check(value)) {
+			PyObject* strVal = PyObject_Repr(value);
+			PyObject* uniVal = NULL;
+
+			if (strVal == NULL) {
+				PyObjC_GIL_FORWARD_EXC();
+			}
+
+			uniVal = PyUnicode_FromEncodedObject(strVal, "ascii", "strict");
+			Py_DECREF(strVal);
+			if (uniVal == NULL) {
+				PyObjC_GIL_FORWARD_EXC();
+			}
+
+			NSString* stringVal = PyObjC_PythonToId(uniVal);
+			Py_DECREF(uniVal);
+			
+			num = [[NSDecimalNumber alloc] initWithString:stringVal];
+			result = [num decimalValue];
+			[num release];
+			PyObjC_GIL_RETURN(result);
+
+		} else {
+			PyErr_Format(PyExc_TypeError, "cannot convert object of %s to NSDecimal",
+					value->ob_type->tp_name);
+			PyObjC_GIL_FORWARD_EXC();
+		}
+
+	PyObjC_END_WITH_GIL
+
+
+
+	num = [[NSDecimalNumber alloc] 
+		initWithMantissa:mantissa
+			exponent:exponent
+		      isNegative:negative];
+	result = [num decimalValue];
+	[num release];
+	return result;
 }
 
 -(double)doubleValue
 			} else {
 				[self release];
 				[proxy retain];
-				self = (OC_PythonObject*)proxy;
+				self = (OC_PythonNumber*)proxy;
 			}