This repository has been archived by the owner on Oct 17, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit be05d09
Showing
11 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
build/ | ||
*.egg-info/ | ||
dist/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
2.7.9 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2015 hbc | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# Donkey [WIP] | ||
|
||
A simple cron-like library for executing scheduled jobs. | ||
|
||
|
||
Donkey is inspired by [Cron][cron-go]. | ||
|
||
[cron-go]: https://github.com/robfig/cron | ||
|
||
|
||
```python | ||
from datetime import datetime | ||
from donkey import JobQueue, Worker | ||
|
||
|
||
q = JobQueue() | ||
|
||
|
||
@q.job(3) | ||
def this_job_runs_every_3_seconds(): | ||
print('Fuzz', datetime.now()) | ||
|
||
|
||
@q.job(5) | ||
def this_job_runs_every_5_seconds(): | ||
print('Buzz', datetime.now()) | ||
|
||
|
||
Worker().run(q) | ||
# Fuzz 2015-02-03 16:41:01.408136 | ||
# Buzz 2015-02-03 16:41:03.404123 | ||
# Fuzz 2015-02-03 16:41:04.406813 | ||
# Fuzz 2015-02-03 16:41:07.408426 | ||
# Buzz 2015-02-03 16:41:08.406851 | ||
# Fuzz 2015-02-03 16:41:10.408415 | ||
# Fuzz 2015-02-03 16:41:13.403260 | ||
# Buzz 2015-02-03 16:41:13.403319 | ||
``` | ||
|
||
## TODO | ||
|
||
- [ ] tests. | ||
- [ ] add jobs at run time. | ||
- [ ] job states & stats (see [rq][rq]). | ||
- [ ] other backend (namely `thread`, `stackless`) support. | ||
|
||
|
||
[rq]: http://python-rq.org/ | ||
|
||
|
||
## License | ||
|
||
[MIT](LICENSE) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# coding: utf-8 | ||
|
||
''' | ||
donkey | ||
~~~~~~~ | ||
A simple cron-like library for executing scheduled jobs. | ||
''' | ||
|
||
from .worker import Worker | ||
from .job import Schedule, Job, JobQueue | ||
|
||
|
||
__all__ = ['Worker', 'Schedule', 'Job', 'JobQueue'] | ||
|
||
|
||
__version__ = '0.0.1' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# coding: utf-8 | ||
|
||
''' | ||
donkey.compat | ||
~~~~~~~~~~~~~ | ||
Compatibility. | ||
''' | ||
|
||
import gevent | ||
|
||
|
||
def spawn(func, *args, **kwargs): | ||
'''Spawn a function. | ||
By now only `gevent` is supported. | ||
:param func: function to be ran. | ||
''' | ||
return gevent.spawn(func, *args, **kwargs) | ||
|
||
|
||
def sleep(seconds): | ||
'''Sleep for some times. | ||
:param seconds: seonds you want to sleep. | ||
''' | ||
return gevent.sleep(seconds) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
# coding: utf-8 | ||
|
||
''' | ||
donkey.job | ||
~~~~~~~~~~ | ||
Donkey job data structure. | ||
''' | ||
|
||
from .compat import spawn | ||
|
||
|
||
class Schedule(object): | ||
'''Job schedule defination. | ||
:param interval: execute interval (in seconds). | ||
''' | ||
|
||
def __init__(self, interval): | ||
# TODO support fuzzy language / crontab format | ||
self.interval = int(interval) | ||
|
||
def next(self, now): | ||
'''Calculate next runnable time (in unix timestamp). | ||
:param now: current unix timestamp. | ||
''' | ||
return now + self.interval | ||
|
||
|
||
class Job(object): | ||
'''Scheduled job. | ||
TODO job state | ||
:param exec_: the actual job. | ||
:param schedule: a :class:`donkey.Schedule` instance. | ||
''' | ||
|
||
def __init__(self, exec_, schedule): | ||
self.exec_ = exec_ | ||
self.schedule = schedule | ||
|
||
self._last_run_at = None | ||
self._next_run_at = None | ||
|
||
def run(self): | ||
'''Execute the job''' | ||
rv = self.exec_() | ||
|
||
return rv | ||
|
||
def reschedule(self, now): | ||
'''Reschedule the job base on current timestamp. | ||
:param now: current unix timestamp. | ||
''' | ||
self._last_run_at = self.next_run_at | ||
self._next_run_at = self.schedule.next(now) | ||
|
||
@property | ||
def last_run_at(self): | ||
'''Last ran timestamp.''' | ||
return self._last_run_at | ||
|
||
@property | ||
def next_run_at(self): | ||
'''Next ran timestamp.''' | ||
return self._next_run_at | ||
|
||
|
||
class JobQueue(object): | ||
|
||
def __init__(self): | ||
# TODO race condition | ||
self.jobs = [] | ||
|
||
def add(self, job): | ||
'''Add a job to the queue. | ||
:param job: a :class:`donkey.Job` instance. | ||
''' | ||
self.jobs.append(job) | ||
|
||
def job(self, interval): | ||
'''Return a decorator for adding a function as job: | ||
@queue.job(300) | ||
def my_job(): | ||
print('I am working...') | ||
:param interval: execute interval (in seconds). | ||
''' | ||
def wrapper(func): | ||
job = Job(func, Schedule(interval)) | ||
|
||
return self.add(job) | ||
|
||
return wrapper | ||
|
||
def get_runnable_jobs(self): | ||
'''Get runnable jobs.''' | ||
# Sort by next run time, sooner the better. | ||
self.jobs.sort(key=lambda j: j.next_run_at) | ||
|
||
return self.jobs | ||
|
||
@property | ||
def next_run_at(self): | ||
'''Get next running unix timestamp.''' | ||
jobs = self.get_runnable_jobs() | ||
|
||
if not jobs: | ||
return None | ||
return jobs[0].next_run_at | ||
|
||
def run_jobs(self, now): | ||
'''Execute jobs run at this moment. | ||
:param now: current unix timestamp. | ||
''' | ||
for job in self.jobs: | ||
if job.next_run_at != now: | ||
return | ||
spawn(job.run) | ||
job.reschedule(now) | ||
|
||
def reschedule_jobs(self, now): | ||
'''Reschedule all jobs base on current timestamp. | ||
:param now: current unix timestamp. | ||
''' | ||
for job in self.jobs: | ||
job.reschedule(now) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# coding: utf-8 | ||
|
||
''' | ||
donkey.worker | ||
~~~~~~~~~~~~~ | ||
Donkey worker. | ||
''' | ||
|
||
from time import time as get_now_timestamp | ||
|
||
from .compat import sleep | ||
|
||
|
||
A_LONG_TIME = 10 * 365 * 24 * 3600 # 10 years | ||
|
||
|
||
class Worker(object): | ||
|
||
def run(self, queue): | ||
'''Let the worker run. | ||
:param queue: :class:`donkey.JobQueue` instance. | ||
''' | ||
queue.reschedule_jobs(get_now_timestamp()) | ||
|
||
while True: | ||
effective = queue.next_run_at or A_LONG_TIME | ||
|
||
# Wait until next schedule comes... | ||
sleep(effective - get_now_timestamp()) | ||
queue.run_jobs(effective) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
gevent |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# coding: utf-8 | ||
|
||
from setuptools import setup, find_packages | ||
|
||
README = open('README.md').read() | ||
CHANGES = open('CHANGES.md').read() | ||
|
||
|
||
setup( | ||
name='donkey', | ||
version='0.0.1', | ||
|
||
author='hbc', | ||
author_email='[email protected]', | ||
url='https://github.com/bcho/donkey', | ||
|
||
description='A simple cron-like library for executing scheduled jobs.', | ||
long_description='\n'.join((README, CHANGES)), | ||
license='MIT', | ||
|
||
packages=find_packages(exclude=['tests']), | ||
include_package_data=True, | ||
install_requires=[ | ||
'gevent', | ||
], | ||
|
||
classifiers=[ | ||
'Development Status :: 4 - Beta', | ||
'Operating System :: POSIX :: Linux', | ||
'Programming Language :: Python :: 3', | ||
'Programming Language :: Python :: 2', | ||
'Topic :: Software Development :: Libraries :: Python Modules', | ||
'Intended Audience :: Developers', | ||
'License :: OSI Approved :: MIT License', | ||
] | ||
) |