PyObjC, repr, str, и unicode

написал Владимир Пузанов - September 25, 2008 – 15:55

Питон прекрасно подходит для прототипирования. Особенно через интерпретатор ipython. К сожалению попытки поработать с PyObjC показали, что биндинги очень косячные.

Основная проблема в том, что __repr__ объекта должен возвращать короткое его описание (во внутреннем формате), а PyObjC возвращает вывод -[description], что напримаер для NSXMLElement - весь внутренний XML. В unicode.

И так, будем фиксить.

Для начала нам понадобятся собственно PyObjC:

svn co http://svn.red-bean.com/pyobjc PyObjC

свежий py2app:

svn co http://svn.pythonmac.org/py2app py2app

и свежие setuptools:

sudo easy_install setuptools

Собираем и устанавливаем py2app:

cd py2app
sudo python setup.py install

Теперь идем в исходники PyObjC и вносим правки:

cd ../PyObjC/pyobjc-core
mate Modules/objc/objc-object.m

(текстовый редактор оставляю на ваше усмотрение).

Ищем в файле функцию object_repr и приводим ее к такому виду:

static PyObject*
object_repr(PyObject* _self)
{
	PyObjCObject* self = (PyObjCObject*)_self;
	PyObject* res;
 
	if (self->flags & PyObjCObject_kMAGIC_COOKIE) {
		return PyString_FromFormat(
			"<%s objective-c magic instance %p>",
			self->ob_type->tp_name, self->objc_object);
	}
#if 0
	if ((self->flags & PyObjCObject_kUNINITIALIZED) == 0 && !PyObjCObject_IsClassic(self)) {
		/* Try to call the method 'description', which is the ObjC
		 * equivalent of __repr__. If that fails we'll fall back to
		 * the default repr.
		 * Don't call 'description' for uninitialized objects, that
		 * is undefined behaviour and will crash the interpreter sometimes.
		 */
		res = PyObject_CallMethod((PyObject*)self, "description", NULL);
		if (res == NULL) {
			PyErr_Clear();
		} else {
			return res;
		}
	}
#endif
	return PyString_FromFormat(
		"<%s objective-c instance %p>",
		self->ob_type->tp_name, self->objc_object);
}

По сути мы выключаем всю логику вызова description, и выводим результат красивым <%s objective-c instance %p>.

Теперь продублируйте оригинал функции (без #if 0/#endif) под ней с именем object_str. Это необходимо для работы str() и unicode(). Найдите ниже по тексту структуру “PyObjCClassObject PyObjCObject_Type = {” и вместо “0″ напротив /* tp_str */ впишите object_str. Это зарегистрирует новую функцию:

PyObjCClassObject PyObjCObject_Type = {
   {
     {
	PyObject_HEAD_INIT(&amp;PyObjCClass_Type)
	0,					/* ob_size */
	"objc_object",				/* tp_name */
	sizeof(PyObjCObject),			/* tp_basicsize */
	0,					/* tp_itemsize */
	/* methods */
	object_dealloc,		 		/* tp_dealloc */
	0,					/* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	0,					/* tp_compare */
	object_repr,				/* tp_repr */
	0,					/* tp_as_number */
	0,					/* tp_as_sequence */
	0,		       			/* tp_as_mapping */
	0,					/* tp_hash */
	0,					/* tp_call */
	object_str,					/* tp_str */
	object_getattro,			/* tp_getattro */
	object_setattro,			/* tp_setattro */
	...

Все. Файл можно сохранять и закрывать. соберите PyObjC:

python setup.py build

По идее его можно установить, но я просто заменил собранный _objc.so (он лежит в /build/lib.macosx-10.5-i386-2.5/objc/_objc.so), положив его вместо оригинального (в /System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python/PyObjC/objc).

И так, у нас все работает:

In [36]: i
Out[36]: <NSXMLElement objective-c instance 0x1fe1c40>
 
In [37]: str(i)
---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)
 
/Users/farcaller/Developer/PyObjC/<ipython console> in <module>()
 
UnicodeEncodeError: 'ascii' codec cant encode characters in position 89-96: ordinal not in range(128)
 
In [38]: unicode(i)[:10]
Out[38]: u'<div class'
 
In [39]: err
Out[39]: <NSError objective-c instance 0x1f3fee0>
 
In [40]: str(err)[:10]
Out[40]: 'Error Doma'

str() работает только на ascii-строках, unicode() на любых.

Написать комментарий