[PATCH evemu 4/4] py: Add and use wrapper for libevemu calls

Benjamin Tissoires benjamin.tissoires at gmail.com
Fri Jan 10 16:32:27 PST 2014


Hi Daniel,

On Fri, Jan 10, 2014 at 3:30 AM, Daniel Martin <consume.noise at gmail.com> wrote:
> Introduce a class LibEvemu in base.py, which wraps API calls to
> libevemu, and replace _call()s by using it.
> As this obsoletes the class EvEmuBase, it's removed as well.
>
> v2: Removed tests if functions can be found in the loaded library and
>     added tests that the loaded library is static in the class.
>
> Signed-off-by: Daniel Martin <consume.noise at gmail.com>
> ---
>  python/evemu/__init__.py        |  93 ++++++---------
>  python/evemu/base.py            | 254 +++++++++++++++++++++++++++++++++++++---
>  python/evemu/const.py           |  33 ------
>  python/evemu/tests/test_base.py |  29 ++++-
>  4 files changed, 292 insertions(+), 117 deletions(-)
>
> diff --git a/python/evemu/__init__.py b/python/evemu/__init__.py
> index f474036..8e725c7 100644
> --- a/python/evemu/__init__.py
> +++ b/python/evemu/__init__.py
> @@ -53,36 +53,28 @@ class Device(object):
>              raise TypeError("expected file or file name")
>
>          self._is_propfile = self._check_is_propfile(self._file)
> -        self._evemu = evemu.base.EvEmuBase()
>          self._libc = evemu.base.LibC()
> +        self._libevemu = evemu.base.LibEvemu()
>          self._uinput = None
>
> -        libevemu_new = self._evemu.get_lib().evemu_new
> -        libevemu_new.restype = ctypes.c_void_p
> -        self._evemu_device = libevemu_new("")
> +        self._evemu_device = self._libevemu.evemu_new(b"")
>
>          if self._is_propfile:
>              fs = self._libc.fdopen(self._file.fileno(), b"r")
> -            self._evemu._call(self._evemu.get_lib().evemu_read,
> -                              self._evemu_device,
> -                              fs)
> +            self._libevemu.evemu_read(self._evemu_device, fs)
>              self._uinput = os.open(evemu.const.UINPUT_NODE, os.O_WRONLY)
>              self._file = self._create_devnode()
>          else:
> -            self._evemu._call(self._evemu.get_lib().evemu_extract,
> -                             self._evemu_device,
> -                             self._file.fileno())
> +            self._libevemu.evemu_extract(self._evemu_device,
> +                                         self._file.fileno())
>
>      def __del__(self):
>          if hasattr(self, "_is_propfile") and self._is_propfile:
>              self._file.close()
> -            self._evemu._call(self._evemu.get_lib().evemu_destroy,
> -                              self._evemu_device, self._uinput)
> +            self._libevemu.evemu_destroy(self._evemu_device, self._uinput)
>
>      def _create_devnode(self):
> -        self._evemu._call(self._evemu.get_lib().evemu_create,
> -                          self._evemu_device,
> -                          self._uinput)
> +        self._libevemu.evemu_create(self._evemu_device, self._uinput)
>          return open(self._find_newest_devnode(self.name), 'r+b', buffering=0)
>
>      def _find_newest_devnode(self, target_name):
> @@ -129,9 +121,7 @@ class Device(object):
>              raise TypeError("expected file")
>
>          fs = self._libc.fdopen(prop_file.fileno(), b"w")
> -        self._evemu._call(self._evemu.get_lib().evemu_write,
> -                          self._evemu_device,
> -                          fs)
> +        self._libevemu.evemu_write(self._evemu_device, fs)
>          self._libc.fflush(fs)
>
>      def play(self, events_file):
> @@ -146,9 +136,7 @@ class Device(object):
>              raise TypeError("expected file")
>
>          fs = self._libc.fdopen(events_file.fileno(), b"r")
> -        self._evemu._call(self._evemu.get_lib().evemu_play,
> -                          fs,
> -                          self._file.fileno())
> +        self._libevemu.evemu_play(fs, self._file.fileno())
>
>      def record(self, events_file, timeout=10000):
>          """
> @@ -163,10 +151,7 @@ class Device(object):
>              raise TypeError("expected file")
>
>          fs = self._libc.fdopen(events_file.fileno(), b"w")
> -        self._evemu._call(self._evemu.get_lib().evemu_record,
> -                          fs,
> -                          self._file.fileno(),
> -                          timeout)
> +        self._libevemu.evemu_record(fs, self._file.fileno(), timeout)
>          self._libc.fflush(fs)
>
>      @property
> @@ -174,8 +159,7 @@ class Device(object):
>          """
>          Gets the version of the evemu library used to create the Device.
>          """
> -        return self._evemu._call(self._evemu.get_lib().evemu_get_version,
> -                                 self._evemu_device)
> +        return self._libevemu.evemu_get_version(self._evemu_device)
>
>      @property
>      def devnode(self):
> @@ -189,71 +173,60 @@ class Device(object):
>          """
>          Gets the name of the input device (as reported by the device).
>          """
> -        func = self._evemu.get_lib().evemu_get_name
> -        func.restype = ctypes.c_char_p
> -        return self._evemu._call(func, self._evemu_device)
> +        result = self._libevemu.evemu_get_name(self._evemu_device)
> +        return result.decode(evemu.const.ENCODING)
>
>      @property
>      def id_bustype(self):
>          """
>          Identifies the kernel device bustype.
>          """
> -        return self._evemu._call(self._evemu.get_lib().evemu_get_id_bustype,
> -                                 self._evemu_device)
> +        return self._libevemu.evemu_get_id_bustype(self._evemu_device)
>
>      @property
>      def id_vendor(self):
>          """
>          Identifies the kernel device vendor.
>          """
> -        return self._evemu._call(self._evemu.get_lib().evemu_get_id_vendor,
> -                                 self._evemu_device)
> +        return self._libevemu.evemu_get_id_vendor(self._evemu_device)
>
>      @property
>      def id_product(self):
>          """
>          Identifies the kernel device product.
>          """
> -        return self._evemu._call(self._evemu.get_lib().evemu_get_id_product,
> -                                 self._evemu_device)
> +        return self._libevemu.evemu_get_id_product(self._evemu_device)
>
>      @property
>      def id_version(self):
>          """
>          Identifies the kernel device version.
>          """
> -        return self._evemu._call(self._evemu.get_lib().evemu_get_id_version,
> -                                 self._evemu_device)
> +        return self._libevemu.evemu_get_id_version(self._evemu_device)
>
>      def get_abs_minimum(self, event_code):
> -        return self._evemu._call(self._evemu.get_lib().evemu_get_abs_minimum,
> -                                 self._evemu_device,
> -                                 int(event_code))
> +        return self._libevemu.evemu_get_abs_minimum(self._evemu_device,
> +                                                    event_code)
>
>      def get_abs_maximum(self, event_code):
> -        return self._evemu._call(self._evemu.get_lib().evemu_get_abs_maximum,
> -                                 self._evemu_device,
> -                                 event_code)
> +        return self._libevemu.evemu_get_abs_maximum(self._evemu_device,
> +                                                    event_code)
>
>      def get_abs_fuzz(self, event_code):
> -        return self._evemu._call(self._evemu.get_lib().evemu_get_abs_fuzz,
> -                                 self._evemu_device,
> -                                 event_code)
> +        return self._libevemu.evemu_get_abs_fuzz(self._evemu_device,
> +                                                 event_code)
>
>      def get_abs_flat(self, event_code):
> -        return self._evemu._call(self._evemu.get_lib().evemu_get_abs_flat,
> -                                 self._evemu_device,
> -                                 event_code)
> +        return self._libevemu.evemu_get_abs_flat(self._evemu_device,
> +                                                 event_code)
>
>      def get_abs_resolution(self, event_code):
> -        return self._evemu._call(self._evemu.get_lib().evemu_get_abs_resolution,
> -                                 self._evemu_device,
> -                                 event_code)
> +        return self._libevemu.evemu_get_abs_resolution(self._evemu_device,
> +                                                       event_code)
>
>      def has_prop(self, event_code):
> -        return bool(self._evemu._call(self._evemu.get_lib().evemu_has_prop,
> -                                      self._evemu_device,
> -                                      event_code))
> +        result = self._libevemu.evemu_has_prop(self._evemu_device, event_code)
> +        return bool(result)
>
>      def has_event(self, event_type, event_code):
>          """
> @@ -267,8 +240,8 @@ class Device(object):
>          used to simulate gestures for a higher number of touches than are
>          possible with just 2-touch hardware.
>          """
> -        return bool(self._evemu._call(self._evemu.get_lib().evemu_has_event,
> -                                      self._evemu_device,
> -                                      event_type,
> -                                      event_code))
> +        result = self._libevemu.evemu_has_event(self._evemu_device,
> +                                                event_type,
> +                                                event_code)
> +        return bool(result)
>
> diff --git a/python/evemu/base.py b/python/evemu/base.py
> index 884c861..b366e2f 100644
> --- a/python/evemu/base.py
> +++ b/python/evemu/base.py
> @@ -160,25 +160,243 @@ class LibC(LibraryWrapper):
>          }
>
>
> -class EvEmuBase(object):
> +class LibEvemu(LibraryWrapper):
>      """
> -    A base wrapper class for the evemu functions, accessed via ctypes.
> +    Wrapper for API calls to the evemu library.
>      """
> -    def __init__(self):
> -        self._lib = ctypes.CDLL(evemu.const.LIB, use_errno=True)
> -
> -    def _call(self, api_call, *parameters):
> -        result = api_call(*parameters)
> -        if result < 0 and self.get_c_errno() != 0:
> -            raise evemu.exception.ExecutionError("%s: %s" % (
> -                api_call.__name__, self.get_c_error()))
> -        return result
> -
> -    def get_c_errno(self):
> -        return ctypes.get_errno()
>
> -    def get_c_error(self):
> -        return os.strerror(ctypes.get_errno())
> +    @staticmethod
> +    def _cdll():
> +        return ctypes.CDLL(evemu.const.LIB, use_errno=True)
>
> -    def get_lib(self):
> -        return self._lib
> +    _api_prototypes = {
> +        #struct evemu_device *evemu_new(const char *name);
> +        "evemu_new": {
> +            "argtypes": (c_char_p,),
> +            "restype": c_void_p,
> +            "errcheck": expect_not_none
> +            },
> +        #void evemu_delete(struct evemu_device *dev);
> +        "evemu_delete": {
> +            "argtypes": (c_void_p,),
> +            "restype": None
> +            },
> +        #unsigned int evemu_get_version(const struct evemu_device *dev);
> +        "evemu_get_version": {
> +            "argtypes": (c_void_p,),
> +            "restype": c_uint,
> +            },
> +        #const char *evemu_get_name(const struct evemu_device *dev);
> +        "evemu_get_name": {
> +            "argtypes": (c_void_p,),
> +            "restype": c_char_p,
> +            "errcheck": expect_not_none
> +            },
> +        #void evemu_set_name(struct evemu_device *dev, const char *name);
> +        "evemu_set_name": {
> +            "argtypes": (c_void_p, c_char_p),
> +            "restype": None
> +            },
> +        #unsigned int evemu_get_id_bustype(const struct evemu_device *dev);
> +        "evemu_get_id_bustype": {
> +            "argtypes": (c_void_p,),
> +            "restype": c_uint
> +            },
> +        #void evemu_set_id_bustype(struct evemu_device *dev,
> +        #                          unsigned int bustype);
> +        "evemu_set_id_bustype": {
> +            "argtypes": (c_void_p, c_uint),
> +            "restype": None
> +            },
> +        #unsigned int evemu_get_id_vendor(const struct evemu_device *dev);
> +        "evemu_get_id_vendor": {
> +            "argtypes": (c_void_p,),
> +            "restype": c_uint
> +            },
> +        #void evemu_set_id_vendor(struct evemu_device *dev,
> +        #                         unsigned int vendor);
> +        "evemu_set_id_vendor": {
> +            "argtypes": (c_void_p, c_uint),
> +            "restype": None
> +            },
> +        #unsigned int evemu_get_id_product(const struct evemu_device *dev);
> +        "evemu_get_id_product": {
> +            "argtypes": (c_void_p,),
> +            "restype": c_uint
> +            },
> +        #void evemu_set_id_product(struct evemu_device *dev,
> +        #                          unsigned int product);
> +        "evemu_set_id_product": {
> +            "argtypes": (c_void_p, c_uint),
> +            "restype": None
> +            },
> +        #unsigned int evemu_get_id_version(const struct evemu_device *dev);
> +        "evemu_get_id_version": {
> +            "argtypes": (c_void_p,),
> +            "restype": c_uint
> +            },
> +        #void evemu_set_id_version(struct evemu_device *dev,
> +        #                          unsigned int version);
> +        "evemu_set_id_version": {
> +            "argtypes": (c_void_p, c_uint),
> +            "restype": None
> +            },
> +        #int evemu_get_abs_current_value(const struct evemu_device *dev,
> +        #                                int code);
> +        "evemu_get_abs_current_value": {
> +            "argtypes": (c_void_p, c_int),
> +            "restype": c_int,
> +            "errcheck": expect_ge_zero
> +            },
> +        #int evemu_get_abs_minimum(const struct evemu_device *dev, int code);
> +        "evemu_get_abs_minimum": {
> +            "argtypes": (c_void_p, c_int),
> +            "restype": c_int,
> +            "errcheck": expect_ge_zero
> +            },
> +        #void evemu_set_abs_minimum(struct evemu_device *dev, int code,
> +        #                           int min);
> +        "evemu_set_abs_minimum": {
> +            "argtypes": (c_void_p, c_int, c_int),
> +            "restype": None
> +            },
> +        #int evemu_get_abs_maximum(const struct evemu_device *dev, int code);
> +        "evemu_get_abs_maximum": {
> +            "argtypes": (c_void_p, c_int),
> +            "restype": c_int,
> +            "errcheck": expect_ge_zero
> +            },
> +        #void evemu_set_abs_maximum(struct evemu_device *dev, int code,
> +        #                           int max);
> +        "evemu_set_abs_maximum": {
> +            "argtypes": (c_void_p, c_int, c_int),
> +            "restype": None
> +            },
> +        #int evemu_get_abs_fuzz(const struct evemu_device *dev, int code);
> +        "evemu_get_abs_fuzz": {
> +            "argtypes": (c_void_p, c_int),
> +            "restype": c_int,
> +            "errcheck": expect_ge_zero
> +            },
> +        #void evemu_set_abs_fuzz(struct evemu_device *dev, int code, int fuzz);
> +        "evemu_set_abs_fuzz": {
> +            "argtypes": (c_void_p, c_int, c_int),
> +            "restype": None,
> +            },
> +        #int evemu_get_abs_flat(const struct evemu_device *dev, int code);
> +        "evemu_get_abs_flat": {
> +            "argtypes": (c_void_p, c_int),
> +            "restype": c_int,
> +            "errcheck": expect_ge_zero
> +            },
> +        #void evemu_set_abs_flat(struct evemu_device *dev, int code, int flat);
> +        "evemu_set_abs_flat": {
> +            "argtypes": (c_void_p, c_int, c_int),
> +            "restype": None
> +            },
> +        #int evemu_get_abs_resolution(const struct evemu_device *dev,
> +        #                             int code);
> +        "evemu_get_abs_resolution": {
> +            "argtypes": (c_void_p, c_int),
> +            "restype": c_int,
> +            "errcheck": expect_ge_zero
> +            },
> +        #void evemu_set_abs_resolution(struct evemu_device *dev, int code,
> +        #                              int res);
> +        "evemu_set_abs_resolution": {
> +            "argtypes": (c_void_p, c_int, c_int),
> +            "restype": None
> +            },
> +        #int evemu_has_prop(const struct evemu_device *dev, int code);
> +        "evemu_has_prop": {
> +            "argtypes": (c_void_p, c_int),
> +            "restype": c_int,
> +            "errcheck": expect_ge_zero
> +            },
> +        #int evemu_has_event(const struct evemu_device *dev, int type,
> +        #                    int code);
> +        "evemu_has_event": {
> +            "argtypes": (c_void_p, c_int, c_int),
> +            "restype": c_int,
> +            "errcheck": expect_ge_zero
> +            },
> +        #int evemu_has_bit(const struct evemu_device *dev, int type);
> +        "evemu_has_bit": {
> +            "argtypes": (c_void_p, c_int),
> +            "restype": c_int,
> +            "errcheck": expect_ge_zero
> +            },
> +        #int evemu_extract(struct evemu_device *dev, int fd);
> +        "evemu_extract": {
> +            "argtypes": (c_void_p, c_int),
> +            "restype": c_int,
> +            "errcheck": expect_eq_zero
> +            },
> +        #int evemu_write(const struct evemu_device *dev, FILE *fp);
> +        "evemu_write": {
> +            "argtypes": (c_void_p, c_void_p),
> +            "restype": c_int,
> +            "errcheck": expect_eq_zero
> +            },
> +        #int evemu_read(struct evemu_device *dev, FILE *fp);
> +        "evemu_read": {
> +            "argtypes": (c_void_p, c_void_p),
> +            "restype": c_int,
> +            "errcheck": expect_gt_zero
> +            },
> +        #int evemu_write_event(FILE *fp, const struct input_event *ev);
> +        "evemu_write_event": {
> +            "argtypes": (c_void_p, c_void_p),
> +            "restype": c_int,
> +            "errcheck": expect_gt_zero
> +            },
> +        #int evemu_create_event(struct input_event *ev, int type, int code,
> +        #                       int value);
> +        "evemu_create_event": {
> +            "argtypes": (c_void_p, c_int, c_int, c_int),
> +            "restype": c_int,
> +            "errcheck": expect_eq_zero
> +            },
> +        #int evemu_read_event(FILE *fp, struct input_event *ev);
> +        "evemu_read_event": {
> +            "argtypes": (c_void_p, c_void_p),
> +            "restype": c_int,
> +            "errcheck": expect_gt_zero
> +            },
> +        #int evemu_read_event_realtime(FILE *fp, struct input_event *ev,
> +        #                            struct timeval *evtime);
> +        "evemu_read_event_realtime": {
> +            "argtypes": (c_void_p, c_void_p, c_void_p),
> +            "restype": c_int,
> +            "errcheck": expect_gt_zero
> +            },
> +        #int evemu_record(FILE *fp, int fd, int ms);
> +        "evemu_record": {
> +            "argtypes": (c_void_p, c_int, c_int),
> +            "restype": c_int,
> +            "errcheck": expect_eq_zero
> +            },
> +        #int evemu_play_one(int fd, const struct input_event *ev);
> +        "evemu_play_one": {
> +            "argtypes": (c_int, c_void_p),
> +            "restype": c_int,
> +            "errcheck": expect_eq_zero
> +            },
> +        #int evemu_play(FILE *fp, int fd);
> +        "evemu_play": {
> +            "argtypes": (c_void_p, c_int),
> +            "restype": c_int,
> +            "errcheck": expect_eq_zero
> +            },
> +        #int evemu_create(struct evemu_device *dev, int fd);
> +        "evemu_create": {
> +            "argtypes": (c_void_p, c_int),
> +            "restype": c_int,
> +            "errcheck": expect_eq_zero
> +            },
> +        #void evemu_destroy(struct evemu_device *dev);
> +        "evemu_destroy": {
> +            "argtypes": (c_void_p,),
> +            "restype": None
> +            },
> +        }

Thanks for this job.
I would rather prefer having some kind of script which would generate
the dict for us. However, I don't think it is possible to generate
this automatically without spending hours.
How about adding a test during the build which would fail if
"src/libevemu.ver" and _api_prototypes are not matching (at least for
additions/removals). I am thinking of it because I just added 1
function to the API, and I did not updated the python wrapper. I guess
this will happen again in the future, and a failure during compile
would force us to update it accordingly.

Any thoughts on that?
I'll add this to my to-do list for next week I think.

Cheers,
Benjamin

> diff --git a/python/evemu/const.py b/python/evemu/const.py
> index 35f1878..19c507a 100644
> --- a/python/evemu/const.py
> +++ b/python/evemu/const.py
> @@ -2,39 +2,6 @@ LIB = "libevemu.so"
>  ENCODING="iso8859-1"
>  UINPUT_NODE = "/dev/uinput"
>
> -# The following should be examined every release of evemu
> -API = [
> -    "evemu_new",
> -    "evemu_delete",
> -    "evemu_extract",
> -    "evemu_write",
> -    "evemu_read",
> -    "evemu_write_event",
> -    "evemu_record",
> -    "evemu_read_event",
> -    "evemu_play",
> -    "evemu_create",
> -    "evemu_destroy",
> -    # Device settrs
> -    "evemu_set_name",
> -    # Device gettrs
> -    "evemu_get_version",
> -    "evemu_get_name",
> -    "evemu_get_id_bustype",
> -    "evemu_get_id_vendor",
> -    "evemu_get_id_product",
> -    "evemu_get_id_version",
> -    "evemu_get_abs_minimum",
> -    "evemu_get_abs_maximum",
> -    "evemu_get_abs_fuzz",
> -    "evemu_get_abs_flat",
> -    "evemu_get_abs_resolution",
> -    # Device hasers
> -    "evemu_has_prop",
> -    "evemu_has_event",
> -    "evemu_has_bit",
> -    ]
> -
>  event_types = {
>      "EV_SYN": 0x00,
>      "EV_KEY": 0x01,
> diff --git a/python/evemu/tests/test_base.py b/python/evemu/tests/test_base.py
> index 29bdb15..bc35c73 100644
> --- a/python/evemu/tests/test_base.py
> +++ b/python/evemu/tests/test_base.py
> @@ -7,12 +7,6 @@ import evemu.testing.testcase
>
>  class EvEmuBaseTestCase(evemu.testing.testcase.BaseTestCase):
>
> -    def test_so_library_found(self):
> -        wrapper = evemu.base.EvEmuBase()
> -        # Make sure that the library loads
> -        self.assertNotEqual(
> -            wrapper._lib._name.find("libevemu"), -1)
> -
>      def test_libc_found(self):
>          lib = evemu.base.LibC._load()
>          self.assertNotEqual(lib, None)
> @@ -36,5 +30,28 @@ class EvEmuBaseTestCase(evemu.testing.testcase.BaseTestCase):
>
>          self.assertEqual(first._loaded_lib, second._loaded_lib)
>
> +    def test_libevemu_found(self):
> +        lib = evemu.base.LibEvemu._load()
> +        self.assertNotEqual(lib, None)
> +        self.assertTrue(lib._name.startswith("libevemu"))
> +
> +    def test_libevemu_static_from_load(self):
> +        first = evemu.base.LibEvemu._load()
> +        self.assertNotEqual(first, None)
> +
> +        second = evemu.base.LibEvemu._load()
> +        self.assertNotEqual(second, None)
> +
> +        self.assertEqual(first, second)
> +
> +    def test_libevemu_static_in_object(self):
> +        first = evemu.base.LibEvemu()
> +        self.assertNotEqual(first, None)
> +
> +        second = evemu.base.LibEvemu()
> +        self.assertNotEqual(second, None)
> +
> +        self.assertEqual(first._loaded_lib, second._loaded_lib)
> +
>  if __name__ == "__main__":
>      unittest.main()
> --
> 1.8.5.2
>
> _______________________________________________
> Input-tools mailing list
> Input-tools at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/input-tools


More information about the Input-tools mailing list