Skip to content

Commit

Permalink
Make Flask extension compatible with Flask>=2.2.0
Browse files Browse the repository at this point in the history
Flask 2.2.0 deprecated the context stack in favour of using Python
ContextVar, and introduced a fake stack for some sort of backward
compatibility. However, it is not compatible enough for us as the
existing code would blindly push context replacing any existing
one, which will break application code.

The extension now does the recommended action of storing its data on
flask.g - in this case we store the context that needs to be popped
since _app_ctx_stack is deprecated.

There are no existing tests for this extension but I've tested it
locally to my satisfaction.

Fixes: #24
  • Loading branch information
bigjools committed Oct 13, 2022
1 parent 2d44004 commit aedbdd7
Showing 1 changed file with 34 additions and 5 deletions.
39 changes: 34 additions & 5 deletions spinach/contrib/flask_spinach.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import click
from flask import current_app, _app_ctx_stack
import flask

import spinach
from spinach import signals
from spinach.const import DEFAULT_WORKER_NUMBER, DEFAULT_QUEUE


class Spinach:

def __init__(self, app=None):
self.app = app
if app is not None:
Expand Down Expand Up @@ -38,19 +38,48 @@ def spinach_run_workers(threads, queue, stop_when_queue_empty):

@signals.job_started.connect_via(namespace)
def job_started(*args, job=None, **kwargs):
app.app_context().push()
if not flask.has_app_context():
ctx = app.app_context()
ctx.push()
flask.g.spinach_ctx = ctx
self.job_started(job)

@signals.job_finished.connect_via(namespace)
def job_finished(*args, job=None, **kwargs):
_app_ctx_stack.pop()
self.job_finished(job)
try:
flask.g.spinach_ctx.pop()
except AttributeError:
# This means we didn't create the context. Ignore.
pass

@classmethod
def job_started(cls, *args, job=None, **kwargs):
"""Callback for subclasses to receive job_started signals.
There's no guarantee of ordering for Signal's callbacks,
so use this callback instead to make sure the app context
was pushed.
"""
pass

@classmethod
def job_finished(cls, *args, job=None, **kwargs):
"""Callback for subclasses to receive job_finished signals.
There's no guarantee of ordering for Signal's callbacks,
so use this callback instead to make sure the app context
was pushed.
"""
pass

@property
def spin(self):
if self.app is not None:
return self.app.extensions['spinach']

try:
return current_app.extensions['spinach']
return flask.current_app.extensions['spinach']
except (AttributeError, TypeError, KeyError):
raise RuntimeError('Spinach extension not initialized. '
'Did you forget to call init_app?')
Expand Down

0 comments on commit aedbdd7

Please sign in to comment.