Skip to content

Commit 2fef54e

Browse files
authored
Merge pull request #25 from taskbadger/sk/scope
add concept of scope to store global task data
2 parents a7f154b + 38b0ffe commit 2fef54e

File tree

4 files changed

+94
-3
lines changed

4 files changed

+94
-3
lines changed

taskbadger/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from .decorators import track
22
from .integrations import Action, EmailIntegration, WebhookIntegration
33
from .internal.models import StatusEnum
4-
from .mug import Session
4+
from .mug import Badger, Session
55
from .safe_sdk import create_task_safe, update_task_safe
66
from .sdk import DefaultMergeStrategy, Task, create_task, get_task, init, update_task
77

@@ -15,3 +15,7 @@
1515
__version__ = importlib_metadata.version(__name__)
1616
except importlib_metadata.PackageNotFoundError:
1717
__version__ = "dev"
18+
19+
20+
def current_scope():
21+
return Badger.current.scope()

taskbadger/mug.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,29 @@ def __exit__(self, *args, **kwargs):
7373
self.client = None
7474

7575

76+
class Scope:
77+
"""Scope holds global data which will be added to every task created within the current scope.
78+
79+
Scope data will be merged with task data when creating a task where data provided directly to the task
80+
will override scope data.
81+
"""
82+
83+
def __init__(self):
84+
self.stack = []
85+
self.context = {}
86+
87+
def __enter__(self):
88+
self.stack.append(self.context)
89+
self.context = self.context.copy()
90+
return self
91+
92+
def __exit__(self, *args):
93+
self.context = self.stack.pop()
94+
95+
def __setitem__(self, key, value):
96+
self.context[key] = value
97+
98+
7699
class MugMeta(type):
77100
@property
78101
def current(cls):
@@ -94,6 +117,7 @@ def __init__(self, settings_or_mug=None):
94117
self.settings = settings_or_mug
95118

96119
self._session = ReentrantSession()
120+
self._scope = Scope()
97121

98122
def bind(self, settings):
99123
self.settings = settings
@@ -104,6 +128,9 @@ def session(self) -> ReentrantSession:
104128
def client(self) -> AuthenticatedClient:
105129
return self.settings.get_client()
106130

131+
def scope(self) -> Scope:
132+
return self._scope
133+
107134
@classmethod
108135
def is_configured(cls):
109136
return cls.current.settings is not None

taskbadger/sdk.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,10 @@ def create_task(
100100
task = TaskRequest(
101101
name=name, status=status, value=value, value_max=value_max, max_runtime=max_runtime, stale_timeout=stale_timeout
102102
)
103-
if data:
104-
task.data = TaskRequestData.from_dict(data)
103+
scope_data = Badger.current.scope().context
104+
if scope_data or data:
105+
data = data or {}
106+
task.data = TaskRequestData.from_dict({**scope_data, **data})
105107
if actions:
106108
task.additional_properties = {"actions": [a.to_dict() for a in actions]}
107109
kwargs = _make_args(json_body=task)

tests/test_scope.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import random
2+
import threading
3+
import time
4+
5+
import pytest
6+
7+
from taskbadger import create_task, init
8+
from taskbadger.mug import GLOBAL_MUG, Badger
9+
from tests.test_sdk_primatives import _json_task_response, _verify_task
10+
11+
12+
def test_scope_singleton():
13+
assert Badger.current == GLOBAL_MUG
14+
scope = Badger.current.scope()
15+
assert scope.context == {}
16+
assert scope.stack == []
17+
assert scope == Badger.current.scope()
18+
19+
20+
def test_scope_context():
21+
scope = Badger.current.scope()
22+
assert scope.context == {}
23+
assert scope.stack == []
24+
with scope:
25+
assert scope.stack == [{}]
26+
scope.context["foo"] = "bar"
27+
with scope:
28+
assert scope.stack == [{}, {"foo": "bar"}]
29+
assert scope.context == {"foo": "bar"}
30+
scope.context["bar"] = "bazz"
31+
with scope:
32+
assert scope.context == {"foo": "bar", "bar": "bazz"}
33+
scope.context.clear()
34+
assert scope.context == {"foo": "bar"}
35+
assert scope.stack == [{}]
36+
assert scope.context == {}
37+
assert scope.stack == []
38+
39+
40+
@pytest.fixture(autouse=True)
41+
def init_skd():
42+
init("org", "project", "token")
43+
44+
45+
def test_create_task_with_scope(httpx_mock):
46+
with Badger.current.scope() as scope:
47+
scope["foo"] = "bar"
48+
scope["bar"] = "bazz"
49+
httpx_mock.add_response(
50+
url="https://taskbadger.net/api/org/project/tasks/",
51+
method="POST",
52+
match_headers={"Authorization": "Bearer token"},
53+
match_content=b'{"name": "name", "status": "pending", "data": {"foo": "bar", "bar": "buzzer"}}',
54+
json=_json_task_response(),
55+
status_code=201,
56+
)
57+
task = create_task("name", data={"bar": "buzzer"})
58+
_verify_task(task)

0 commit comments

Comments
 (0)