1. Ronald Oussoren
  2. pyobjc

Commits

Ronald Oussoren  committed c652211

This checking adds the class OC_PythonDate as the proxy
class for instances of datetime.date and datetime.datetime.

  • Participants
  • Parent commits ff29c08
  • Branches pyobjc-ancient

Comments (0)

Files changed (9)

File pyobjc-core/Lib/objc/test/test_nsdate_proxy.py

View file
  • Ignore whitespace
+import objc.test
+from objc.test.fnd import NSDateFormatter, NSArray, NSDate
+import datetime
+
+class TestNSDateProxy (objc.test.TestCase):
+    # Test for proxied datetime.date and datetime.datetime objects,
+    # these have a custom proxy class.
+
+    def testFormattingForDate(self):
+        # This is jus a round-about way of testing that the right proxy
+        # object is created
+        formatter = NSDateFormatter.alloc().initWithDateFormat_allowNaturalLanguage_(
+                "%Y-%m-%d", True)
+
+        date = datetime.date.today()
+
+        value = formatter.stringFromDate_(NSDate.date())
+        self.assertEquals(value, date.strftime('%Y-%m-%d'))
+
+        value = formatter.stringFromDate_(date)
+        self.assertEquals(value, date.strftime('%Y-%m-%d'))
+
+
+    def testFormattingForDateTime(self):
+        # This is jus a round-about way of testing that the right proxy
+        # object is created
+        formatter = NSDateFormatter.alloc().initWithDateFormat_allowNaturalLanguage_(
+                "%Y-%m-%d %H:%M:%S", True)
+
+        date = datetime.datetime.now()
+
+        value = formatter.stringFromDate_(date)
+        self.assertEquals(value, date.strftime('%Y-%m-%d %H:%M:%S'))
+
+        
+if __name__ == "__main__":
+    objc.test.main()

File pyobjc-core/Modules/objc/OC_PythonDate.h

View file
  • Ignore whitespace
+#import "pyobjc.h"
+#import <Foundation/Foundation.h>
+
+@interface OC_PythonDate : NSDate
+{
+	PyObject* value;
+	NSDate*   oc_value;
+}
+
++ depythonifyObject:(PyObject*)object;
++ newWithPythonObject:(PyObject*)value;
+- initWithPythonObject:(PyObject*)value;
+-(void)dealloc;
+-(PyObject*)__pyobjc_PythonObject__;
+
+/* Implementation of the NSDate interface */
+
+/* This one is the biggy: this is the once required method, all other
+ * NSDate methods build on this.
+ */
+-(NSTimeInterval)timeIntervalSinceReferenceDate;
+
+/* These two are only present to *disable* coding, not implement it */
+- (void)encodeWithCoder:(NSCoder*)coder;
+- initWithCoder:(NSCoder*)coder;
+
+@end

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

View file
  • Ignore whitespace
+#include "pyobjc.h"
+#import "OC_PythonDate.h"
+
+static PyObject* datetime_types = NULL;
+
+@implementation OC_PythonDate 
+
++ depythonifyObject:(PyObject*)object;
+{
+	if (datetime_types == NULL) {
+		/* Initialize the mapping table, don't worry about
+		 * import errors: if we're running in an Py2app application
+		 * that doesn't use datetime we won't be able to import the
+		 * module
+		 */
+		datetime_types = PyList_New(0);
+		if (datetime_types == NULL) {
+			return nil;
+		}
+		PyObject* name = PyString_FromString("datetime");
+		if (name == NULL) {
+			return nil;
+		}
+		PyObject* datetime = PyImport_Import(name);
+		Py_DECREF(name); name = NULL;
+
+		if (datetime == NULL) {
+			Py_DECREF(datetime);
+			PyErr_Clear();
+			return nil;
+		}
+
+		PyList_Append(datetime_types,
+			PyObject_GetAttrString(datetime, "date"));
+		PyList_Append(datetime_types,
+			PyObject_GetAttrString(datetime, "datetime"));
+		if (PyErr_Occurred()) {
+			Py_DECREF(datetime);
+			return nil;
+		}
+		Py_DECREF(datetime);
+	}
+
+
+	if (PySequence_Contains(datetime_types, (PyObject*)(object->ob_type))) {
+		return [[OC_PythonDate alloc] initWithPythonObject:object];
+	}
+	return nil;
+}
+
++ newWithPythonObject:(PyObject*)v;
+{
+	OC_PythonArray* res;
+
+	res = [[OC_PythonDate alloc] initWithPythonObject:v];
+	[res autorelease];
+	return res;
+}
+
+- initWithPythonObject:(PyObject*)v;
+{
+	self = [super init];
+	if (unlikely(self == nil)) return nil;
+
+	oc_value = nil;
+
+	Py_INCREF(v);
+	Py_XDECREF(value);
+	value = v;
+	return self;
+}
+
+-(PyObject*)__pyobjc_PythonObject__
+{
+	Py_INCREF(value);
+	return value;
+}
+
+-(void)release
+{
+	/* See comment in OC_PythonUnicode */
+	PyObjC_BEGIN_WITH_GIL
+		[super release];
+	PyObjC_END_WITH_GIL
+}
+
+-(void)dealloc
+{
+	[oc_value  release];
+	oc_value = nil;
+
+	PyObjC_BEGIN_WITH_GIL
+		PyObjC_UnregisterObjCProxy(value, self);
+		Py_XDECREF(value);
+
+	PyObjC_END_WITH_GIL
+
+	[super dealloc];
+}
+
+
+- (void)encodeWithCoder:(NSCoder*)coder
+{
+	/* 
+	 * Forcefully disable coding for now, to avoid generating invalid
+	 * encoded streams.
+	 */        
+	[NSException raise:NSInvalidArgumentException format:@"PyObjC: Encoding python objects of type %s is not supported", value->ob_type->tp_name, coder];
+	
+}
+
+- initWithCoder:(NSCoder*)coder
+{
+	[NSException raise:NSInvalidArgumentException format:@"PyObjC: Decoding python objects is not supported", coder];
+	return nil;
+}
+
+-(NSDate*)_make_oc_value
+{
+	if (oc_value == nil) {
+		PyObjC_BEGIN_WITH_GIL
+			PyObject* v;
+
+			v = PyObject_CallMethod(value, "strftime", "s",
+				"%Y-%m-%d %H:%M:%S %z");
+			if (v == NULL) {
+				/* Raise ObjC exception */
+			}
+
+
+			oc_value = [NSDate dateWithString:
+			    [NSString stringWithCString: PyString_AsString(v)]];
+			[oc_value retain];
+			Py_DECREF(v);
+
+			if (oc_value == nil) {
+				/* The first try will fail when the date/datetime object
+				 * isn't timezone aware, try again with a default timezone
+				 */
+				char buf[128];
+
+				NSTimeZone* zone = [NSTimeZone defaultTimeZone];
+				NSInteger offset = [zone secondsFromGMT];
+				char posneg;
+				if (offset < 0) {
+					posneg = '-';
+					offset = -offset;
+				} else {
+					posneg = '+';
+				}
+				offset = offset / 60; /* Seconds to minutes */
+
+				int minutes = offset % 60;
+				int hours = offset / 60;
+
+
+
+				snprintf(buf, sizeof(buf), "%%Y-%%m-%%d %%H:%%M:%%S %c%02d%02d",
+					posneg, hours, minutes);
+				v = PyObject_CallMethod(value, "strftime", "s", buf);
+				if (v == NULL) {
+					/* Raise ObjC exception */
+				}
+
+				oc_value = [NSDate dateWithString:
+				    [NSString stringWithCString: PyString_AsString(v)]];
+				[oc_value retain];
+				Py_DECREF(v);
+			}
+
+
+
+		PyObjC_END_WITH_GIL
+	} 
+	return oc_value;
+}
+
+-(NSTimeInterval)timeIntervalSinceReferenceDate
+{
+	return [[self _make_oc_value] timeIntervalSinceReferenceDate];
+}
+
+
+- (id)addTimeInterval:(NSTimeInterval)seconds
+{
+	return [[self _make_oc_value] addTimeInterval:seconds];
+}
+
+- (NSComparisonResult)compare:(NSDate *)anotherDate
+{
+	return [[self _make_oc_value] compare:anotherDate];
+}
+
+- (NSCalendarDate *)dateWithCalendarFormat:(NSString *)formatString timeZone:(NSTimeZone *)timeZone
+{
+	return [[self _make_oc_value] dateWithCalendarFormat:formatString timeZone:timeZone];
+}
+
+- (NSString*)description
+{
+	return [[self _make_oc_value] description];
+}
+
+- (NSString *)descriptionWithCalendarFormat:(NSString *)formatString timeZone:(NSTimeZone *)aTimeZone locale:(id)localeDictionary
+{
+	return [[self _make_oc_value] descriptionWithCalendarFormat:formatString timeZone:aTimeZone locale:localeDictionary];
+}
+
+- (NSString *)descriptionWithLocale:(id)localeDictionary
+{
+	return [[self _make_oc_value] descriptionWithLocale:localeDictionary];
+}
+
+- (NSDate *)earlierDate:(NSDate *)anotherDate
+{
+	if ([[self _make_oc_value] earlierDate:anotherDate] == self) {
+		return self;
+	} else {
+		return anotherDate;
+	}
+}
+
+
+- (BOOL)isEqualToDate:(NSDate *)anotherDate
+{
+	return [[self _make_oc_value] isEqualToDate:anotherDate];
+}
+
+
+- (NSDate *)laterDate:(NSDate *)anotherDate
+{
+	if ([[self _make_oc_value] laterDate:anotherDate] == self) {
+		return self;
+	} else {
+		return anotherDate;
+	}
+}
+
+- (NSTimeInterval)timeIntervalSince1970
+{
+	return [[self _make_oc_value] timeIntervalSince1970];
+}
+
+- (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate
+{
+	return [[self _make_oc_value] timeIntervalSinceDate:anotherDate];
+}
+
+- (NSTimeInterval)timeIntervalSinceNow
+{
+	return [[self _make_oc_value] timeIntervalSinceNow];
+}
+
+
+@end /* implementation OC_PythonDate */

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

View file
  • Ignore whitespace
 		if (PyErr_Occurred()) {
 			PyObjC_GIL_FORWARD_EXC();
 		}
+		
+		/* Check if the object is "datetime-like" */
+		instance = [OC_PythonDate depythonifyObject:obj];
+		if (instance != nil) {
+			PyObjC_GIL_RETURN(instance);
+		} 
+		if (PyErr_Occurred()) {
+			PyObjC_GIL_FORWARD_EXC();
+		}
 
 		/* If all else fails use the generic proxy */
 		instance = [[self alloc] initWithObject:obj];

File pyobjc-core/Modules/objc/pyobjc-api.h

View file
  • Ignore whitespace
 #endif
 #endif
 
-#ifndef NSUInteger
-# define NSUInteger unsigned int
-#endif
-#ifndef NSInteger
-# define NSInteger int
-#endif
-
-
 #import <Foundation/NSException.h>
 
 struct PyObjC_WeakLink {

File pyobjc-core/Modules/objc/pyobjc-compat.h

View file
  • Ignore whitespace
 
 #else
 
+#ifndef Py_ARG_SIZE_T
 #define Py_ARG_SIZE_T "n"
 #endif
 
+#endif
+
 
 /* NSInteger and friends are available on Leopard and later, define them here
  * for compatibility with earlier versions.

File pyobjc-core/Modules/objc/pyobjc.h

View file
  • Ignore whitespace
 #include "OC_PythonUnicode.h"
 #include "OC_PythonString.h"
 #include "OC_PythonEnumerator.h"
+#include "OC_PythonDate.h"
 #include "method-signature.h"
 #include "objc_util.h"
 #include "objc-class.h"

File pyobjc-core/Modules/objc/test/testbndl.m

View file
  • Ignore whitespace
 
 #include <Python.h>
 #include <pyobjc-api.h>
+#include <pyobjc-compat.h>
 
 #import <Foundation/Foundation.h>
 

File pyobjc-core/NEWS.txt

View file
  • Ignore whitespace
 Version 2.1 (...)
 -----------------
 
+- There now is a custom proxy class for instances of ``datetime.date`` and
+  ``datetime.datetime``, which takes away the need to manually convert these
+  instances before using them from Objective-C (such as using an 
+  ``NSDateFormatter``)
 
 - PyObjC can now run in 64-bit mode.
 
   haven't tried running real programs yet and hence there might be issues 
   lurking below the surface.
 
-  NOTE2: PPC64 support is completely untested, aka "it compiles and therefore 
-  must work".
+  NOTE: 64-bit support does not yet work on PPC due to a bug in libffi which
+  prefents catching Objective-C exceptions.
 
   This requires Leopard (OSX 10.5), earlier version of the OS don't have a 
   64-bit Objective-C runtime at all.  This currently also requires a copy of