bt2: always use staticmethod() with native function class attributes
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Fri, 31 May 2019 11:56:44 +0000 (07:56 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Fri, 31 May 2019 14:19:24 +0000 (10:19 -0400)
commit2f16a6a228d0059349b8566979c9d579e7e271b3
treec490c16821cdfa19a6e5d71bb042158d0725537b
parent877cb1e2f9ca0f090b474f06e6fc593cce61331f
bt2: always use staticmethod() with native function class attributes

This is a recurrent pattern in the Python bindings:

    class Something(_SomeBase):
        _set_value = native_bt.something_set_value

Then template methods in `_SomeBase` can use this class attribute by
name within the template.

This function needs to be a static method, because template methods do:

    self._set_value(...)

and native_bt.something_set_value() must not receive `self` as its first
argument. For some reason, when you set a class attribute to a native
function (defined within an extension module), it is automatically a
static method, so you don't need to specify it manually. It is not the
case for any other Python function.

With SWIG 3, the generated wrapper code in `native_bt.py` looks like:

    def value_unsigned_integer_set(integer_obj, val):
        return _native_bt.value_unsigned_integer_set(integer_obj, val)
    value_unsigned_integer_set = _native_bt.value_unsigned_integer_set

There's a defined Python function, but then the same name is reassigned
to the native function itself (`_native_bt.value_unsigned_integer_set`).
I didn't investigate why SWIG does that; it could be an optimization,
but then I don't get why the Python wrapper is created in the first
place. This means that, with:

    class UnsignedIntegerValue(_IntegerValue):
        ...
        _set_value = native_bt.value_unsigned_integer_set
        ...

UnsignedIntegerValue._set_value() is a static method. However, with
SWIG 4, the generated wrapper looks like this:

    def value_unsigned_integer_set(integer_obj, val):
        return _native_bt.value_unsigned_integer_set(integer_obj, val)

For some reason, the

    value_unsigned_integer_set = _native_bt.value_unsigned_integer_set

part is gone.

Now, UnsignedIntegerValue._set_value() is not a static method anymore,
so calling it with `self._set_value()` gives the expected:

    TypeError: value_unsigned_integer_set() takes 2 positional arguments
    but 3 were given

Instead of trying to make SWIG 4 generate this native function
assignment again, I prefer to be more explicit/clean and use
staticmethod() to force the function into a static method.

From https://docs.python.org/3/library/functions.html#staticmethod,
the documentation also suggests this:

> Like all decorators, it is also possible to call staticmethod as a
> regular function and do something with its result. This is needed in
> some cases where you need a reference to a function from a class body
> and you want to avoid the automatic transformation to instance method.
> For these cases, use this idiom:
>
>     class C:
>         builtin_open = staticmethod(open)

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Change-Id: Ic51397e34132adc78fddc3105878080a8bf74ff9
Reviewed-on: https://review.lttng.org/c/babeltrace/+/1357
Tested-by: jenkins
Reviewed-by: Simon Marchi <simon.marchi@efficios.com>
bindings/python/bt2/bt2/component.py
bindings/python/bt2/bt2/graph.py
bindings/python/bt2/bt2/plugin.py
bindings/python/bt2/bt2/query_executor.py
bindings/python/bt2/bt2/value.py
This page took 0.025446 seconds and 4 git commands to generate.