sg-python-api 0.1.2__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sg_python_api-0.1.2.dist-info → sg_python_api-0.2.0.dist-info}/METADATA +21 -24
- sg_python_api-0.2.0.dist-info/RECORD +58 -0
- shotgun_api3/__init__.py +4 -0
- shotgun_api3/_async_client.py +347 -281
- shotgun_api3/_async_driver.py +25 -55
- shotgun_api3/_async_httpx2.py +92 -59
- shotgun_api3/_async_transport.py +38 -3
- shotgun_api3/_capabilities.py +4 -3
- shotgun_api3/_client.py +296 -422
- shotgun_api3/_config.py +245 -120
- shotgun_api3/_connections.py +144 -0
- shotgun_api3/_crud.py +25 -250
- shotgun_api3/_driver.py +29 -54
- shotgun_api3/_execution.py +1 -3
- shotgun_api3/_files.py +193 -21
- shotgun_api3/_methods.py +59 -411
- shotgun_api3/_protocol.py +27 -53
- shotgun_api3/_transport.py +82 -407
- shotgun_api3/_version.py +7 -2
- shotgun_api3/lib/mockgun/__init__.py +20 -7
- shotgun_api3/lib/mockgun/_activity.py +510 -0
- shotgun_api3/lib/mockgun/_async_client.py +196 -0
- shotgun_api3/lib/mockgun/_dates.py +164 -0
- shotgun_api3/lib/mockgun/_errors.py +18 -0
- shotgun_api3/lib/mockgun/_events.py +1031 -0
- shotgun_api3/lib/mockgun/_follows.py +217 -0
- shotgun_api3/lib/mockgun/_handlers.py +558 -0
- shotgun_api3/lib/mockgun/_links.py +440 -0
- shotgun_api3/lib/mockgun/_new_client.py +92 -0
- shotgun_api3/lib/mockgun/_presets.py +492 -0
- shotgun_api3/lib/mockgun/_query.py +2163 -0
- shotgun_api3/lib/mockgun/_retirement.py +368 -0
- shotgun_api3/lib/mockgun/_schema.py +172 -0
- shotgun_api3/lib/mockgun/_schema_writes.py +425 -0
- shotgun_api3/lib/mockgun/_search.py +378 -0
- shotgun_api3/lib/mockgun/_server.py +169 -0
- shotgun_api3/lib/mockgun/_sessions.py +206 -0
- shotgun_api3/lib/mockgun/_site.py +150 -0
- shotgun_api3/lib/mockgun/_site_data.py +584 -0
- shotgun_api3/lib/mockgun/_storage.py +208 -0
- shotgun_api3/lib/mockgun/_store.py +1446 -0
- shotgun_api3/lib/mockgun/_summaries.py +964 -0
- shotgun_api3/lib/mockgun/_sync_client.py +190 -0
- shotgun_api3/lib/mockgun/_threads.py +226 -0
- shotgun_api3/lib/mockgun/_thumbnails.py +146 -0
- shotgun_api3/lib/mockgun/_transport.py +145 -0
- shotgun_api3/lib/mockgun/_uploads.py +417 -0
- shotgun_api3/lib/mockgun/_wire.py +120 -0
- shotgun_api3/lib/mockgun/mockgun.py +14 -0
- shotgun_api3/lib/sgtimezone.py +24 -1
- sg_python_api-0.1.2.dist-info/RECORD +0 -31
- shotgun_api3/_signatures.py +0 -53
- shotgun_api3/lib/mockgun/_async.py +0 -315
- shotgun_api3/lib/mockgun/_mockgun.py +0 -1666
- {sg_python_api-0.1.2.dist-info → sg_python_api-0.2.0.dist-info}/WHEEL +0 -0
- {sg_python_api-0.1.2.dist-info → sg_python_api-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""Emulate observed follow operations and Note auto-follow side effects."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from typing import cast
|
|
6
|
+
|
|
7
|
+
from ._errors import MockgunError
|
|
8
|
+
from ._links import display_name
|
|
9
|
+
from ._sessions import RequestContext
|
|
10
|
+
from ._store import Row, Store
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _reference(store: Store, operation: str, value: object, role: str) -> tuple[str, int, Row]:
|
|
14
|
+
"""Require an active declared record for a follow argument."""
|
|
15
|
+
if not isinstance(value, dict):
|
|
16
|
+
raise MockgunError(f'mockgun cannot {operation} {role}: the entity link is unsettled')
|
|
17
|
+
link = cast('dict[str, object]', value)
|
|
18
|
+
kind = link.get('type')
|
|
19
|
+
identity = link.get('id')
|
|
20
|
+
if not isinstance(kind, str) or not isinstance(identity, int) or isinstance(identity, bool):
|
|
21
|
+
raise MockgunError(
|
|
22
|
+
f'mockgun cannot {operation} {role} {kind} {identity}: invalid identity'
|
|
23
|
+
)
|
|
24
|
+
if role == 'user' and kind != 'HumanUser':
|
|
25
|
+
raise MockgunError(
|
|
26
|
+
f'mockgun cannot {operation} user {kind} {identity}: expected HumanUser'
|
|
27
|
+
)
|
|
28
|
+
if not store.schema.declares(kind):
|
|
29
|
+
raise MockgunError(f'mockgun cannot {operation} {kind} {identity}: undeclared type')
|
|
30
|
+
row = store.row(kind, identity)
|
|
31
|
+
if row is None or row['__retired']:
|
|
32
|
+
raise MockgunError(f'mockgun cannot {operation} {kind} {identity}: missing or retired')
|
|
33
|
+
return kind, identity, row
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _pair(store: Store, operation: str, params: Mapping[str, object]) -> tuple[int, str, int]:
|
|
37
|
+
"""Validate the user and target of a follow write."""
|
|
38
|
+
_, human_id, _ = _reference(store, operation, params.get('user'), 'user')
|
|
39
|
+
kind, identity, _ = _reference(store, operation, params.get('entity'), 'entity')
|
|
40
|
+
return human_id, kind, identity
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _project(store: Store, params: Mapping[str, object]) -> dict[str, object] | None:
|
|
44
|
+
"""Validate an optional Project restriction for a following query."""
|
|
45
|
+
project = params.get('project')
|
|
46
|
+
if project is None:
|
|
47
|
+
return None
|
|
48
|
+
kind, identity, _ = _reference(store, 'following', project, 'project')
|
|
49
|
+
if kind != 'Project':
|
|
50
|
+
raise MockgunError(f'mockgun cannot following project {kind} {identity}: expected Project')
|
|
51
|
+
return {'type': 'Project', 'id': identity}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def follow(store: Store, params: Mapping[str, object]) -> dict[str, object]:
|
|
55
|
+
"""Record an idempotent HumanUser follow and echo the supplied links.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
store: Site records and journaled side tables.
|
|
59
|
+
params: RPC user and entity links.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
The observed follow result with the caller's link shapes.
|
|
63
|
+
|
|
64
|
+
Raises:
|
|
65
|
+
MockgunError: If the user or target cannot be resolved to an active,
|
|
66
|
+
declared record in the supported follow form.
|
|
67
|
+
"""
|
|
68
|
+
followed = True
|
|
69
|
+
store.side_set('follows', _pair(store, 'follow', params), followed)
|
|
70
|
+
return {
|
|
71
|
+
'followed': True,
|
|
72
|
+
'user': deepcopy(params['user']),
|
|
73
|
+
'entity': deepcopy(params['entity']),
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def follow_created_note(store: Store, row: Row, *, context: RequestContext) -> None:
|
|
78
|
+
"""Follow a created Note or a Reply's Note for its HumanUser actor.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
store: Site records and journaled side tables.
|
|
82
|
+
row: Newly created Note or Reply.
|
|
83
|
+
context: Actor for the enclosing create transaction.
|
|
84
|
+
"""
|
|
85
|
+
actor = context.actor
|
|
86
|
+
if actor is None or actor.entity_type != 'HumanUser':
|
|
87
|
+
return
|
|
88
|
+
if row['type'] == 'Note':
|
|
89
|
+
note_id = cast('int', row['id'])
|
|
90
|
+
elif row['type'] == 'Reply':
|
|
91
|
+
entity = row.get('entity')
|
|
92
|
+
if not isinstance(entity, dict) or entity.get('type') != 'Note':
|
|
93
|
+
return
|
|
94
|
+
note_id = cast('int', entity['id'])
|
|
95
|
+
else:
|
|
96
|
+
return
|
|
97
|
+
followed = True
|
|
98
|
+
store.side_set('follows', (actor.entity_id, 'Note', note_id), followed)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def followers(store: Store, params: Mapping[str, object]) -> list[dict[str, object]]:
|
|
102
|
+
"""List the observed zero or one HumanUser follower of an active record.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
store: Site records and journaled side tables.
|
|
106
|
+
params: RPC entity link.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
An empty list or a one-element list containing a follower dictionary
|
|
110
|
+
with stable identity and observed key order.
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
MockgunError: If the target or follower is unavailable, more than one
|
|
114
|
+
follower exists, or required follower metadata is undeclared or
|
|
115
|
+
unsettled.
|
|
116
|
+
"""
|
|
117
|
+
kind, identity, _ = _reference(store, 'followers', params.get('entity'), 'entity')
|
|
118
|
+
humans = store.followers(kind, identity)
|
|
119
|
+
if len(humans) > 1:
|
|
120
|
+
raise MockgunError(
|
|
121
|
+
f'mockgun cannot followers {kind} {identity}: order of two followers is unsettled'
|
|
122
|
+
)
|
|
123
|
+
if not humans:
|
|
124
|
+
return []
|
|
125
|
+
if not store.schema.declares('HumanUser', 'sg_status_list'):
|
|
126
|
+
raise MockgunError(
|
|
127
|
+
f'mockgun cannot followers {kind} {identity}: HumanUser.sg_status_list is undeclared'
|
|
128
|
+
)
|
|
129
|
+
_, human_id, human = _reference(
|
|
130
|
+
store, 'followers', {'type': 'HumanUser', 'id': humans[0]}, 'user'
|
|
131
|
+
)
|
|
132
|
+
store.check_known(human, 'sg_status_list')
|
|
133
|
+
status = human.get('sg_status_list')
|
|
134
|
+
name = display_name(store, 'HumanUser', human_id)
|
|
135
|
+
if name is None or not isinstance(status, str):
|
|
136
|
+
raise MockgunError(
|
|
137
|
+
f'mockgun cannot followers {kind} {identity}: '
|
|
138
|
+
f'HumanUser {human_id} name or status is unsettled'
|
|
139
|
+
)
|
|
140
|
+
return [
|
|
141
|
+
{
|
|
142
|
+
'name': name,
|
|
143
|
+
'type': 'HumanUser',
|
|
144
|
+
'id': human_id,
|
|
145
|
+
'valid': 'valid',
|
|
146
|
+
'status': status,
|
|
147
|
+
'uuid': store.link_uuid('HumanUser', human_id),
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def following(store: Store, params: Mapping[str, object]) -> list[dict[str, object]]:
|
|
153
|
+
"""List active follows after observed type and project restrictions.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
store: Site records and journaled side tables.
|
|
157
|
+
params: RPC user link and optional project and entity type.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
Identity links in ascending ID for a single resulting type.
|
|
161
|
+
|
|
162
|
+
Raises:
|
|
163
|
+
MockgunError: If the user, project, or a reached followed record is
|
|
164
|
+
unavailable, a required project field is undeclared or unknown, or
|
|
165
|
+
the result contains multiple entity types with unsettled order.
|
|
166
|
+
"""
|
|
167
|
+
_, human_id, _ = _reference(store, 'following', params.get('user'), 'user')
|
|
168
|
+
entity_type = params.get('entity_type')
|
|
169
|
+
if entity_type is not None and not isinstance(entity_type, str):
|
|
170
|
+
raise MockgunError(f'mockgun cannot following entity_type {entity_type}: invalid type')
|
|
171
|
+
project_link = _project(store, params)
|
|
172
|
+
result: list[dict[str, object]] = []
|
|
173
|
+
for key, _ in store.side_items('follows'):
|
|
174
|
+
if key[0] != human_id:
|
|
175
|
+
continue
|
|
176
|
+
kind = cast('str', key[1])
|
|
177
|
+
identity = cast('int', key[2])
|
|
178
|
+
if entity_type is not None and kind != entity_type:
|
|
179
|
+
continue
|
|
180
|
+
_, _, row = _reference(store, 'following', {'type': kind, 'id': identity}, 'entity')
|
|
181
|
+
if project_link is not None:
|
|
182
|
+
if not store.schema.declares(kind, 'project'):
|
|
183
|
+
raise MockgunError(
|
|
184
|
+
f'mockgun cannot following {kind} {identity}: project field is undeclared'
|
|
185
|
+
)
|
|
186
|
+
store.check_known(row, 'project')
|
|
187
|
+
if row.get('project') != project_link:
|
|
188
|
+
continue
|
|
189
|
+
result.append({'type': kind, 'id': identity})
|
|
190
|
+
if len({item['type'] for item in result}) > 1:
|
|
191
|
+
raise MockgunError(
|
|
192
|
+
f'mockgun cannot following HumanUser {human_id}: order across types is unsettled'
|
|
193
|
+
)
|
|
194
|
+
result.sort(key=lambda item: cast('int', item['id']))
|
|
195
|
+
return result
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def unfollow(store: Store, params: Mapping[str, object]) -> dict[str, object]:
|
|
199
|
+
"""Remove an idempotent follow and echo the supplied links.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
store: Site records and journaled side tables.
|
|
203
|
+
params: RPC user and entity links.
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
The observed unfollow result with the caller's link shapes.
|
|
207
|
+
|
|
208
|
+
Raises:
|
|
209
|
+
MockgunError: If the user or target cannot be resolved to an active,
|
|
210
|
+
declared record in the supported follow form.
|
|
211
|
+
"""
|
|
212
|
+
store.side_set('follows', _pair(store, 'unfollow', params), None)
|
|
213
|
+
return {
|
|
214
|
+
'unfollowed': True,
|
|
215
|
+
'user': deepcopy(params['user']),
|
|
216
|
+
'entity': deepcopy(params['entity']),
|
|
217
|
+
}
|