Skip to content

Commit

Permalink
Merge pull request #37 from ikalnytskyi/flaskscopes-fixes
Browse files Browse the repository at this point in the history
Isolate dependencies between Flask scopes
  • Loading branch information
ikalnytskyi authored Dec 24, 2018
2 parents 2b0a894 + 801a43d commit 3d1a154
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 2 deletions.
21 changes: 19 additions & 2 deletions src/picobox/contrib/flaskscopes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Scopes for Flask framework."""

import uuid

import picobox
import flask

Expand All @@ -9,17 +11,32 @@ class _flaskscope(picobox.Scope):

_store = None

def __init__(self):
# Both application and request scopes are merely proxies to
# corresponding storage objects in Flask. This means multiple
# scope instances will share the same storage object under the
# hood, and this is not what we want. So we need to generate
# some unique key per scope instance and use that key to
# distinguish dependencies stored by different scope instances.
self._uuid = str(uuid.uuid4())

def set(self, key, value):
try:
dependencies = self._store.__dependencies__
except AttributeError:
dependencies = self._store.__dependencies__ = {}

try:
dependencies = dependencies[self._uuid]
except KeyError:
dependencies = dependencies.setdefault(self._uuid, {})

dependencies[key] = value

def get(self, key):
try:
rv = self._store.__dependencies__[key]
except AttributeError:
rv = self._store.__dependencies__[self._uuid][key]
except (AttributeError, KeyError):
raise KeyError(key)
return rv

Expand Down
27 changes: 27 additions & 0 deletions tests/contrib/test_flaskscopes.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,33 @@ def test_scope_get_keyerror(request, scopename, ctx, supported_key):
scope.get(supported_key)


@pytest.mark.parametrize('scopename, ctx', [
('appscope', 'appcontext'),
('reqscope', 'reqcontext'),
])
def test_scope_state_not_leaked(request, scopename, ctx):
ctxfn = request.getfixturevalue(ctx)

scope_a = request.getfixturevalue(scopename)
value_a = object()

scope_b = type(scope_a)()
value_b = object()

with ctxfn():
scope_a.set('the-key', value_a)
assert scope_a.get('the-key') is value_a

with pytest.raises(KeyError, match='the-key'):
scope_b.get('the-key')

scope_b.set('the-key', value_b)
assert scope_b.get('the-key') is value_b

scope_a.set('the-key', value_a)
assert scope_a.get('the-key') is value_a


@pytest.mark.parametrize('scopename, ctx', [
('appscope', 'appcontext'),
])
Expand Down

0 comments on commit 3d1a154

Please sign in to comment.