Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 970db05

Browse files
author
A.Shpak
committedDec 7, 2024·
feat: add typing
1 parent a94c8bf commit 970db05

File tree

6 files changed

+46
-38
lines changed

6 files changed

+46
-38
lines changed
 

‎.github/workflows/tox.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
name: tox
22

3-
on: [push, pull_request]
3+
on: [ push, pull_request ]
44

55
jobs:
66
tox:
77

88
runs-on: ubuntu-latest
99
strategy:
1010
matrix:
11-
python-version: ["3.9", "3.10", "3.11", "3.12"]
11+
python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
1212

1313
steps:
1414
- uses: actions/checkout@v4
1515

1616
- name: Install uv
1717
uses: astral-sh/setup-uv@v3
1818
with:
19-
version: "0.4.18"
19+
version: "0.5"
2020
enable-cache: true
2121

2222
- name: Set up Python ${{ matrix.python-version }}

‎.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,6 @@ MANIFEST
5555
.epg_data/*
5656

5757
# uv
58-
.python-version
58+
.python-version
59+
60+
.venv/

‎aiocron/__init__.py

+38-26
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
# -*- coding: utf-8 -*-
22
from croniter.croniter import croniter
3+
import zoneinfo
34
from datetime import datetime
45
from functools import wraps, partial
56
from tzlocal import get_localzone
6-
from uuid import uuid4
7+
from uuid import uuid4, UUID
78
import time
89
import asyncio
910
import sys
1011
import inspect
12+
import typing as tp
1113

1214

13-
async def null_callback(*args):
15+
async def null_callback(*args: tp.Any) -> tuple[tp.Any, ...]:
1416
return args
1517

1618

@@ -30,52 +32,54 @@ async def wrapper(*args, **kwargs):
3032
class Cron(object):
3133
def __init__(
3234
self,
33-
spec,
34-
func=None,
35-
args=(),
36-
kwargs=None,
37-
start=False,
38-
uuid=None,
39-
loop=None,
40-
tz=None,
41-
croniter_kwargs=None,
42-
):
35+
spec: str,
36+
func: tp.Optional[tp.Callable[..., tp.Union[tp.Any, tp.Awaitable[tp.Any]]]] = None,
37+
args: tuple[tp.Any, ...] = (),
38+
kwargs: tp.Optional[tp.Mapping[str, tp.Any]] = None,
39+
start: bool = False,
40+
uuid: tp.Optional[UUID] = None,
41+
loop: tp.Optional[asyncio.AbstractEventLoop] = None,
42+
tz: tp.Optional[zoneinfo.ZoneInfo] = None,
43+
croniter_kwargs: tp.Optional[tp.Mapping[str, tp.Any]] = None,
44+
) -> None:
4345
self.spec = spec
4446
if func is not None:
4547
kwargs = kwargs or {}
4648
self.func = func if not (args or kwargs) else partial(func, *args, **kwargs)
4749
else:
4850
self.func = null_callback
4951
self.tz = get_localzone() if tz is None else tz
50-
self.cron = wrap_func(self.func)
52+
self.cron: tp.Callable[..., tp.Awaitable[tp.Any]] = wrap_func(self.func)
5153
self.auto_start = start
5254
self.uuid = uuid if uuid is not None else uuid4()
53-
self.handle = self.future = self.croniter = None
55+
self.handle = None
56+
self.future: tp.Optional[asyncio.Future] = None
57+
self.croniter: tp.Optional[croniter] = None
5458
self.loop = loop if loop is not None else asyncio.get_event_loop()
5559
if start and self.func is not null_callback:
5660
self.handle = self.loop.call_soon_threadsafe(self.start)
5761
self.croniter_kwargs = croniter_kwargs or {}
5862

59-
def start(self):
63+
def start(self) -> None:
6064
"""Start scheduling"""
6165
self.stop()
6266
self.initialize()
6367
self.handle = self.loop.call_at(self.get_next(), self.call_next)
6468

65-
def stop(self):
69+
def stop(self) -> None:
6670
"""Stop scheduling"""
6771
if self.handle is not None:
6872
self.handle.cancel()
6973
self.handle = self.future = self.croniter = None
7074

71-
async def next(self, *args):
75+
async def next(self, *args: tp.Any) -> tp.Any:
7276
"""yield from .next()"""
7377
self.initialize()
7478
self.future = asyncio.Future(loop=self.loop)
7579
self.handle = self.loop.call_at(self.get_next(), self.call_func, *args)
7680
return await self.future
7781

78-
def initialize(self):
82+
def initialize(self) -> None:
7983
"""Initialize croniter and related times"""
8084
if self.croniter is None:
8185
self.time = time.time()
@@ -85,19 +89,19 @@ def initialize(self):
8589
self.spec, start_time=self.datetime, **self.croniter_kwargs
8690
)
8791

88-
def get_next(self):
92+
def get_next(self) -> float:
8993
"""Return next iteration time related to loop time"""
9094
return self.loop_time + (self.croniter.get_next(float) - self.time)
9195

92-
def call_next(self):
96+
def call_next(self) -> None:
9397
"""Set next hop in the loop. Call task"""
9498
if self.handle is not None:
9599
self.handle.cancel()
96100
next_time = self.get_next()
97101
self.handle = self.loop.call_at(next_time, self.call_next)
98102
self.call_func()
99103

100-
def call_func(self, *args, **kwargs):
104+
def call_func(self, *args: tp.Any, **kwargs: tp.Any) -> None:
101105
"""Called. Take care of exceptions using gather"""
102106
"""Check the version of python installed"""
103107
if sys.version_info[0:2] >= (3, 10):
@@ -109,7 +113,7 @@ def call_func(self, *args, **kwargs):
109113
self.cron(*args, **kwargs), loop=self.loop, return_exceptions=True
110114
).add_done_callback(self.set_result)
111115

112-
def set_result(self, result):
116+
def set_result(self, result: asyncio.Future) -> None:
113117
"""Set future's result if needed (can be an exception).
114118
Else raise if needed."""
115119
result = result.result()[0]
@@ -122,22 +126,30 @@ def set_result(self, result):
122126
elif isinstance(result, Exception):
123127
raise result
124128

125-
def __call__(self, func):
129+
def __call__(self, func: tp.Callable[..., tp.Awaitable[tp.Any]]) -> 'Cron':
126130
"""Used as a decorator"""
127131
self.func = func
128132
self.cron = wrap_func(func)
129133
if self.auto_start:
130134
self.loop.call_soon_threadsafe(self.start)
131135
return self
132136

133-
def __str__(self):
137+
def __str__(self) -> str:
134138
return "{0.spec} {0.func}".format(self)
135139

136-
def __repr__(self):
140+
def __repr__(self) -> str:
137141
return "<Cron {0.spec} {0.func}>".format(self)
138142

139143

140-
def crontab(spec, func=None, args=(), kwargs=None, start=True, loop=None, tz=None):
144+
def crontab(
145+
spec: str,
146+
func: tp.Optional[tp.Callable[..., tp.Union[tp.Any, tp.Awaitable[tp.Any]]]] = None,
147+
args: tuple[tp.Any, ...] = (),
148+
kwargs: tp.Optional[tp.Mapping[str, tp.Any]] = None,
149+
start: bool = False,
150+
loop: tp.Optional[asyncio.AbstractEventLoop] = None,
151+
tz: tp.Optional[zoneinfo.ZoneInfo] = None,
152+
) -> Cron:
141153
return Cron(
142154
spec, func=func, args=args, kwargs=kwargs, start=start, loop=loop, tz=tz
143155
)

‎aiocron/__main__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import argparse
77

88

9-
def main():
9+
def main() -> None:
1010
parser = argparse.ArgumentParser()
1111
parser.prog = "python -m aiocron"
1212
parser.add_argument(

‎pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ classifiers = [
1818
"Programming Language :: Python :: 3.10",
1919
"Programming Language :: Python :: 3.11",
2020
"Programming Language :: Python :: 3.12",
21+
"Programming Language :: Python :: 3.13",
2122
"License :: OSI Approved :: MIT License",
2223
"Topic :: Software Development :: Libraries :: Python Modules",
2324
]
@@ -46,7 +47,6 @@ build-backend = "setuptools.build_meta"
4647
[tool.setuptools]
4748
packages = ["aiocron"]
4849

49-
5050
[tool.pytest.ini_options]
5151
addopts = "--cov aiocron --cov-report term-missing"
5252
testpaths = ["tests"]

‎uv.lock

-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
Please sign in to comment.