Commit 1c363c0c authored by Jon K Hellan's avatar Jon K Hellan Committed by Jon Kåre Hellan

Remove the plugin.

2002-10-11  Jon K Hellan  <hellan@acm.org>

	* Remove the plugin.
parent 9fa2059e
BUGS
2000-01-14 Jon Kåre Hellan
I have encountered some problems while working on the Python plugin,
which I believe are really symptoms of bugs elsewhere.
1. If init_plugin returns error, plugin_load closes the plugin with
g_module_close. However, this results in an illegal memory access
(segfault) inside dlclose. This happened even with a modified
init_plugin which immediately returned error status, so it's got
nothing to do with what happens inside init_plugin.
There are lots of missing features, of course.
2002-10-11 Jon K Hellan <hellan@acm.org>
* Remove the plugin.
2002-10-11 Jon K Hellan <hellan@acm.org>
* python.c (value_from_python): s/gnum_int/int/
......
gnumeric_pythondir = $(gnumeric_datadir)/python
INCLUDES = \
-DPLUGIN_ID=\"Gnumeric_python\" \
-DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
-I$(top_srcdir)/src -I$(top_builddir)/src \
$(PY_CFLAGS) \
$(GNUMERIC_CFLAGS)
gnumeric_plugin_pythondir = $(gnumeric_plugindir)/python
xmldir = $(gnumeric_plugin_pythondir)
gnumeric_plugin_python_LTLIBRARIES = python.la
python_la_LDFLAGS = -module -avoid-version $(PY_LIB_LOC)
python_la_LIBADD = -l$(PY_LIBS) $(PY_EXTRA_LIBS)
python_la_SOURCES = python.c
Pythonscriptsdir = $(gnumeric_pythondir)
Pythonscripts_DATA = gnumeric_startup.py gnumeric_defs.py
xml_in_files = plugin.xml.in
xml_DATA = $(xml_in_files:.xml.in=.xml)
@INTLTOOL_XML_RULE@
EXTRA_DIST = $(Pythonscripts_DATA) $(xml_in_files)
DISTCLEANFILES = $(xml_DATA)
# Definitions for the Python plugin for Gnumeric.
import types
class Boolean:
def __init__(self, value):
self.value = value
def __str__(self):
if self.__nonzero__():
return 'TRUE'
else:
return 'FALSE'
def __nonzero__(self):
return self.value != 0
TRUE = Boolean(1)
FALSE = Boolean(0)
class CellRef:
def __init__(self, column, row, col_relative=0,
row_relative=0, sheet=""):
if ((type (column) != types.IntType) or
(type (row) != types.IntType) or
(type (col_relative) != types.IntType) or
(type (row_relative) != types.IntType)):
#(type (sheet) != types.StringType)):
raise TypeError, "Wrong types for a CellRef"
self.column = column
self.row = row
self.col_relative = col_relative
self.row_relative = row_relative
self.sheet = sheet
class CellRange:
def __init__ (self, cell_a, cell_b):
if not ((cell_a.__class__ is CellRef) and
(cell_b.__class__ is CellRef)):
raise TypeError, "Wrong types for a CellRange"
self.range = (cell_a, cell_b)
import gnumeric
import gnumeric_defs
def gnumeric_mid(context,text,start_num,num_chars):
return text[start_num-1:start_num+num_chars-1];
help_mid = \
"@FUNCTION=PY_MID\n" \
"@SYNTAX=PY_MID(text,start,num_chars)\n" \
"@DESCRIPTION=" \
"Returns a specific number of characters from a text string, " \
"starting at START and spawning NUM_CHARS. Index is counted " \
"starting from one"
gnumeric.register_function("py_mid", "Python", "sff",
"text, start_num, num_chars",
help_mid, gnumeric_mid);
# This is a totally pointless function. But it illustrates how to invoke a
# gnumeric function from Python. Note that the argument list must be a
# sequence. Caveat: "(1)" is not a tuple. "(1,)" and "(1,2)" are.
def py_abs(context, f):
return gnumeric.apply(context, "abs", (f,))
help_py_abs = """@FUNCTION=PY_ABS
@SYNTAX=PY_ABS(num)
@DESCRIPTION=Return the absolute value of number."""
gnumeric.register_function("py_abs", "Python", "f", "num",
help_py_abs, py_abs);
# load user init-file if present
def run_user_init_file():
import os
home_gnumericrc = os.environ["HOME"] + "/.gnumeric/rc.py"
if os.path.isfile(home_gnumericrc):
execfile(home_gnumericrc, globals())
run_user_init_file()
<?xml version="1.0"?>
<plugin id="Gnumeric_python">
<information>
<_name>Python Plugin</_name>
<_description>This plugin provides Python language support in Gnumeric</_description>
</information>
<loader type="Gnumeric_Builtin:module">
<attribute name="module_file" value="python.la"/>
</loader>
<services>
<service type="general"></service>
</services>
</plugin>
/*
* Support for Python in gnumeric.
*/
/**
* TODO:
* Cell value
* Cell ranges - The sheet attribute is not yet handled.
*/
#include <gnumeric-config.h>
#include <gnumeric-i18n.h>
#include <gnumeric.h>
#include <gnome.h>
#include <string.h>
#include "func.h"
#include "plugin.h"
#include "plugin-util.h"
#include "error-info.h"
#include "module-plugin-defs.h"
#include "gutils.h"
#include "value.h"
#include "str.h"
#include "command-context.h"
#include "expr.h"
#include "expr-impl.h"
#include "Python.h"
GNUMERIC_MODULE_PLUGIN_INFO_DECL;
/* Classes we define in Python code, and where we define them. */
#define GNUMERIC_DEFS_MODULE "gnumeric_defs"
#define BOOLEAN_CLASS "Boolean"
#define CELL_REF_CLASS "CellRef"
#define CELL_RANGE_CLASS "CellRange"
static PyObject *value_to_python (Value *v);
static Value *value_from_python (PyObject *o, EvalPos const *pos);
/* This is standard idiom for defining exceptions in extension modules. */
static PyObject *GnumericError;
/**
* string_from_exception
*
* Converts the current Python exception to a C string. Returns C string.
* Caller must free it.
*/
static char *
string_from_exception (void)
{
const char *header = _("Python exception");
const char *retval = header;
char buf[256];
PyObject *ptype = NULL, *pvalue = NULL, *ptraceback = NULL;
PyObject *stype = NULL, *svalue = NULL;
PyErr_Fetch (&ptype, &pvalue, &ptraceback);
if (!ptype)
goto cleanup;
if (pvalue)
svalue = PyObject_Str (pvalue);
if (PyErr_GivenExceptionMatches (ptype, GnumericError)) {
/* This recovers a VALUE_ERROR which the Python code received
* from C */
retval = PyString_AsString (svalue);
} else {
int pos;
/* Include exception class in the error message */
stype = PyObject_Str (ptype);
if (!stype)
goto cleanup;
pos = snprintf (buf, sizeof buf, "%s: %s",
header, PyString_AsString (stype));
retval = buf;
if (!svalue)
goto cleanup;
if (pos + 3 < (int)sizeof (buf))
snprintf (buf + pos , sizeof buf - pos , ": %s",
PyString_AsString (svalue));
}
cleanup:
Py_XDECREF (stype);
Py_XDECREF (svalue);
PyErr_Restore (ptype, pvalue, ptraceback);
return g_strdup (retval);
}
/**
* value_from_exception
* @ei function eval info
*
* Converts the current Python exception to a new VALUE_ERROR.
*
* For now, also prints traceback to standard error.
*/
static Value *
value_from_exception (FunctionEvalInfo *ei)
{
char *exc_string = string_from_exception ();
Value *v = value_new_error (ei->pos, exc_string);
g_free (exc_string);
PyErr_Print (); /* Traceback to stderr for now */
PyErr_Clear ();
return v;
}
/*
* Support for registering Python-based functions for use in formulas.
*/
/**
* cell_ref_to_python
* @v value union
*
* Converts a cell reference to Python. Returns owned reference to python
* object.
*/
static PyObject *
cell_ref_to_python (CellRef *cell)
{
PyObject *mod, *klass, *ret;
mod = PyImport_ImportModule ((char *) GNUMERIC_DEFS_MODULE);
if (mod == NULL)
return NULL;
klass = PyObject_GetAttrString (mod, (char *) CELL_REF_CLASS);
if (klass == NULL)
return NULL;
ret = PyObject_CallFunction (klass, (char *) "iiii",
cell->col, cell->row,
cell->col_relative, cell->row_relative
/*, sheet */);
Py_DECREF (klass);
return ret;
}
/**
* range_to_python
* @v value union
*
* Converts a cell range to Python. Returns owned reference to python object. */
static PyObject *
range_to_python (Value *v)
{
PyObject *mod, *klass = NULL, *ret = NULL;
mod = PyImport_ImportModule ((char *) GNUMERIC_DEFS_MODULE);
if (mod == NULL)
return NULL;
klass = PyObject_GetAttrString (mod, (char *) CELL_RANGE_CLASS);
if (klass == NULL)
return NULL;
/* FIXME : Support inverted ranges */
ret = PyObject_CallFunction
(klass, (char *) "O&O&",
cell_ref_to_python, &v->v_range.cell.a,
cell_ref_to_python, &v->v_range.cell.b);
Py_DECREF (klass);
return ret;
}
/**
* boolean_to_python
* @v value union
*
* Converts a boolean to Python. Returns owned reference to python object.
* gnumeric_defs.FALSE and gnumeric_defs.TRUE are pre-initialized instances of
* the class gnumeric_defs.Boolean.
*/
static PyObject *
boolean_to_python (Value *v)
{
PyObject *mod;
mod = PyImport_ImportModule ((char *) GNUMERIC_DEFS_MODULE);
if (mod == NULL)
return NULL;
return PyObject_GetAttrString
(mod, (char *) (v->v_bool.val ? "TRUE" : "FALSE"));
}
/**
* row_to_python
* @v value union
* @i column index
*
* Converts an array row to Python.
* Returns owned reference to python object.
*/
static PyObject *
row_to_python (Value *v, int j)
{
PyObject * list = NULL, *o = NULL;
int cols, i;
cols = v->v_array.x;
list = PyList_New (cols);
if (list == NULL)
return NULL;
for (i = 0; i < cols; i++) {
o = value_to_python (v->v_array.vals[i][j]);
if (o == NULL) {
Py_DECREF (list);
return NULL;
}
PyList_SetItem (list, i, o);
}
return list;
}
/**
* array_to_python
* @v value union
*
* Converts an array to Python. Returns owned reference to python object.
*
* User visible array notation in Gnumeric: Row n, col m is [n][m]
* In C, the same elt is v->v_array.vals[m][n]
* For scripting, I think it's best to do it the way the user sees it,
* i.e. opposite from C.
*/
static PyObject *
array_to_python (Value *v)
{
PyObject * list = NULL, *row = NULL;
int rows, j;
rows = v->v_array.y;
list = PyList_New (rows);
if (list == NULL)
return NULL;
for (j = 0; j < rows; j++) {
row = row_to_python (v, j);
if (row == NULL) {
Py_DECREF (list);
return NULL;
}
PyList_SetItem (list, j, row);
}
return list;
}
/**
* value_to_python
* @v value union
*
* Converts a Gnumeric value to Python. Returns owned reference to python
* object.
*
* VALUE_ERROR is not handled here. It is not possible to receive VALUE_ERROR
* as a parameter to a function. But a C function may return VALUE_ERROR. In
* this case, the caller an exception is raised before value_to_python is
* called. */
static PyObject *
value_to_python (Value *v)
{
PyObject *o;
switch (v->type) {
case VALUE_INTEGER:
o = PyInt_FromLong (v->v_int.val);
break;
case VALUE_FLOAT:
o = PyFloat_FromDouble (v->v_float.val);
break;
case VALUE_STRING:
o = PyString_FromString (v->v_str.val->str);
break;
case VALUE_CELLRANGE:
o = range_to_python (v);
break;
case VALUE_EMPTY:
Py_INCREF (Py_None);
o = Py_None;
break;
case VALUE_BOOLEAN:
o = boolean_to_python (v);
break;
case VALUE_ARRAY:
o = array_to_python (v);
break;
case VALUE_ERROR: /* See comment */
default:
o = NULL;
break;
}
return o;
}
/**
* boolean_check
* @o Python object
*
* Returns TRUE if object is an instance of gnumeric_defs.Boolean and
* FALSE otherwise.
* I don't believe this is 100% kosher, but should work in practice. A 100%
* solution seems to require that the boolean class be defined in C.
*/
static int
boolean_check (PyObject *o)
{
PyObject *klass;
gchar *s;
if (!PyObject_HasAttrString (o, (char *) "__class__"))
return FALSE;
klass = PyObject_GetAttrString (o, (char *) "__class__");
s = PyString_AsString (PyObject_Str (klass));
Py_XDECREF (klass);
return (s != NULL &&
strcmp (s, (GNUMERIC_DEFS_MODULE "." BOOLEAN_CLASS)) == 0);
}
/**
* boolean_from_python
* @o Python object
*
* Converts an instance of gnumeric_defs.Boolean to Gnumeric boolean.
* Returns Gnumeric value on success, NULL on failure.
*/
static Value *
boolean_from_python (PyObject *o)
{
PyObject *ret;
Value *v;
if (!(ret = PyObject_CallMethod (o, (char *) "__nonzero__", NULL)))
return NULL;
v = value_new_bool (PyInt_AsLong (ret) ? TRUE : FALSE);
Py_DECREF (ret);
return v;
}
/**
* range_check
* @o Python object
*
* Returns TRUE if object is an instance of gnumeric_defs.CellRange and FALSE
* otherwise.
* I don't believe this is 100% kosher, but should work in practice. A 100 %
* solution seems to require that the cell range and cell ref classes are
* defined in C.
*/
static int
range_check (PyObject *o)
{
PyObject *klass;
gchar *s;
if (!PyObject_HasAttrString (o, (char *) "__class__"))
return FALSE;
klass = PyObject_GetAttrString (o, (char *) "__class__");
s = PyString_AsString (PyObject_Str (klass));
Py_XDECREF (klass);
return (s != NULL &&
strcmp (s, (GNUMERIC_DEFS_MODULE "." CELL_RANGE_CLASS)) == 0);
}
/**
* cell_ref_from_python
* @o Python object
* @c Cell reference
*
* Converts a Python cell reference to Gnumeric. Returns TRUE on success and
* FALSE on failure.
* Used as a converter function by PyArg_ParseTuple.
*/
static int
cell_ref_from_python (PyObject *o, CellRef *c)
{
int ret = FALSE;
PyObject *column = NULL, *row = NULL;
PyObject *col_relative = NULL, *row_relative = NULL/*, *sheet = NULL */;
column = PyObject_GetAttrString (o, (char *) "column");
if (!column || !PyInt_Check (column))
goto cleanup;
row = PyObject_GetAttrString (o, (char *) "row");
if (!row || !PyInt_Check (row))
goto cleanup;
col_relative = PyObject_GetAttrString (o, (char *) "col_relative");
if (!col_relative || !PyInt_Check (col_relative))
goto cleanup;
row_relative = PyObject_GetAttrString (o, (char *) "row_relative");
if (!row_relative || !PyInt_Check (row_relative))
goto cleanup;
/* sheet = PyObject_GetAttrString (o, (char *) "sheet"); */
/* if (!sheet || !PyString_Check (sheet) */
/* goto cleanup; */
c->col = (int) PyInt_AsLong (column);
c->row = (int) PyInt_AsLong (row);
c->col_relative = (unsigned char) PyInt_AsLong (col_relative);
c->row_relative = (unsigned char) PyInt_AsLong (row_relative);
c->sheet = NULL; /* = string_get (PyString_AsString (sheet)); */
ret = TRUE;
cleanup:
Py_XDECREF (column);
Py_XDECREF (row);
Py_XDECREF (col_relative);
Py_XDECREF (row_relative);
/* Py_XDECREF (sheet); */
return ret;
}
/**
* range_from_python
* @o Python object
*
* Converts a Python cell range to Gnumeric. Returns Gnumeric value on success,
* NULL on failure.
*/
static Value *
range_from_python (PyObject *o, EvalPos const *pos)
{
PyObject *range = NULL;
CellRef a, b;
Value *ret = NULL;
if ((range = PyObject_GetAttrString (o, (char *) "range")) == NULL)
return NULL;
if (!PyArg_ParseTuple (range, (char *) "O&O&",
cell_ref_from_python, &a,
cell_ref_from_python, &b))
goto cleanup;
ret = value_new_cellrange (&a, &b, pos->eval.col, pos->eval.row);
cleanup:
Py_DECREF (range);
return ret;
}
/**
* array_check
* @o Python object
*
* Returns TRUE if object is a list of lists and FALSE otherwise
*/
static int
array_check (PyObject *o)
{
PyObject *item;
if (!PyList_Check (o))
return FALSE;
else if (PyList_Size == 0)
return FALSE;
else if ((item = PyList_GetItem (o, 0)) == NULL)
return FALSE;
else if (!PyList_Check (item))
return FALSE;
else
return TRUE;
}
/**
* row_from_python
* @o python list
* @rowno rowno
* @array array
*
* Converts a Python list object to array row. Returns 0 on success, -1
* on failure.
*
* Row n, col m is [n][m]. */
static int
row_from_python (PyObject *o, int rowno, Value *array,
EvalPos const *pos)
{
PyObject *item;
int i;
int cols = array->v_array.x;
for (i = 0; i < cols; i++) {
if ((item = PyList_GetItem (o, i)) == NULL)
return -1;
array->v_array.vals[i][rowno] =
value_from_python (item, pos);
}
return 0;
}
/**
* array_from_python
* @o python sequence
*
* Converts a Python sequence object to array. Returns Gnumeric value on
* success, NULL on failure.
*
* Row n, col m is [n][m].
*/
static Value *
array_from_python (PyObject *o, EvalPos const *pos)
{
Value *v = NULL, *array = NULL;
PyObject *row = NULL;
int rows, cols, j;
rows = PyList_Size (o);
for (j = 0; j < rows; j++) {
if ((row = PyList_GetItem (o, j)) == NULL)
goto cleanup;
if (!PyList_Check (row)) {
PyErr_SetString (PyExc_TypeError, "Sequence expected");
goto cleanup;
}
cols = PyList_Size (row);
if (j == 0) {
array = value_new_array (cols, rows);
} else if (cols != array->v_array.x) {
PyErr_SetString (PyExc_TypeError,
"Rectangular array expected");
goto cleanup;
}
if ((row_from_python (row, j, array, pos)) != 0)
goto cleanup;
}
v = array;
cleanup:
if (array && array != v)
value_release (array);
return v;
}
/**
* value_from_python
* @o Python object
*
* Converts a Python object to a Gnumeric value. Returns Gnumeric value, or
* NULL on failure.
*/
static Value *
value_from_python (PyObject *o, EvalPos const *pos)
{
Value *v = NULL;
if (o == Py_None) {
v = value_new_empty ();
} else if (PyInt_Check (o)){
v = value_new_int ((int) PyInt_AsLong (o));
} else if (PyFloat_Check (o)) {
v = value_new_float ((gnum_float) PyFloat_AsDouble (o));
} else if (PyString_Check (o)) {
v = value_new_string (PyString_AsString (o));
} else if (boolean_check (o)) {
v = boolean_from_python (o);
} else if (array_check (o)) {
v = array_from_python (o, pos);
} else if (range_check (o)) {
v = range_from_python (o, pos);
} else {
PyErr_SetString (PyExc_TypeError, _("Unknown Python type"));
}
return v;
}
typedef struct {
FunctionDefinition *fndef;
FunctionCategory *category;