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(&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() на любых.