Phil Dibowitz wrote:
>...
> libconcord/bindings/python/libconcord.py
>
> Stephen - can you please do a code review on this? I'm actually learning
> python, but I'm not yet qualified to do a code review.
Well, the original patch has fallen out of my mailbox...
>From what I recall, when Andreas first submitted his patch, I had some
issues with the way the debug/tracing code was integrated. I since went
back and implemented it in a much more generic way, and have also
updated the Python bindings to match the new IR learning APIs.
Attached is a patch that should update just the Python bindings to match
the new APIs, and add the new tracing code at the same time. I'd suggest
merging this instead of the bindings changes in Andreas' patch.
A related point, re: changing the API. You had mentioned that Andreas'
patch shouldn't change the ABI number (DLL version), but you'd do it
before each release where the ABI version changed. Doesn't it make more
sense to bump either when the first ABI change is made after a release,
or even each time a change is made? With this policy, we don't get:
release N, ABI version X
CVS changes made to ABI, ABI still version X
final CVS change before release, ABI version now X+1
release N+1, ABI version X+1
Sure, in the above, each release has a sane ABI number, but if people
are developing/running applications against CVS, the ABI number doesn't
make quite as much sense.
Index: libconcord/bindings/python/README
===================================================================
RCS file: /cvsroot/concordance/concordance/libconcord/bindings/python/README,v
retrieving revision 1.1
diff -u -p -r1.1 README
--- libconcord/bindings/python/README 15 Apr 2008 02:34:08 -0000 1.1
+++ libconcord/bindings/python/README 29 Aug 2008 03:38:24 -0000
@@ -32,3 +32,14 @@ the bindings to your PYTHONPATH. For exa
export PYTHONPATH=/path/to/libconcord/bindings/python
+Debugging
+====================
+
+If you set the environment variable LIBCONCORD_PY_TRACE to string "1", then
+libconcord.py will trace all function calls. This may prove helpful when
+debugging client applications.
+
+For example, in bash:
+
+LIBCONCORD_PY_TRACE=1 ./congruity /path/to/Connectivity.EZHex
+
Index: libconcord/bindings/python/libconcord.py
===================================================================
RCS file: /cvsroot/concordance/concordance/libconcord/bindings/python/libconcord.py,v
retrieving revision 1.1
diff -u -p -r1.1 libconcord.py
--- libconcord/bindings/python/libconcord.py 8 Apr 2008 09:50:29 -0000 1.1
+++ libconcord/bindings/python/libconcord.py 29 Aug 2008 03:38:24 -0000
@@ -1,5 +1,5 @@
#
-# vi: formatoptions+=tc textwidth=80 tabstop=8 shiftwidth=8 noexpandtab:
+# vi: formatoptions+=tc textwidth=80 tabstop=8 shiftwidth=4 expandtab:
#
# $Id: libconcord.py,v 1.1 2008/04/08 09:50:29 jaymzh Exp $
#
@@ -18,19 +18,56 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
-
from ctypes import *
import platform
+import os
import sys
+import traceback
+
+# Debug request parsing
+
+debug = (os.environ.get("LIBCONCORD_PY_TRACE", "0") == "1")
+
+# Define the libconcord ABI this libconcord.py corresponds to
+# Bump this when the .so file version gets bumped
+
+ABI_VERSION = 0
+
+# Load the DLL
-# Internal DLL handle
if platform.system() == 'Windows':
_dll = cdll.LoadLibrary('libconcord.dll')
else:
- _dll = cdll.LoadLibrary('libconcord.so.0')
+ _dll = cdll.LoadLibrary('libconcord.so.' + str(ABI_VERSION))
+
+# Public libconcord API: Custom types
+
+# typedef void (*lc_callback)(uint32_t, uint32_t, uint32_t, void*);
+callback_type = CFUNCTYPE(None, c_uint, c_uint, c_uint, py_object)
+
+# Public libconcord API: Error codes
+
+LC_ERROR = 1
+LC_ERROR_INVALID_DATA_FROM_REMOTE = 2
+LC_ERROR_READ = 3
+LC_ERROR_WRITE = 4
+LC_ERROR_INVALIDATE = 5
+LC_ERROR_ERASE = 6
+LC_ERROR_VERIFY = 7
+LC_ERROR_POST = 8
+LC_ERROR_GET_TIME = 9
+LC_ERROR_SET_TIME = 10
+LC_ERROR_CONNECT = 11
+LC_ERROR_OS = 12
+LC_ERROR_OS_NET = 13
+LC_ERROR_OS_FILE = 14
+LC_ERROR_UNSUPP = 15
+LC_ERROR_INVALID_CONFIG = 16
+
+# Public libconcord API: Exception types
-# This exception may be raised by any function that returns an LC_ERROR_*.
class LibConcordException(Exception):
+ """This exception may be raised by any function that returns an LC_ERROR_*."""
def __init__(self, func, result):
self.func = func
self.result = result
@@ -46,7 +83,48 @@ class LibConcordException(Exception):
repr(self.result_str)
)
+# Internal helpers for parameter prototyping
+
+class _param(object):
+ def __init__(self, name, ctypes_type):
+ self.name = name
+ self.ctypes_type = ctypes_type
+
+class _in(_param):
+ def real_ctypes_type(self):
+ return self.ctypes_type
+
+class _out(_param):
+ def real_ctypes_type(self):
+ return POINTER(self.ctypes_type)
+
+# Internal helpers for parameter tracing
+
+def _dump_simple(type):
+ def _dump_simple_imp(value):
+ if not isinstance(value, type):
+ value = type(value)
+ return repr(value.value)
+ return _dump_simple_imp
+
+def _dump_pointer(name):
+ def _dump_pointer_imp(value):
+ return "<<%s @0x%08x>>" % (name, addressof(value))
+ return _dump_pointer_imp
+
+_dumpers = {
+ c_char_p: _dump_simple(c_char_p),
+ c_int: _dump_simple(c_int),
+ c_long: _dump_simple(c_long),
+ c_uint: _dump_simple(c_uint),
+ c_ulong: _dump_simple(c_ulong),
+ POINTER(c_ubyte): _dump_pointer('buffer'),
+ callback_type: _dump_pointer('function'),
+ py_object: _dump_simple(py_object),
+}
+
# Internal function result checking functionality
+
class _CheckRetCode(object):
def __init__(self, func_name):
self.func_name = func_name
@@ -55,21 +133,88 @@ class _CheckRetCode(object):
result = args[0]
if result != 0:
raise LibConcordException(self.func_name, result)
-
+
+# Internal function tracing functionality
+
+_byref_type = type(byref(c_int()))
+def from_param(obj):
+ if type(obj) == _byref_type:
+ return obj._obj
+ if isinstance(obj, _Pointer):
+ return obj.contents
+ raise TypeError, "Output parameters should be byref/pointers"
+
+class _DebugWrapper(object):
+ def __init__(self, callable):
+ self.callable = callable
+
+ def __call__(self, *args):
+ sys.stdout.write("libconcord." + self.callable.__name__ + "(")
+ delim = ""
+ for (val, proto) in zip(args, self.callable._lcpy_protos):
+ if isinstance(proto, _param):
+ name = proto.name
+ if isinstance(proto, _in):
+ try:
+ type = proto.ctypes_type
+ value = _dumpers[type](val)
+ except:
+ value = "???"
+ else:
+ value = "<<out>>"
+ else:
+ name = "???"
+ value = "???"
+ sys.stdout.write("%s%s=%s" % (delim, name, value))
+ delim = ", "
+ print ")"
+ try:
+ ret = self.callable(*args)
+ print " Returned: " + repr(ret)
+ for (val, proto) in zip(args, self.callable._lcpy_protos):
+ if not isinstance(proto, _out):
+ continue
+ name = proto.name
+ type = proto.ctypes_type
+ try:
+ value = _dumpers[type](from_param(val))
+ except:
+ value = "???"
+ print " <<out>> %s=%s" % (name, value)
+ return ret
+ except:
+ print " Threw: "
+ s = traceback.format_exc()
+ for sl in s.split('\n'):
+ print " " + sl
+ raise
+
# Internal ctypes function wrapper creation
+
def _create_func(
func_name,
error_checker,
- *args
+ rettype,
+ *protos
):
- ftype = CFUNCTYPE(*args)
+ def real_ctypes_type(type):
+ if not isinstance(type, _param):
+ return type
+ return type.real_ctypes_type()
+ real_protos = [real_ctypes_type(x) for x in protos]
+ ftype = CFUNCTYPE(rettype, *real_protos)
f = ftype((func_name, _dll))
+ f._lcpy_rettype = rettype
+ f._lcpy_protos = protos
+ f.__name__ = func_name
if error_checker:
f.errcheck = error_checker(func_name)
+ if debug:
+ f = _DebugWrapper(f)
+ f.__name__ = func_name
return f
-# typedef void (*lc_callback)(uint32_t, uint32_t, uint32_t, void*);
-callback_type = CFUNCTYPE(None, c_uint, c_uint, c_uint, py_object)
+# Public libconcord API: Function prototypes
# const char *get_mfg();
get_mfg = _create_func(
@@ -241,7 +386,7 @@ get_serial = _create_func(
'get_serial',
None,
c_char_p,
- c_int
+ _in('p', c_int)
)
# int get_config_bytes_used();
@@ -321,12 +466,12 @@ get_time_timezone = _create_func(
c_char_p
)
-# int delete_blob(uint8_t *ptr);
+# void delete_blob(uint8_t *ptr);
delete_block = _create_func(
'delete_blob',
- _CheckRetCode,
+ None,
c_int,
- POINTER(c_ubyte)
+ _in('ptr', POINTER(c_ubyte))
);
LC_FILE_TYPE_CONNECTIVITY = 0
@@ -339,9 +484,9 @@ identify_file = _create_func(
'identify_file',
_CheckRetCode,
c_int,
- POINTER(c_ubyte),
- c_uint,
- POINTER(c_int)
+ _in('in', POINTER(c_ubyte)),
+ _in('size', c_uint),
+ _out('type', c_int)
)
# int init_concord();
@@ -363,8 +508,8 @@ get_identity = _create_func(
'get_identity',
_CheckRetCode,
c_int,
- callback_type,
- py_object
+ _in('cb', callback_type),
+ _in('cb_arg', py_object)
)
# int reset_remote();
@@ -393,8 +538,8 @@ post_connect_test_success = _create_func
'post_connect_test_success',
_CheckRetCode,
c_int,
- POINTER(c_ubyte),
- c_uint
+ _in('data', POINTER(c_ubyte)),
+ _in('size', c_uint)
)
# int post_preconfig(uint8_t *data, uint32_t size);
@@ -402,8 +547,8 @@ post_preconfig = _create_func(
'post_preconfig',
_CheckRetCode,
c_int,
- POINTER(c_ubyte),
- c_uint
+ _in('data', POINTER(c_ubyte)),
+ _in('size', c_uint)
)
# int post_postconfig(uint8_t *data, uint32_t size);
@@ -411,8 +556,8 @@ post_postconfig = _create_func(
'post_postconfig',
_CheckRetCode,
c_int,
- POINTER(c_ubyte),
- c_uint
+ _in('data', POINTER(c_ubyte)),
+ _in('size', c_uint)
)
# int post_postfirmware(uint8_t *data, uint32_t size);
@@ -420,8 +565,8 @@ post_postfirmware = _create_func(
'post_postfirmware',
_CheckRetCode,
c_int,
- POINTER(c_ubyte),
- c_uint
+ _in('data', POINTER(c_ubyte)),
+ _in('size', c_uint)
)
# int invalidate_flash();
@@ -437,10 +582,10 @@ read_config_from_remote = _create_func(
'read_config_from_remote',
_CheckRetCode,
c_int,
- POINTER(POINTER(c_ubyte)),
- POINTER(c_uint),
- callback_type,
- py_object
+ _out('out', POINTER(c_ubyte)),
+ _out('size', c_uint),
+ _in('cb', callback_type),
+ _in('cb_arg', py_object)
)
# int write_config_to_remote(uint8_t *in, uint32_t size,
@@ -449,10 +594,10 @@ write_config_to_remote = _create_func(
'write_config_to_remote',
_CheckRetCode,
c_int,
- POINTER(c_ubyte),
- c_uint,
- callback_type,
- py_object
+ _in('in', POINTER(c_ubyte)),
+ _in('size', c_uint),
+ _in('cb', callback_type),
+ _in('cb_arg', py_object)
)
# int read_file(char *file_name, uint8_t **out, uint32_t *size);
@@ -460,9 +605,9 @@ read_file = _create_func(
'read_file',
_CheckRetCode,
c_int,
- c_char_p,
- POINTER(POINTER(c_ubyte)),
- POINTER(c_uint)
+ _in('file_name', c_char_p),
+ _out('out', POINTER(c_ubyte)),
+ _out('size', c_uint)
)
# int write_config_to_file(uint8_t *in, uint32_t size, char *file_name,
@@ -471,10 +616,10 @@ write_config_to_file = _create_func(
'write_config_to_file',
_CheckRetCode,
c_int,
- POINTER(c_ubyte),
- c_uint,
- c_char_p,
- c_int
+ _in('in', POINTER(c_ubyte)),
+ _in('size', c_uint),
+ _in('file_name', c_char_p),
+ _in('binary', c_int)
)
# int verify_remote_config(uint8_t *in, uint32_t size, lc_callback cb,
@@ -483,10 +628,10 @@ verify_remote_config = _create_func(
'verify_remote_config',
_CheckRetCode,
c_int,
- POINTER(c_ubyte),
- c_uint,
- callback_type,
- py_object
+ _in('in', POINTER(c_ubyte)),
+ _in('size', c_uint),
+ _in('cb', callback_type),
+ _in('cb_arg', py_object)
)
# int erase_config(uint32_t size, lc_callback cb, void *cb_arg);
@@ -494,9 +639,9 @@ erase_config = _create_func(
'erase_config',
_CheckRetCode,
c_int,
- c_uint,
- callback_type,
- py_object
+ _in('size', c_uint),
+ _in('cb', callback_type),
+ _in('cb_arg', py_object)
)
# int find_config_binary(uint8_t *config, uint32_t config_size,
@@ -505,10 +650,10 @@ find_config_binary = _create_func(
'find_config_binary',
_CheckRetCode,
c_int,
- POINTER(c_ubyte),
- c_uint,
- POINTER(POINTER(c_ubyte)),
- POINTER(c_uint)
+ _in('config', POINTER(c_ubyte)),
+ _in('config_size', c_uint),
+ _out('binary_ptr', POINTER(c_ubyte)),
+ _out('binary_size', c_uint)
)
# int erase_safemode(lc_callback cb, void *cb_arg);
@@ -516,8 +661,8 @@ erase_safemode = _create_func(
'erase_safemode',
_CheckRetCode,
c_int,
- callback_type,
- py_object
+ _in('cb', callback_type),
+ _in('cb_arg', py_object)
)
# int read_safemode_from_remote(uint8_t **out, uint32_t *size, lc_callback cb,
@@ -526,20 +671,20 @@ read_safemode_from_remote = _create_func
'read_safemode_from_remote',
_CheckRetCode,
c_int,
- POINTER(POINTER(c_ubyte)),
- POINTER(c_ubyte),
- callback_type,
- py_object
+ _out('out', POINTER(c_ubyte)),
+ _out('size', c_ubyte),
+ _in('cb', callback_type),
+ _in('cb_arg', py_object)
)
-# int write_safemode_to_file(uint8_t *in, uint32_t size,char *file_name);
+# int write_safemode_to_file(uint8_t *in, uint32_t size, char *file_name);
write_safemode_to_file = _create_func(
'write_safemode_to_file',
_CheckRetCode,
c_int,
- POINTER(c_ubyte),
- c_uint,
- c_char_p
+ _in('in', POINTER(c_ubyte)),
+ _in('size', c_uint),
+ _in('file_name', c_char_p)
)
# int is_fw_update_supported(int direct);
@@ -547,7 +692,7 @@ is_fw_update_supported = _create_func(
'is_fw_update_supported',
None,
c_int,
- c_int
+ _in('direct', c_int)
)
# int is_config_safe_after_fw();
@@ -576,9 +721,9 @@ erase_firmware = _create_func(
'erase_firmware',
_CheckRetCode,
c_int,
- c_int,
- callback_type,
- py_object
+ _in('direct', c_int),
+ _in('cb', callback_type),
+ _in('cb_arg', py_object)
)
# int read_firmware_from_remote(uint8_t **out, uint32_t *size, lc_callback cb,
@@ -587,10 +732,10 @@ read_firmware_from_remote = _create_func
'read_firmware_from_remote',
_CheckRetCode,
c_int,
- POINTER(POINTER(c_ubyte)),
- POINTER(c_uint),
- callback_type,
- py_object
+ _out('out', POINTER(c_ubyte)),
+ _out('size', c_uint),
+ _in('cb', callback_type),
+ _in('cb_arg', py_object)
)
# int write_firmware_to_remote(uint8_t *in, uint32_t size, int direct,
@@ -599,11 +744,11 @@ write_firmware_to_remote = _create_func(
'write_firmware_to_remote',
_CheckRetCode,
c_int,
- POINTER(c_ubyte),
- c_uint,
- c_int,
- callback_type,
- py_object
+ _in('in', POINTER(c_ubyte)),
+ _in('size', c_uint),
+ _in('direct', c_int),
+ _in('cb', callback_type),
+ _in('cb_arg', py_object)
)
# int write_firmware_to_file(uint8_t *in, uint32_t size, char *file_name,
@@ -612,10 +757,10 @@ write_firmware_to_file = _create_func(
'write_firmware_to_file',
_CheckRetCode,
c_int,
- POINTER(c_ubyte),
- c_uint,
- c_char_p,
- c_int
+ _in('in', POINTER(c_ubyte)),
+ _in('size', c_uint),
+ _in('file_name', c_char_p),
+ _in('binary', c_int)
)
# int extract_firmware_binary(uint8_t *xml, uint32_t xml_size, uint8_t **out,
@@ -624,19 +769,82 @@ extract_firmware_binary = _create_func(
'extract_firmware_binary',
_CheckRetCode,
c_int,
- POINTER(c_ubyte),
- c_uint,
- POINTER(POINTER(c_ubyte)),
- POINTER(c_uint)
+ _in('xml', POINTER(c_ubyte)),
+ _in('xml_size', c_uint),
+ _out('out', POINTER(c_ubyte)),
+ _out('size', c_uint)
)
-# int learn_ir_commands(uint8_t *data, uint32_t size, int post);
-learn_ir_commands = _create_func(
- 'learn_ir_commands',
+# int get_key_names(uint8_t *xml, uint32_t xml_size,
+# char ***key_names, uint32_t *key_names_length);
+get_key_names = _create_func(
+ 'get_key_names',
_CheckRetCode,
c_int,
- POINTER(c_ubyte),
- c_uint,
- c_int
+ _in('xml', POINTER(c_ubyte)),
+ _in('xml_size', c_uint),
+ _out('key_names', POINTER(c_char_p)),
+ _out('key_names_length', c_uint)
+)
+
+# void delete_key_names(char **key_names, uint32_t key_names_length);
+delete_key_names = _create_func(
+ 'delete_key_names',
+ None,
+ c_int,
+ _in('key_names', POINTER(c_char_p)),
+ _in('key_names_length', c_uint)
+)
+
+# int learn_from_remote(uint32_t *carrier_clock,
+# uint32_t **ir_signal, uint32_t *ir_signal_length);
+learn_from_remote = _create_func(
+ 'learn_from_remote',
+ None,
+ c_int,
+ _out('carrier_clock', c_uint),
+ _out('ir_signal', POINTER(c_uint)),
+ _out('ir_signal_length', c_uint)
+)
+
+# void delete_ir_signal(uint32_t *ir_signal);
+delete_ir_signal = _create_func(
+ 'delete_ir_signal',
+ None,
+ c_int,
+ _in('ir_signal', POINTER(c_uint))
+)
+
+# int encode_for_posting(uint32_t carrier_clock,
+# int32_t *ir_signal, uint32_t ir_signal_length,
+# char **encoded_signal);
+encode_for_posting = _create_func(
+ 'encode_for_posting',
+ None,
+ c_int,
+ _in('carrier_clock', c_uint),
+ _in('ir_signal', POINTER(c_uint)),
+ _in('ir_signal_length', c_uint),
+ _out('encoded_signal', c_char_p)
+)
+
+# void delete_encoded_signal(char *encoded_signal);
+delete_encoded_signal = _create_func(
+ 'delete_encoded_signal',
+ None,
+ c_int,
+ _in('encoded_signal', c_char_p),
+)
+
+# int post_new_code(uint8_t *data, uint32_t size,
+# char *key_name, char *encoded_signal);
+post_new_code = _create_func(
+ 'post_new_code',
+ None,
+ c_int,
+ _in('xml', POINTER(c_ubyte)),
+ _in('xml_size', c_uint),
+ _in('key_name', c_char_p),
+ _in('encoded_signal', c_char_p)
)
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
concordance-devel mailing list
concordance-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/concordance-devel