After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 728611 - Python plugin: "Function implementation not available." when I use functools.partial
Python plugin: "Function implementation not available." when I use functools....
Status: RESOLVED FIXED
Product: Gnumeric
Classification: Applications
Component: General
1.12.x
Other Linux
: Normal normal
: ---
Assigned To: Jody Goldberg
Jody Goldberg
Depends on:
Blocks:
 
 
Reported: 2014-04-20 14:31 UTC by Thomas Levine
Modified: 2014-04-22 01:46 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
The spellbook for the example plugin (583 bytes, application/gzip)
2014-04-21 00:38 UTC, Thomas Levine
Details
A spreadsheet that uses the example plugin. (1.79 KB, application/x-gnumeric)
2014-04-21 00:39 UTC, Thomas Levine
Details
Here's a screenshot of example.gnumeric (26.66 KB, image/png)
2014-04-21 02:04 UTC, Thomas Levine
Details

Description Thomas Levine 2014-04-20 14:31:41 UTC
As explained here,
https://mail.gnome.org/archives/gnumeric-list/2014-April/msg00013.html

I have written a Python function that renders "Function implementation not available." without printing other error messages.

It appears that the issue relates to the use of functools.partial.
For example, this works,

    from libsheetmusic.spreadsheet import scale
    def diatonic_scale(scientific_note):
        return scale('diatonic',scientific_note)
    sheetmusic_functions = { 'diatonic_scale': diatonic_scale }

and this doesn't.

    import functools
    from libsheetmusic.spreadsheet import scale
    sheetmusic_functions = { 'diatonic_scale': functools.partial(scale, 'diatonic') }

libsheetmusic is part of sheetmusic.
http://pypi.python.org/pypi/sheetmusic
Comment 1 Thomas Levine 2014-04-20 15:55:40 UTC
I suspect that it's related to how functools.partial doesn't return a function.
http://stackoverflow.com/questions/13483527/why-doesnt-functools-partial-return-a-real-function-and-how-to-create-one-that
Comment 2 Andreas J. Guelzow 2014-04-20 17:54:36 UTC
If you think that it is related to functools.partial, then how is this a Gnumeric issue?
Comment 3 Morten Welinder 2014-04-20 18:35:56 UTC
Andreas: that's a Gnumeric error:

func.c: return value_new_error (ei->pos, _("Function implementation not available."));
Comment 4 Morten Welinder 2014-04-20 18:37:42 UTC
That error means that the module was loaded, but didn't provide the function
it promised to provide.
Comment 5 Thomas Levine 2014-04-20 23:40:06 UTC
I guess some idea of what should happen would help. Here are some ways that this could work.

* All callables are allowed as values in the functions dictionary.
* Only functions are allowed as values in the functions dictionary, and an error message tells me when I set something other than a function as a value.
* The documentation indicates that only functions are allowed in the functions dictionary.

I think Gnumeric would be better if one of the above were true, so I think it's worth considering it a Gnumeric issue.
Comment 6 Morten Welinder 2014-04-20 23:54:14 UTC
If you take away functools.partial(scale, 'diatonic') and put some
trivial function in its place, do things work?

My hypothesis is that some of the framework (plugin.xml.in etc.) isn't
setup right.  Attaching the plugin code would be nice.
Comment 7 Thomas Levine 2014-04-21 00:38:35 UTC
Created attachment 274780 [details]
The spellbook for the example plugin
Comment 8 Thomas Levine 2014-04-21 00:39:19 UTC
Created attachment 274781 [details]
A spreadsheet that uses the example plugin.
Comment 9 Thomas Levine 2014-04-21 00:40:02 UTC
If you take away functools.partial(scale, 'diatonic') and put some
trivial function in its place, things do work; see the first example
in my original description.

I've attached a minimal example of the issue.
You can also get it at this git repository.
git@github.com:tlevine/functions-must-be-functions.git
Comment 10 Morten Welinder 2014-04-21 01:03:42 UTC
Your functions_must_be_functions variable needs to define the
functions you advertise in plugin.xml: type, function, other_callable.

See https://git.gnome.org/browse/gnumeric/tree/plugins/py-func
Comment 11 Thomas Levine 2014-04-21 01:49:05 UTC
I pondered whether it would be easy to write a Python library to convert
Python callables into something Gnumeric would like. Here's my progress on that.

I wrapped all of the callables in functions.

    def to_function(name, callable):
        def f(*args, **kwargs):
            return callable(*args, **kwargs)
        f.__name__ = name
        return f

    callables = {'diatonic_scale': ... }
    sheetmusic_functions = {k:to_function(k, v) for k,v in callables.items()}

Now it works within a session, but it breaks when I close the file and open it again; the following text gets rendered from the cell in Gnumeric.

    Python exception (<type 'exceptions.SystemError'>: Objects/tupleobject.c:125: bad argument to internal function)

I that the file references the internal "f" function.

I'm starting to think that it'll be easier just to write out all of these
functions with "def".
Comment 12 Thomas Levine 2014-04-21 01:53:54 UTC
(In reply to comment #10)
> Your functions_must_be_functions variable needs to define the
> functions you advertise in plugin.xml: type, function, other_callable.
> 
> See https://git.gnome.org/browse/gnumeric/tree/plugins/py-func

Have I done this? For easy reference,
I have copied below "functions_must_be.py" from attachment 274780 [details]
(https://bugzilla.gnome.org/attachment.cgi?id=274780).

    class c:
        def __call__(self):
            return 2

    functions_must_be_functions = {
        'type': int,
        'function': lambda: 1,
        'other_callable': c(),
    }
Comment 13 Thomas Levine 2014-04-21 02:04:04 UTC
Created attachment 274789 [details]
Here's a screenshot of example.gnumeric

I had to change "type" to "python_type" because it clashed with another
function, but this is otherwise the same as the stuff I posted above.
Comment 14 Morten Welinder 2014-04-21 13:19:42 UTC
We subject the value to a PyFunction_Check (in function gplp_func_desc_load
in python-loader.c)

If you know of a better check for callability of a python object, we can
change it.

I note, that we use PyCallable_Check elsewhere, but I cannot test this myself.
Comment 15 Morten Welinder 2014-04-22 01:46:02 UTC
This problem has been fixed in our software repository. The fix will go into the next software release. Thank you for your bug report.