Learn how you to extend PyFM with your own functions.
A FileMaker plug-in is a dynamically loadable library that receives calls from FileMaker and may call FileMaker back. From the user point of view a plug-in extends FileMaker with one or more functions.
The PyFM plug-in structure is different from a typical FileMaker plug-in: In addition to the usual dynamic library code it has a Zip archive at the end. The Zip archive stores files written in Python language and the code part includes a built-in Python interpreter that is set up to look for its library in this archive.
All this means that you can write your own Python modules, add them to this Zip archive, and they will become a part of the plug-in.
If you peek into the archive you'll see that most files are .pyc
files (compiled Python bytecode) as oppposed to .py
(actual Python
sources). This is a pragmatic decision to speed up code loading and save
space in the archive. Plain-text .py
sources will work just fine.
To access an arbitrary Python object the plug-in provides a glue function: PyRun. The function takes at least two arguments: the name of the Python module and the name of the object in the module. For example:
PyRun( "foo", "bar" )
This call will try to find the foo
module and then the bar
object
inside the module (foo.bar
). If the found Python object is callable, the
function will call the object; also, if it received more than two arguments,
it will pass the rest of them as parameters for the call. For example:
PyRun( "foo", "bar", 123, Table::Field )
This call will find the foo.bar
object and call it with parameters 123
and the contents of Table::Field
.
If the object is not callable, PyRun will return it as is and ignore the arguments.
The called Python function can call other Python modules and, if necessary,
access the FileMaker plug-in API through the built-in filemaker
module.
You need to write a plain Python function. It may accept a fixed or arbitrary number of arguments, as it's common in Python:
def foo(a, b, c): # exactly three arguments pass def bar(a, b, c, d=42) # three or four arguments pass def baz(a, b, c, d=42, *args): # from three to unlimited arguments pass
You can provide a default value for an argument, but otherwise you cannot use keywords, because FileMaker has no idea of keywords.
In your function you can make calls to other Python modules (including your
own) and use the filemaker
module to work with the FileMaker plug-in API.
At the end you'll probably return a result. There's also a way to conveniently
set FileMaker variables upon return.
If your function specifies parameters, it will get instances of FileMaker
types. In Python these are members of the filemaker
module: Text
,
Number
, Date
, Time
, Timestamp
, and Container
:
import filemaker def foo(bar): return "The type of bar is %s." % type(bar).__name__
An important thing about function parameters is that they are read-only and have limited lifetime.
Normally FileMaker data types are modifiable; you can create a Text
instance and then alter it in-place; function parameters are not like that,
you can only read them. If you need to modify the value of the parameter,
make a copy:
import filemaker def foo(bar): return "Uppercase bar is %s" % filemaker.Text(bar).upper()
Function parameters are not unique in being read-only; everything you receive from FileMaker is read-only (see below about calling back to FileMaker).
In addition to being read-only function parameters also have limited lifetime: they only stay valid until you return control back to FileMaker. If you need to keep the parameter around between calls, make a copy:
import filemaker def foo(bar): result = u"Bar is %s and the last bar was %s" % (bar, last_bar) last_bar = filemaker.Text(bar) return result last_bar = None
Your function can make calls back to FileMaker using the evaluate
function
and the execute_sql
family of functions. All results you'll get from
these calls are read-only; usually it's one of primary data types, except the
execute_sql_2
function that returns a Dataset
that consists of
Datarows
that, in turn, consist of primary data types, all read-only. You
can also place a call to a script to the call stack; the script will run after
your function returns.
Your function may return instances of main filemaker
types (Text
,
Number
, Date
, Time
, Timestamp
, or Container
) or compatible
Python types: str
, unicode
, int
, long
, float
, and types of
the datetime
module: date
, time
, datetime
, and timedelta
.
You can also return bool
and None
or not return anything at all (in
this case Python will return None
itself).
Note that as of now there's no compatible Python type for
filemaker
Container
; you have to use the native type.
For example:
from filemaker import Container def foo(): return Container(FNAM='file.ext', FILE='abc def ghi')
Here we create a new filemaker
Container
, set its displayed file name
to file.ext
and file data to abc def ghi
. (In general case file data
are a binary stream; here it's a binary stream of a plain text file.)
In addition to a single result your function may also set FileMaker variables upon return. To do this return more than one result like that:
def foo(): return 42, '$foo', 'Foo', '$bar', ('Bar 1', 'Bar 2')
Here the direct result of the function will be 42
and it will also set
variables: $foo
to "Foo"
(the first repetition), $bar[1]
to
"Bar 1"
, and $bar[2]
to "Bar 2"
.
PyFM embeds Python v2.7.11, a good part of the standard library, and some third-party modules. (The full list of included modules is yet to come.) Some of the standard modules are not yet there because they are C extensions and it's a bit more cumbersome to embed them; some are just not applicable (such as Sun OS modules, of course, or Tcl/Tk modules that are not designed to be used from inside a DLL).