Commits

Tetsuya Morimoto committed cddb646

fixed PEP8 coding style except some errors
added tox configurations

  • Participants
  • Parent commits 8b7c3f9

Comments (0)

Files changed (9)

 [egg_info]	
 tag_build = dev
-tag_svn_revision = true
+tag_svn_revision = true
+
+[pytest]
+pep8ignore = E302 E501 E701 W601

File src/traccron/__init__.py

+__import__('pkg_resources').declare_namespace(__name__)

File src/traccron/api.py

     """
     Interface for component task
     """
-    
+
     def wake_up(self, *args):
         """
         Call by the scheduler when the task need to be executed
         """
         raise NotImplementedError
-    
+
     def getId(self):
         """
         Return the key to use in trac.ini to configure this task
         """
         raise NotImplementedError
-    
+
     def getDescription(self):
         """
         Return the description of this task to be used in the admin panel.
         """
         raise NotImplementedError
-    
+
 
 class ISchedulerType(Interface):
     """
     Interface for scheduler type. A Scheduler type is a sort of scheduler that
     can trigger a task based on a specific scheduling.
-    """        
+    """
 
-        
     def getId(self):
         """
         Return the id to use in trac.ini for this schedule type
         """
         raise NotImplementedError
-    
+
     def getHint(self):
         """
-        Return a description of what it is and the format used to defined the schedule
+        Return a description of what it is and the format used to defined
+        the schedule
         """
         raise NotImplementedError
-    
+
     def isTriggerTime(self, task, currentTime):
         """
-        Test is accordingly to this scheduler and given currentTime, is time to fire the task
+        Test is accordingly to this scheduler and given currentTime,
+        is time to fire the task
         """
         raise NotImplementedError
-    
-    
+
 
 class ITaskEventListener(Interface):
     """
     Interface that listen to event occuring on task
-    """    
-    
+    """
+
     def onStartTask(self, task):
         """
         called by the core system when the task is triggered,
         just after the task wake_up method exit
         """
         raise NotImplementedError
-    
+
     def getId(self):
         """
         return the id of the listener. It is used in trac.ini
         """
         raise NotImplementedError
-    
+
     def getDescription(self):
         """
         Return a description used in admin panel
         """
         raise NotImplementedError
-    
+
 class IHistoryTaskExecutionStore(Interface):
     """
-    Interface that store an history of task execution. 
+    Interface that store an history of task execution.
     """
-    
+
     def addExecution(self, task, start, end, success):
         """
         Add a new execution of a task into this history.
         end is the end time in seconf from EPOC
         """
         raise NotImplementedError
-    
+
     def getExecution(self, task=None, fromTime=None, toTime=None, sucess=None):
         """
         Return a iterator on all execution stored. Each element is a tuple
         of (task, start time, end time, success status) where
         task is the task object
         start time and end time are second from EPOC
-        success status is a boolean value 
-        
+        success status is a boolean value
+
         Optional paramater can be used to filter the result.
         """
         raise NotImplementedError
-    
+
     def clear(self):
         """
         Clear all event in this history store
         """
-        
+
         raise NotImplementedError
-    

File src/traccron/core.py

 
 ###############################################################################
 ##
-##        C O R E    C L A S S E S     O F     T H E    P L U G I N 
+##        C O R E    C L A S S E S     O F     T H E    P L U G I N
 ##
 ###############################################################################
 
 from datetime import datetime, timedelta
-from time import  time, localtime
+from time import time, localtime
 from threading import Timer
 
-
-
 from trac.core import Component, ExtensionPoint, implements
 from trac.admin import IAdminPanelProvider
 from trac.web.chrome import ITemplateProvider
 from trac.util.text import exception_to_unicode
 from trac.util.datefmt import utc, http_date
 
-from traccron.api import ICronTask, IHistoryTaskExecutionStore, ISchedulerType, ITaskEventListener
+from traccron.api import ICronTask
+from traccron.api import IHistoryTaskExecutionStore
+from traccron.api import ISchedulerType
+from traccron.api import ITaskEventListener
 
 
-class Core(Component):    
+class Core(Component):
     """
     Main class of the Trac Cron Plugin. This is the entry point
     for Trac plugin architecture
-    """    
-    
+    """
+
     implements(IAdminPanelProvider, ITemplateProvider, IRequestHandler)
-    
+
     cron_tack_list = ExtensionPoint(ICronTask)
-    
+
     supported_schedule_type = ExtensionPoint(ISchedulerType)
-    
-    task_event_list = ExtensionPoint(ITaskEventListener)    
-    
+
+    task_event_list = ExtensionPoint(ITaskEventListener)
+
     history_store_list = ExtensionPoint(IHistoryTaskExecutionStore)
 
     current_ticker = None
-    
-    def __init__(self,*args,**kwargs):
+
+    def __init__(self, *args, **kwargs):
         """
         Intercept the instanciation to start the ticker
-        """        
+        """
         Component.__init__(self, *args, **kwargs)
-        self.cronconf = CronConfig(self.env)        
-        self.webUi = WebUi(self)        
-        self.apply_config()        
+        self.cronconf = CronConfig(self.env)
+        self.webUi = WebUi(self)
+        self.apply_config()
 
-        
     def apply_config(self, wait=False):
         """
         Read configuration and apply it
             # try to execute task a first time
             # because we don't want to wait for interval to elapse
             self.check_task()
-            Core.current_ticker = Ticker(self.env,self.getCronConf().get_ticker_interval(), self.check_task)
+            Core.current_ticker = Ticker(self.env, self.getCronConf().get_ticker_interval(), self.check_task)
         else:
             self.env.log.debug("ticker is disabled")
-            
 
     def getCronConf(self):
         """
         Return the configuration for TracCronPlugin
         """
         return self.cronconf
-    
+
     def getTaskList(self):
         """
         Return the list of existing task
         """
         return self.cron_tack_list
-    
+
     def getSupportedScheduleType(self):
         """
         Return the list of supported schedule type
         """
         return self.supported_schedule_type
-    
-    
+
     def getHistoryList(self):
         """
         Return the list of history store
         """
         return self.history_store_list
-    
+
     def getTaskListnerList(self):
         """
         Return the list of task event listener
         """
         return self.task_event_list
-    
+
     def clearHistory(self):
         """
         Clear history store
         """
         for history in self.history_store_list:
             history.clear()
-    
+
     def check_task(self):
         """
         Check if any task need to be executed. This method is called by the Ticker.
         """
         # store current time
         currentTime = localtime(time())
-        self.env.log.debug("check existing task");
+        self.env.log.debug("check existing task")
         for task in self.cron_tack_list:
-            if self.cronconf.is_task_enabled(task):                
+            if self.cronconf.is_task_enabled(task):
                 # test current time with task planing
                 self.env.log.debug("check task " + task.getId())
                 for schedule in self.supported_schedule_type:
                         if schedule.isTriggerTime(task, currentTime):
                             self._runTask(task, schedule)
                         else:
-                            self.env.log.debug("nothing to do for " + task.getId());
+                            self.env.log.debug("nothing to do for " + task.getId())
                     else:
                         self.env.log.debug("schedule " + schedule.getId() + " is disabled")
             else:
                 self.env.log.debug("task " + task.getId() + " is disabled")
 
-
     def runTask(self, task, parameters=None):
         '''
         run a given task with specified argument string
         parameters maybe comma-separated values
         '''
         self._runTask(task, parameters=parameters)
-        
+
     # IAdminPanel interface
-    
+
     def get_admin_panels(self, req):
         return self.webUi.get_admin_panels(req)
 
-
     def render_admin_panel(self, req, category, page, path_info):
         return self.webUi.render_admin_panel(req, category, page, path_info)
 
     # ITemplateProvider interface
-    
+
     def get_htdocs_dirs(self):
         return self.webUi.get_htdocs_dirs()
 
-
     def get_templates_dirs(self):
         return self.webUi.get_templates_dirs()
-    
+
     # IRequestHandler interface
-    
+
     def match_request(self, req):
         return self.webUi.match_request(req)
 
-    def process_request(self, req): 
+    def process_request(self, req):
         return self.webUi.process_request(req)
-    
+
     # internal method
-  
+
     def _notify_start_task(self, task):
         for listener in self.task_event_list:
             # notify only if listener is enabled
             if self.cronconf.is_task_listener_enabled(listener):
-                try:    
+                try:
                     listener.onStartTask(task)
                 except Exception, e:
-                    self.env.log.warn("listener %s failed  onStartTask event : %s" % (str(listener),exception_to_unicode(e)))                                    
-
+                    self.env.log.warn("listener %s failed  onStartTask event : %s" % (str(listener), exception_to_unicode(e)))
 
     def _notify_end_task(self, task, success=True):
         for listener in self.task_event_list:
                 try:
                     listener.onEndTask(task, success)
                 except Exception, e:
-                    self.env.log.warn("listener %s failed  onEndTask event : %s" % (str(listener),exception_to_unicode(e)))
+                    self.env.log.warn("listener %s failed  onEndTask event : %s" % (str(listener), exception_to_unicode(e)))
 
     def _runTask(self, task, schedule=None, parameters=None):
-        self.env.log.info("executing task " + task.getId()) # notify listener
+        self.env.log.info("executing task " + task.getId())  # notify listener
         self._notify_start_task(task)
         try:
             args = []
-            if schedule : 
+            if schedule:
                 args = self.cronconf.get_schedule_arg_list(task, schedule)
             elif parameters:
                 args = parameters
             self.env.log.info("task " + task.getId() + " finished")
 
 
-
-
-
 class Ticker():
     """
     A Ticker is simply a simply timer that will repeatly wake up.
     """
-    
-    
+
     def __init__(self, env, interval, callback):
         """
         Create a new Ticker.
         self.callback = callback
         self.timer = None
         self.create_new_timer()
-        
+
     def create_new_timer(self, wait=False, delay=None):
         """
         Create a new timer before killing existing one if required.
-        wait : if True the current thread wait until running task finished. Default is False
+        wait : if True the current thread wait until running task finished.
+        Default is False
         """
         self.env.log.debug("create new ticker")
-        if (self.timer != None):
+        if self.timer is not None:
             self.timer.cancel()
-            if ( wait ):
-                self.timer.join()            
-        
+            if (wait):
+                self.timer.join()
+
         if delay:
             # use specified delay to wait
             _delay = delay
         else:
             # use default delay
-            _delay = self.interval * 60 
-        self.timer = Timer( _delay, self.wake_up)
+            _delay = self.interval * 60
+        self.timer = Timer(_delay, self.wake_up)
         self.timer.start()
         self.env.log.debug("new ticker started")
 
         in_hurry = True
         while (in_hurry):
             wake_up_time = datetime(*datetime.now().timetuple()[:6])
-        
+
             self.callback()
-            
+
             now = datetime(*datetime.now().timetuple()[:6])
-            next_wake_up_time = wake_up_time + timedelta(minutes=self.interval)            
+            next_wake_up_time = wake_up_time + timedelta(minutes=self.interval)
             self.env.log.debug("last wake up time %s" % str(wake_up_time))
             self.env.log.debug("next wake up time %s" % str(next_wake_up_time))
             self.env.log.debug("current time %s" % str(now))
 
-            if  now < next_wake_up_time:  
+            if now < next_wake_up_time:
                 # calculate amount of second to wait in second
                 seconds_before_next_wake_up = (next_wake_up_time - now).seconds
-                # need to adjust it to ensure to skip this last wake up  minute window                            
-                if next_wake_up_time.second == 0 :
-                    # slide up 1 second  
+                # need to adjust it to ensure to skip this last wake up  minute window
+                if next_wake_up_time.second == 0:
+                    # slide up 1 second
                     seconds_before_next_wake_up += 1
                 # need to adjust it to ensure to not skip next minute window
                 elif next_wake_up_time.second == 59:
-                    # slid down 1 second 
+                    # slid down 1 second
                     seconds_before_next_wake_up -= 1
-                    
+
                 self.env.log.debug("adjusted wait %s secondes" % seconds_before_next_wake_up)
                 self.create_new_timer(delay=seconds_before_next_wake_up)
                 in_hurry = False
-            else :
+            else:
                 # the next wake up is over,
                 seconds_before_next_wake_up = (now - next_wake_up_time).seconds
                 self.env.log.warn("task processing duration overtake ticker interval\n"
-                                  "next wake up is over since %d seconds " % seconds_before_next_wake_up) 
-        
-    
+                                  "next wake up is over since %d seconds " % seconds_before_next_wake_up)
+
     def cancel(self, wait=False):
         self.timer.cancel()
         if (wait):
             self.timer.join()
 
-        
- 
+
 class CronConfig():
     """
     This class read and write configuration for TracCronPlugin
     """
-    
+
     TRACCRON_SECTION = "traccron"
-    
+
     TICKER_ENABLED_KEY = "ticker_enabled"
     TICKER_ENABLED_DEFAULT = "False"
-    
+
     TICKER_INTERVAL_KEY = "ticker_interval"
-    TICKER_INTERVAL_DEFAULT = 1 #minutes
-    
+    TICKER_INTERVAL_DEFAULT = 1  # minutes
+
     TASK_ENABLED_KEY = "enabled"
     TASK_ENABLED_DEFAULT = "True"
-    
+
     SCHEDULE_ENABLED_KEY = "enabled"
     SCHEDULE_ENABLED_DEFAULT = "True"
 
-    SCHEDULE_ARGUMENT_KEY ="arg"
+    SCHEDULE_ARGUMENT_KEY = "arg"
     SCHEDULE_ARGUMENT_DEFAULT = ""
-    
+
     TASK_LISTENER_ENABLED_KEY = "enabled"
-    TASK_LISTENER_ENABLED_DEFAULT = "True"    
-    
+    TASK_LISTENER_ENABLED_DEFAULT = "True"
+
     EMAIL_NOTIFIER_TASK_BASEKEY = "email_task_event"
-    
+
     EMAIL_NOTIFIER_TASK_LIMIT_KEY = "limit"
     EMAIL_NOTIFIER_TASK_LIMIT_DEFAULT = 1
-    
+
     EMAIL_NOTIFIER_TASK_RECIPIENT_KEY = "recipient"
     EMAIL_NOTIFIER_TASK_RECIPIENT_DEFAULT = ""
-    
+
     EMAIL_NOTIFIER_TASK_ONLY_ERROR_KEY = "only_error"
     EMAIL_NOTIFIER_TASK_ONLY_ERROR_DEFAULT = "False"
-    
-    UNREACHABLE_MILESTONE_TASK_BASEKEY="unreachable_milestone"
+
+    UNREACHABLE_MILESTONE_TASK_BASEKEY = "unreachable_milestone"
     UNREACHABLE_MILESTONE_TASK_RECIPIENT_KEY = "recipient"
     UNREACHABLE_MILESTONE_TASK_RECIPIENT_DEFAULT = ""
-    
 
     def __init__(self, env):
         self.env = env
-            
+
     def get_ticker_enabled(self):
         return self.env.config.getbool(self.TRACCRON_SECTION, self.TICKER_ENABLED_KEY, self.TICKER_ENABLED_DEFAULT)
 
     def set_ticker_enabled(self, value):
         self.env.config.set(self.TRACCRON_SECTION, self.TICKER_ENABLED_KEY, value)
-    
+
     def get_ticker_interval(self):
         return self.env.config.getint(self.TRACCRON_SECTION, self.TICKER_INTERVAL_KEY, self.TICKER_INTERVAL_DEFAULT)
-    
+
     def set_ticker_interval(self, value):
         self.env.config.set(self.TRACCRON_SECTION, self.TICKER_INTERVAL_KEY, value)
-    
+
     def get_schedule_value(self, task, schedule_type):
         """
         Return the raw value of the schedule
         """
-        return  self.env.config.get(self.TRACCRON_SECTION,task.getId() + "." + schedule_type.getId(), None)
+        return self.env.config.get(self.TRACCRON_SECTION, task.getId() + "." + schedule_type.getId(), None)
 
     def get_schedule_value_list(self, task, schedule_type):
         """
         Return the list of value for the schedule type and task
         """
-        return  self.env.config.getlist(self.TRACCRON_SECTION,task.getId() + "." + schedule_type.getId())
+        return self.env.config.getlist(self.TRACCRON_SECTION, task.getId() + "." + schedule_type.getId())
 
     def set_schedule_value(self, task, schedule_type, value):
         self.env.config.set(self.TRACCRON_SECTION, task.getId() + "." + schedule_type.getId(), value)
-    
+
     def is_task_enabled(self, task):
         """
         Return the value that indicate if the task is enabled
 
     def set_task_enabled(self, task, value):
         self.env.config.set(self.TRACCRON_SECTION, task.getId() + "." + self.TASK_ENABLED_KEY, value)
-    
+
     def is_schedule_enabled(self, task, schedule):
         """
         Return the value that indicate if the schedule for a given task is enabled
         """
-        return self.env.config.getbool(self.TRACCRON_SECTION, task.getId() + "." + schedule.getId() + "." +  self.SCHEDULE_ENABLED_KEY, self.SCHEDULE_ENABLED_DEFAULT)
+        return self.env.config.getbool(self.TRACCRON_SECTION, task.getId() + "." + schedule.getId() + "." + self.SCHEDULE_ENABLED_KEY, self.SCHEDULE_ENABLED_DEFAULT)
 
     def set_schedule_enabled(self, task, schedule, value):
         self.env.config.set(self.TRACCRON_SECTION, task.getId() + "." + schedule.getId() + "." + self.SCHEDULE_ENABLED_KEY, value)
-    
+
     def get_schedule_arg(self, task, schedule):
         """
         Return the raw value of argument for a given schedule of a task
         """
         return self.env.config.get(self.TRACCRON_SECTION, task.getId() + "." + schedule.getId() + "." + self.SCHEDULE_ARGUMENT_KEY, None)
- 
 
     def get_schedule_arg_list(self, task, schedule):
         """
         Return the list of argument for a given schedule of a task
         """
         return self.env.config.getlist(self.TRACCRON_SECTION, task.getId() + "." + schedule.getId() + "." + self.SCHEDULE_ARGUMENT_KEY)
-    
+
     def set_schedule_arg(self, task, schedule, value):
         self.env.config.set(self.TRACCRON_SECTION, task.getId() + "." + schedule.getId() + "." + self.SCHEDULE_ARGUMENT_KEY, value)
-    
-  
+
     def get_email_notifier_task_limit(self):
         """
         Return the number of task event to notify.
         """
-        return self.env.config.getint(self.TRACCRON_SECTION, 
-                                      self.EMAIL_NOTIFIER_TASK_BASEKEY + "." +  self.EMAIL_NOTIFIER_TASK_LIMIT_KEY, self.EMAIL_NOTIFIER_TASK_LIMIT_DEFAULT)
-    
+        return self.env.config.getint(self.TRACCRON_SECTION,
+                                      self.EMAIL_NOTIFIER_TASK_BASEKEY + "." + self.EMAIL_NOTIFIER_TASK_LIMIT_KEY, self.EMAIL_NOTIFIER_TASK_LIMIT_DEFAULT)
+
     def get_email_notifier_task_recipient(self):
         """
         Return the recipients for task listener as raw value
         """
         return self.env.config.get(self.TRACCRON_SECTION,
-                                   self.EMAIL_NOTIFIER_TASK_BASEKEY + "." +  self.EMAIL_NOTIFIER_TASK_RECIPIENT_KEY, self.EMAIL_NOTIFIER_TASK_RECIPIENT_DEFAULT)
-    
+                                   self.EMAIL_NOTIFIER_TASK_BASEKEY + "." + self.EMAIL_NOTIFIER_TASK_RECIPIENT_KEY, self.EMAIL_NOTIFIER_TASK_RECIPIENT_DEFAULT)
+
     def get_email_notifier_task_recipient_list(self):
         """
         Return the recipients for task listener
         """
         return self.env.config.getlist(self.TRACCRON_SECTION,
-                                       self.EMAIL_NOTIFIER_TASK_BASEKEY + "." +  self.EMAIL_NOTIFIER_TASK_RECIPIENT_KEY)
+                                       self.EMAIL_NOTIFIER_TASK_BASEKEY + "." + self.EMAIL_NOTIFIER_TASK_RECIPIENT_KEY)
 
     def is_email_notifier_only_error(self):
         """
-        Return the value that indicate of the notification must be sent only 
+        Return the value that indicate of the notification must be sent only
         for task on error
         """
         return self.env.config.getbool(self.TRACCRON_SECTION,
-                                       self.EMAIL_NOTIFIER_TASK_BASEKEY + "."  + self.EMAIL_NOTIFIER_TASK_ONLY_ERROR_KEY,
+                                       self.EMAIL_NOTIFIER_TASK_BASEKEY + "." + self.EMAIL_NOTIFIER_TASK_ONLY_ERROR_KEY,
                                        self.EMAIL_NOTIFIER_TASK_ONLY_ERROR_DEFAULT)
-    
+
     def set_email_notifier_only_error(self, value):
         self.env.config.set(self.TRACCRON_SECTION, self.EMAIL_NOTIFIER_TASK_BASEKEY + "." + self.EMAIL_NOTIFIER_TASK_ONLY_ERROR_KEY, value)
-    
+
     def is_task_listener_enabled(self, listener):
         return self.env.config.getbool(self.TRACCRON_SECTION, listener.getId() + "." + self.TASK_LISTENER_ENABLED_KEY, self.TASK_LISTENER_ENABLED_DEFAULT)
-    
+
     def set_task_listener_enabled(self, listener, value):
-        self.env.config.set(self.TRACCRON_SECTION, listener.getId() + "." + self.TASK_LISTENER_ENABLED_KEY, value) 
-
+        self.env.config.set(self.TRACCRON_SECTION, listener.getId() + "." + self.TASK_LISTENER_ENABLED_KEY, value)
 
     def get_unreachable_milestone_task_recipient_list(self):
         """
     def get_unreachable_milestone_task_recipient(self):
         """
         Return raw value of unreachable milestone recipient
-        """    
+        """
         return self.env.config.get(self.TRACCRON_SECTION,
                                    self.UNREACHABLE_MILESTONE_TASK_BASEKEY + "." + self.UNREACHABLE_MILESTONE_TASK_RECIPIENT_KEY,
                                    self.UNREACHABLE_MILESTONE_TASK_RECIPIENT_DEFAULT)
-    
+
     def set_value(self, key, value):
         self.env.config.set(self.TRACCRON_SECTION, key, value)
-    
+
     def remove_value(self, key):
         self.env.config.remove(self.TRACCRON_SECTION, key)
-    
+
     def save(self):
         self.env.config.save()
 
     Class that deal with Web stuff. It is the both the controller and the page builder.
     """
 
-    def __init__(self, core):        
+    def __init__(self, core):
         self.env = core.env
-        self.cron_task_list = core.getTaskList()        
+        self.cron_task_list = core.getTaskList()
         self.cronconf = core.getCronConf()
         self.history_store_list = core.getHistoryList()
         self.all_schedule_type = core.getSupportedScheduleType()
-        self.cron_listener_list = core.getTaskListnerList()        
+        self.cron_listener_list = core.getTaskListnerList()
         self.core = core
-        
+
     # IAdminPanelProvider
-    
+
     def get_admin_panels(self, req):
-        if ('TRAC_ADMIN' in req.perm) :
+        if ('TRAC_ADMIN' in req.perm):
             yield ('traccron', 'Trac Cron', 'cron_admin', u'Settings')
             yield ('traccron', 'Trac Cron', 'cron_history', u'History')
-            yield ('traccron', 'Trac Cron','cron_listener', u'Listener')
-          
-
+            yield ('traccron', 'Trac Cron', 'cron_listener', u'Listener')
 
     def render_admin_panel(self, req, category, page, path_info):
-        req.perm.assert_permission('TRAC_ADMIN')                
-        
+        req.perm.assert_permission('TRAC_ADMIN')
+
         if req.method == 'POST':
-            if 'save' in req.args:     
-                if page == 'cron_admin':                     
+            if 'save' in req.args:
+                if page == 'cron_admin':
                     self._saveSettings(req, category, page)
                 elif page == 'cron_listener':
-                    self._saveListenerSettings(req, category, page)   
+                    self._saveListenerSettings(req, category, page)
             elif 'clear' in req.args:
                 if page == 'cron_history':
-                    self._clearHistory(req, category, page)                 
-        else:             
+                    self._clearHistory(req, category, page)
+        else:
             # which view to display ?
-            if page == 'cron_admin':                
+            if page == 'cron_admin':
                 return self._displaySettingView()
             elif page == 'cron_history':
                 return self._displayHistoryView(req)
             elif page == 'cron_listener':
                 return self._displayListenerView()
-                
 
     # ITemplateProvider
 
     def get_htdocs_dirs(self):
         return []
 
-
     def get_templates_dirs(self):
         from pkg_resources import resource_filename
         return [resource_filename(__name__, 'templates')]
 
     # IRequestHandler interface
-    
+
     def match_request(self, req):
         return req.path_info.startswith('/traccron/')
-        
-    def process_request(self, req): 
+
+    def process_request(self, req):
         if req.path_info == '/traccron/runtask':
             self._runtask(req)
-        elif  req.path_info == '/traccron/cron_history': 
+        elif req.path_info == '/traccron/cron_history':
             return self._displayHistoryView(req)
         else:
             self.env.log.warn("Trac Cron Plugin was unable to handle %s" % req.path_info)
             add_warning(req, "The request was not handled by trac cron plugin")
-            req.redirect(req.href.admin('traccron','cron_admin'))
-            
+            req.redirect(req.href.admin('traccron', 'cron_admin'))
 
-    # internal method    
+    # internal method
 
     def _create_history_list(self):
         """
         Create list of task execution history
         """
-        _history = []        
-        
+        _history = []
+
         for store in self.history_store_list:
             for task, start, end, success in store.getExecution():
-                startTime  = localtime(start)
-                endTime  = localtime(end)         
+                startTime = localtime(start)
+                endTime = localtime(end)
                 date = datetime.fromtimestamp(start, utc)
                 execution = {
-                                 "timestamp": start,
-                                 "datetime": date ,
-                                 "dateuid" : self._to_utimestamp(date),
-                                 "date": "%d-%d-%d" % (startTime.tm_mon, startTime.tm_mday, startTime.tm_year),
-                                 "task":task.getId(),
-                                 "start": "%02d h %02d" % (startTime.tm_hour, startTime.tm_min),
-                                 "end": "%02d h %02d" % (endTime.tm_hour, endTime.tm_min),
-                                 "success": success                                 
-                                 }       
+                    "timestamp": start,
+                    "datetime": date,
+                    "dateuid": self._to_utimestamp(date),
+                    "date": "%d-%d-%d" % (startTime.tm_mon, startTime.tm_mday, startTime.tm_year),
+                    "task": task.getId(),
+                    "start": "%02d h %02d" % (startTime.tm_hour, startTime.tm_min),
+                    "end": "%02d h %02d" % (endTime.tm_hour, endTime.tm_min),
+                    "success": success
+                }
                 _history.append(execution)
 
         #apply sorting
-        _history.sort(None, lambda(x):x["timestamp"])
+        _history.sort(None, lambda(x): x["timestamp"])
         return _history
-    
+
     _epoc = datetime(1970, 1, 1, tzinfo=utc)
-    
+
     def _to_utimestamp(self, dt):
         """Return a microsecond POSIX timestamp for the given `datetime`."""
         if not dt:
         """Try to save the config, and display either a success notice or a
         failure warning.
         """
-        try:            
-
+        try:
             self.cronconf.save()
-
             if notices is None:
                 notices = [_('Your changes have been saved.')]
             for notice in notices:
                                'writable by the web server. Your changes have '
                                'not been saved.'))
 
-    def _displaySettingView(self, data= {}):
-        data.update({self.cronconf.TICKER_ENABLED_KEY:self.cronconf.get_ticker_enabled(), self.cronconf.TICKER_INTERVAL_KEY:self.cronconf.get_ticker_interval()})
+    def _displaySettingView(self, data={}):
+        data.update({
+            self.cronconf.TICKER_ENABLED_KEY: self.cronconf.get_ticker_enabled(),
+            self.cronconf.TICKER_INTERVAL_KEY: self.cronconf.get_ticker_interval()
+        })
         task_list = []
         for task in self.cron_task_list:
             task_data = {}
                 if task_arg is None:
                     task_arg = ""
                 all_schedule_value[schedule.getId()] = {
-                    "value":value, 
-                    "hint":schedule.getHint(), 
-                    "enabled":task_enabled, 
-                    "arg":task_arg}
-            
+                    "value": value,
+                    "hint": schedule.getHint(),
+                    "enabled": task_enabled,
+                    "arg": task_arg
+                }
+
             task_data['schedule_list'] = all_schedule_value
             task_list.append(task_data)
-        
-        data['task_list'] = task_list      
+
+        data['task_list'] = task_list
         return 'cron_admin.html', data
-    
+
     def _displayHistoryView(self, req, data={}):
         # create history list
         data['history_list'] = self._create_history_list()
-        
+
         format = req.args.get('format')
-        if ( format == 'rss'):
-            return 'cron_history.rss', data,'application/rss+xml'
+        if (format == 'rss'):
+            return 'cron_history.rss', data, 'application/rss+xml'
         else:
-            rss_href = req.href.traccron('cron_history',             
-                                      format='rss')
+            rss_href = req.href.traccron('cron_history', format='rss')
             add_link(req, 'alternate', rss_href, _('RSS Feed'),
                      'application/rss+xml', 'rss')
             return 'cron_history.html', data
-        
+
     def _displayListenerView(self, data={}):
         listener_list = []
         for listener in self.cron_listener_list:
             listener_data = {
-                             'id': listener.getId(),
-                             'enabled':self.cronconf.is_task_listener_enabled(listener) ,
-                             'description': listener.getDescription()
-                            }
+                'id': listener.getId(),
+                'enabled': self.cronconf.is_task_listener_enabled(listener),
+                'description': listener.getDescription()
+            }
             listener_list.append(listener_data)
         data["listener_list"] = listener_list
-        
         return 'cron_listener.html', data
-    
+
     def _saveSettings(self, req, category, page):
         arg_name_list = [self.cronconf.TICKER_ENABLED_KEY, self.cronconf.TICKER_INTERVAL_KEY]
         for task in self.cron_task_list:
                 arg_name_list.append(task_id + "." + schedule_id)
                 arg_name_list.append(task_id + "." + schedule_id + "." + self.cronconf.SCHEDULE_ENABLED_KEY)
                 arg_name_list.append(task_id + "." + schedule_id + "." + self.cronconf.SCHEDULE_ARGUMENT_KEY)
-        
+
         for arg_name in arg_name_list:
             arg_value = req.args.get(arg_name, "").strip()
             self.env.log.debug("receive req arg " + arg_name + "=[" + arg_value + "]")
                     self.cronconf.remove_value(arg_name)
             else:
                 self.cronconf.set_value(arg_name, arg_value)
-        
+
         self._save_config(req)
         self.core.apply_config(wait=True)
         req.redirect(req.abs_href.admin(category, page))
-        
 
     def _saveListenerSettings(self, req, category, page):
         arg_name_list = []
         for listener in self.cron_listener_list:
             listener_id = listener.getId()
             arg_name_list.append(listener_id + "." + self.cronconf.TASK_LISTENER_ENABLED_KEY)
-        
+
         for arg_name in arg_name_list:
             arg_value = req.args.get(arg_name, "").strip()
             self.env.log.debug("receive req arg " + arg_name + "=[" + arg_value + "]")
                     self.cronconf.remove_value(arg_name)
             else:
                 self.cronconf.set_value(arg_name, arg_value)
-        
+
         self._save_config(req)
         self.core.apply_config(wait=True)
         req.redirect(req.abs_href.admin(category, page))
-        
+
     def _clearHistory(self, req, category, page):
         self.core.clearHistory()
         req.redirect(req.abs_href.admin(category, page))
 
     def _runtask(self, req):
-        taskId = req.args.get('task','')
-        
+        taskId = req.args.get('task', '')
+
         taskWithId = filter(lambda x: x.getId() == taskId, self.cron_task_list)
-        
+
         if len(taskWithId) == 0:
             self.env.log.error("The task with id %s was not found" % taskId)
             add_warning(req, "The task with id %s was not found" % taskId)
-            req.redirect(req.href.admin('traccron','cron_admin'))
+            req.redirect(req.href.admin('traccron', 'cron_admin'))
         elif len(taskWithId) > 1:
             self.env.log.error("Multiple task with id %s was not found" % taskId)
-            add_warning(req, "Multiple task with id %s was not found" % taskId) 
-            req.redirect(req.href.admin('traccron','cron_admin'))
+            add_warning(req, "Multiple task with id %s was not found" % taskId)
+            req.redirect(req.href.admin('traccron', 'cron_admin'))
         else:
             task = taskWithId[0]
-           
+
             #create parameters list if needed
-            value = req.args.get('parameters', None)            
-            if value:               
+            value = req.args.get('parameters', None)
+            if value:
                 parameters = [item.strip() for item in value.split(',')]
                 self.core.runTask(task, parameters=parameters)
-            else:           
+            else:
                 self.core.runTask(task)
-            req.redirect(req.href.admin('traccron','cron_history'))
+            req.redirect(req.href.admin('traccron', 'cron_history'))

File src/traccron/history.py

 from traccron.api import IHistoryTaskExecutionStore
 
 class MemoryHistoryStore(Component, IHistoryTaskExecutionStore):
-    
+
     implements(IHistoryTaskExecutionStore)
-    
+
     history = []
-    
+
     def addExecution(self, task, start, end, success):
         """
         Add a new execution of a task into this history
         """
-        self.history.append((task,start,end,success))
-    
+        self.history.append((task, start, end, success))
+
     def getExecution(self, task=None, fromTime=None, toTime=None, sucess=None):
         """
         Return a iterator on all execution stored. Each element is a tuple
         """
         for h in self.history:
             yield h
-    
+
     def clear(self):
         self.history[:] = []
-    

File src/traccron/listener.py

 ##
 ###############################################################################
 
-from time import  time, localtime
+from time import time, localtime
 
 from trac.core import Component, implements, ExtensionPoint
 from trac.notification import NotifyEmail
 from traccron.api import ITaskEventListener, IHistoryTaskExecutionStore
 from traccron.core import CronConfig
 
+
 class NotificationEmailTaskEvent(Component, ITaskEventListener, ITemplateProvider):
     """
     This task listener send notification mail about task event.
     """
     implements(ITaskEventListener)
-    
-    
+
     class NotifyEmailTaskEvent(NotifyEmail):
-            
-            template_name  = "notify_task_event_template.txt"
-            
+
+            template_name = "notify_task_event_template.txt"
+
             def __init__(self, env):
                 NotifyEmail.__init__(self, env)
                 self.cronconf = CronConfig(self.env)
 
             def get_recipients(self, resid):
                 """
-                Return the recipients as defined in trac.ini.                
+                Return the recipients as defined in trac.ini.
                 """
-                reclist = self.cronconf.get_email_notifier_task_recipient_list()     
+                reclist = self.cronconf.get_email_notifier_task_recipient_list()
                 return (reclist, [])
-                
-            
+
             def notifyTaskEvent(self, task_event_list):
                 """
                 Send task event by mail if recipients is defined in trac.ini
                 """
-                self.env.log.debug("notifying task event...")             
-                if self.cronconf.get_email_notifier_task_recipient() :                                    
+                self.env.log.debug("notifying task event...")
+                if self.cronconf.get_email_notifier_task_recipient():
                     # prepare the data for the email content generation
-                    mess = ""      
+                    mess = ""
                     start = True
                     for event in task_event_list:
-                        if start:                        
-                            mess = mess + "task[%s]" % (event.task.getId(),)                       
+                        if start:
+                            mess = mess + "task[%s]" % (event.task.getId(),)
                             mess = mess + "\nstarted at %d h %d" % (event.time.tm_hour, event.time.tm_min)
                             mess = mess + "\n"
                         else:
                             else:
                                 mess = mess + "\nFAILURE"
                             mess = mess + "\n\n"
-                        start = not start                            
+                        start = not start
 
-                    self.data.update({
-                                     "notify_body": mess,                                        
-                                      })                                          
+                    self.data.update({"notify_body": mess})
                     NotifyEmail.notify(self, None, "task event notification")
                 else:
                     self.env.log.debug("no recipient for task event, aborting")
             def send(self, torcpts, ccrcpts):
                 return NotifyEmail.send(self, torcpts, ccrcpts)
 
-    
-    
-    def __init__(self):        
+    def __init__(self):
         self.cronconf = CronConfig(self.env)
         self.task_event_buffer = []
         self.task_count = 0
         self.notifier = None
-    
-    
+
     class StartTaskEvent():
         """
         Store the event of a task start
         def __init__(self, task):
             self.task = task
             self.time = localtime(time())
-    
-    
+
     class EndTaskEvent():
         """
         Store the event of a task end
             self.task = task
             self.time = localtime(time())
             self.success = success
-            
-            
-            
+
     def get_htdocs_dirs(self):
         return []
 
-
     def get_templates_dirs(self):
         from pkg_resources import resource_filename
         return [resource_filename(__name__, 'templates')]
-    
-    
+
     def onStartTask(self, task):
         """
         called by the core system when the task is triggered,
         just before the waek_up method is called
         """
-        self.task_event_buffer.append(NotificationEmailTaskEvent.StartTaskEvent(task)) 
+        self.task_event_buffer.append(NotificationEmailTaskEvent.StartTaskEvent(task))
         self.task_count = self.task_count + 1
 
     def onEndTask(self, task, success):
             return
         self.task_event_buffer.append(NotificationEmailTaskEvent.EndTaskEvent(task, success))
         # if the buffer reach the count then we notify
-        if ( self.task_count >= self.cronconf.get_email_notifier_task_limit()):
+        if (self.task_count >= self.cronconf.get_email_notifier_task_limit()):
             # send the mail
             if not self.notifier:
                 self.notifier = NotificationEmailTaskEvent.NotifyEmailTaskEvent(self.env)
             self.notifier.notifyTaskEvent(self.task_event_buffer)
-            
+
             # reset task event buffer
             self.task_event_buffer[:] = []
-            self.task_count= 0
+            self.task_count = 0
 
     def getId(self):
         return self.cronconf.EMAIL_NOTIFIER_TASK_BASEKEY
-    
+
     def getDescription(self):
         return self.__doc__
-    
-class HistoryTaskEvent(Component,ITaskEventListener):
+
+class HistoryTaskEvent(Component, ITaskEventListener):
     """
     This task event listener catch task execution to fill all History store in its environment
     """
-    
+
     implements(ITaskEventListener)
-    
+
     history_store_list = ExtensionPoint(IHistoryTaskExecutionStore)
-    
-    
+
     def onStartTask(self, task):
         """
         called by the core system when the task is triggered,
         """
         called by the core system when the task execution is finished,
         just after the task wake_up method exit
-        """ 
-        # currently Core assume that task are not threaded so any end event     
+        """
+        # currently Core assume that task are not threaded so any end event
         # match the previous start event
         assert task.getId() == self.task.getId()
         self.end = time()
         self.success = success
-        
+
         # notify all history store
         self._notify_history()
-    
+
     def getId(self):
         """
         return the id of the listener. It is used in trac.ini
         """
         return "history_task_event"
-    
+
     def getDescription(self):
         return self.__doc__
-    
+
     def _notify_history(self):
         for historyStore in self.history_store_list:
             historyStore.addExecution(self.task, self.start, self.end, self.success)
-            

File src/traccron/scheduler.py

 ##
 ###############################################################################
 
-from trac.core import Component, implements    
+from trac.core import Component, implements
 from traccron.api import ISchedulerType
 from traccron.core import CronConfig
 
     """
     Define a sort of scheduling. Base class for any scheduler type implementation
     """
-    
-    implements(ISchedulerType)   
-    
-    def __init__(self):    
+
+    implements(ISchedulerType)
+
+    def __init__(self):
         self.cronconf = CronConfig(self.env)
-                
 
-        
     def getId(self):
         """
         Return the id to use in trac.ini for this schedule type
         """
         raise NotImplementedError
-    
+
     def getHint(self):
         """
         Return a description of what it is and the format used to defined the schedule
         """
         return ""
-    
+
     def isTriggerTime(self, task, currentTime):
         """
         Test is accordingly to this scheduler and given currentTime, is time to fire the task
                 return True
         self.env.log.debug("no matching schedule found")
         return False
-    
+
     def compareTime(self, currentTime, schedule_value):
         """
         Test is accordingly to this scheduler, given currentTime and schedule value,
         is time to fire the task.
         currentTime is a structure computed by time.localtime(time())
-        scheduled_value is the value of the configuration in trac.ini      
+        scheduled_value is the value of the configuration in trac.ini
         """
         raise NotImplementedError
-    
+
     def _get_task_schedule_value_list(self, task):
         return self.cronconf.get_schedule_value_list(task, self)
-    
-
 
 
 class DailyScheduler(Component, SchedulerType):
     """
     Scheduler that trigger a task once a day based uppon a defined time
     """
-    
+
     def __init__(self):
         SchedulerType.__init__(self)
-        
+
     def getId(self):
         return "daily"
-    
+
     def getHint(self):
-        return "ex: 8h30 fire every day at 8h30" 
-               
-    
-    def compareTime(self, currentTime, schedule_value):        
+        return "ex: 8h30 fire every day at 8h30"
+
+    def compareTime(self, currentTime, schedule_value):
         # compare value with current
         if schedule_value:
-            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime)  + " with schedule_value " + schedule_value)
+            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime) + " with schedule_value " + schedule_value)
         else:
-            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime)  + " with NO schedule_value ")
+            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime) + " with NO schedule_value ")
         if schedule_value:
             return schedule_value == str(currentTime.tm_hour) + "h" + str(currentTime.tm_min)
         else:
             return False
-         
 
-class HourlyScheduler(Component,SchedulerType):
+
+class HourlyScheduler(Component, SchedulerType):
     """
     Scheduler that trigger a task once an hour at a defined time
     """
     def __init__(self):
         SchedulerType.__init__(self)
 
-    
     def getId(self):
         return "hourly"
-    
+
     def getHint(self):
-        return "ex: 45 fire every hour at 0h45 then 1h45 and so on" 
-    
-    def compareTime(self, currentTime, schedule_value):        
+        return "ex: 45 fire every hour at 0h45 then 1h45 and so on"
+
+    def compareTime(self, currentTime, schedule_value):
         # compare value with current
         if schedule_value:
-            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime)  + " with schedule_value " + schedule_value)
+            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime) + " with schedule_value " + schedule_value)
         else:
-            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime)  + " with NO schedule_value ")
+            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime) + " with NO schedule_value ")
         if schedule_value:
             return schedule_value == str(currentTime.tm_min)
         else:
-            return False    
+            return False
 
 
-class WeeklyScheduler(Component,SchedulerType):
+class WeeklyScheduler(Component, SchedulerType):
     """
     Scheduler that trigger a task once a week at a defined day and time
     """
     def __init__(self):
         SchedulerType.__init__(self)
 
-    
     def getId(self):
         return "weekly"
-    
+
     def getHint(self):
-        return "ex: 0@12h00 fire every monday at 12h00" 
+        return "ex: 0@12h00 fire every monday at 12h00"
 
-    
-    def compareTime(self, currentTime, schedule_value):        
+    def compareTime(self, currentTime, schedule_value):
         # compare value with current
         if schedule_value:
-            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime)  + " with schedule_value " + schedule_value)
+            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime) + " with schedule_value " + schedule_value)
         else:
-            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime)  + " with NO schedule_value ")
+            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime) + " with NO schedule_value ")
         if schedule_value:
             return schedule_value == str(currentTime.tm_wday) + "@" + str(currentTime.tm_hour) + "h" + str(currentTime.tm_min)
         else:
-            return False    
+            return False
 
 
 class MonthlyScheduler(Component, SchedulerType):
     def __init__(self):
         SchedulerType.__init__(self)
 
-    
     def getId(self):
         return "monthly"
-    
+
     def getHint(self):
-        return "ex: 15@12h00 fire every month on the 15th day of month at 12h00" 
+        return "ex: 15@12h00 fire every month on the 15th day of month at 12h00"
 
-    
-    def compareTime(self, currentTime, schedule_value):        
+    def compareTime(self, currentTime, schedule_value):
         # compare value with current
         if schedule_value:
-            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime)  + " with schedule_value " + schedule_value)
+            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime) + " with schedule_value " + schedule_value)
         else:
-            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime)  + " with NO schedule_value ")
+            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime) + " with NO schedule_value ")
         if schedule_value:
             return schedule_value == str(currentTime.tm_mday) + "@" + str(currentTime.tm_hour) + "h" + str(currentTime.tm_min)
         else:
-            return False   
+            return False
+
 
 class CronScheduler(Component, SchedulerType):
     """
         """
         Stand for ? in cron expression
         """
-        pass        
-    
+        pass
+
     class CronExpressionError(Exception):
         pass
-    
+
     _allMatch = AllMatch()
     _omitMatch = OmitMatch()
-    _event_parameter_for_cron_pos = {0:None, 1:"min",2:"hour",3:"day",4:"month",5:"dow",6:"year"}   
+    _event_parameter_for_cron_pos = {
+        0: None,
+        1: "min",
+        2: "hour",
+        3: "day",
+        4: "month",
+        5: "dow",
+        6: "year"
+    }
 
     # The actual Event class
     class Event(object):
-        
-        
-        
-        def __init__(self,  min, hour, 
-                    day,  month, dow, year):
+        def __init__(self, min, hour, day, month, dow, year):
             self.mins = self.conv_to_set(min)
-            self.hours= self.conv_to_set(hour)
-            self.days = self.conv_to_set(day)            
+            self.hours = self.conv_to_set(hour)
+            self.days = self.conv_to_set(day)
             self.months = self.conv_to_set(month)
             self.dow = self.conv_to_set(dow)
             self.year = self.conv_to_set(year)
 
         def conv_to_set(self, obj):  # Allow single integer to be provided
-            if isinstance(obj, (int,long)):
+            if isinstance(obj, (int, long)):
                 return set([obj])  # Single item
             if not isinstance(obj, set):
                 obj = set(obj)
 
         def matchtime(self, t):
             """Return True if this event should trigger at the specified localtime"""
-            return ((t.tm_min     in self.mins) and
-                    (t.tm_hour    in self.hours) and
-                    (t.tm_mday    in self.days) and
-                    (t.tm_mon     in self.months) and
-                    (t.tm_wday    in self.dow) and
-                    (t.tm_year    in self.year))
-
-
+            return ((t.tm_min in self.mins) and
+                    (t.tm_hour in self.hours) and
+                    (t.tm_mday in self.days) and
+                    (t.tm_mon in self.months) and
+                    (t.tm_wday in self.dow) and
+                    (t.tm_year in self.year))
 
     def getId(self):
         return "cron"
 
-
     def getHint(self):
         return "use cron like expression"
 
-
     def compareTime(self, currentTime, schedule_value):
-        
         if schedule_value:
-            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime)  + " with schedule_value " + schedule_value)
+            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime) + " with schedule_value " + schedule_value)
         else:
-            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime)  + " with NO schedule_value ")            
-        if schedule_value:           
+            self.env.log.debug(self.getId() + " compare currentTime=" + str(currentTime) + " with NO schedule_value ")
+        if schedule_value:
             try:
                 kwargs = self._parse_cron_expression(cron=schedule_value)
             except CronScheduler.CronExpressionError:
                 self.env.log.debug("Failed to parse cron expression, can't compare current time")
                 return False
             else:
-                return CronScheduler.Event(**kwargs).matchtime(t=currentTime)                
+                return CronScheduler.Event(**kwargs).matchtime(t=currentTime)
         else:
-            return False   
-
-
+            return False
 
     def _parse_cron_default(self, kwargs, event_param, value, min_value, max_value, adjust=0):
         """
                 # sanity check
                 _begin = int(begin)
                 _end = int(end)
-                if (_begin < min_value):
-                    self.env.log.error("invalid cron expression: start value of %s out of range [%d-%d] for %s" % (value,min_value,max_value, event_param))
-                    raise CronScheduler.CronExpressionError() 
-                if (_end > max_value):
-                    self.env.log.error("invalid cron expression: end value of %s out of range [%d-%d] for %s" % (value,min_value,max_value,event_param))
-                    raise CronScheduler.CronExpressionError()  
+                if _begin < min_value:
+                    self.env.log.error("invalid cron expression: start value of %s out of range [%d-%d] for %s" % (value, min_value, max_value, event_param))
+                    raise CronScheduler.CronExpressionError()
+                if _end > max_value:
+                    self.env.log.error("invalid cron expression: end value of %s out of range [%d-%d] for %s" % (value, min_value, max_value, event_param))
+                    raise CronScheduler.CronExpressionError()
                 # cron range expression is inclusive
                 kwargs[event_param] = range(_begin + adjust, _end + 1 + adjust)
             else:
                 begin, sep, step = value.partition("/")
-                if  sep =='/':
+                if sep == '/':
                     # sanity check
-                    _begin = int(begin)                
-                    if ( ( _begin < min_value ) or ( _begin > max_value) ):
-                        self.env.log.error("invalid cron expression: start value of %s out of range [%d-%d] for %s" % (value,min_value,max_value,event_param))
-                        raise CronScheduler.CronExpressionError() 
+                    _begin = int(begin)
+                    if ((_begin < min_value) or (_begin > max_value)):
+                        self.env.log.error("invalid cron expression: start value of %s out of range [%d-%d] for %s" % (value, min_value, max_value, event_param))
+                        raise CronScheduler.CronExpressionError()
                     _step = int(step)
                     # cron range expression is inclusive
-                    kwargs[event_param] = range(_begin + adjust, max_value + 1 + adjust, _step)                                            
+                    kwargs[event_param] = range(_begin + adjust, max_value + 1 + adjust, _step)
                 else:
                     # assuming  int single value
-                    _value = int(value)                
-                    if (( _value < min_value ) or ( _value > max_value) ):                        
+                    _value = int(value)
+                    if ((_value < min_value) or (_value > max_value)):
                         self.env.log.error("invalid cron expression: value of %s out of range [%d-%d] for %s" % (value, min_value, max_value, event_param))
-                        raise CronScheduler.CronExpressionError() 
+                        raise CronScheduler.CronExpressionError()
 
                     kwargs[event_param] = _value + adjust
-    
+
         # range value
 
     def _parse_cron_dmonth(self, kwargs, __event_parameter_for_cron_pos, event_param, value):
             else:
                 kwargs[event_param] = CronScheduler._omitMatch
         else:
-            self._parse_cron_default(kwargs, event_param, value,1,31)       
-
+            self._parse_cron_default(kwargs, event_param, value, 1, 31)
 
     def _parse_cron_dweek(self, kwargs, __event_parameter_for_cron_pos, event_param, value):
         other_event_parm = __event_parameter_for_cron_pos.get(3)
                 raise CronScheduler.CronExpressionError()
             else:
                 kwargs[event_param] = CronScheduler._omitMatch
-        else:        
+        else:
             # day of week starts at 1
             # since python localtime day of week start from 0
-            self._parse_cron_default(kwargs, event_param, value, 1,7,adjust=-1)
-    
-               
+            self._parse_cron_default(kwargs, event_param, value, 1, 7, adjust=-1)
 
     def _parse_cron_expression(self, cron):
         '''
             self.env.log.error("cron expression must have at least 6 items")
             raise CronScheduler.CronExpressionError()
         __event_parameter_for_cron_pos = CronScheduler._event_parameter_for_cron_pos
-        for pos in __event_parameter_for_cron_pos.keys():                        
-            event_param = __event_parameter_for_cron_pos.get(pos)                    
-            if event_param and pos < len(arglist):                
-                value = arglist[pos]                
+        for pos in __event_parameter_for_cron_pos.keys():
+            event_param = __event_parameter_for_cron_pos.get(pos)
+            if event_param and pos < len(arglist):
+                value = arglist[pos]
                 if pos == 1:
-                    self._parse_cron_default(kwargs, event_param, value,0,59)
+                    self._parse_cron_default(kwargs, event_param, value, 0, 59)
                 elif pos == 2:
-                    self._parse_cron_default(kwargs, event_param, value,0,23)
+                    self._parse_cron_default(kwargs, event_param, value, 0, 23)
                 elif pos == 3:
                     self._parse_cron_dmonth(kwargs, __event_parameter_for_cron_pos, event_param, value)
                 elif pos == 4:
-                    self._parse_cron_default(kwargs, event_param, value,1,12)
+                    self._parse_cron_default(kwargs, event_param, value, 1, 12)
                 elif pos == 5:
-                    self._parse_cron_dweek(kwargs, __event_parameter_for_cron_pos, event_param, value)                   
+                    self._parse_cron_dweek(kwargs, __event_parameter_for_cron_pos, event_param, value)
                 elif pos == 6:
-                    self._parse_cron_default(kwargs, event_param, value,1970,2099)
-                
+                    self._parse_cron_default(kwargs, event_param, value, 1970, 2099)
+
         # deal with optional item
         year_parameter_name = __event_parameter_for_cron_pos.get(6)
         if not kwargs.has_key(year_parameter_name):
             kwargs[year_parameter_name] = CronScheduler._allMatch
-        
+
         self.env.log.debug("result of parsing is %s" % str(kwargs))
-        return kwargs
+        return kwargs

File src/traccron/task.py

 ##             O U T    O F    T H E    B O X    T A S K
 ##
 ###############################################################################
-from time import  time, localtime
-from trac.ticket.model import Ticket 
+from time import time, localtime
+from trac.ticket.model import Ticket
 from trac.core import Component, implements
 from trac.notification import NotifyEmail
 from trac.web.chrome import ITemplateProvider
 from traccron.api import ICronTask
 from traccron.core import CronConfig
 
-class HeartBeatTask(Component,ICronTask):
+class HeartBeatTask(Component, ICronTask):
     """
     This is a simple task for testing purpose.
     It only write a trace in log at debug level
     """
-    
+
     implements(ICronTask)
-    
+
     def wake_up(self, *args):
         if len(args) > 0:
             for arg in args:
                 self.env.log.debug("Heart beat: " + arg)
         else:
             self.env.log.debug("Heart beat: boom boom !!!")
-    
+
     def getId(self):
         return "heart_beat"
-    
+
     def getDescription(self):
         return self.__doc__
-        
+
 
 class SleepingTicketReminderTask(Component, ICronTask, ITemplateProvider):
     """
     Remind user about sleeping ticket they are assigned to.
     """
-       
+
     implements(ICronTask, ITemplateProvider)
-    
+
     def get_htdocs_dirs(self):
         return []
 
-
     def get_templates_dirs(self):
         from pkg_resources import resource_filename
         return [resource_filename(__name__, 'templates')]
 
-    
     def wake_up(self, *args):
-        delay = 3        
+        delay = 3
         if len(args) > 0:
             delay = int(args[0])
-        
-        
+
         class SleepingTicketNotification(NotifyEmail):
-            
-            template_name  = "sleeping_ticket_template.txt"
-            
+
+            template_name = "sleeping_ticket_template.txt"
+
             def __init__(self, env):
                 NotifyEmail.__init__(self, env)
 
             def get_recipients(self, owner):
-                return ([owner],[])
+                return ([owner], [])
 
-                
-            
             def remind(self, tiketsByOwner):
                 """
                 Send a digest mail to ticket owner to remind him of those
                 sleeping tickets
                 """
-                for owner in tiketsByOwner.keys():  
-                    # prepare the data for the email content generation                      
+                for owner in tiketsByOwner.keys():
+                    # prepare the data for the email content generation
                     self.data.update({
-                                      "ticket_count": len(tiketsByOwner[owner]),
-                                      "delay": delay
-                                      })                                          
+                        "ticket_count": len(tiketsByOwner[owner]),
+                        "delay": delay
+                    })
                     NotifyEmail.notify(self, owner, "Sleeping ticket notification")
 
             def send(self, torcpts, ccrcpts):
                 return NotifyEmail.send(self, torcpts, ccrcpts)
 
-            
         class OrphanedTicketNotification(NotifyEmail):
-            
-            template_name  = "orphaned_ticket_template.txt"
-            
+
+            template_name = "orphaned_ticket_template.txt"
+
             def __init__(self, env):
                 NotifyEmail.__init__(self, env)
-                
+
             def get_recipients(self, reporter):
-                return ([reporter],[])
-            
+                return ([reporter], [])
+
             def remind(self, tiketsByReporter):
                 """
                 Send a digest mail to the reporter to remind them
                 of those orphaned tickets
                 """
-                for reporter in tiketsByReporter.keys():  
-                    # prepare the data for the email content generation                      
+                for reporter in tiketsByReporter.keys():
+                    # prepare the data for the email content generation
                     self.data.update({
-                                      "ticket_count": len(tiketsByReporter[owner]),
-                                      "delay": delay
-                                      })                                          
+                        "ticket_count": len(tiketsByReporter[owner]),
+                        "delay": delay
+                    })
                     NotifyEmail.notify(self, reporter, "orphaned ticket notification")
 
+            def send(self, torcpts, ccrcpts):
+                return NotifyEmail.send(self, torcpts, ccrcpts)
 
-            def send(self, torcpts, ccrcpts):
-                return NotifyEmail.send(self, torcpts, ccrcpts)            
-            
-                                
-        # look for ticket assigned but not touched since more that the delay       
+        # look for ticket assigned but not touched since more that the delay
         db = self.env.get_db_cnx()
         cursor = db.cursor()
         # assigned ticket
         cursor.execute("""
-                SELECT t.id , t.owner  FROM ticket t, ticket_change tc                        
-                WHERE  t.id = tc.ticket  
+                SELECT t.id , t.owner  FROM ticket t, ticket_change tc
+                WHERE  t.id = tc.ticket
                 AND    t.status in ('new','assigned','accepted')
                 AND    (SELECT MAX(tc2.time) FROM ticket_change tc2 WHERE tc2.ticket=tc.ticket)  < %s GROUP BY t.id
-            """, (time() - delay * 24 * 60 * 60,) )
+            """, (time() - delay * 24 * 60 * 60,))
         dico = {}
         for ticket, owner in cursor:
             self.env.log.info("warning ticket %d assigned to %s but is inactive since more than %d day" % (ticket, owner, delay))
                SELECT t.id, t.reporter  FROM  ticket t
                WHERE  t.id not in (select tc.ticket FROM ticket_change tc WHERE tc.ticket=t.id)
                AND t.time < %s AND t.status = 'new'
-            """, (time() - delay * 24 * 60 * 60,) )
+            """, (time() - delay * 24 * 60 * 60,))
         dico = {}
         for ticket, reporter in cursor:
             self.env.log.info("warning ticket %d is new but orphaned" % (ticket,))
             else:
                 dico[reporter] = [ticket]
         OrphanedTicketNotification(self.env).remind(dico)
-    
+
     def getId(self):
         return "sleeping_ticket"
-    
+
     def getDescription(self):
         return self.__doc__
-    
 
 
 class UnreachableMilestoneTask(Component, ICronTask, ITemplateProvider):
     """
     Send notification about near milestone with opened ticked
     """
-       
+
     implements(ICronTask, ITemplateProvider)
-       
+
     def __init__(self):
         self.cronconf = CronConfig(self.env)
-    
+
     def get_htdocs_dirs(self):
         return []
 
-
     def get_templates_dirs(self):
         from pkg_resources import resource_filename
         return [resource_filename(__name__, 'templates')]
 
-    
-
-
     def wake_up(self, *args):
-        delay = 3        
+        delay = 3
         if len(args) > 0:
             delay = int(args[0])
-        
+
         class BaseTicketNotification(NotifyEmail):
-            
+
             def __init__(self, env, milestone):
                 NotifyEmail.__init__(self, env)
                 self.milestone = milestone
-            
+
             def populate_unreachable_tickets_data(self, tickets):
-                self.data['milestone'] = tickets[0]['milestone'] # we are not called if there is no tickets
+                self.data['milestone'] = tickets[0]['milestone']  # we are not called if there is no tickets
                 due_date = localtime(tickets[0]['due'])
                 self.data['due_date'] = "%d-%d-%d" % (due_date.tm_mon, due_date.tm_mday, due_date.tm_year)
                 tickets_list = ""
                     tickets_list += ticket['summary'] + "\n"
                     tickets_list += self.env.abs_href.ticket(ticket['ticket']) + "\n"
                     tickets_list += "\n"
-        
+
                 self.data['tickets_list'] = tickets_list
 
-
         class ReporterOpenedTicketNotification(BaseTicketNotification):
             """
             Notify reporter about an opened ticket in
-            a near milestone            
-            """            
-            template_name  = "opened_ticket_for_reporter_template.txt"
-            
+            a near milestone
+            """
+            template_name = "opened_ticket_for_reporter_template.txt"
+
             def __init__(self, env, milestone):
                 BaseTicketNotification.__init__(self, env, milestone)
-                
-              
 
-            def get_recipients(self, reporter):                
-                return ([reporter],[])
+            def get_recipients(self, reporter):
+                return ([reporter], [])
 
-                
-            
             def notify_opened_ticket(self, reporter, tickets):
                 """
                 Send a digest mail to ticket owner and reporter
-                about ticket still opened 
+                about ticket still opened
                 """
-                
-                self.populate_unreachable_tickets_data(tickets)                           
+
+                self.populate_unreachable_tickets_data(tickets)
                 NotifyEmail.notify(self, reporter, "Milestone %s with still opened ticket" % self.milestone)
 
             def send(self, torcpts, ccrcpts):
                 return NotifyEmail.send(self, torcpts, ccrcpts)
-            
-            
+
         class OwnerOpenedTicketNotification(BaseTicketNotification):
             """
             Notify owner about an opened ticket in
-            a near milestone            
+            a near milestone
             """
-            template_name  = "opened_ticket_for_owner_template.txt"
-            
+            template_name = "opened_ticket_for_owner_template.txt"
+
             def __init__(self, env, milestone):
                 BaseTicketNotification.__init__(self, env, milestone)
-              
 
-            def get_recipients(self, owner):                
-                return ([owner],[])
+            def get_recipients(self, owner):
+                return ([owner], [])
 
-                
-            
             def notify_opened_ticket(self, owner, tickets):
                 """
                 Send a digest mail to ticket owner
-                about ticket still opened 
+                about ticket still opened
                 """
-                
-                self.populate_unreachable_tickets_data(tickets)                           
+
+                self.populate_unreachable_tickets_data(tickets)
                 NotifyEmail.notify(self, owner, "Milestone %s still has opened ticket" % self.milestone)
 
             def send(self, torcpts, ccrcpts):
                 return NotifyEmail.send(self, torcpts, ccrcpts)
 
-            
         class UnreachableMilestoneNotification(BaseTicketNotification):
             """
             Notify the specified person (ex: admin, release manager) that a milestone
-            is about to closed but there still are opened ticket 
+            is about to closed but there still are opened ticket
             """
-            
-            template_name  = "unreachable_milestone_template.txt"
-            
+
+            template_name = "unreachable_milestone_template.txt"
+
             def __init__(self, env, milestone):
                 BaseTicketNotification.__init__(self, env, milestone)
-                self.cronconf = CronConfig(self.env)                
-                
+                self.cronconf = CronConfig(self.env)
+
             def get_recipients(self, milestone):
                 reclist = self.cronconf.get_unreachable_milestone_task_recipient_list()
-                return (reclist,[])
-            
+                return (reclist, [])
+
             def notify_unreachable_milestone(self, tickets):
                 """
                 Send a digest mail listing all tickets still opened in the milestone
                 """
                 self.populate_unreachable_tickets_data(tickets)
-               
+
                 NotifyEmail.notify(self, self.milestone, "Milestone %s still has opened ticket" % self.milestone)
 
+            def send(self, torcpts, ccrcpts):
+                return NotifyEmail.send(self, torcpts, ccrcpts)
 
-            def send(self, torcpts, ccrcpts):
-                return NotifyEmail.send(self, torcpts, ccrcpts)            
-            
-                                
         # look opened ticket in near milestone
         db = self.env.get_db_cnx()
         cursor = db.cursor()
         # select ticket whom milestone are due in less than specified delay
         cursor.execute("""
-                SELECT t.id , t.owner, t.reporter, t.milestone, t.summary, m.due  FROM ticket t, milestone m                        
-                WHERE  t.milestone = m.name  
-                AND    m.due < %s                
-            """, (time() + delay * 24 * 60 * 60,) )
+                SELECT t.id , t.owner, t.reporter, t.milestone, t.summary, m.due  FROM ticket t, milestone m
+                WHERE  t.milestone = m.name
+                AND    m.due < %s
+            """, (time() + delay * 24 * 60 * 60,))
         dico = {}
         dico_reporter = {}
         dico_owner = {}
         for ticket, owner, reporter, milestone, summary, due in cursor:
             self.env.log.info("warning ticket %d will probably miss its milestone %s" % (ticket, milestone))
-            ticket_data = {'ticket':ticket,
-                           'owner': owner,
-                           'reporter' : reporter,
-                           'milestone' : milestone,
-                           'summary': summary,
-                           'due' : due
-                         }
+            ticket_data = {
+                'ticket': ticket,
+                'owner': owner,
+                'reporter': reporter,
+                'milestone': milestone,
+                'summary': summary,
+                'due': due
+            }
             if dico.has_key(milestone):
                 dico[milestone].append(ticket_data)
             else:
                 dico[milestone] = [ticket_data]
-                
+
             if dico_owner.has_key(owner):
                 if dico_owner[owner].has_key(milestone):
                     dico_owner[owner][milestone].append(ticket_data)
                 else:
                     dico_owner[owner][milestone] = [ticket_data]
             else:
-                dico_owner[owner] = {milestone:[ticket_data]}
-                
+                dico_owner[owner] = {milestone: [ticket_data]}
+
             if dico_reporter.has_key(reporter):