Skip to content

Commit

Permalink
Merge pull request #38 from Paillat-dev/master
Browse files Browse the repository at this point in the history
Modernize project: Use pyproject.toml, uv package manager, update Python support, and revise examples
  • Loading branch information
gawel authored Oct 15, 2024
2 parents b396c78 + 22adfaf commit a94c8bf
Show file tree
Hide file tree
Showing 15 changed files with 590 additions and 548 deletions.
25 changes: 16 additions & 9 deletions .github/workflows/tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,22 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python: [3.7, 3.8, 3.9, "3.10", "3.11"]
python-version: ["3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v4
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
python-version: ${{ matrix.python }}
- name: Install Tox and any other packages
run: pip install tox
- name: Run Tox
run: tox -e py
version: "0.4.18"
enable-cache: true

- name: Set up Python ${{ matrix.python-version }}
run: uv python install ${{ matrix.python-version }}

- name: Install the project
run: uv sync --extra test

- name: Run tests
run: uv run tox -e py
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@ MANIFEST




.DS_Store

# Jupyter Notebook
.ipynb_checkpoints

.epg_data/*

# uv
.python-version
13 changes: 5 additions & 8 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
graft docs
prune docs/_build
prune .github/
include LICENSE *.rst *.cfg *.ini pyproject.toml uv.lock .coveragerc
graft aiocron
graft tests
include Pipfile Pipfile.lock *.rst *.cfg *.ini LICENSE
global-exclude *.pyc
global-exclude __pycache__
include .coveragerc
graft docs
prune docs/_build
prune .github
recursive-include examples *.py
exclude .installed.cfg
global-exclude *.pyc __pycache__
18 changes: 0 additions & 18 deletions Pipfile

This file was deleted.

368 changes: 0 additions & 368 deletions Pipfile.lock

This file was deleted.

42 changes: 25 additions & 17 deletions aiocron/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ async def null_callback(*args):

def wrap_func(func):
"""wrap in a coroutine"""

@wraps(func)
async def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
Expand All @@ -27,9 +28,18 @@ async def wrapper(*args, **kwargs):


class Cron(object):

def __init__(self, spec, func=None, args=(), kwargs=None, start=False,
uuid=None, loop=None, tz=None, croniter_kwargs=None):
def __init__(
self,
spec,
func=None,
args=(),
kwargs=None,
start=False,
uuid=None,
loop=None,
tz=None,
croniter_kwargs=None,
):
self.spec = spec
if func is not None:
kwargs = kwargs or {}
Expand Down Expand Up @@ -63,7 +73,7 @@ async def next(self, *args):
self.initialize()
self.future = asyncio.Future(loop=self.loop)
self.handle = self.loop.call_at(self.get_next(), self.call_func, *args)
return (await self.future)
return await self.future

def initialize(self):
"""Initialize croniter and related times"""
Expand All @@ -72,9 +82,7 @@ def initialize(self):
self.datetime = datetime.now(self.tz)
self.loop_time = self.loop.time()
self.croniter = croniter(
self.spec,
start_time=self.datetime,
**self.croniter_kwargs
self.spec, start_time=self.datetime, **self.croniter_kwargs
)

def get_next(self):
Expand All @@ -92,16 +100,14 @@ def call_next(self):
def call_func(self, *args, **kwargs):
"""Called. Take care of exceptions using gather"""
"""Check the version of python installed"""
if (sys.version_info[0:2] >= (3, 10)):
if sys.version_info[0:2] >= (3, 10):
asyncio.gather(
self.cron(*args, **kwargs),
return_exceptions=True
).add_done_callback(self.set_result)
self.cron(*args, **kwargs), return_exceptions=True
).add_done_callback(self.set_result)
else:
asyncio.gather(
self.cron(*args, **kwargs),
loop=self.loop, return_exceptions=True
).add_done_callback(self.set_result)
self.cron(*args, **kwargs), loop=self.loop, return_exceptions=True
).add_done_callback(self.set_result)

def set_result(self, result):
"""Set future's result if needed (can be an exception).
Expand All @@ -125,11 +131,13 @@ def __call__(self, func):
return self

def __str__(self):
return '{0.spec} {0.func}'.format(self)
return "{0.spec} {0.func}".format(self)

def __repr__(self):
return '<Cron {0.spec} {0.func}>'.format(self)
return "<Cron {0.spec} {0.func}>".format(self)


def crontab(spec, func=None, args=(), kwargs=None, start=True, loop=None, tz=None):
return Cron(spec, func=func, args=args, kwargs=kwargs, start=start, loop=loop, tz=tz)
return Cron(
spec, func=func, args=args, kwargs=kwargs, start=start, loop=loop, tz=tz
)
15 changes: 9 additions & 6 deletions aiocron/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,21 @@
def main():
parser = argparse.ArgumentParser()
parser.prog = "python -m aiocron"
parser.add_argument("-n", type=int, default=1,
help="loop N times. 0 for infinite loop", )
parser.add_argument(
"-n",
type=int,
default=1,
help="loop N times. 0 for infinite loop",
)
parser.add_argument("crontab", help='quoted crontab. like "* * * * *"')
parser.add_argument("command", nargs='+',
help="shell command to run")
parser.add_argument("command", nargs="+", help="shell command to run")
args = parser.parse_args()

cron = args.crontab
try:
croniter(cron)
except ValueError:
parser.error('Invalid cron format')
parser.error("Invalid cron format")

cmd = args.command

Expand All @@ -43,5 +46,5 @@ def calback():
pass


if __name__ == '__main__': # pragma: no cover
if __name__ == "__main__": # pragma: no cover
main()
42 changes: 21 additions & 21 deletions examples/simple.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,49 @@
# -*- coding: utf-8 -*-
from aiocron import crontab
from aiocron import asyncio
import asyncio
import logging

logging.basicConfig()
logging.basicConfig(level=logging.DEBUG)

loop = asyncio.get_event_loop()
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)


@crontab('* * * * * */3')
@crontab("* * * * * */3", loop=loop)
def mycron():
print('function')

print("function")

@crontab('* * * * * */2', start=False)
@crontab("* * * * * */2", start=False, loop=loop)
def mycron2(i):
if i == 2:
raise ValueError(i)
return 'yielded function (%i)' % i

return f"yielded function ({i})"

@asyncio.coroutine
def main():
cron = crontab('* * * * * */2')
async def main():
cron = crontab("* * * * * */2", loop=loop)
for i in range(3):
try:
yield from cron.next()
await cron.next()
except Exception:
pass
else:
print('yielded (%i)' % i)
print(f"yielded ({i})")

for i in range(3):
try:
res = yield from mycron2.next(i)
res = await mycron2.next(i)
except Exception as e:
print(repr(e))
else:
print(res)


loop.run_until_complete(main())
if __name__ == "__main__":
try:
loop.run_until_complete(main())
finally:
loop.close()

"""
Will print:
Expected output (may vary slightly due to timing):
yielded (0)
function
Expand All @@ -53,5 +53,5 @@ def main():
yielded function (0)
function
yielded function (1)
yielded function (2)
"""
ValueError(2)
"""
32 changes: 19 additions & 13 deletions examples/threaded.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,40 @@


class CronThread(threading.Thread):

def __init__(self):
super(CronThread, self).__init__()
self.loop = None
self.start()
time.sleep(.1)
time.sleep(0.1) # Give time for the loop to start

def run(self):
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
self.loop.run_forever()
self.loop.close()

def stop(self):
self.loop.call_soon_threadsafe(self.loop.stop)
self.join()
if self.loop:
self.loop.call_soon_threadsafe(self.loop.stop)
self.join()
self.loop.close()

def crontab(self, *args, **kwargs):
kwargs['loop'] = self.loop
kwargs["loop"] = self.loop
return aiocron.crontab(*args, **kwargs)


cron = CronThread()


@cron.crontab('* * * * * *')
@asyncio.coroutine
def run():
yield from asyncio.sleep(.1)
print('It works')
@cron.crontab("* * * * * *")
async def run():
await asyncio.sleep(0.1)
print("It works")


asyncio.get_event_loop().run_forever()
cron.stop()
# Run for a short time then stop
try:
time.sleep(5) # Let it run for 5 seconds
finally:
cron.stop()
print("Cron stopped")
Loading

0 comments on commit a94c8bf

Please sign in to comment.