Показаны сообщения с ярлыком python. Показать все сообщения
Показаны сообщения с ярлыком python. Показать все сообщения

16 мая 2014 г.

PySide: Internal C++ object already deleted

Эта ошибка возникает в следующем случае:
  • Имеется Python(PySide)-объект с ненулевым счетчиком ссылок, который привязан к некоторому C++-объекту QObject.
  • Где-то в C++-коде C++-объект уничтожается без ведома Python.
  • Где-то в Python-коде происходит обращение к Python-объекту, который теперь ссылается на уничтоженный QObject.

Наиболее вероятный сценарий уничтожения QObject в C++ без ведома Python такой:
  • По какой-то причине из Python или из C++ было запущено уничтожение родительского объекта QObject.
  • Управление получил C++-деструктор родительского QObject, который уничтожил все дочерние C++-объекты QObject.
  • Ссылки на эти дочерние объекты из Python остались висячими.

Соотвественно, при появлении такой ошибки стоит проверить, не были ли уничтожены родительские объекты.

Если объект был уничтожен совершенно внезапно (например, при нажатии никак не связанной с ним кнопки), возможно родительские объекты были удалены сборщиком мусора, который просто не был запущен до этого.

Отлаживать такую ситуацию довольно просто:
  • Собираем Python с отладочными символами.
  • Запускаем Python под GDB.
  • Ставим breakpoint на С++-деструктор того QObject, который внезапно удаляется.
  • Смотрим C++-backtrace (команда bt) и наслаждаемся поддержкой Python в GDB, которая позволяет распечатывать все PyObject.
  • Смотрим Python-backtrace (команда py-bt).

Если дело в сборщике мусора, то по backtrace-ам можно увидеть, начиная с какого объекта началось удаление.

Если GDB не распечатывает PyObject и py-bt не работает, скорее всего Python собран без отладочных символов.
Если GDB периодически печатает ошибку:

Python Exception <type 'exceptions.TypeError'> object of type 'FakeRepr' has no len()

то можно воспользоваться патчем.

12 мая 2014 г.

Python: загрузка модуля из заданного пути


Примеры даны для следующей структуры каталогов:

.
`-- foo
    `-- bar
        `-- __init__.py

Загрузка foo.bar по пути к __init__.py

Python 2

import imp
bar = imp.load_source('bar', '/foo/bar/__init__.py')

Python 3

import importlib.machinery
loader = importlib.machinery.SourceFileLoader('bar', '/foo/bar/__init__.py')
bar = loader.load_module('bar')

Загрузка foo.bar по пути к bar

Python 3

import importlib.machinery
loader = importlib.machinery.PathFinder.find_module('bar', ['/foo'])
bar = loader.load_module('bar')


15 мая 2013 г.

Аннотации в PyBindGen

 Ссылки


PyBindGen

PyBindGen - это генератор биндингов к коду на C/C++ для Python. Он позволяет сгенерировать код на C или C++, компилирующийся в динамическую библиотеку. Эта библиотека будет модулем Python с интерфейсом для всех классов и функций C/C++.

Есть два основных варианта его использования:
  1. Использовать модуль pybindgen и вручную описать на Python все классы, методы и функции, для которых нужно сгенерировать биндинги.
  2. Использовать модуль pybindgen.gccxmlparser, который позволяет распарсить заголовочные файлы C/C++ с помощью pygccxml и автоматически сгенерировать скрипт Python, аналогичный написанному вручную в пункте (1). Этот скрипт будет в свою очередь генерировать биндинги.

Атрибуты

При возникновении неоднозначных ситуаций, их нужно устранять, указывая для классов, функций и их параметров дополнительные атрибуты, в частности политику передачи владения объектами.
  1. Если используется вариант (1), атрибуты указываются при добавлении методов в скрипте на Python.
  2. Если используется вариант (2), атрибуты указываются в коде на C/C++ в комментариях специального формата (аннотациях).

Аннотации

Аннотация - это комментарий вида:

// -#- АННОТАЦИЯ -#-

Между // и -#- должен быть пробел. Комментарий должен быть строго на предыдущей строке от объявления.

Для функции, метода или поля:

// -#- АННОТАЦИЯ -#-
void foo();

Для класса или структуры:

class bar
// -#- АННОТАЦИЯ -#-
{
...
};

Игнорирование

Аннотация ignore отключает генерацию биндингов для объявления.

// -#- ignore -#-
void foo()

Наследование

Аннотация allow_subclassing включает возможность наследования классов Python от классов C++.

class foo
// -#- allow_subclassing=true -#-
{
...
};

Если наследник в питоне имеет конструктор, нужно не забыть вызвать в нем конструктор базового класса, иначе сгенерированный биндинг будет проинициализирован неправильно.

class bar(foo):
    def __init__(self):
        foo.__init__(self)
        # do something

Подсчет ссылок

Если указать методы объекта, реализующие подсчет ссылок, они будут автоматически использоваться в Python. Это может значительно упростить соглашения о передачи владения объектов, см. раздел "Владение".

class foo
// -#- incref_method=IncRef; decref_method=DecRef; peekref_method=PeekRef -#-
{
  void IncRef();
  void DecRef();
  int PeekRef();
};

Владение

Эти атрибуты устраняют неоднозначности поведения сгенерированного кода при передаче и возвращении указателей. Набор атрибутов аналогичен политикам в Boost.Python.

Использовать объекты с подсчетом ссылок проще:
  • политики владения для объектов без подсчета ссылок зависят от поведения каждого конкретного метода и способов создания и удаления объектов;
  • политики для объектов с подсчетом ссылок зависят только от соглашения, вызывает ли incref() и decref() вызывающая сторона или вызываемая, в остальном владение автоматически разруливается подсчетом ссылок.

Аннотации имеют вид:

// -#- @параметр(атрибут=значение, атрибут=значение); @параметр(...); ... -#-

Специальный параметр @return обозначает возвращаемое значение.

Атрибуты для объектов без подсчета ссылок


Параметры

Атрибут transfer_ownership определяет, передает ли Python владение переданным объектом коду C++:
  • Если равен true:
    • Python теряет владение объектом.
    • Python больше не отвечает за удаление объекта.
    • Python обнулит все свои ссылки на этот объект.
    • Конструктор копирования не вызывается.
  • Если равен false:
    •  Python сохраняет владение объектом.
    • Для объекта будет вызван конструктор копирования и в C++ будет передана копия. Если у объекта запрещен конструктор копирования, биндинги не скомпилируются. Если объект полиморфный, могут возникнуть проблемы в рантайме.

// -#- @p1(transfer_ownership=false) -#-
void myfunc(myclass *p1);

Возвращаемое значение

Атрибут caller_owns_return определяет, получает ли Python владение объектом, возвращенным из C++:
  • Если равен true:
    • Python получает владение объектом.
    • Python отвечает за удаление объекта.
    • C++-код больше не имеет права использовать этот объект.
    • Конструктор копирования не вызывается.
  • Если равен false:
    • Python не владеет объектом.
    • Python не отвечает за удаление объекта.
    • C++-код по-прежнему может использовать объект и отвечает за его удаление.
    • Для возвращенного объекта будет вызван конструктор копирования, и в в Python будет использоваться созданная копия. Если у объекта запрещен конструктор копирования, биндинги не скомпилируются. Если объект полиморфный, могут возникнуть проблемы в рантайме.

// -#- @return(caller_owns_return=true) -#-
myclass * myfunc();


Атрибут reference_existing_object может модифицировать поведение caller_owns_return:
  • Если равен true:
    • Python получает владение объектом.
    • Python отвечает за удаление объекта.
    • C++-код по-прежнему может использовать объект.
    • C++-код при этом больше не отвечает за удаление объекта. Программист ответственен за то, чтобы Python не удалил объект раньше, чем C++-код перестал его использовать.
    • Конструктор копирования не вызывается.

// -#- @return(reference_existing_object=true) -#-
myclass * myfunc();

 

Атрибут return_internal_reference указывает, что метод вернул указатель на объект, являющийся составной частью вызываемого объекта.

struct foo
{
  bar b;

  // -#- @return(return_internal_reference=true) -#-
  bar * get_bar()
  {
    return &b;
  }
};

Атрибуты объектов с подсчетом ссылок


Параметры

Атрибут transfer_ownership определяет, кто отвечает за увеличения счетчика ссылок.
  • Если равен true, Python должен увеличить счетчик ссылок перед вызовом, если хочет продолжать использовать объект.
  • Если равен false, Python не должен ничего делать. Если C++-код хочет пользоваться объектом, он сам увеличит счетчик ссылок.

Возвращаемое значение

Атрибут caller_owns_return определяет, кто отвечает за увеличения счетчика ссылок.
  • Если равен true, C++-код уже увеличил счетчик ссылок перед тем, как вернуть объект.
  • Если равен false, Python должен сам увеличить счетчик ссылок, если хочет пользоваться объектом.

Выходные аргументы

Если атрибут direction равен out, C++-код будет модифицировать переданный объект.

// -#- @p1(direction=out) -#-
void foo(int *p1);


Массивы

Если параметр является массивом, его длину можно указать с помощью атрибута array_length.

// -#- @a1(direction=out, array_length=100) -#-
void bar(int *a1);