Ayon-Python-Api 0.3.4__tar.gz → 0.3.6__tar.gz

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.
Files changed (24) hide show
  1. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/PKG-INFO +1 -1
  2. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_api/__init__.py +32 -0
  3. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_api/_api.py +82 -0
  4. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_api/constants.py +22 -0
  5. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_api/graphql_queries.py +30 -1
  6. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_api/operations.py +93 -24
  7. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_api/server_api.py +453 -116
  8. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_api/utils.py +39 -0
  9. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_api/version.py +1 -1
  10. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_python_api.egg-info/PKG-INFO +1 -1
  11. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_python_api.egg-info/SOURCES.txt +0 -1
  12. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/pyproject.toml +1 -1
  13. ayon-python-api-0.3.4/ayon_api/thumbnails.py +0 -219
  14. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/LICENSE +0 -0
  15. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/README.md +0 -0
  16. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_api/entity_hub.py +0 -0
  17. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_api/events.py +0 -0
  18. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_api/exceptions.py +0 -0
  19. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_api/graphql.py +0 -0
  20. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_python_api.egg-info/dependency_links.txt +0 -0
  21. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_python_api.egg-info/requires.txt +0 -0
  22. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/ayon_python_api.egg-info/top_level.txt +0 -0
  23. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/setup.cfg +0 -0
  24. {ayon-python-api-0.3.4 → ayon-python-api-0.3.6}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ayon-python-api
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -30,6 +30,8 @@ from ._api import (
30
30
  set_client_version,
31
31
  get_default_settings_variant,
32
32
  set_default_settings_variant,
33
+ get_sender,
34
+ set_sender,
33
35
 
34
36
  get_base_url,
35
37
  get_rest_url,
@@ -46,6 +48,11 @@ from ._api import (
46
48
  patch,
47
49
  delete,
48
50
 
51
+ get_timeout,
52
+ set_timeout,
53
+ get_max_retries,
54
+ set_max_retries,
55
+
49
56
  get_event,
50
57
  get_events,
51
58
  dispatch_event,
@@ -92,6 +99,7 @@ from ._api import (
92
99
  get_users,
93
100
 
94
101
  get_attributes_for_type,
102
+ get_attributes_fields_for_type,
95
103
  get_default_fields_for_type,
96
104
 
97
105
  get_project_anatomy_preset,
@@ -110,6 +118,11 @@ from ._api import (
110
118
  get_addons_project_settings,
111
119
  get_addons_settings,
112
120
 
121
+ get_secrets,
122
+ get_secret,
123
+ save_secret,
124
+ delete_secret,
125
+
113
126
  get_project_names,
114
127
  get_projects,
115
128
  get_project,
@@ -124,6 +137,8 @@ from ._api import (
124
137
  get_folders_hierarchy,
125
138
 
126
139
  get_tasks,
140
+ get_task_by_id,
141
+ get_task_by_name,
127
142
 
128
143
  get_folder_ids_with_products,
129
144
  get_product_by_id,
@@ -154,6 +169,7 @@ from ._api import (
154
169
  get_workfile_info,
155
170
  get_workfile_info_by_id,
156
171
 
172
+ get_thumbnail_by_id,
157
173
  get_thumbnail,
158
174
  get_folder_thumbnail,
159
175
  get_version_thumbnail,
@@ -216,6 +232,8 @@ __all__ = (
216
232
  "set_client_version",
217
233
  "get_default_settings_variant",
218
234
  "set_default_settings_variant",
235
+ "get_sender",
236
+ "set_sender",
219
237
 
220
238
  "get_base_url",
221
239
  "get_rest_url",
@@ -232,6 +250,11 @@ __all__ = (
232
250
  "patch",
233
251
  "delete",
234
252
 
253
+ "get_timeout",
254
+ "set_timeout",
255
+ "get_max_retries",
256
+ "set_max_retries",
257
+
235
258
  "get_event",
236
259
  "get_events",
237
260
  "dispatch_event",
@@ -278,6 +301,7 @@ __all__ = (
278
301
  "get_users",
279
302
 
280
303
  "get_attributes_for_type",
304
+ "get_attributes_fields_for_type",
281
305
  "get_default_fields_for_type",
282
306
 
283
307
  "get_project_anatomy_preset",
@@ -295,6 +319,11 @@ __all__ = (
295
319
  "get_addons_project_settings",
296
320
  "get_addons_settings",
297
321
 
322
+ "get_secrets",
323
+ "get_secret",
324
+ "save_secret",
325
+ "delete_secret",
326
+
298
327
  "get_project_names",
299
328
  "get_projects",
300
329
  "get_project",
@@ -308,6 +337,8 @@ __all__ = (
308
337
  "get_folders",
309
338
 
310
339
  "get_tasks",
340
+ "get_task_by_id",
341
+ "get_task_by_name",
311
342
 
312
343
  "get_folder_ids_with_products",
313
344
  "get_product_by_id",
@@ -338,6 +369,7 @@ __all__ = (
338
369
  "get_workfile_info",
339
370
  "get_workfile_info_by_id",
340
371
 
372
+ "get_thumbnail_by_id",
341
373
  "get_thumbnail",
342
374
  "get_folder_thumbnail",
343
375
  "get_version_thumbnail",
@@ -392,6 +392,28 @@ def set_default_settings_variant(variant):
392
392
  return con.set_default_settings_variant(variant)
393
393
 
394
394
 
395
+ def get_sender():
396
+ """Sender used to send requests.
397
+
398
+ Returns:
399
+ Union[str, None]: Sender name or None.
400
+ """
401
+
402
+ con = get_server_api_connection()
403
+ return con.get_sender()
404
+
405
+
406
+ def set_sender(sender):
407
+ """Change sender used for requests.
408
+
409
+ Args:
410
+ sender (Union[str, None]): Sender name or None.
411
+ """
412
+
413
+ con = get_server_api_connection()
414
+ return con.set_sender(sender)
415
+
416
+
395
417
  def get_base_url():
396
418
  con = get_server_api_connection()
397
419
  return con.get_base_url()
@@ -452,6 +474,26 @@ def delete(*args, **kwargs):
452
474
  return con.delete(*args, **kwargs)
453
475
 
454
476
 
477
+ def get_timeout(*args, **kwargs):
478
+ con = get_server_api_connection()
479
+ return con.get_timeout(*args, **kwargs)
480
+
481
+
482
+ def set_timeout(*args, **kwargs):
483
+ con = get_server_api_connection()
484
+ return con.set_timeout(*args, **kwargs)
485
+
486
+
487
+ def get_max_retries(*args, **kwargs):
488
+ con = get_server_api_connection()
489
+ return con.get_max_retries(*args, **kwargs)
490
+
491
+
492
+ def set_max_retries(*args, **kwargs):
493
+ con = get_server_api_connection()
494
+ return con.set_max_retries(*args, **kwargs)
495
+
496
+
455
497
  def get_event(*args, **kwargs):
456
498
  con = get_server_api_connection()
457
499
  return con.get_event(*args, **kwargs)
@@ -704,6 +746,26 @@ def get_addons_settings(*args, **kwargs):
704
746
  return con.get_addons_settings(*args, **kwargs)
705
747
 
706
748
 
749
+ def get_secrets(*args, **kwargs):
750
+ con = get_server_api_connection()
751
+ return con.get_secrets(*args, **kwargs)
752
+
753
+
754
+ def get_secret(*args, **kwargs):
755
+ con = get_server_api_connection()
756
+ return con.delete_secret(*args, **kwargs)
757
+
758
+
759
+ def save_secret(*args, **kwargs):
760
+ con = get_server_api_connection()
761
+ return con.delete_secret(*args, **kwargs)
762
+
763
+
764
+ def delete_secret(*args, **kwargs):
765
+ con = get_server_api_connection()
766
+ return con.delete_secret(*args, **kwargs)
767
+
768
+
707
769
  def get_project_names(*args, **kwargs):
708
770
  con = get_server_api_connection()
709
771
  return con.get_project_names(*args, **kwargs)
@@ -734,6 +796,16 @@ def get_tasks(*args, **kwargs):
734
796
  return con.get_tasks(*args, **kwargs)
735
797
 
736
798
 
799
+ def get_task_by_id(*args, **kwargs):
800
+ con = get_server_api_connection()
801
+ return con.get_task_by_id(*args, **kwargs)
802
+
803
+
804
+ def get_task_by_name(*args, **kwargs):
805
+ con = get_server_api_connection()
806
+ return con.get_task_by_name(*args, **kwargs)
807
+
808
+
737
809
  def get_folder_by_id(*args, **kwargs):
738
810
  con = get_server_api_connection()
739
811
  return con.get_folder_by_id(*args, **kwargs)
@@ -904,6 +976,11 @@ def delete_project(project_name):
904
976
  return con.delete_project(project_name)
905
977
 
906
978
 
979
+ def get_thumbnail_by_id(project_name, thumbnail_id):
980
+ con = get_server_api_connection()
981
+ con.get_thumbnail_by_id(project_name, thumbnail_id)
982
+
983
+
907
984
  def get_thumbnail(project_name, entity_type, entity_id, thumbnail_id=None):
908
985
  con = get_server_api_connection()
909
986
  con.get_thumbnail(project_name, entity_type, entity_id, thumbnail_id)
@@ -934,6 +1011,11 @@ def update_thumbnail(project_name, thumbnail_id, src_filepath):
934
1011
  return con.update_thumbnail(project_name, thumbnail_id, src_filepath)
935
1012
 
936
1013
 
1014
+ def get_attributes_fields_for_type(entity_type):
1015
+ con = get_server_api_connection()
1016
+ return con.get_attributes_fields_for_type(entity_type)
1017
+
1018
+
937
1019
  def get_default_fields_for_type(entity_type):
938
1020
  con = get_server_api_connection()
939
1021
  return con.get_default_fields_for_type(entity_type)
@@ -1,9 +1,31 @@
1
1
  # Environments where server url and api key are stored for global connection
2
2
  SERVER_URL_ENV_KEY = "AYON_SERVER_URL"
3
3
  SERVER_API_ENV_KEY = "AYON_API_KEY"
4
+ SERVER_TIMEOUT_ENV_KEY = "AYON_SERVER_TIMEOUT"
5
+ SERVER_RETRIES_ENV_KEY = "AYON_SERVER_RETRIES"
6
+
4
7
  # Backwards compatibility
5
8
  SERVER_TOKEN_ENV_KEY = SERVER_API_ENV_KEY
6
9
 
10
+ # --- User ---
11
+ DEFAULT_USER_FIELDS = {
12
+ "roles",
13
+ "name",
14
+ "isService",
15
+ "isManager",
16
+ "isGuest",
17
+ "isAdmin",
18
+ "defaultRoles",
19
+ "createdAt",
20
+ "active",
21
+ "hasPassword",
22
+ "updatedAt",
23
+ "apiKeyPreview",
24
+ "attrib.avatarUrl",
25
+ "attrib.email",
26
+ "attrib.fullName",
27
+ }
28
+
7
29
  # --- Product types ---
8
30
  DEFAULT_PRODUCT_TYPE_FIELDS = {
9
31
  "name",
@@ -247,9 +247,11 @@ def products_graphql_query(fields):
247
247
  query = GraphQlQuery("ProductsQuery")
248
248
 
249
249
  project_name_var = query.add_variable("projectName", "String!")
250
- folder_ids_var = query.add_variable("folderIds", "[String!]")
251
250
  product_ids_var = query.add_variable("productIds", "[String!]")
252
251
  product_names_var = query.add_variable("productNames", "[String!]")
252
+ folder_ids_var = query.add_variable("folderIds", "[String!]")
253
+ product_types_var = query.add_variable("productTypes", "[String!]")
254
+ statuses_var = query.add_variable("statuses", "[String!]")
253
255
 
254
256
  project_field = query.add_field("project")
255
257
  project_field.set_filter("name", project_name_var)
@@ -258,6 +260,8 @@ def products_graphql_query(fields):
258
260
  products_field.set_filter("ids", product_ids_var)
259
261
  products_field.set_filter("names", product_names_var)
260
262
  products_field.set_filter("folderIds", folder_ids_var)
263
+ products_field.set_filter("productTypes", product_types_var)
264
+ products_field.set_filter("statuses", statuses_var)
261
265
 
262
266
  nested_fields = fields_to_dict(set(fields))
263
267
  add_links_fields(products_field, nested_fields)
@@ -462,3 +466,28 @@ def events_graphql_query(fields):
462
466
  for k, v in value.items():
463
467
  query_queue.append((k, v, field))
464
468
  return query
469
+
470
+
471
+ def users_graphql_query(fields):
472
+ query = GraphQlQuery("Users")
473
+ names_var = query.add_variable("userNames", "[String!]")
474
+
475
+ users_field = query.add_field_with_edges("users")
476
+ users_field.set_filter("names", names_var)
477
+
478
+ nested_fields = fields_to_dict(set(fields))
479
+
480
+ query_queue = collections.deque()
481
+ for key, value in nested_fields.items():
482
+ query_queue.append((key, value, users_field))
483
+
484
+ while query_queue:
485
+ item = query_queue.popleft()
486
+ key, value, parent = item
487
+ field = parent.add_field(key)
488
+ if value is FIELD_VALUE:
489
+ continue
490
+
491
+ for k, v in value.items():
492
+ query_queue.append((k, v, field))
493
+ return query
@@ -1,3 +1,4 @@
1
+ import os
1
2
  import copy
2
3
  import collections
3
4
  import uuid
@@ -22,6 +23,8 @@ def new_folder_entity(
22
23
  name,
23
24
  folder_type,
24
25
  parent_id=None,
26
+ status=None,
27
+ tags=None,
25
28
  attribs=None,
26
29
  data=None,
27
30
  thumbnail_id=None,
@@ -32,12 +35,14 @@ def new_folder_entity(
32
35
  Args:
33
36
  name (str): Is considered as unique identifier of folder in project.
34
37
  folder_type (str): Type of folder.
35
- parent_id (Optional[str]]): Id of parent folder.
38
+ parent_id (Optional[str]): Parent folder id.
39
+ status (Optional[str]): Product status.
40
+ tags (Optional[List[str]]): List of tags.
36
41
  attribs (Optional[Dict[str, Any]]): Explicitly set attributes
37
42
  of folder.
38
43
  data (Optional[Dict[str, Any]]): Custom folder data. Empty dictionary
39
44
  is used if not passed.
40
- thumbnail_id (Optional[str]): Id of thumbnail related to folder.
45
+ thumbnail_id (Optional[str]): Thumbnail id related to folder.
41
46
  entity_id (Optional[str]): Predefined id of entity. New id is
42
47
  created if not passed.
43
48
 
@@ -54,7 +59,7 @@ def new_folder_entity(
54
59
  if parent_id is not None:
55
60
  parent_id = _create_or_convert_to_id(parent_id)
56
61
 
57
- return {
62
+ output = {
58
63
  "id": _create_or_convert_to_id(entity_id),
59
64
  "name": name,
60
65
  # This will be ignored
@@ -64,6 +69,11 @@ def new_folder_entity(
64
69
  "attrib": attribs,
65
70
  "thumbnailId": thumbnail_id
66
71
  }
72
+ if status:
73
+ output["status"] = status
74
+ if tags:
75
+ output["tags"] = tags
76
+ return output
67
77
 
68
78
 
69
79
  def new_product_entity(
@@ -71,6 +81,7 @@ def new_product_entity(
71
81
  product_type,
72
82
  folder_id,
73
83
  status=None,
84
+ tags=None,
74
85
  attribs=None,
75
86
  data=None,
76
87
  entity_id=None
@@ -81,8 +92,9 @@ def new_product_entity(
81
92
  name (str): Is considered as unique identifier of
82
93
  product under folder.
83
94
  product_type (str): Product type.
84
- folder_id (str): Id of parent folder.
95
+ folder_id (str): Parent folder id.
85
96
  status (Optional[str]): Product status.
97
+ tags (Optional[List[str]]): List of tags.
86
98
  attribs (Optional[Dict[str, Any]]): Explicitly set attributes
87
99
  of product.
88
100
  data (Optional[Dict[str, Any]]): product entity data. Empty dictionary
@@ -110,6 +122,8 @@ def new_product_entity(
110
122
  }
111
123
  if status:
112
124
  output["status"] = status
125
+ if tags:
126
+ output["tags"] = tags
113
127
  return output
114
128
 
115
129
 
@@ -119,6 +133,8 @@ def new_version_entity(
119
133
  task_id=None,
120
134
  thumbnail_id=None,
121
135
  author=None,
136
+ status=None,
137
+ tags=None,
122
138
  attribs=None,
123
139
  data=None,
124
140
  entity_id=None
@@ -128,10 +144,12 @@ def new_version_entity(
128
144
  Args:
129
145
  version (int): Is considered as unique identifier of version
130
146
  under product.
131
- product_id (str): Id of parent product.
132
- task_id (Optional[str]]): Id of task under which product was created.
133
- thumbnail_id (Optional[str]]): Thumbnail related to version.
134
- author (Optional[str]]): Name of version author.
147
+ product_id (str): Parent product id.
148
+ task_id (Optional[str]): Task id under which product was created.
149
+ thumbnail_id (Optional[str]): Thumbnail related to version.
150
+ author (Optional[str]): Name of version author.
151
+ status (Optional[str]): Version status.
152
+ tags (Optional[List[str]]): List of tags.
135
153
  attribs (Optional[Dict[str, Any]]): Explicitly set attributes
136
154
  of version.
137
155
  data (Optional[Dict[str, Any]]): Version entity custom data.
@@ -164,6 +182,10 @@ def new_version_entity(
164
182
  output["thumbnailId"] = thumbnail_id
165
183
  if author:
166
184
  output["author"] = author
185
+ if tags:
186
+ output["tags"] = tags
187
+ if status:
188
+ output["status"] = status
167
189
  return output
168
190
 
169
191
 
@@ -173,6 +195,8 @@ def new_hero_version_entity(
173
195
  task_id=None,
174
196
  thumbnail_id=None,
175
197
  author=None,
198
+ status=None,
199
+ tags=None,
176
200
  attribs=None,
177
201
  data=None,
178
202
  entity_id=None
@@ -182,10 +206,12 @@ def new_hero_version_entity(
182
206
  Args:
183
207
  version (int): Is considered as unique identifier of version
184
208
  under product. Should be same as standard version if there is any.
185
- product_id (str): Id of parent product.
186
- task_id (Optional[str]): Id of task under which product was created.
209
+ product_id (str): Parent product id.
210
+ task_id (Optional[str]): Task id under which product was created.
187
211
  thumbnail_id (Optional[str]): Thumbnail related to version.
188
212
  author (Optional[str]): Name of version author.
213
+ status (Optional[str]): Version status.
214
+ tags (Optional[List[str]]): List of tags.
189
215
  attribs (Optional[Dict[str, Any]]): Explicitly set attributes
190
216
  of version.
191
217
  data (Optional[Dict[str, Any]]): Version entity data.
@@ -215,18 +241,32 @@ def new_hero_version_entity(
215
241
  output["thumbnailId"] = thumbnail_id
216
242
  if author:
217
243
  output["author"] = author
244
+ if tags:
245
+ output["tags"] = tags
246
+ if status:
247
+ output["status"] = status
218
248
  return output
219
249
 
220
250
 
221
251
  def new_representation_entity(
222
- name, version_id, attribs=None, data=None, entity_id=None
252
+ name,
253
+ version_id,
254
+ files,
255
+ status=None,
256
+ tags=None,
257
+ attribs=None,
258
+ data=None,
259
+ entity_id=None
223
260
  ):
224
261
  """Create skeleton data of representation entity.
225
262
 
226
263
  Args:
227
264
  name (str): Representation name considered as unique identifier
228
265
  of representation under version.
229
- version_id (str): Id of parent version.
266
+ version_id (str): Parent version id.
267
+ files (list[dict[str, str]]): List of files in representation.
268
+ status (Optional[str]): Representation status.
269
+ tags (Optional[List[str]]): List of tags.
230
270
  attribs (Optional[Dict[str, Any]]): Explicitly set attributes
231
271
  of representation.
232
272
  data (Optional[Dict[str, Any]]): Representation entity data.
@@ -243,27 +283,42 @@ def new_representation_entity(
243
283
  if data is None:
244
284
  data = {}
245
285
 
246
- return {
286
+ output = {
247
287
  "id": _create_or_convert_to_id(entity_id),
248
288
  "versionId": _create_or_convert_to_id(version_id),
289
+ "files": files,
249
290
  "name": name,
250
291
  "data": data,
251
292
  "attrib": attribs
252
293
  }
294
+ if tags:
295
+ output["tags"] = tags
296
+ if status:
297
+ output["status"] = status
298
+ return output
253
299
 
254
300
 
255
- def new_workfile_info_doc(
256
- filename, folder_id, task_name, files, data=None, entity_id=None
301
+ def new_workfile_info(
302
+ filepath,
303
+ task_id,
304
+ status=None,
305
+ tags=None,
306
+ attribs=None,
307
+ description=None,
308
+ data=None,
309
+ entity_id=None
257
310
  ):
258
311
  """Create skeleton data of workfile info entity.
259
312
 
260
313
  Workfile entity is at this moment used primarily for artist notes.
261
314
 
262
315
  Args:
263
- filename (str): Filename of workfile.
264
- folder_id (str): Id of folder under which workfile live.
265
- task_name (str): Task under which was workfile created.
266
- files (List[str]): List of rootless filepaths related to workfile.
316
+ filepath (str): Rootless workfile filepath.
317
+ task_id (str): Task under which was workfile created.
318
+ status (Optional[str]): Workfile status.
319
+ tags (Optional[List[str]]): Workfile tags.
320
+ attribs (Options[dic[str, Any]]): Explicitly set attributes.
321
+ description (Optional[str]): Workfile description.
267
322
  data (Optional[Dict[str, Any]]): Additional metadata.
268
323
  entity_id (Optional[str]): Predefined id of entity. New id is created
269
324
  if not passed.
@@ -272,17 +327,31 @@ def new_workfile_info_doc(
272
327
  Dict[str, Any]: Skeleton of workfile info entity.
273
328
  """
274
329
 
330
+ if attribs is None:
331
+ attribs = {}
332
+
333
+ if "extension" not in attribs:
334
+ attribs["extension"] = os.path.splitext(filepath)[-1]
335
+
336
+ if description:
337
+ attribs["description"] = description
338
+
275
339
  if not data:
276
340
  data = {}
277
341
 
278
- return {
342
+ output = {
279
343
  "id": _create_or_convert_to_id(entity_id),
280
- "parent": _create_or_convert_to_id(folder_id),
281
- "task_name": task_name,
282
- "filename": filename,
344
+ "taskId": task_id,
345
+ "path": filepath,
283
346
  "data": data,
284
- "files": files
347
+ "attrib": attribs
285
348
  }
349
+ if status:
350
+ output["status"] = status
351
+
352
+ if tags:
353
+ output["tags"] = tags
354
+ return output
286
355
 
287
356
 
288
357
  @six.add_metaclass(ABCMeta)