diff --git a/django_apscheduler/jobstores.py b/django_apscheduler/jobstores.py index 13a592d..8e59c2b 100644 --- a/django_apscheduler/jobstores.py +++ b/django_apscheduler/jobstores.py @@ -18,22 +18,26 @@ LOGGER = logging.getLogger("django_apscheduler") -def ignore_database_error(func): - from functools import wraps +def ignore_database_error(on_error_value=None): - @wraps(func) - def inner(*a, **k): - try: - return func(*a, **k) - except (OperationalError, ProgrammingError) as e: - warnings.warn( - "Got OperationalError: {}. " - "Please, check that you have migrated the database via python manage.py migrate".format(e), - category=RuntimeWarning, - stacklevel=3 - ) + def dec(func): + from functools import wraps + + @wraps(func) + def inner(*a, **k): + try: + return func(*a, **k) + except (OperationalError, ProgrammingError) as e: + warnings.warn( + "Got OperationalError: {}. " + "Please, check that you have migrated the database via python manage.py migrate".format(e), + category=RuntimeWarning, + stacklevel=3 + ) + return on_error_value + return inner + return dec - return inner class DjangoJobStore(BaseJobStore): """ @@ -46,41 +50,43 @@ def __init__(self, pickle_protocol=pickle.HIGHEST_PROTOCOL): super(DjangoJobStore, self).__init__() self.pickle_protocol = pickle_protocol - @ignore_database_error + @ignore_database_error() def lookup_job(self, job_id): - logging.debug("Lookup for a job %s", job_id) + LOGGER.debug("Lookup for a job %s", job_id) try: job_state = DjangoJob.objects.get(name=job_id).job_state except DjangoJob.DoesNotExist: return None r = self._reconstitute_job(job_state) if job_state else None - logging.debug("Got %s", r) + LOGGER.debug("Got %s", r) return r - @ignore_database_error + @ignore_database_error(on_error_value=[]) def get_due_jobs(self, now): - logging.debug("get_due_jobs for time=%s", now) + LOGGER.debug("get_due_jobs for time=%s", now) try: out = self._get_jobs(next_run_time__lte=serialize_dt(now)) - logging.debug("Got %s", out) + LOGGER.debug("Got %s", out) return out except: - logging.exception("") + LOGGER.exception("Exception during getting jobs") + return [] + - @ignore_database_error + @ignore_database_error() def get_next_run_time(self): try: return deserialize_dt(DjangoJob.objects.first().next_run_time) except AttributeError: # no active jobs return None - @ignore_database_error + @ignore_database_error(on_error_value=[]) def get_all_jobs(self): jobs = self._get_jobs() self._fix_paused_jobs_sorting(jobs) return jobs - @ignore_database_error + @ignore_database_error() def add_job(self, job): if DjangoJob.objects.filter( name=job.id @@ -93,14 +99,14 @@ def add_job(self, job): job_state=pickle.dumps(job.__getstate__(), self.pickle_protocol) ) - @ignore_database_error + @ignore_database_error() def update_job(self, job): updated = DjangoJob.objects.filter(name=job.id).update( next_run_time=serialize_dt(job.next_run_time), job_state=pickle.dumps(job.__getstate__(), self.pickle_protocol) ) - logging.debug( + LOGGER.debug( "Update job %s: next_run_time=%s, job_state=%s", job, serialize_dt(job.next_run_time), @@ -109,16 +115,17 @@ def update_job(self, job): ) if updated == 0: - logging.info("Job with id %s not found", job.id) + LOGGER.info("Job with id %s not found", job.id) raise JobLookupError(job.id) - @ignore_database_error + @ignore_database_error() def remove_job(self, job_id): - deleted, _ = DjangoJob.objects.filter(name=job_id).delete() - if deleted == 0: - raise JobLookupError(job_id) + qs = DjangoJob.objects.filter(name=job_id) + if not qs.exists(): + LOGGER.warning("Job with id %s not found. Can't remove job.", job_id) + qs.delete() - @ignore_database_error + @ignore_database_error() def remove_all_jobs(self): with connections["default"].cursor() as c: c.execute(""" @@ -170,7 +177,7 @@ def __init__(self, storage=None): self.storage = storage or DjangoResultStorage() def __call__(self, event): - logging.debug("Got event: %s, %s, %s", + LOGGER.debug("Got event: %s, %s, %s", event, type(event), event.__dict__) # print event, type(event), event.__dict__ try: diff --git a/examples/example_apscheduler/example_apscheduler/settings.py b/examples/example_apscheduler/example_apscheduler/settings.py index 2cf2ac2..f1ed86c 100644 --- a/examples/example_apscheduler/example_apscheduler/settings.py +++ b/examples/example_apscheduler/example_apscheduler/settings.py @@ -120,3 +120,31 @@ # https://docs.djangoproject.com/en/1.11/howto/static-files/ STATIC_URL = '/static/' + +LOGGING = { + "version": 1, + "disable_existing_loggers": False, + 'formatters': { + 'default': { + 'format': '%(name)s:%(levelname)s %(asctime)s %(message)s' + } + }, + 'handlers': { + 'default': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'default' + } + }, + 'loggers': { + '': { + 'handlers': ['default'], + 'level': 'DEBUG', + 'propagate': False + } + } +} + + + + diff --git a/examples/example_apscheduler/example_apscheduler/urls.py b/examples/example_apscheduler/example_apscheduler/urls.py index 36410fc..2f0e0b7 100644 --- a/examples/example_apscheduler/example_apscheduler/urls.py +++ b/examples/example_apscheduler/example_apscheduler/urls.py @@ -22,3 +22,5 @@ ] import example_apscheduler.jobs # NOQA @isort:skip +import logging +logging.basicConfig(level="DEBUG") diff --git a/setup.py b/setup.py index dd552b5..268083f 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='django-apscheduler', - version='0.2.7', + version='0.2.8', description='APScheduler for Django', classifiers=[ "Development Status :: 4 - Beta", diff --git a/tests/test_jobstore.py b/tests/test_jobstore.py index 239453b..0aad6ab 100644 --- a/tests/test_jobstore.py +++ b/tests/test_jobstore.py @@ -60,6 +60,14 @@ def test_add_job(db, scheduler): assert DjangoJob.objects.count() == 1 +def test_issue_20(db, scheduler): + assert isinstance(scheduler, DebugScheduler) + scheduler.add_job(job, trigger="interval", seconds=1, id="job") + scheduler.start() + assert DjangoJob.objects.count() == 1 + scheduler.remove_job("job") + assert DjangoJob.objects.count() == 0 + @pytest.mark.target def test_remove_job(db, scheduler): diff --git a/tox.ini b/tox.ini index 0cdd36c..f5509e2 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = {py36}-{dj1,dj2},{py27-dj1},{py35}-{dj1,dj2} +envlist = {py36}-{dj1,dj18,dj2},{py27-dj1,dj18},{py35}-{dj1,dj18,dj2} skipsdist = true [pytest] @@ -10,6 +10,7 @@ deps = pytest pytest-django pytest-cov mock + dj18: Django==1.8 dj1: Django<2.0.0 dj2: Django==2.*