diff --git a/INSTALL b/INSTALL index d8c749b..ce5b226 100644 --- a/INSTALL +++ b/INSTALL @@ -9,7 +9,7 @@ - Tested with Cython 0.17.1 * EFL core library - - eo, evas, ecore, edje, elementary, edbus and emotion + - eo, evas, ecore, edje, emotion and elementary * pkg-config (http://www.freedesktop.org/wiki/Software/pkg-config) - Windows executable (and GLib dependency) can be downloaded from diff --git a/TODO b/TODO index 486b027..80dddd1 100644 --- a/TODO +++ b/TODO @@ -20,7 +20,6 @@ TODO ==== * ecore.Poller -* ecore.FileMonitor * alert on signal and subprocess module usage (was in python-ecore/ecore/__init__.py) * evas.SmartObject * edje: complete the unit tests diff --git a/efl/ecore/efl.ecore.pyx b/efl/ecore/efl.ecore.pyx index a554bac..67b9e40 100644 --- a/efl/ecore/efl.ecore.pyx +++ b/efl/ecore/efl.ecore.pyx @@ -190,11 +190,20 @@ ECORE_EXE_TERM_WITH_PARENT = enums.ECORE_EXE_TERM_WITH_PARENT ECORE_EXE_PRIORITY_INHERIT = 9999 - # Ecore_File_Progress_Return: ECORE_FILE_PROGRESS_CONTINUE = 0 ECORE_FILE_PROGRESS_ABORT = 1 +# Ecore_File_Event +ECORE_FILE_EVENT_NONE = enums.ECORE_FILE_EVENT_NONE +ECORE_FILE_EVENT_CREATED_FILE = enums.ECORE_FILE_EVENT_CREATED_FILE +ECORE_FILE_EVENT_CREATED_DIRECTORY = enums.ECORE_FILE_EVENT_CREATED_DIRECTORY +ECORE_FILE_EVENT_DELETED_FILE = enums.ECORE_FILE_EVENT_DELETED_FILE +ECORE_FILE_EVENT_DELETED_DIRECTORY = enums.ECORE_FILE_EVENT_DELETED_DIRECTORY +ECORE_FILE_EVENT_DELETED_SELF = enums.ECORE_FILE_EVENT_DELETED_SELF +ECORE_FILE_EVENT_MODIFIED = enums.ECORE_FILE_EVENT_MODIFIED +ECORE_FILE_EVENT_CLOSED = enums.ECORE_FILE_EVENT_CLOSED + cdef Eina_Bool _ecore_task_cb(void *data) with gil: cdef Eo obj = data @@ -281,5 +290,6 @@ include "efl.ecore_fd_handler.pxi" include "efl.ecore_events.pxi" include "efl.ecore_exe.pxi" include "efl.ecore_file_download.pxi" +include "efl.ecore_file_monitor.pxi" init() diff --git a/efl/ecore/efl.ecore_file_monitor.pxi b/efl/ecore/efl.ecore_file_monitor.pxi new file mode 100644 index 0000000..3d9436d --- /dev/null +++ b/efl/ecore/efl.ecore_file_monitor.pxi @@ -0,0 +1,81 @@ +# Copyright (C) 2007-2013 various contributors (see AUTHORS) +# +# This file is part of Python-EFL. +# +# Python-EFL is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# Python-EFL is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this Python-EFL. If not, see . + +from cpython cimport PyUnicode_AsUTF8String + + +cdef void _file_monitor_cb(void *data, Ecore_File_Monitor *em, Ecore_File_Event event, const_char *path) with gil: + obj = data + try: + obj._exec_monitor(event, path) + except Exception, e: + traceback.print_exc() + + +cdef class FileMonitor(object): + """ TODOC """ + def __init__(self, path, monitor_cb, *args, **kargs): + + if not callable(monitor_cb): + raise TypeError("Parameter 'monitor_cb' must be callable") + + self.monitor_cb = monitor_cb + self.args = args + self.kargs = kargs + + if isinstance(path, unicode): path = PyUnicode_AsUTF8String(path) + self.mon = ecore_file_monitor_add( + path if path is not None else NULL, + _file_monitor_cb, self) + if not self.mon: + raise SystemError("could not monitor '%s'" % (path)) + + Py_INCREF(self) + + def __dealloc__(self): + if self.mon != NULL: + ecore_file_monitor_del(self.mon) + self.mon = NULL + self.monitor_cb = None + self.args = None + self.kargs = None + + def __str__(self): + return "%s(monitor_cb=%s, args=%s, kargs=%s)" % \ + (self.__class__.__name__, self.monitor_cb, self.args, self.kargs) + + def __repr__(self): + return ("%s(%#x, monitor_cb=%s, args=%s, kargs=%s, refcount=%d)") % \ + (self.__class__.__name__, self, + self.monitor_cb, self.args, self.kargs, PY_REFCOUNT(self)) + + cdef object _exec_monitor(self, Ecore_File_Event event, const_char *path): + if self.monitor_cb: + return self.monitor_cb(event, _ctouni(path), *self.args, **self.kargs) + return 0 + + def delete(self): + """ TODOC """ + if self.mon != NULL: + ecore_file_monitor_del(self.mon) + self.mon = NULL + Py_DECREF(self) + + property path: + """ TODOC """ + def __get__(self): + return _ctouni(ecore_file_monitor_path_get(self.mon)) diff --git a/include/efl.ecore.enums.pxd b/include/efl.ecore.enums.pxd index 86268d9..fe72acb 100644 --- a/include/efl.ecore.enums.pxd +++ b/include/efl.ecore.enums.pxd @@ -52,3 +52,15 @@ cdef extern from "Ecore.h": ctypedef enum Ecore_Animator_Source: ECORE_ANIMATOR_SOURCE_TIMER ECORE_ANIMATOR_SOURCE_CUSTOM + + +cdef extern from "Ecore_File.h": + ctypedef enum Ecore_File_Event: + ECORE_FILE_EVENT_NONE + ECORE_FILE_EVENT_CREATED_FILE + ECORE_FILE_EVENT_CREATED_DIRECTORY + ECORE_FILE_EVENT_DELETED_FILE + ECORE_FILE_EVENT_DELETED_DIRECTORY + ECORE_FILE_EVENT_DELETED_SELF + ECORE_FILE_EVENT_MODIFIED + ECORE_FILE_EVENT_CLOSED diff --git a/include/efl.ecore.pxd b/include/efl.ecore.pxd index 3d92871..5b7bd26 100644 --- a/include/efl.ecore.pxd +++ b/include/efl.ecore.pxd @@ -18,7 +18,8 @@ from efl cimport * from efl.c_eo cimport Eo as cEo from efl.eo cimport Eo -from efl.ecore.enums cimport Ecore_Fd_Handler_Flags, Ecore_Exe_Flags +from efl.ecore.enums cimport Ecore_Fd_Handler_Flags, Ecore_Exe_Flags, \ + Ecore_File_Event cdef extern from "Ecore.h": @@ -165,9 +166,12 @@ cdef extern from "Ecore.h": cdef extern from "Ecore_File.h": ctypedef struct Ecore_File_Download_Job + ctypedef struct Ecore_File_Monitor ctypedef void (*Ecore_File_Download_Completion_Cb)(void *data, const_char *file, int status) ctypedef int (*Ecore_File_Download_Progress_Cb)(void *data, const_char *file, long int dltotal, long int dlnow, long int ultotal, long int ulnow) + ctypedef void (*Ecore_File_Monitor_Cb)(void *data, Ecore_File_Monitor *em, Ecore_File_Event event, const_char *path) + int ecore_file_init() int ecore_file_shutdown() @@ -180,6 +184,10 @@ cdef extern from "Ecore_File.h": void *data, Ecore_File_Download_Job **job_ret) + Ecore_File_Monitor *ecore_file_monitor_add(const_char *path, Ecore_File_Monitor_Cb func, void *data) + void ecore_file_monitor_del(Ecore_File_Monitor *ecore_file_monitor) + const_char *ecore_file_monitor_path_get(Ecore_File_Monitor *ecore_file_monitor) + #################################################################### # Python classes @@ -316,3 +324,11 @@ cdef class FileDownload: long int dltotal, long int dlnow, long int ultotal, long int ulnow) + +cdef class FileMonitor: + cdef Ecore_File_Monitor *mon + cdef readonly object monitor_cb + cdef readonly object args + cdef readonly object kargs + + cdef object _exec_monitor(self, Ecore_File_Event event, const_char *path) diff --git a/tests/ecore/test_09_file_monitor.py b/tests/ecore/test_09_file_monitor.py new file mode 100644 index 0000000..9d3519e --- /dev/null +++ b/tests/ecore/test_09_file_monitor.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python + +from efl import ecore +import unittest +import os +import tempfile + + +counters = [0, 0, 0, 0, 0, 0 , 0, 0] + +def monitor_cb(event, path, tmp_path): + """ for reference: + if event == ecore.ECORE_FILE_EVENT_MODIFIED: + print("EVENT_MODIFIED: '%s'" % path) + elif event == ecore.ECORE_FILE_EVENT_CLOSED: + print("EVENT_CLOSED: '%s'" % path) + elif event == ecore.ECORE_FILE_EVENT_CREATED_FILE: + print("ECORE_FILE_EVENT_CREATED_FILE: '%s'" % path) + elif event == ecore.ECORE_FILE_EVENT_CREATED_DIRECTORY: + print("ECORE_FILE_EVENT_CREATED_DIRECTORY: '%s'" % path) + elif event == ecore.ECORE_FILE_EVENT_DELETED_FILE: + print("ECORE_FILE_EVENT_DELETED_FILE: '%s'" % path) + elif event == ecore.ECORE_FILE_EVENT_DELETED_DIRECTORY: + print("ECORE_FILE_EVENT_DELETED_DIRECTORY: '%s'" % path) + elif event == ecore.ECORE_FILE_EVENT_DELETED_SELF: + print("ECORE_FILE_EVENT_DELETED_SELF: '%s'" % path) + """ + counters[event] += 1 + +def do_stuff(tmp_path, fm): + folder1 = os.path.join(tmp_path, "folder1") + folder2 = os.path.join(tmp_path, "folder2") + file1 = os.path.join(tmp_path, "file1") + file2 = os.path.join(tmp_path, "file2") + + # this should trigger two ECORE_FILE_EVENT_CREATED_DIRECTORY + os.mkdir(folder1) + os.mkdir(folder2) + + # this should trigger two ECORE_FILE_EVENT_DELETED_DIRECTORY + os.rmdir(folder1) + os.rmdir(folder2) + + # this should trigger two ECORE_FILE_EVENT_CREATED_FILE + fp1 = open(file1, 'a') + fp2 = open(file2, 'a') + + # this should trigger two ECORE_FILE_EVENT_MODIFIED + fp1.write("nothing to say") + fp2.write("nothing to say") + + # this should trigger two ECORE_FILE_EVENT_CLOSED + fp1.close() + fp2.close() + + # this should trigger two ECORE_FILE_EVENT_DELETED_FILE + os.remove(file1) + os.remove(file2) + + # this should trigger one ECORE_FILE_EVENT_DELETED_SELF !!! we get two + os.rmdir(tmp_path) + + +class TestFileMonitor(unittest.TestCase): + def testInit(self): + path = tempfile.mkdtemp() + fm = ecore.FileMonitor(path, monitor_cb, path) + self.assertIsInstance(fm, ecore.FileMonitor) + self.assertEqual(fm.path, path) + + ecore.Timer(0.1, do_stuff, path, fm) + ecore.Timer(1.0, lambda: ecore.main_loop_quit()) + + ecore.main_loop_begin() + self.assertEqual(fm.path, path) + fm.delete() + + # FIXME: we receive two ECORE_FILE_EVENT_DELETED_SELF, it's wrong + # should be [0, 2, 2, 2, 2, 1, 2, 2] + self.assertEqual(counters, [0, 2, 2, 2, 2, 2, 2, 2]) + + +if __name__ == '__main__': + unittest.main(verbosity=2) + ecore.shutdown()