Skip to content

Commit

Permalink
Merge branch '3.x' into fix_wakeup_time
Browse files Browse the repository at this point in the history
  • Loading branch information
agronholm authored Aug 12, 2023
2 parents ab13a1f + a99ab4a commit 5022573
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 107 deletions.
28 changes: 21 additions & 7 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ on:
push:
tags:
- "[0-9]+.[0-9]+.[0-9]+"
- "[0-9]+.[0-9]+.[0-9].post[0-9]+"
- "[0-9]+.[0-9]+.[0-9][a-b][0-9]+"
- "[0-9]+.[0-9]+.[0-9]rc[0-9]+"
- "[0-9]+.[0-9]+.[0-9]+.post[0-9]+"
- "[0-9]+.[0-9]+.[0-9]+[a-b][0-9]+"
- "[0-9]+.[0-9]+.[0-9]+rc[0-9]+"

jobs:
publish:
build:
runs-on: ubuntu-latest
environment: release
steps:
- uses: actions/checkout@v3
- name: Set up Python
Expand All @@ -20,8 +21,21 @@ jobs:
- name: Install dependencies
run: pip install build
- name: Create packages
run: python -m build -s -w .
run: python -m build
- name: Archive packages
uses: actions/upload-artifact@v3
with:
name: dist
path: dist

publish:
needs: build
runs-on: ubuntu-latest
environment: release
permissions:
id-token: write
steps:
- name: Retrieve packages
uses: actions/download-artifact@v3
- name: Upload packages
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.pypi_password }}
28 changes: 13 additions & 15 deletions apscheduler/schedulers/qt.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
from __future__ import absolute_import

from importlib import import_module
from itertools import product

from apscheduler.schedulers.base import BaseScheduler

try:
from PyQt5.QtCore import QObject, QTimer
except (ImportError, RuntimeError): # pragma: nocover
for version, pkgname in product(range(6, 1, -1), ("PySide", "PyQt")):
try:
from PyQt4.QtCore import QObject, QTimer
qtcore = import_module(pkgname + str(version) + ".QtCore")
except ImportError:
try:
from PySide6.QtCore import QObject, QTimer # noqa
except ImportError:
try:
from PySide2.QtCore import QObject, QTimer # noqa
except ImportError:
try:
from PySide.QtCore import QObject, QTimer # noqa
except ImportError:
raise ImportError('QtScheduler requires either PyQt5, PyQt4, PySide6, PySide2 '
'or PySide installed')
pass
else:
QTimer = qtcore.QTimer
break
else:
raise ImportError(
"QtScheduler requires either PySide/PyQt (v6 to v2) installed"
)


class QtScheduler(BaseScheduler):
Expand Down
29 changes: 8 additions & 21 deletions apscheduler/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from datetime import date, datetime, time, timedelta, tzinfo
from calendar import timegm
from functools import partial
from inspect import isclass, ismethod
from inspect import isclass, isfunction, ismethod
import re
import sys

Expand Down Expand Up @@ -214,28 +214,15 @@ def get_callable_name(func):
:rtype: str
"""
# the easy case (on Python 3.3+)
if hasattr(func, '__qualname__'):
if ismethod(func):
self = func.__self__
cls = self if isclass(self) else type(self)
return f"{cls.__qualname__}.{func.__name__}"
elif isclass(func) or isfunction(func):
return func.__qualname__

# class methods, bound and unbound methods
f_self = getattr(func, '__self__', None) or getattr(func, 'im_self', None)
if f_self and hasattr(func, '__name__'):
f_class = f_self if isclass(f_self) else f_self.__class__
else:
f_class = getattr(func, 'im_class', None)

if f_class and hasattr(func, '__name__'):
return '%s.%s' % (f_class.__name__, func.__name__)

# class or class instance
if hasattr(func, '__call__'):
# class
if hasattr(func, '__name__'):
return func.__name__

elif hasattr(func, '__call__') and callable(func.__call__):
# instance of a class with a __call__ method
return func.__class__.__name__
return type(func).__qualname__

raise TypeError('Unable to determine a name for %r -- maybe it is not a callable?' % func)

Expand Down
4 changes: 2 additions & 2 deletions docs/modules/triggers/interval.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ You can use ``start_date`` and ``end_date`` to limit the total time in which the

The :meth:`~apscheduler.schedulers.base.BaseScheduler.scheduled_job` decorator works nicely too::


@sched.scheduled_job('interval', id='my_job_id', hours=2)
def job_function():
print("Hello World")
Expand All @@ -64,5 +64,5 @@ The ``jitter`` option enables you to add a random component to the execution tim
multiple servers and don't want them to run a job at the exact same moment or if you want to prevent multiple jobs
with similar options from always running concurrently::

# Run the `job_function` every hour with an extra-delay picked randomly in a [-120,+120] seconds window.
# Run the `job_function` every hour with an extra delay picked randomly between 0 and 120 seconds.
sched.add_job(job_function, 'interval', hours=1, jitter=120)
9 changes: 9 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ Version history
To find out how to migrate your application from a previous version of
APScheduler, see the :doc:`migration section <migration>`.

UNRELEASED
----------

* Ensured consistent support for both PySide and PyQt (v6 to v2) on QtScheduler
* Replaced uses of the deprecated ``pkg_resources`` module with ``importlib.metadata``
* Fixed scheduling class methods like ``B.methodname`` where the ``B`` class inherits
from class ``A`` and ``methodname`` is a class method of class ``A``


3.10.1
------

Expand Down
26 changes: 14 additions & 12 deletions examples/schedulers/qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,24 @@
from datetime import datetime
import signal
import sys
from importlib import import_module
from itertools import product

from apscheduler.schedulers.qt import QtScheduler

try:
from PyQt5.QtWidgets import QApplication, QLabel
except ImportError:
for version, pkgname in product(range(6, 1, -1), ("PySide", "PyQt")):
try:
from PyQt4.QtGui import QApplication, QLabel
qtwidgets = import_module(pkgname + str(version) + ".QtWidgets")
except ImportError:
try:
from PySide6.QtWidgets import QApplication, QLabel
except ImportError:
try:
from PySide2.QtWidgets import QApplication, QLabel
except ImportError:
from PySide.QtGui import QApplication, QLabel
pass
else:
QApplication = qtwidgets.QApplication
QLabel = qtwidgets.QLabel
break
else:
raise ImportError(
"Could not import the QtWidgets module from either PySide or PyQt"
)


def tick():
Expand All @@ -44,4 +46,4 @@ def tick():
scheduler.start()

# Execution will block here until the user closes the windows or Ctrl+C is pressed.
app.exec_()
app.exec()
11 changes: 1 addition & 10 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
# coding: utf-8
from datetime import datetime
import sys

import pytest
import pytz
from unittest.mock import Mock

from apscheduler.job import Job
from apscheduler.schedulers.base import BaseScheduler
from apscheduler.schedulers.blocking import BlockingScheduler

try:
from unittest.mock import Mock
except ImportError:
from mock import Mock


def pytest_ignore_collect(path, config):
return path.basename.endswith('_py35.py') and sys.version_info < (3, 5)


def minpython(*version):
version_str = '.'.join([str(num) for num in version])
Expand Down
53 changes: 35 additions & 18 deletions tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,22 @@
from datetime import date, datetime, timedelta, tzinfo
from functools import partial, wraps
from types import ModuleType
from unittest.mock import Mock

import pytest
import pytz
import six

from apscheduler.job import Job
from apscheduler.util import (asbool, asint, astimezone, check_callable_args,
convert_to_datetime, datetime_ceil,
datetime_repr, datetime_to_utc_timestamp,
get_callable_name, maybe_ref, obj_to_ref,
ref_to_obj, repr_escape, timedelta_seconds,
utc_timestamp_to_datetime)
from apscheduler.util import (
asbool, asint, astimezone, check_callable_args,
convert_to_datetime, datetime_ceil,
datetime_repr, datetime_to_utc_timestamp,
get_callable_name, iscoroutinefunction_partial, maybe_ref, obj_to_ref,
ref_to_obj, repr_escape, timedelta_seconds,
utc_timestamp_to_datetime,
)
from tests.conftest import maxpython, minpython

try:
from unittest.mock import Mock
except ImportError:
from mock import Mock


class DummyClass(object):
def meth(self):
Expand All @@ -45,7 +42,7 @@ def innerclassmeth(cls):
pass


class InheritedDummyClass(Job):
class InheritedDummyClass(DummyClass):
pass


Expand Down Expand Up @@ -182,15 +179,16 @@ def test_datetime_repr(input, expected):
class TestGetCallableName(object):
@pytest.mark.parametrize('input,expected', [
(asint, 'asint'),
(DummyClass.staticmeth, 'DummyClass.staticmeth' if
hasattr(DummyClass, '__qualname__') else 'staticmeth'),
(DummyClass.staticmeth, 'DummyClass.staticmeth'),
(DummyClass.classmeth, 'DummyClass.classmeth'),
(DummyClass.meth, 'meth' if sys.version_info[:2] == (3, 2) else 'DummyClass.meth'),
(DummyClass.meth, 'DummyClass.meth'),
(DummyClass().meth, 'DummyClass.meth'),
(DummyClass, 'DummyClass'),
(DummyClass(), 'DummyClass')
(DummyClass(), 'DummyClass'),
(InheritedDummyClass.classmeth, "InheritedDummyClass.classmeth"),
(DummyClass.InnerDummyClass.innerclassmeth, "DummyClass.InnerDummyClass.innerclassmeth")
], ids=['function', 'static method', 'class method', 'unbounded method', 'bounded method',
'class', 'instance'])
'class', 'instance', "class method in inherited", "inner class method"])
def test_inputs(self, input, expected):
assert get_callable_name(input) == expected

Expand Down Expand Up @@ -362,3 +360,22 @@ def wrapper(arg):
func()

check_callable_args(wrapper, (1,), {})


class TestIsCoroutineFunctionPartial:
@staticmethod
def not_a_coro(x):
pass

@staticmethod
async def a_coro(x):
pass

def test_non_coro(self):
assert not iscoroutinefunction_partial(self.not_a_coro)

def test_coro(self):
assert iscoroutinefunction_partial(self.a_coro)

def test_coro_partial(self):
assert iscoroutinefunction_partial(partial(self.a_coro, 1))
22 changes: 0 additions & 22 deletions tests/test_util_py35.py

This file was deleted.

0 comments on commit 5022573

Please sign in to comment.