[Pkg-privacy-commits] [onionbalance] 91/117: Remove schedule dependency
Donncha O'Cearbahill
donncha-guest at moszumanska.debian.org
Wed Dec 16 23:18:52 UTC 2015
This is an automated email from the git hooks/post-receive script.
donncha-guest pushed a commit to branch debian/sid
in repository onionbalance.
commit 8936c55e2ff8485896f858a43363899418b7b4fd
Author: Donncha O'Cearbhaill <donncha at donncha.is>
Date: Wed Sep 16 17:39:13 2015 +0200
Remove schedule dependency
The schedule Python package is not packaged in Debian. It is a single
file package and I forking it and including the code directly in the
OnionBalance package.
---
onionbalance/manager.py | 2 +-
onionbalance/schedule.py | 396 +++++++++++++++++++++++++++++++++++++++++++++++
requirements.txt | 1 -
setup.py | 1 -
4 files changed, 397 insertions(+), 3 deletions(-)
diff --git a/onionbalance/manager.py b/onionbalance/manager.py
index 092fe0e..e4e0a89 100644
--- a/onionbalance/manager.py
+++ b/onionbalance/manager.py
@@ -12,12 +12,12 @@ import logging
# import Crypto.PublicKey
import stem
from stem.control import Controller, EventType
-import schedule
from onionbalance import log
from onionbalance import settings
from onionbalance import config
from onionbalance import eventhandler
+from onionbalance import schedule
import onionbalance.service
import onionbalance.instance
diff --git a/onionbalance/schedule.py b/onionbalance/schedule.py
new file mode 100644
index 0000000..5dde1e4
--- /dev/null
+++ b/onionbalance/schedule.py
@@ -0,0 +1,396 @@
+# -*- coding: utf-8 -*-
+"""
+Python job scheduling for humans.
+
+An in-process scheduler for periodic jobs that uses the builder pattern
+for configuration. Schedule lets you run Python functions (or any other
+callable) periodically at pre-determined intervals using a simple,
+human-friendly syntax.
+
+Inspired by Addam Wiggins' article "Rethinking Cron" [1] and the
+"clockwork" Ruby module [2][3].
+
+Features:
+ - A simple to use API for scheduling jobs.
+ - Very lightweight and no external dependencies.
+ - Excellent test coverage.
+ - Works with Python 2.7 and 3.3
+
+Usage:
+ >>> import schedule
+ >>> import time
+
+ >>> def job(message='stuff'):
+ >>> print("I'm working on:", message)
+
+ >>> schedule.every(10).minutes.do(job)
+ >>> schedule.every().hour.do(job, message='things')
+ >>> schedule.every().day.at("10:30").do(job)
+
+ >>> while True:
+ >>> schedule.run_pending()
+ >>> time.sleep(1)
+
+[1] http://adam.heroku.com/past/2010/4/13/rethinking_cron/
+[2] https://github.com/tomykaira/clockwork
+[3] http://adam.heroku.com/past/2010/6/30/replace_cron_with_clockwork/
+"""
+import datetime
+import functools
+import logging
+import time
+
+logger = logging.getLogger('schedule')
+
+
+class CancelJob(object):
+ pass
+
+
+class Scheduler(object):
+ def __init__(self):
+ self.jobs = []
+
+ def run_pending(self):
+ """Run all jobs that are scheduled to run.
+
+ Please note that it is *intended behavior that tick() does not
+ run missed jobs*. For example, if you've registered a job that
+ should run every minute and you only call tick() in one hour
+ increments then your job won't be run 60 times in between but
+ only once.
+ """
+ runnable_jobs = (job for job in self.jobs if job.should_run)
+ for job in sorted(runnable_jobs):
+ self._run_job(job)
+
+ def run_all(self, delay_seconds=0):
+ """Run all jobs regardless if they are scheduled to run or not.
+
+ A delay of `delay` seconds is added between each job. This helps
+ distribute system load generated by the jobs more evenly
+ over time."""
+ logger.info('Running *all* %i jobs with %is delay inbetween',
+ len(self.jobs), delay_seconds)
+ for job in self.jobs:
+ self._run_job(job)
+ time.sleep(delay_seconds)
+
+ def clear(self):
+ """Deletes all scheduled jobs."""
+ del self.jobs[:]
+
+ def cancel_job(self, job):
+ """Delete a scheduled job."""
+ try:
+ self.jobs.remove(job)
+ except ValueError:
+ pass
+
+ def every(self, interval=1):
+ """Schedule a new periodic job."""
+ job = Job(interval)
+ self.jobs.append(job)
+ return job
+
+ def _run_job(self, job):
+ ret = job.run()
+ if isinstance(ret, CancelJob) or ret is CancelJob:
+ self.cancel_job(job)
+
+ @property
+ def next_run(self):
+ """Datetime when the next job should run."""
+ if not self.jobs:
+ return None
+ return min(self.jobs).next_run
+
+ @property
+ def idle_seconds(self):
+ """Number of seconds until `next_run`."""
+ return (self.next_run - datetime.datetime.now()).total_seconds()
+
+
+class Job(object):
+ """A periodic job as used by `Scheduler`."""
+ def __init__(self, interval):
+ self.interval = interval # pause interval * unit between runs
+ self.job_func = None # the job job_func to run
+ self.unit = None # time units, e.g. 'minutes', 'hours', ...
+ self.at_time = None # optional time at which this job runs
+ self.last_run = None # datetime of the last run
+ self.next_run = None # datetime of the next run
+ self.period = None # timedelta between runs, only valid for
+ self.start_day = None # Specific day of the week to start on
+
+ def __lt__(self, other):
+ """PeriodicJobs are sortable based on the scheduled time
+ they run next."""
+ return self.next_run < other.next_run
+
+ def __repr__(self):
+ def format_time(t):
+ return t.strftime('%Y-%m-%d %H:%M:%S') if t else '[never]'
+
+ timestats = '(last run: %s, next run: %s)' % (
+ format_time(self.last_run), format_time(self.next_run))
+
+ if hasattr(self.job_func, '__name__'):
+ job_func_name = self.job_func.__name__
+ else:
+ job_func_name = repr(self.job_func)
+ args = [repr(x) for x in self.job_func.args]
+ kwargs = ['%s=%s' % (k, repr(v))
+ for k, v in self.job_func.keywords.items()]
+ call_repr = job_func_name + '(' + ', '.join(args + kwargs) + ')'
+
+ if self.at_time is not None:
+ return 'Every %s %s at %s do %s %s' % (
+ self.interval,
+ self.unit[:-1] if self.interval == 1 else self.unit,
+ self.at_time, call_repr, timestats)
+ else:
+ return 'Every %s %s do %s %s' % (
+ self.interval,
+ self.unit[:-1] if self.interval == 1 else self.unit,
+ call_repr, timestats)
+
+ @property
+ def second(self):
+ assert self.interval == 1
+ return self.seconds
+
+ @property
+ def seconds(self):
+ self.unit = 'seconds'
+ return self
+
+ @property
+ def minute(self):
+ assert self.interval == 1
+ return self.minutes
+
+ @property
+ def minutes(self):
+ self.unit = 'minutes'
+ return self
+
+ @property
+ def hour(self):
+ assert self.interval == 1
+ return self.hours
+
+ @property
+ def hours(self):
+ self.unit = 'hours'
+ return self
+
+ @property
+ def day(self):
+ assert self.interval == 1
+ return self.days
+
+ @property
+ def days(self):
+ self.unit = 'days'
+ return self
+
+ @property
+ def week(self):
+ assert self.interval == 1
+ return self.weeks
+
+ @property
+ def monday(self):
+ assert self.interval == 1
+ self.start_day = 'monday'
+ return self.weeks
+
+ @property
+ def tuesday(self):
+ assert self.interval == 1
+ self.start_day = 'tuesday'
+ return self.weeks
+
+ @property
+ def wednesday(self):
+ assert self.interval == 1
+ self.start_day = 'wednesday'
+ return self.weeks
+
+ @property
+ def thursday(self):
+ assert self.interval == 1
+ self.start_day = 'thursday'
+ return self.weeks
+
+ @property
+ def friday(self):
+ assert self.interval == 1
+ self.start_day = 'friday'
+ return self.weeks
+
+ @property
+ def saturday(self):
+ assert self.interval == 1
+ self.start_day = 'saturday'
+ return self.weeks
+
+ @property
+ def sunday(self):
+ assert self.interval == 1
+ self.start_day = 'sunday'
+ return self.weeks
+
+ @property
+ def weeks(self):
+ self.unit = 'weeks'
+ return self
+
+ def at(self, time_str):
+ """Schedule the job every day at a specific time.
+
+ Calling this is only valid for jobs scheduled to run every
+ N day(s).
+ """
+ assert self.unit in ('days', 'hours') or self.start_day
+ hour, minute = [t for t in time_str.split(':')]
+ minute = int(minute)
+ if self.unit == 'days' or self.start_day:
+ hour = int(hour)
+ assert 0 <= hour <= 23
+ elif self.unit == 'hours':
+ hour = 0
+ assert 0 <= minute <= 59
+ self.at_time = datetime.time(hour, minute)
+ return self
+
+ def do(self, job_func, *args, **kwargs):
+ """Specifies the job_func that should be called every time the
+ job runs.
+
+ Any additional arguments are passed on to job_func when
+ the job runs.
+ """
+ self.job_func = functools.partial(job_func, *args, **kwargs)
+ try:
+ functools.update_wrapper(self.job_func, job_func)
+ except AttributeError:
+ # job_funcs already wrapped by functools.partial won't have
+ # __name__, __module__ or __doc__ and the update_wrapper()
+ # call will fail.
+ pass
+ self._schedule_next_run()
+ return self
+
+ @property
+ def should_run(self):
+ """True if the job should be run now."""
+ return datetime.datetime.now() >= self.next_run
+
+ def run(self):
+ """Run the job and immediately reschedule it."""
+ logger.info('Running job %s', self)
+ ret = self.job_func()
+ self.last_run = datetime.datetime.now()
+ self._schedule_next_run()
+ return ret
+
+ def _schedule_next_run(self):
+ """Compute the instant when this job should run next."""
+ # Allow *, ** magic temporarily:
+ assert self.unit in ('seconds', 'minutes', 'hours', 'days', 'weeks')
+ self.period = datetime.timedelta(**{self.unit: self.interval})
+ self.next_run = datetime.datetime.now() + self.period
+ if self.start_day is not None:
+ assert self.unit == 'weeks'
+ weekdays = (
+ 'monday',
+ 'tuesday',
+ 'wednesday',
+ 'thursday',
+ 'friday',
+ 'saturday',
+ 'sunday'
+ )
+ assert self.start_day in weekdays
+ weekday = weekdays.index(self.start_day)
+ days_ahead = weekday - self.next_run.weekday()
+ if days_ahead <= 0: # Target day already happened this week
+ days_ahead += 7
+ self.next_run += datetime.timedelta(days_ahead) - self.period
+ if self.at_time is not None:
+ assert self.unit in ('days', 'hours') or self.start_day is not None
+ kwargs = {
+ 'minute': self.at_time.minute,
+ 'second': self.at_time.second,
+ 'microsecond': 0
+ }
+ if self.unit == 'days' or self.start_day is not None:
+ kwargs['hour'] = self.at_time.hour
+ self.next_run = self.next_run.replace(**kwargs)
+ # If we are running for the first time, make sure we run
+ # at the specified time *today* (or *this hour*) as well
+ if not self.last_run:
+ now = datetime.datetime.now()
+ if (self.unit == 'days' and self.at_time > now.time() and
+ self.interval == 1):
+ self.next_run = self.next_run - datetime.timedelta(days=1)
+ elif self.unit == 'hours' and self.at_time.minute > now.minute:
+ self.next_run = self.next_run - datetime.timedelta(hours=1)
+ if self.start_day is not None and self.at_time is not None:
+ # Let's see if we will still make that time we specified today
+ if (self.next_run - datetime.datetime.now()).days >= 7:
+ self.next_run -= self.period
+
+# The following methods are shortcuts for not having to
+# create a Scheduler instance:
+
+default_scheduler = Scheduler()
+jobs = default_scheduler.jobs # todo: should this be a copy, e.g. jobs()?
+
+
+def every(interval=1):
+ """Schedule a new periodic job."""
+ return default_scheduler.every(interval)
+
+
+def run_pending():
+ """Run all jobs that are scheduled to run.
+
+ Please note that it is *intended behavior that run_pending()
+ does not run missed jobs*. For example, if you've registered a job
+ that should run every minute and you only call run_pending()
+ in one hour increments then your job won't be run 60 times in
+ between but only once.
+ """
+ default_scheduler.run_pending()
+
+
+def run_all(delay_seconds=0):
+ """Run all jobs regardless if they are scheduled to run or not.
+
+ A delay of `delay` seconds is added between each job. This can help
+ to distribute the system load generated by the jobs more evenly over
+ time."""
+ default_scheduler.run_all(delay_seconds=delay_seconds)
+
+
+def clear():
+ """Deletes all scheduled jobs."""
+ default_scheduler.clear()
+
+
+def cancel_job(job):
+ """Delete a scheduled job."""
+ default_scheduler.cancel_job(job)
+
+
+def next_run():
+ """Datetime when the next job should run."""
+ return default_scheduler.next_run
+
+
+def idle_seconds():
+ """Number of seconds until `next_run`."""
+ return default_scheduler.idle_seconds
diff --git a/requirements.txt b/requirements.txt
index 1394418..5313a02 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,4 @@
stem==1.4.1b
PyYAML==3.11
pycrypto==2.6.1
-schedule==0.3.1
future==0.14.3
diff --git a/setup.py b/setup.py
index 19be4ef..15d14a8 100644
--- a/setup.py
+++ b/setup.py
@@ -43,7 +43,6 @@ setup(
'stem>=1.4.0-dev',
'PyYAML>=3.11',
'pycrypto>=2.6.1',
- 'schedule>=0.3.1',
'future>=0.14.0',
],
tests_require=['tox', 'pytest-mock', 'pytest', 'mock', 'pexpect'],
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/onionbalance.git
More information about the Pkg-privacy-commits
mailing list