It all depends on how you are actually implementing your cronjob running. But if you want events to be able to modify other events I can think of 3 options:
1. Pass a reference to the cron to the function
Your CronTab class must provide a way for events to be able to "inspect" into it. The best option would be to extend keyword arguments with a "cron" argument and pass it through to the function as follows:
class CronTab(object):
def __init__(self, *events):
self.events = events
for event in self.events:
event.kwargs['cron'] = self
# rest of the calss remains unmodified
Doing that, all functions called from the Crontab running will be passed a "cron" parameter with a reference to the cron that is running it
2. Add a "pre-run" marshal(s)
You extend your CronTab class to execute not only events on a particular schedule but also call other function(s) before those events. Functions that get an instance of the event and must return True
for it to be run. If any of them returns something else, the event in question won't run.
A basic (and untested) implementation for that would be:
class CronTab(object):
def __init__(self, *events, marshalls=None):
self.events = events
if marshalls is not None:
# marshalls must be a list of callables that return True or False
self.marshalls = marshalls
else
self.marshalls = []
def run(self):
t=datetime(*datetime.now().timetuple()[:5])
while 1:
for e in self.events:
if all([x(e) for x in self.marshalls]):
e.check(t)
t += timedelta(minutes=1)
while datetime.now() < t:
time.sleep((t - datetime.now()).seconds)
There are obviously better ways to implement this. For example, by doing a reduce
operation and thus avoiding running all marshalls for all events after one of them already returned False. But this should point you in the right direction.
It may also be easier to implement this if the CronTab class actually did more work than just called the Event's instance check
- or at least de-couple the checking of run to actually running the Event - but I didn't want to modify the original code that much.
3. Provide hooks to Events
This is basically the same solution as the one before but a lot more "fine-grained". Instead of just having one list of pre-run marshalls you would have several lists. The exact amount and details you can tailor to your own needs but here would be my take based on your request.
In addition to the general "pre-run marshalls" you can have one such list for each event name. Before running an event, not only you would run all general "pre-run marshalls" but also the ones specific to the event.
One way to implement that is to make the "pre-run marshalls" list into a dictionary of lists and provide a few methods to manage such lists. Something like this:
class CronTab(object):
def __init__(self, *events, marshalls=None):
self.events = events
self.marshalls = dict()
if marshalls is not None:
# marshalls must be a list of callables that return True or False
self.marshalls['__general'] = marshalls
else
self.marshalls['__general'] = []
# event is a string with the name of the event you want to marshall
# or None if you want to marshall all of them
def register(self, callback, event=None):
if event = None:
self.marshalls['__general'].append(callback)
else:
self.marshalls.setdefault(event, list()).append(callback)
def run(self):
t=datetime(*datetime.now().timetuple()[:5])
while 1:
for e in self.events:
if all([x(e) for x in self.marshalls['__general']]) and
all([x(e) for x in self.marshalls[e.name]]):
# the above assumes e.name exists (not in reference implementation)
e.check(t)
t += timedelta(minutes=1)
while datetime.now() < t:
time.sleep((t - datetime.now()).seconds)
In the same way you could add post-run hooks and other weird stuff like that as you see fit.
With regards to your question about the Events disappearing, please show us some code to be able to further troubleshoot the issue.