taskadence 0.1.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.
Files changed (533) hide show
  1. taskadence/__init__.py +100 -0
  2. taskadence/_brand.py +87 -0
  3. taskadence/_client.py +144 -0
  4. taskadence/_core.py +563 -0
  5. taskadence/_generated/__init__.py +11 -0
  6. taskadence/_generated/api/__init__.py +3 -0
  7. taskadence/_generated/api/audit/__init__.py +3 -0
  8. taskadence/_generated/api/audit/audit_export.py +407 -0
  9. taskadence/_generated/api/audit/audit_export_json.py +422 -0
  10. taskadence/_generated/api/audit/audit_list.py +441 -0
  11. taskadence/_generated/api/availability/__init__.py +3 -0
  12. taskadence/_generated/api/availability/availability_create.py +242 -0
  13. taskadence/_generated/api/availability/availability_delete.py +227 -0
  14. taskadence/_generated/api/availability/availability_range.py +293 -0
  15. taskadence/_generated/api/availability/availability_update.py +260 -0
  16. taskadence/_generated/api/connected_apps/__init__.py +3 -0
  17. taskadence/_generated/api/connected_apps/connected_apps_delete.py +213 -0
  18. taskadence/_generated/api/connected_apps/connected_apps_list.py +304 -0
  19. taskadence/_generated/api/designations/__init__.py +3 -0
  20. taskadence/_generated/api/designations/designations_create.py +217 -0
  21. taskadence/_generated/api/designations/designations_delete.py +252 -0
  22. taskadence/_generated/api/designations/designations_list.py +433 -0
  23. taskadence/_generated/api/designations/designations_read.py +261 -0
  24. taskadence/_generated/api/designations/designations_replace.py +276 -0
  25. taskadence/_generated/api/designations/designations_set_visibility.py +254 -0
  26. taskadence/_generated/api/designations/designations_update.py +272 -0
  27. taskadence/_generated/api/goals/__init__.py +3 -0
  28. taskadence/_generated/api/goals/goals_create.py +246 -0
  29. taskadence/_generated/api/goals/goals_delete.py +252 -0
  30. taskadence/_generated/api/goals/goals_file_tasks.py +256 -0
  31. taskadence/_generated/api/goals/goals_list.py +338 -0
  32. taskadence/_generated/api/goals/goals_move.py +252 -0
  33. taskadence/_generated/api/goals/goals_org.py +249 -0
  34. taskadence/_generated/api/goals/goals_reorder.py +238 -0
  35. taskadence/_generated/api/goals/goals_replace.py +260 -0
  36. taskadence/_generated/api/goals/goals_unfile_task.py +241 -0
  37. taskadence/_generated/api/goals/goals_update.py +256 -0
  38. taskadence/_generated/api/health/__init__.py +3 -0
  39. taskadence/_generated/api/health/health_read.py +219 -0
  40. taskadence/_generated/api/mcp/__init__.py +3 -0
  41. taskadence/_generated/api/mcp/mcp_clients.py +237 -0
  42. taskadence/_generated/api/me/__init__.py +3 -0
  43. taskadence/_generated/api/me/me_read.py +243 -0
  44. taskadence/_generated/api/milestones/__init__.py +3 -0
  45. taskadence/_generated/api/milestones/milestones_create.py +252 -0
  46. taskadence/_generated/api/milestones/milestones_delete.py +254 -0
  47. taskadence/_generated/api/milestones/milestones_file_tasks.py +256 -0
  48. taskadence/_generated/api/milestones/milestones_list.py +338 -0
  49. taskadence/_generated/api/milestones/milestones_list_org.py +374 -0
  50. taskadence/_generated/api/milestones/milestones_move.py +256 -0
  51. taskadence/_generated/api/milestones/milestones_reorder.py +238 -0
  52. taskadence/_generated/api/milestones/milestones_replace.py +314 -0
  53. taskadence/_generated/api/milestones/milestones_unfile_task.py +245 -0
  54. taskadence/_generated/api/milestones/milestones_update.py +302 -0
  55. taskadence/_generated/api/oauth_clients/__init__.py +3 -0
  56. taskadence/_generated/api/oauth_clients/oauth_clients_create.py +223 -0
  57. taskadence/_generated/api/oauth_clients/oauth_clients_delete.py +221 -0
  58. taskadence/_generated/api/oauth_clients/oauth_clients_list.py +327 -0
  59. taskadence/_generated/api/oauth_clients/oauth_clients_read.py +236 -0
  60. taskadence/_generated/api/oauth_clients/oauth_clients_rotate_secret.py +213 -0
  61. taskadence/_generated/api/oauth_clients/oauth_clients_update.py +234 -0
  62. taskadence/_generated/api/organization_invites/__init__.py +3 -0
  63. taskadence/_generated/api/organization_invites/organization_invites_accept.py +213 -0
  64. taskadence/_generated/api/organization_invites/organization_invites_create.py +219 -0
  65. taskadence/_generated/api/organization_invites/organization_invites_delete.py +213 -0
  66. taskadence/_generated/api/organization_invites/organization_invites_list.py +383 -0
  67. taskadence/_generated/api/organization_invites/organization_invites_mine.py +332 -0
  68. taskadence/_generated/api/organization_invites/organization_invites_reject.py +213 -0
  69. taskadence/_generated/api/organization_invites/organization_invites_update.py +234 -0
  70. taskadence/_generated/api/organization_members/__init__.py +3 -0
  71. taskadence/_generated/api/organization_members/organization_members_delete.py +246 -0
  72. taskadence/_generated/api/organization_members/organization_members_list.py +395 -0
  73. taskadence/_generated/api/organization_members/organization_members_read.py +250 -0
  74. taskadence/_generated/api/organization_members/organization_members_replace.py +272 -0
  75. taskadence/_generated/api/organization_members/organization_members_update.py +268 -0
  76. taskadence/_generated/api/organizations/__init__.py +3 -0
  77. taskadence/_generated/api/organizations/organizations_access_review.py +244 -0
  78. taskadence/_generated/api/organizations/organizations_list.py +304 -0
  79. taskadence/_generated/api/organizations/organizations_read.py +236 -0
  80. taskadence/_generated/api/organizations/organizations_settings.py +236 -0
  81. taskadence/_generated/api/organizations/organizations_storage.py +244 -0
  82. taskadence/_generated/api/organizations/organizations_update_settings.py +262 -0
  83. taskadence/_generated/api/project_members/__init__.py +3 -0
  84. taskadence/_generated/api/project_members/project_members_create.py +219 -0
  85. taskadence/_generated/api/project_members/project_members_delete.py +246 -0
  86. taskadence/_generated/api/project_members/project_members_list.py +364 -0
  87. taskadence/_generated/api/project_members/project_members_read.py +250 -0
  88. taskadence/_generated/api/project_members/project_members_replace.py +276 -0
  89. taskadence/_generated/api/project_members/project_members_update.py +272 -0
  90. taskadence/_generated/api/project_resources/__init__.py +3 -0
  91. taskadence/_generated/api/project_resources/project_resources_create.py +239 -0
  92. taskadence/_generated/api/project_resources/project_resources_delete.py +252 -0
  93. taskadence/_generated/api/project_resources/project_resources_list.py +355 -0
  94. taskadence/_generated/api/project_resources/project_resources_read.py +256 -0
  95. taskadence/_generated/api/project_resources/project_resources_update.py +270 -0
  96. taskadence/_generated/api/project_resources/project_resources_upload.py +268 -0
  97. taskadence/_generated/api/project_stats/__init__.py +3 -0
  98. taskadence/_generated/api/project_stats/project_stats_read.py +236 -0
  99. taskadence/_generated/api/projects/__init__.py +3 -0
  100. taskadence/_generated/api/projects/projects_create.py +233 -0
  101. taskadence/_generated/api/projects/projects_delete.py +232 -0
  102. taskadence/_generated/api/projects/projects_list.py +386 -0
  103. taskadence/_generated/api/projects/projects_read.py +236 -0
  104. taskadence/_generated/api/projects/projects_reorder.py +235 -0
  105. taskadence/_generated/api/projects/projects_replace.py +252 -0
  106. taskadence/_generated/api/projects/projects_update.py +248 -0
  107. taskadence/_generated/api/releases/__init__.py +3 -0
  108. taskadence/_generated/api/releases/releases_whats_new.py +286 -0
  109. taskadence/_generated/api/sections/__init__.py +3 -0
  110. taskadence/_generated/api/sections/sections_create.py +221 -0
  111. taskadence/_generated/api/sections/sections_delete.py +217 -0
  112. taskadence/_generated/api/sections/sections_list.py +461 -0
  113. taskadence/_generated/api/sections/sections_reorder.py +228 -0
  114. taskadence/_generated/api/sections/sections_update.py +232 -0
  115. taskadence/_generated/api/service_accounts/__init__.py +3 -0
  116. taskadence/_generated/api/service_accounts/service_accounts_create.py +242 -0
  117. taskadence/_generated/api/service_accounts/service_accounts_delete.py +235 -0
  118. taskadence/_generated/api/service_accounts/service_accounts_list.py +319 -0
  119. taskadence/_generated/api/service_accounts/service_accounts_update.py +248 -0
  120. taskadence/_generated/api/sprints/__init__.py +3 -0
  121. taskadence/_generated/api/sprints/sprints_create.py +234 -0
  122. taskadence/_generated/api/sprints/sprints_delete.py +258 -0
  123. taskadence/_generated/api/sprints/sprints_list.py +323 -0
  124. taskadence/_generated/api/sprints/sprints_update.py +252 -0
  125. taskadence/_generated/api/task_attachments/__init__.py +3 -0
  126. taskadence/_generated/api/task_attachments/task_attachments_create.py +290 -0
  127. taskadence/_generated/api/task_attachments/task_attachments_delete.py +251 -0
  128. taskadence/_generated/api/task_attachments/task_attachments_list.py +334 -0
  129. taskadence/_generated/api/task_attachments/task_attachments_read.py +261 -0
  130. taskadence/_generated/api/task_attachments/task_attachments_update.py +279 -0
  131. taskadence/_generated/api/task_comments/__init__.py +3 -0
  132. taskadence/_generated/api/task_comments/task_comments_create.py +235 -0
  133. taskadence/_generated/api/task_comments/task_comments_delete.py +257 -0
  134. taskadence/_generated/api/task_comments/task_comments_list.py +339 -0
  135. taskadence/_generated/api/task_comments/task_comments_read.py +261 -0
  136. taskadence/_generated/api/task_comments/task_comments_reply.py +235 -0
  137. taskadence/_generated/api/task_comments/task_comments_update.py +275 -0
  138. taskadence/_generated/api/task_history/__init__.py +3 -0
  139. taskadence/_generated/api/task_history/task_history_list.py +339 -0
  140. taskadence/_generated/api/task_history/task_history_read.py +261 -0
  141. taskadence/_generated/api/tasks/__init__.py +3 -0
  142. taskadence/_generated/api/tasks/tasks_add_dependency.py +230 -0
  143. taskadence/_generated/api/tasks/tasks_add_subtask.py +230 -0
  144. taskadence/_generated/api/tasks/tasks_create.py +231 -0
  145. taskadence/_generated/api/tasks/tasks_delete.py +236 -0
  146. taskadence/_generated/api/tasks/tasks_list.py +1044 -0
  147. taskadence/_generated/api/tasks/tasks_read.py +236 -0
  148. taskadence/_generated/api/tasks/tasks_remove_dependency.py +223 -0
  149. taskadence/_generated/api/tasks/tasks_remove_subtask.py +223 -0
  150. taskadence/_generated/api/tasks/tasks_replace.py +274 -0
  151. taskadence/_generated/api/tasks/tasks_review.py +270 -0
  152. taskadence/_generated/api/tasks/tasks_set_milestone.py +262 -0
  153. taskadence/_generated/api/tasks/tasks_set_project.py +238 -0
  154. taskadence/_generated/api/tasks/tasks_set_section.py +246 -0
  155. taskadence/_generated/api/tasks/tasks_set_sprint.py +262 -0
  156. taskadence/_generated/api/tasks/tasks_update.py +262 -0
  157. taskadence/_generated/api/teams/__init__.py +3 -0
  158. taskadence/_generated/api/teams/teams_add_member.py +238 -0
  159. taskadence/_generated/api/teams/teams_create.py +221 -0
  160. taskadence/_generated/api/teams/teams_delete.py +282 -0
  161. taskadence/_generated/api/teams/teams_list.py +339 -0
  162. taskadence/_generated/api/teams/teams_list_tasks.py +373 -0
  163. taskadence/_generated/api/teams/teams_read.py +236 -0
  164. taskadence/_generated/api/teams/teams_remove_member.py +227 -0
  165. taskadence/_generated/api/teams/teams_replace.py +260 -0
  166. taskadence/_generated/api/teams/teams_replace_member.py +264 -0
  167. taskadence/_generated/api/teams/teams_update.py +248 -0
  168. taskadence/_generated/api/teams/teams_update_member.py +252 -0
  169. taskadence/_generated/api/tokens/__init__.py +3 -0
  170. taskadence/_generated/api/tokens/tokens_create.py +249 -0
  171. taskadence/_generated/api/tokens/tokens_delete.py +209 -0
  172. taskadence/_generated/api/tokens/tokens_list.py +327 -0
  173. taskadence/_generated/api/tokens/tokens_read.py +236 -0
  174. taskadence/_generated/api/tokens/tokens_rotate.py +219 -0
  175. taskadence/_generated/api/tokens/tokens_update.py +230 -0
  176. taskadence/_generated/api/uploads/__init__.py +3 -0
  177. taskadence/_generated/api/uploads/uploads_complete.py +311 -0
  178. taskadence/_generated/api/uploads/uploads_create.py +251 -0
  179. taskadence/_generated/api/view_pins/__init__.py +3 -0
  180. taskadence/_generated/api/view_pins/view_pins_list.py +315 -0
  181. taskadence/_generated/api/view_pins/view_pins_pin_project.py +230 -0
  182. taskadence/_generated/api/view_pins/view_pins_pin_view.py +213 -0
  183. taskadence/_generated/api/view_pins/view_pins_reorder.py +221 -0
  184. taskadence/_generated/api/view_pins/view_pins_unpin_project.py +213 -0
  185. taskadence/_generated/api/view_pins/view_pins_unpin_view.py +213 -0
  186. taskadence/_generated/api/views/__init__.py +3 -0
  187. taskadence/_generated/api/views/views_create.py +247 -0
  188. taskadence/_generated/api/views/views_delete.py +230 -0
  189. taskadence/_generated/api/views/views_duplicate.py +234 -0
  190. taskadence/_generated/api/views/views_list.py +367 -0
  191. taskadence/_generated/api/views/views_read.py +236 -0
  192. taskadence/_generated/api/views/views_rows.py +336 -0
  193. taskadence/_generated/api/views/views_update.py +254 -0
  194. taskadence/_generated/api/webhooks/__init__.py +3 -0
  195. taskadence/_generated/api/webhooks/webhooks_create.py +231 -0
  196. taskadence/_generated/api/webhooks/webhooks_delete.py +230 -0
  197. taskadence/_generated/api/webhooks/webhooks_deliveries.py +319 -0
  198. taskadence/_generated/api/webhooks/webhooks_delivery.py +254 -0
  199. taskadence/_generated/api/webhooks/webhooks_events.py +312 -0
  200. taskadence/_generated/api/webhooks/webhooks_list.py +323 -0
  201. taskadence/_generated/api/webhooks/webhooks_read.py +236 -0
  202. taskadence/_generated/api/webhooks/webhooks_replay.py +233 -0
  203. taskadence/_generated/api/webhooks/webhooks_rotate_secret.py +217 -0
  204. taskadence/_generated/api/webhooks/webhooks_test.py +221 -0
  205. taskadence/_generated/api/webhooks/webhooks_update.py +256 -0
  206. taskadence/_generated/client.py +272 -0
  207. taskadence/_generated/errors.py +18 -0
  208. taskadence/_generated/models/__init__.py +619 -0
  209. taskadence/_generated/models/access_review.py +167 -0
  210. taskadence/_generated/models/acknowledgement.py +73 -0
  211. taskadence/_generated/models/agent_task_review_enum.py +17 -0
  212. taskadence/_generated/models/audit_export_row.py +358 -0
  213. taskadence/_generated/models/audit_export_row_after_type_0.py +53 -0
  214. taskadence/_generated/models/audit_export_row_before_type_0.py +53 -0
  215. taskadence/_generated/models/audit_page.py +108 -0
  216. taskadence/_generated/models/audit_page_data_item.py +53 -0
  217. taskadence/_generated/models/authorization_server_metadata.py +218 -0
  218. taskadence/_generated/models/availability_create.py +92 -0
  219. taskadence/_generated/models/availability_deleted.py +78 -0
  220. taskadence/_generated/models/availability_entry.py +226 -0
  221. taskadence/_generated/models/availability_member.py +101 -0
  222. taskadence/_generated/models/availability_range.py +131 -0
  223. taskadence/_generated/models/availability_update.py +147 -0
  224. taskadence/_generated/models/body_upload_attachment_v1_task_attachments_post.py +175 -0
  225. taskadence/_generated/models/body_upload_resource_v1_project_resources_upload_post.py +139 -0
  226. taskadence/_generated/models/client_registration_response.py +199 -0
  227. taskadence/_generated/models/commit_type.py +25 -0
  228. taskadence/_generated/models/connected_app.py +243 -0
  229. taskadence/_generated/models/connected_app_revoked.py +93 -0
  230. taskadence/_generated/models/delivery_detail.py +405 -0
  231. taskadence/_generated/models/delivery_detail_attempt_log_item.py +53 -0
  232. taskadence/_generated/models/delivery_detail_payload.py +55 -0
  233. taskadence/_generated/models/delivery_detail_request_headers.py +55 -0
  234. taskadence/_generated/models/delivery_detail_status.py +19 -0
  235. taskadence/_generated/models/delivery_out.py +334 -0
  236. taskadence/_generated/models/delivery_out_status.py +19 -0
  237. taskadence/_generated/models/dependency_link.py +52 -0
  238. taskadence/_generated/models/designation.py +504 -0
  239. taskadence/_generated/models/designation_catalog.py +116 -0
  240. taskadence/_generated/models/designation_create.py +204 -0
  241. taskadence/_generated/models/designation_create_metadata_type_0.py +53 -0
  242. taskadence/_generated/models/designation_deleted.py +86 -0
  243. taskadence/_generated/models/designation_group.py +95 -0
  244. taskadence/_generated/models/designation_metadata_type_0.py +53 -0
  245. taskadence/_generated/models/designation_update.py +213 -0
  246. taskadence/_generated/models/designation_update_metadata_type_0.py +53 -0
  247. taskadence/_generated/models/designation_usage.py +97 -0
  248. taskadence/_generated/models/designation_visibility.py +148 -0
  249. taskadence/_generated/models/environment.py +17 -0
  250. taskadence/_generated/models/goal_create.py +106 -0
  251. taskadence/_generated/models/goal_deleted.py +86 -0
  252. taskadence/_generated/models/goal_move.py +52 -0
  253. taskadence/_generated/models/goal_moved.py +410 -0
  254. taskadence/_generated/models/goal_moved_tasks_type_0_item.py +53 -0
  255. taskadence/_generated/models/goal_order.py +54 -0
  256. taskadence/_generated/models/goal_out.py +402 -0
  257. taskadence/_generated/models/goal_out_tasks_type_0_item.py +53 -0
  258. taskadence/_generated/models/goal_task_unfiled.py +86 -0
  259. taskadence/_generated/models/goal_tasks_file.py +54 -0
  260. taskadence/_generated/models/goal_tasks_filed.py +86 -0
  261. taskadence/_generated/models/goal_update.py +137 -0
  262. taskadence/_generated/models/goals_reordered.py +80 -0
  263. taskadence/_generated/models/health.py +73 -0
  264. taskadence/_generated/models/http_validation_error.py +90 -0
  265. taskadence/_generated/models/invite_status_enum.py +18 -0
  266. taskadence/_generated/models/mcp_artefact.py +121 -0
  267. taskadence/_generated/models/mcp_artefact_kind.py +17 -0
  268. taskadence/_generated/models/mcp_artefact_lang.py +17 -0
  269. taskadence/_generated/models/mcp_client_row.py +192 -0
  270. taskadence/_generated/models/mcp_clients_out.py +144 -0
  271. taskadence/_generated/models/mcp_clients_out_scopes_for_group.py +68 -0
  272. taskadence/_generated/models/mcp_group_out.py +125 -0
  273. taskadence/_generated/models/mcp_group_scopes.py +80 -0
  274. taskadence/_generated/models/mcp_package.py +98 -0
  275. taskadence/_generated/models/mcp_package_registry.py +16 -0
  276. taskadence/_generated/models/mcp_stdio.py +109 -0
  277. taskadence/_generated/models/mcp_stdio_env.py +55 -0
  278. taskadence/_generated/models/mcp_stdio_flags.py +55 -0
  279. taskadence/_generated/models/mcp_stdio_packages.py +70 -0
  280. taskadence/_generated/models/me_auth.py +201 -0
  281. taskadence/_generated/models/me_auth_grant_type.py +17 -0
  282. taskadence/_generated/models/me_auth_token_kind.py +16 -0
  283. taskadence/_generated/models/me_organization.py +121 -0
  284. taskadence/_generated/models/me_out.py +164 -0
  285. taskadence/_generated/models/me_principal.py +151 -0
  286. taskadence/_generated/models/me_principal_type.py +16 -0
  287. taskadence/_generated/models/mention.py +123 -0
  288. taskadence/_generated/models/mention_in.py +105 -0
  289. taskadence/_generated/models/mfa_enrolment_counts.py +79 -0
  290. taskadence/_generated/models/mfa_requirement_enum.py +17 -0
  291. taskadence/_generated/models/milestone_create.py +106 -0
  292. taskadence/_generated/models/milestone_deleted.py +86 -0
  293. taskadence/_generated/models/milestone_move.py +52 -0
  294. taskadence/_generated/models/milestone_moved.py +387 -0
  295. taskadence/_generated/models/milestone_moved_tasks_type_0_item.py +53 -0
  296. taskadence/_generated/models/milestone_order.py +54 -0
  297. taskadence/_generated/models/milestone_org_out.py +388 -0
  298. taskadence/_generated/models/milestone_org_out_tasks_type_0_item.py +53 -0
  299. taskadence/_generated/models/milestone_out.py +379 -0
  300. taskadence/_generated/models/milestone_out_tasks_type_0_item.py +53 -0
  301. taskadence/_generated/models/milestone_task_unfiled.py +86 -0
  302. taskadence/_generated/models/milestone_tasks_file.py +54 -0
  303. taskadence/_generated/models/milestone_tasks_filed.py +86 -0
  304. taskadence/_generated/models/milestone_update.py +161 -0
  305. taskadence/_generated/models/milestones_reordered.py +80 -0
  306. taskadence/_generated/models/o_auth_client_create.py +161 -0
  307. taskadence/_generated/models/o_auth_client_create_client_type.py +16 -0
  308. taskadence/_generated/models/o_auth_client_created.py +491 -0
  309. taskadence/_generated/models/o_auth_client_created_client_type.py +16 -0
  310. taskadence/_generated/models/o_auth_client_created_registration.py +16 -0
  311. taskadence/_generated/models/o_auth_client_created_review_state.py +16 -0
  312. taskadence/_generated/models/o_auth_client_created_status.py +16 -0
  313. taskadence/_generated/models/o_auth_client_out.py +470 -0
  314. taskadence/_generated/models/o_auth_client_out_client_type.py +16 -0
  315. taskadence/_generated/models/o_auth_client_out_registration.py +16 -0
  316. taskadence/_generated/models/o_auth_client_out_review_state.py +16 -0
  317. taskadence/_generated/models/o_auth_client_out_status.py +16 -0
  318. taskadence/_generated/models/o_auth_client_update.py +188 -0
  319. taskadence/_generated/models/o_auth_error_body.py +94 -0
  320. taskadence/_generated/models/org_card.py +310 -0
  321. taskadence/_generated/models/org_goals_out.py +106 -0
  322. taskadence/_generated/models/org_join_mode_enum.py +16 -0
  323. taskadence/_generated/models/org_milestones_out.py +106 -0
  324. taskadence/_generated/models/organization_in_db.py +460 -0
  325. taskadence/_generated/models/organization_in_db_metadata_type_0.py +53 -0
  326. taskadence/_generated/models/organization_invite_create.py +327 -0
  327. taskadence/_generated/models/organization_invite_in_db.py +463 -0
  328. taskadence/_generated/models/organization_invite_update.py +220 -0
  329. taskadence/_generated/models/organization_member_in_db.py +454 -0
  330. taskadence/_generated/models/organization_member_update.py +126 -0
  331. taskadence/_generated/models/organization_settings_out.py +385 -0
  332. taskadence/_generated/models/organization_settings_update.py +364 -0
  333. taskadence/_generated/models/organization_settings_update_idle_timeout_minutes_type_0.py +25 -0
  334. taskadence/_generated/models/page_connected_app.py +109 -0
  335. taskadence/_generated/models/page_delivery_out.py +109 -0
  336. taskadence/_generated/models/page_designation.py +109 -0
  337. taskadence/_generated/models/page_o_auth_client_out.py +109 -0
  338. taskadence/_generated/models/page_org_card.py +109 -0
  339. taskadence/_generated/models/page_organization_invite_in_db.py +109 -0
  340. taskadence/_generated/models/page_organization_member_in_db.py +109 -0
  341. taskadence/_generated/models/page_project_card.py +109 -0
  342. taskadence/_generated/models/page_project_member_in_db.py +109 -0
  343. taskadence/_generated/models/page_project_resource_in_db.py +109 -0
  344. taskadence/_generated/models/page_section_out.py +109 -0
  345. taskadence/_generated/models/page_service_account_out.py +109 -0
  346. taskadence/_generated/models/page_task_attachment_in_db.py +109 -0
  347. taskadence/_generated/models/page_task_comment_in_db.py +109 -0
  348. taskadence/_generated/models/page_task_history_in_db.py +109 -0
  349. taskadence/_generated/models/page_token_out.py +109 -0
  350. taskadence/_generated/models/page_webhook_event_type.py +109 -0
  351. taskadence/_generated/models/page_webhook_out.py +109 -0
  352. taskadence/_generated/models/pin_list_page.py +108 -0
  353. taskadence/_generated/models/pin_order.py +78 -0
  354. taskadence/_generated/models/pin_out.py +203 -0
  355. taskadence/_generated/models/pin_ref.py +64 -0
  356. taskadence/_generated/models/pin_type_enum.py +16 -0
  357. taskadence/_generated/models/priority_enum.py +19 -0
  358. taskadence/_generated/models/problem.py +167 -0
  359. taskadence/_generated/models/problem_errors_item.py +53 -0
  360. taskadence/_generated/models/problem_mfa.py +109 -0
  361. taskadence/_generated/models/problem_mfa_reason.py +16 -0
  362. taskadence/_generated/models/problem_mfa_required_for.py +16 -0
  363. taskadence/_generated/models/problem_type.py +63 -0
  364. taskadence/_generated/models/project_card.py +574 -0
  365. taskadence/_generated/models/project_card_metadata_type_0.py +53 -0
  366. taskadence/_generated/models/project_create.py +494 -0
  367. taskadence/_generated/models/project_create_metadata_type_0.py +53 -0
  368. taskadence/_generated/models/project_goals_out.py +135 -0
  369. taskadence/_generated/models/project_in_db.py +565 -0
  370. taskadence/_generated/models/project_in_db_metadata_type_0.py +53 -0
  371. taskadence/_generated/models/project_lead_out.py +145 -0
  372. taskadence/_generated/models/project_member_create.py +349 -0
  373. taskadence/_generated/models/project_member_in_db.py +368 -0
  374. taskadence/_generated/models/project_member_update.py +161 -0
  375. taskadence/_generated/models/project_milestones_group.py +143 -0
  376. taskadence/_generated/models/project_milestones_out.py +135 -0
  377. taskadence/_generated/models/project_order.py +63 -0
  378. taskadence/_generated/models/project_resource_create.py +191 -0
  379. taskadence/_generated/models/project_resource_in_db.py +384 -0
  380. taskadence/_generated/models/project_resource_update.py +125 -0
  381. taskadence/_generated/models/project_role_enum.py +17 -0
  382. taskadence/_generated/models/project_stats_in_db.py +153 -0
  383. taskadence/_generated/models/project_status_enum.py +24 -0
  384. taskadence/_generated/models/project_update.py +454 -0
  385. taskadence/_generated/models/project_update_metadata_type_0.py +53 -0
  386. taskadence/_generated/models/project_visibility_enum.py +16 -0
  387. taskadence/_generated/models/projects_reordered.py +88 -0
  388. taskadence/_generated/models/protected_resource_metadata.py +137 -0
  389. taskadence/_generated/models/registration_error_body.py +95 -0
  390. taskadence/_generated/models/registration_error_body_error.py +16 -0
  391. taskadence/_generated/models/release.py +215 -0
  392. taskadence/_generated/models/release_note_item.py +278 -0
  393. taskadence/_generated/models/release_summary.py +97 -0
  394. taskadence/_generated/models/release_type.py +18 -0
  395. taskadence/_generated/models/reply_create.py +60 -0
  396. taskadence/_generated/models/review_member.py +261 -0
  397. taskadence/_generated/models/review_meta.py +70 -0
  398. taskadence/_generated/models/review_project.py +101 -0
  399. taskadence/_generated/models/review_summary.py +158 -0
  400. taskadence/_generated/models/review_token.py +184 -0
  401. taskadence/_generated/models/scope_ref.py +82 -0
  402. taskadence/_generated/models/section_create.py +101 -0
  403. taskadence/_generated/models/section_out.py +225 -0
  404. taskadence/_generated/models/section_reorder.py +72 -0
  405. taskadence/_generated/models/section_scope_enum.py +17 -0
  406. taskadence/_generated/models/section_update.py +85 -0
  407. taskadence/_generated/models/sections_for_scopes.py +133 -0
  408. taskadence/_generated/models/service_account_create.py +75 -0
  409. taskadence/_generated/models/service_account_out.py +209 -0
  410. taskadence/_generated/models/service_account_update.py +52 -0
  411. taskadence/_generated/models/sprint_create.py +156 -0
  412. taskadence/_generated/models/sprint_deleted.py +86 -0
  413. taskadence/_generated/models/sprint_list_page.py +116 -0
  414. taskadence/_generated/models/sprint_out.py +371 -0
  415. taskadence/_generated/models/sprint_update.py +208 -0
  416. taskadence/_generated/models/storage_breakdown.py +104 -0
  417. taskadence/_generated/models/storage_by_kind.py +88 -0
  418. taskadence/_generated/models/storage_day.py +78 -0
  419. taskadence/_generated/models/storage_file.py +171 -0
  420. taskadence/_generated/models/storage_file_kind.py +17 -0
  421. taskadence/_generated/models/storage_project.py +101 -0
  422. taskadence/_generated/models/storage_usage.py +233 -0
  423. taskadence/_generated/models/storage_usage_status.py +19 -0
  424. taskadence/_generated/models/subtask_link.py +52 -0
  425. taskadence/_generated/models/task_attachment_in_db.py +333 -0
  426. taskadence/_generated/models/task_attachment_update.py +127 -0
  427. taskadence/_generated/models/task_card_view.py +1019 -0
  428. taskadence/_generated/models/task_card_view_metadata_type_0_item.py +53 -0
  429. taskadence/_generated/models/task_card_view_type_data_type_0.py +53 -0
  430. taskadence/_generated/models/task_comment_create.py +282 -0
  431. taskadence/_generated/models/task_comment_in_db.py +329 -0
  432. taskadence/_generated/models/task_comment_update.py +85 -0
  433. taskadence/_generated/models/task_create.py +669 -0
  434. taskadence/_generated/models/task_create_metadata_type_0_item.py +53 -0
  435. taskadence/_generated/models/task_create_type_data_type_0.py +53 -0
  436. taskadence/_generated/models/task_history_in_db.py +231 -0
  437. taskadence/_generated/models/task_history_in_db_metadata_type_0_item.py +53 -0
  438. taskadence/_generated/models/task_in_db.py +997 -0
  439. taskadence/_generated/models/task_in_db_metadata_type_0_item.py +53 -0
  440. taskadence/_generated/models/task_in_db_type_data_type_0.py +53 -0
  441. taskadence/_generated/models/task_list_page.py +109 -0
  442. taskadence/_generated/models/task_milestone_set.py +67 -0
  443. taskadence/_generated/models/task_project_set.py +66 -0
  444. taskadence/_generated/models/task_review_decision.py +58 -0
  445. taskadence/_generated/models/task_review_decision_decision.py +16 -0
  446. taskadence/_generated/models/task_section_out.py +118 -0
  447. taskadence/_generated/models/task_section_set.py +105 -0
  448. taskadence/_generated/models/task_sprint_set.py +65 -0
  449. taskadence/_generated/models/task_status_enum.py +21 -0
  450. taskadence/_generated/models/task_type_enum.py +16 -0
  451. taskadence/_generated/models/task_update.py +582 -0
  452. taskadence/_generated/models/task_update_metadata_type_0_item.py +53 -0
  453. taskadence/_generated/models/task_update_type_data_type_0.py +53 -0
  454. taskadence/_generated/models/team_create.py +95 -0
  455. taskadence/_generated/models/team_deleted.py +94 -0
  456. taskadence/_generated/models/team_detail.py +298 -0
  457. taskadence/_generated/models/team_list_page.py +116 -0
  458. taskadence/_generated/models/team_member_create.py +73 -0
  459. taskadence/_generated/models/team_member_designation.py +75 -0
  460. taskadence/_generated/models/team_member_out.py +192 -0
  461. taskadence/_generated/models/team_member_update.py +56 -0
  462. taskadence/_generated/models/team_out.py +270 -0
  463. taskadence/_generated/models/team_role_enum.py +16 -0
  464. taskadence/_generated/models/team_update.py +85 -0
  465. taskadence/_generated/models/token_create.py +190 -0
  466. taskadence/_generated/models/token_create_kind.py +16 -0
  467. taskadence/_generated/models/token_created.py +461 -0
  468. taskadence/_generated/models/token_created_grant_type.py +17 -0
  469. taskadence/_generated/models/token_created_kind.py +16 -0
  470. taskadence/_generated/models/token_created_status.py +17 -0
  471. taskadence/_generated/models/token_out.py +452 -0
  472. taskadence/_generated/models/token_out_grant_type.py +17 -0
  473. taskadence/_generated/models/token_out_kind.py +16 -0
  474. taskadence/_generated/models/token_out_status.py +17 -0
  475. taskadence/_generated/models/token_response.py +113 -0
  476. taskadence/_generated/models/token_update.py +52 -0
  477. taskadence/_generated/models/unattached_token.py +192 -0
  478. taskadence/_generated/models/upload_complete_in.py +65 -0
  479. taskadence/_generated/models/upload_create_in.py +193 -0
  480. taskadence/_generated/models/upload_create_in_kind.py +16 -0
  481. taskadence/_generated/models/upload_create_out.py +116 -0
  482. taskadence/_generated/models/upload_create_out_headers.py +55 -0
  483. taskadence/_generated/models/validation_error.py +134 -0
  484. taskadence/_generated/models/validation_error_context.py +53 -0
  485. taskadence/_generated/models/view_create.py +165 -0
  486. taskadence/_generated/models/view_create_display.py +55 -0
  487. taskadence/_generated/models/view_create_query.py +83 -0
  488. taskadence/_generated/models/view_deleted.py +78 -0
  489. taskadence/_generated/models/view_duplicate.py +102 -0
  490. taskadence/_generated/models/view_list_page.py +108 -0
  491. taskadence/_generated/models/view_out.py +295 -0
  492. taskadence/_generated/models/view_out_display.py +53 -0
  493. taskadence/_generated/models/view_out_query.py +53 -0
  494. taskadence/_generated/models/view_resource_enum.py +16 -0
  495. taskadence/_generated/models/view_scope_enum.py +17 -0
  496. taskadence/_generated/models/view_update.py +168 -0
  497. taskadence/_generated/models/view_update_display_type_0.py +53 -0
  498. taskadence/_generated/models/view_update_query_type_0.py +78 -0
  499. taskadence/_generated/models/visibility_update.py +85 -0
  500. taskadence/_generated/models/webhook_actor.py +100 -0
  501. taskadence/_generated/models/webhook_create.py +113 -0
  502. taskadence/_generated/models/webhook_created.py +438 -0
  503. taskadence/_generated/models/webhook_created_status.py +17 -0
  504. taskadence/_generated/models/webhook_event.py +161 -0
  505. taskadence/_generated/models/webhook_event_data.py +140 -0
  506. taskadence/_generated/models/webhook_event_data_after_type_0.py +53 -0
  507. taskadence/_generated/models/webhook_event_data_before_type_0.py +53 -0
  508. taskadence/_generated/models/webhook_event_type.py +97 -0
  509. taskadence/_generated/models/webhook_out.py +428 -0
  510. taskadence/_generated/models/webhook_out_status.py +17 -0
  511. taskadence/_generated/models/webhook_update.py +184 -0
  512. taskadence/_generated/models/webhook_update_status_type_0.py +16 -0
  513. taskadence/_generated/models/whats_new_response.py +107 -0
  514. taskadence/_generated/models/whats_new_response_summary.py +53 -0
  515. taskadence/_generated/types.py +56 -0
  516. taskadence/_operations.py +1969 -0
  517. taskadence/_uploads.py +527 -0
  518. taskadence/_version.py +3 -0
  519. taskadence/cli/__init__.py +612 -0
  520. taskadence/cli/_config.py +166 -0
  521. taskadence/dataframe.py +68 -0
  522. taskadence/errors.py +350 -0
  523. taskadence/models.py +14 -0
  524. taskadence/pagination.py +137 -0
  525. taskadence/py.typed +0 -0
  526. taskadence/resources.py +6127 -0
  527. taskadence/webhooks.py +149 -0
  528. taskadence-0.1.0.dist-info/METADATA +524 -0
  529. taskadence-0.1.0.dist-info/RECORD +533 -0
  530. taskadence-0.1.0.dist-info/WHEEL +4 -0
  531. taskadence-0.1.0.dist-info/entry_points.txt +3 -0
  532. taskadence-0.1.0.dist-info/licenses/LICENSE +202 -0
  533. taskadence-0.1.0.dist-info/licenses/NOTICE +2 -0
taskadence/_core.py ADDED
@@ -0,0 +1,563 @@
1
+ """The request layer under both clients: build a request from an `Operation`, send it with retries, turn the answer
2
+ into a typed model, a `Page`, `NotModified`, or an exception.
3
+
4
+ WHY THE FACADE SENDS ITS OWN REQUESTS. The generated client (`taskadence._generated`) is the source of the MODELS and of
5
+ each operation's response PARSER (`_parse_response`, which the facade calls, so typing is exactly the generator's).
6
+ Its request functions are not used to send, because (1) they always send every spec default — `limit=100`,
7
+ `sort_by=title`, the deprecated `offset=0` and a leaked `is_registration=false` — where the facade sends only what the
8
+ caller passed; (2) they give no hook per call for retries, `Idempotency-Key`, `If-Match` / `If-None-Match` or the
9
+ `ETag`; (3) they name the list grammar's `filter[status]` as a keyword `filterstatus`. The operation table
10
+ (`_operations.py`) is generated from the same spec, and `tests/test_drift.py` checks each operation's method and URL
11
+ against the generated `_get_kwargs`.
12
+
13
+ RETRIES. Safe requests are retried on 429 (honouring `Retry-After`), 502 / 503 / 504 and connection failures, with
14
+ exponential backoff and jitter, up to `max_retries`: GET / HEAD / DELETE / PUT always, PATCH only with `If-Match`, POST
15
+ only with an `Idempotency-Key` (every create operation gets a fresh uuid4 key unless the caller passes one, or None).
16
+ A `Retry-After` longer than `max_retry_after` is not waited out: the `RateLimitedError` is raised at once.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import datetime as _dt
22
+ import enum
23
+ import json
24
+ import logging
25
+ import mimetypes
26
+ import os
27
+ import platform
28
+ import random
29
+ import time
30
+ import uuid
31
+ import warnings
32
+ from collections.abc import Callable, Mapping, Sequence
33
+ from dataclasses import dataclass, field
34
+ from pathlib import PurePath
35
+ from typing import IO, Any, Final
36
+ from urllib.parse import quote
37
+
38
+ import httpx
39
+
40
+ from . import errors
41
+ from ._brand import BRAND_NAME, ENV_PREFIX, SLUG, getenv
42
+ from ._version import __version__
43
+ from .pagination import AsyncPage, Page
44
+
45
+ logger = logging.getLogger(SLUG)
46
+
47
+ TOKEN_ENV = ENV_PREFIX + "TOKEN" # the variable NAME (`_brand.getenv` also reads the pre-rename name)
48
+ URL_ENV = ENV_PREFIX + "API_URL"
49
+ VERSION_HEADER = f"{BRAND_NAME}-Version"
50
+
51
+ RETRY_STATUSES: Final = frozenset({429, 502, 503, 504})
52
+ IDEMPOTENT_METHODS: Final = frozenset({"GET", "HEAD", "DELETE", "PUT", "OPTIONS"})
53
+ BACKOFF_BASE = 0.5
54
+ BACKOFF_MAX = 8.0
55
+
56
+ FileContent = bytes | IO[bytes]
57
+ FileInput = bytes | IO[bytes] | str | os.PathLike[str] | tuple[str, FileContent] | tuple[str, FileContent, str]
58
+
59
+
60
+ class NotModifiedType:
61
+ """The answer to a read with `if_none_match=<etag>` when the resource has not changed (HTTP 304). Falsy."""
62
+
63
+ _instance: NotModifiedType | None = None
64
+
65
+ def __new__(cls) -> NotModifiedType:
66
+ if cls._instance is None:
67
+ cls._instance = super().__new__(cls)
68
+ return cls._instance
69
+
70
+ def __bool__(self) -> bool:
71
+ return False
72
+
73
+ def __repr__(self) -> str:
74
+ return "NotModified"
75
+
76
+
77
+ NotModified: Final = NotModifiedType()
78
+
79
+
80
+ class _Auto:
81
+ def __repr__(self) -> str:
82
+ return "AUTO"
83
+
84
+
85
+ AUTO: Final = _Auto()
86
+ """`idempotency_key=AUTO` (the default on creates): a fresh uuid4 per call, reused by that call's retries."""
87
+
88
+
89
+ class ConfigurationError(errors.APIError, ValueError):
90
+ """The client cannot be built as asked (no token, a malformed base URL)."""
91
+
92
+
93
+ @dataclass(frozen=True)
94
+ class Operation:
95
+ """One public API operation, as the facade needs it. Instances are generated into `_operations.py`."""
96
+
97
+ op_id: str
98
+ method: str
99
+ path: str
100
+ path_params: tuple[str, ...] = ()
101
+ query: tuple[str, ...] = ()
102
+ filters: tuple[str, ...] = ()
103
+ body: str | None = None # "json" | "multipart" | None
104
+ files: tuple[str, ...] = () # multipart fields that carry a file
105
+ is_list: bool = False # `{data, next_cursor}` (possibly one member of a union)
106
+ is_create: bool = False # honours `Idempotency-Key`
107
+ if_match: bool = False
108
+ if_none_match: bool = False
109
+ returns: str = "model" # "model" | "text" | "none"
110
+ parse: Callable[..., Any] | None = field(default=None, repr=False, compare=False)
111
+ item: Callable[[Any], Any] | None = field(default=None, repr=False, compare=False)
112
+ scopes: tuple[str, ...] = ()
113
+ summary: str = ""
114
+
115
+
116
+ @dataclass
117
+ class _Request:
118
+ method: str
119
+ url: str
120
+ params: list[tuple[str, str]]
121
+ headers: dict[str, str]
122
+ content: bytes | None = None
123
+ files: list[tuple[str, tuple[str, bytes, str]]] | None = None
124
+ data: dict[str, str] | None = None
125
+
126
+ @property
127
+ def retryable(self) -> bool:
128
+ if self.method in IDEMPOTENT_METHODS:
129
+ return True
130
+ if self.method == "PATCH":
131
+ return "If-Match" in self.headers
132
+ if self.method == "POST":
133
+ return "Idempotency-Key" in self.headers
134
+ return False
135
+
136
+
137
+ # ---------------------------------------------------------------------------
138
+ # Encoding
139
+ # ---------------------------------------------------------------------------
140
+
141
+
142
+ def _json_default(value: Any) -> Any:
143
+ if hasattr(value, "to_dict"):
144
+ return value.to_dict()
145
+ if isinstance(value, (_dt.datetime, _dt.date)):
146
+ return value.isoformat()
147
+ if isinstance(value, enum.Enum):
148
+ return value.value
149
+ if isinstance(value, (set, frozenset, tuple)):
150
+ return list(value)
151
+ raise TypeError(f"{type(value).__name__} is not JSON serializable")
152
+
153
+
154
+ def _scalar(value: Any) -> str:
155
+ if isinstance(value, bool):
156
+ return "true" if value else "false"
157
+ if isinstance(value, (_dt.datetime, _dt.date)):
158
+ return value.isoformat()
159
+ if isinstance(value, enum.Enum):
160
+ return str(value.value)
161
+ return str(value)
162
+
163
+
164
+ def _query_value(value: Any) -> str:
165
+ """A list is comma-joined (the list grammar's multi-value rule); anything else is its string form."""
166
+ if isinstance(value, (list, tuple, set, frozenset)):
167
+ return ",".join(_scalar(v) for v in value)
168
+ return _scalar(value)
169
+
170
+
171
+ def _file_part(name: str, value: FileInput) -> tuple[str, tuple[str, bytes, str]]:
172
+ filename: str | None = None
173
+ mime: str | None = None
174
+ content: Any = value
175
+ if isinstance(value, tuple):
176
+ filename, content = value[0], value[1]
177
+ mime = value[2] if len(value) > 2 else None
178
+ if isinstance(content, (str, os.PathLike)):
179
+ path = os.fspath(content)
180
+ filename = filename or PurePath(path).name
181
+ with open(path, "rb") as handle:
182
+ data = handle.read()
183
+ elif isinstance(content, bytes):
184
+ data = content
185
+ else:
186
+ data = content.read()
187
+ filename = filename or PurePath(str(getattr(content, "name", "upload"))).name
188
+ filename = filename or "upload"
189
+ mime = mime or mimetypes.guess_type(filename)[0] or "application/octet-stream"
190
+ return name, (filename, data, mime)
191
+
192
+
193
+ # ---------------------------------------------------------------------------
194
+ # The shared core
195
+ # ---------------------------------------------------------------------------
196
+
197
+
198
+ class _BaseClient:
199
+ def __init__(
200
+ self,
201
+ token: str | None,
202
+ base_url: str | None,
203
+ *,
204
+ timeout: float,
205
+ max_retries: int,
206
+ api_version: str | None,
207
+ default_base_url: str,
208
+ spec_version: str,
209
+ max_retry_after: float,
210
+ headers: Mapping[str, str] | None,
211
+ ) -> None:
212
+ token = token or getenv(TOKEN_ENV)
213
+ if not token:
214
+ raise ConfigurationError(
215
+ f"No token: pass {BRAND_NAME}(token=...) or set {TOKEN_ENV} (a `tkd_live_…` access token)."
216
+ )
217
+ base = (base_url or getenv(URL_ENV) or default_base_url).strip().rstrip("/")
218
+ base = base.removesuffix("/v1")
219
+ if not base.startswith(("http://", "https://")):
220
+ raise ConfigurationError(f"base_url must start with http:// or https:// (got {base!r})")
221
+ self.base_url = base
222
+ self.timeout = timeout
223
+ self.max_retries = max(0, int(max_retries))
224
+ self.max_retry_after = max_retry_after
225
+ self.api_version = api_version or spec_version
226
+ self._headers = {
227
+ "Authorization": f"Bearer {token}",
228
+ "Accept": "application/json, application/problem+json",
229
+ "User-Agent": f"{SLUG}-python/{__version__} python/{platform.python_version()}",
230
+ VERSION_HEADER: self.api_version,
231
+ **dict(headers or {}),
232
+ }
233
+ self._warned: set[str] = set()
234
+ self._sleep: Callable[[float], Any] = time.sleep
235
+
236
+ def __repr__(self) -> str: # never the token
237
+ return f"<{type(self).__name__} base_url={self.base_url!r}>"
238
+
239
+ # -- building -------------------------------------------------------------------------------------------------
240
+
241
+ def _build(
242
+ self,
243
+ op: Operation,
244
+ *,
245
+ path: Sequence[Any] = (),
246
+ query: Mapping[str, Any] | None = None,
247
+ filter: Mapping[str, Any] | None = None,
248
+ body: Any = None,
249
+ form: Mapping[str, Any] | None = None,
250
+ if_match: str | None = None,
251
+ if_none_match: str | None = None,
252
+ idempotency_key: str | _Auto | None = AUTO,
253
+ ) -> _Request:
254
+ if len(path) != len(op.path_params):
255
+ raise TypeError(f"{op.op_id} takes {len(op.path_params)} path argument(s): {', '.join(op.path_params)}")
256
+ url = op.path.format(
257
+ **{name: quote(_scalar(value), safe="") for name, value in zip(op.path_params, path, strict=True)}
258
+ )
259
+ params: list[tuple[str, str]] = []
260
+ for key, value in (query or {}).items():
261
+ if value is not None:
262
+ params.append((key, _query_value(value)))
263
+ if filter:
264
+ unknown = sorted(set(filter) - set(op.filters))
265
+ if unknown:
266
+ allowed = ", ".join(op.filters) or "none"
267
+ raise ValueError(f"{op.op_id}: unknown filter key(s) {', '.join(unknown)}; allowed: {allowed}")
268
+ for key, value in filter.items():
269
+ if value is not None:
270
+ params.append((f"filter[{key}]", _query_value(value)))
271
+ headers: dict[str, str] = {}
272
+ if op.is_create and idempotency_key is not None:
273
+ headers["Idempotency-Key"] = str(uuid.uuid4()) if isinstance(idempotency_key, _Auto) else idempotency_key
274
+ if if_match is not None:
275
+ headers["If-Match"] = if_match
276
+ if if_none_match is not None:
277
+ headers["If-None-Match"] = if_none_match
278
+ request = _Request(op.method, url, params, headers)
279
+ if op.body == "json" and body is not None:
280
+ payload = body.to_dict() if hasattr(body, "to_dict") else body
281
+ request.content = json.dumps(payload, default=_json_default).encode()
282
+ headers["Content-Type"] = "application/json"
283
+ elif op.body == "multipart":
284
+ request.files, request.data = [], {}
285
+ for key, value in (form or {}).items():
286
+ if value is None:
287
+ continue
288
+ if key in op.files:
289
+ request.files.append(_file_part(key, value))
290
+ else:
291
+ request.data[key] = _scalar(value)
292
+ return request
293
+
294
+ # -- retries --------------------------------------------------------------------------------------------------
295
+
296
+ def _retry_delay(self, attempt: int, response: httpx.Response | None) -> float | None:
297
+ """Seconds to wait before attempt `attempt + 1`, or None: do not retry."""
298
+ if attempt >= self.max_retries:
299
+ return None
300
+ if response is not None:
301
+ after = errors.retry_after_seconds(response.headers)
302
+ if after is not None:
303
+ return after if after <= self.max_retry_after else None
304
+ ceiling = min(BACKOFF_MAX, BACKOFF_BASE * (2.0**attempt))
305
+ return ceiling / 2 + random.uniform(0, ceiling / 2) # noqa: S311 - jitter, not cryptography
306
+
307
+ def _log(
308
+ self,
309
+ request: _Request,
310
+ response: httpx.Response | None,
311
+ started: float,
312
+ attempt: int,
313
+ failure: Exception | None = None,
314
+ ) -> None:
315
+ if not logger.isEnabledFor(logging.DEBUG):
316
+ return
317
+ took = (time.monotonic() - started) * 1000
318
+ if response is not None:
319
+ logger.debug(
320
+ "%s %s -> %s in %.0f ms (attempt %d, request_id %s)",
321
+ request.method,
322
+ request.url,
323
+ response.status_code,
324
+ took,
325
+ attempt + 1,
326
+ response.headers.get("x-request-id"),
327
+ )
328
+ else:
329
+ logger.debug(
330
+ "%s %s -> %s after %.0f ms (attempt %d)",
331
+ request.method,
332
+ request.url,
333
+ type(failure).__name__,
334
+ took,
335
+ attempt + 1,
336
+ )
337
+
338
+ # -- answering ------------------------------------------------------------------------------------------------
339
+
340
+ def _warn_deprecated(self, op: Operation, response: httpx.Response) -> None:
341
+ if op.op_id in self._warned:
342
+ return
343
+ deprecation = response.headers.get("deprecation")
344
+ sunset = response.headers.get("sunset")
345
+ if not (deprecation or sunset):
346
+ return
347
+ self._warned.add(op.op_id)
348
+ successor = response.headers.get("link", "")
349
+ text = f"{BRAND_NAME}: {op.op_id} ({op.method} {op.path}) is deprecated"
350
+ if sunset:
351
+ text += f" and will be removed after {sunset}"
352
+ if successor:
353
+ text += f"; successor: {successor}"
354
+ warnings.warn(text, DeprecationWarning, stacklevel=5)
355
+
356
+ def _answer(
357
+ self,
358
+ op: Operation,
359
+ response: httpx.Response,
360
+ parse_client: Any,
361
+ again: Callable[[str], Any] | None,
362
+ page_cls: type[Page[Any]] | type[AsyncPage[Any]],
363
+ ) -> Any:
364
+ self._warn_deprecated(op, response)
365
+ if response.status_code == 304:
366
+ return NotModified
367
+ if response.status_code >= 400:
368
+ raise errors.from_response(response)
369
+ if op.returns == "none" or response.status_code == 204:
370
+ return None
371
+ if op.returns == "text":
372
+ return response.text
373
+ etag = response.headers.get("etag")
374
+ if op.is_list and op.item is not None:
375
+ try:
376
+ raw = response.json()
377
+ except ValueError as exc:
378
+ raise errors.ResponseValidationError(
379
+ f"{op.op_id}: the response is not JSON", operation=op.op_id, body=response.text
380
+ ) from exc
381
+ if isinstance(raw, list): # a bare array (pre-4.1 shape): one page, no cursor — as `unwrapList` reads it
382
+ items = [self._parse_item(op, item) for item in raw]
383
+ return page_cls(items, None, envelope=raw, etag=etag)
384
+ assert op.parse is not None, op.op_id
385
+ try:
386
+ parsed = op.parse(client=parse_client, response=response)
387
+ except (KeyError, TypeError, ValueError, AttributeError) as exc:
388
+ raise errors.ResponseValidationError(
389
+ f"{op.op_id}: the response does not match the models this SDK was generated from ({exc!r}); "
390
+ "upgrade taskadence, or read `.body`",
391
+ operation=op.op_id,
392
+ body=_json_or_text(response),
393
+ ) from exc
394
+ if parsed is None:
395
+ raise errors.ResponseValidationError(
396
+ f"{op.op_id}: HTTP {response.status_code} is not a status the API documents for this operation",
397
+ operation=op.op_id,
398
+ body=_json_or_text(response),
399
+ )
400
+ if op.is_list and hasattr(parsed, "data") and hasattr(parsed, "next_cursor"):
401
+ cursor = parsed.next_cursor if isinstance(parsed.next_cursor, str) else None
402
+ return page_cls(list(parsed.data), cursor, envelope=parsed, etag=etag, fetch_next=again)
403
+ if etag and hasattr(parsed, "etag"):
404
+ parsed.etag = etag
405
+ return parsed
406
+
407
+ @staticmethod
408
+ def _parse_item(op: Operation, raw: Any) -> Any:
409
+ assert op.item is not None
410
+ try:
411
+ return op.item(raw)
412
+ except (KeyError, TypeError, ValueError, AttributeError) as exc:
413
+ raise errors.ResponseValidationError(
414
+ f"{op.op_id}: an item does not match the generated model ({exc!r})", operation=op.op_id, body=raw
415
+ ) from exc
416
+
417
+
418
+ def _json_or_text(response: httpx.Response) -> Any:
419
+ try:
420
+ return response.json()
421
+ except ValueError:
422
+ return response.text
423
+
424
+
425
+ class SyncCore(_BaseClient):
426
+ """The synchronous transport (`Taskadence`)."""
427
+
428
+ def __init__(self, *args: Any, http_client: httpx.Client | None = None, **kwargs: Any) -> None:
429
+ super().__init__(*args, **kwargs)
430
+ self._owns_http = http_client is None
431
+ self._http = http_client or httpx.Client(timeout=self.timeout, follow_redirects=False)
432
+ from ._generated.client import Client as _ParseClient
433
+
434
+ self._parse_client = _ParseClient(base_url=self.base_url, raise_on_unexpected_status=False)
435
+
436
+ def _send(self, request: _Request) -> httpx.Response:
437
+ attempt = 0
438
+ while True:
439
+ started = time.monotonic()
440
+ try:
441
+ response = self._http.request(
442
+ request.method,
443
+ self.base_url + request.url,
444
+ params=tuple(request.params),
445
+ headers={**self._headers, **request.headers},
446
+ content=request.content,
447
+ files=request.files,
448
+ data=request.data,
449
+ )
450
+ except (httpx.ConnectError, httpx.ConnectTimeout, httpx.RemoteProtocolError) as exc:
451
+ self._log(request, None, started, attempt, exc)
452
+ delay = self._retry_delay(attempt, None) if request.retryable else None
453
+ if delay is None:
454
+ raise (
455
+ errors.APITimeoutError if isinstance(exc, httpx.TimeoutException) else errors.APIConnectionError
456
+ )(f"{request.method} {request.url}: {exc}") from exc
457
+ except httpx.TimeoutException as exc:
458
+ self._log(request, None, started, attempt, exc)
459
+ raise errors.APITimeoutError(f"{request.method} {request.url}: {exc}") from exc
460
+ else:
461
+ self._log(request, response, started, attempt)
462
+ delay = (
463
+ self._retry_delay(attempt, response)
464
+ if request.retryable and response.status_code in RETRY_STATUSES
465
+ else None
466
+ )
467
+ if delay is None:
468
+ return response
469
+ response.close()
470
+ self._sleep(delay)
471
+ attempt += 1
472
+
473
+ def _call(self, op: Operation, **kwargs: Any) -> Any:
474
+ request = self._build(op, **kwargs)
475
+ response = self._send(request)
476
+
477
+ def again(cursor: str) -> Any:
478
+ return self._call(op, **{**kwargs, "query": {**(kwargs.get("query") or {}), "cursor": cursor}})
479
+
480
+ return self._answer(op, response, self._parse_client, again, Page)
481
+
482
+ def _upload(self, op: Operation, *, progress: Callable[[int, int], object] | None = None, **kwargs: Any) -> Any:
483
+ """A file upload: direct to storage, else the operation's multipart route (`_uploads`)."""
484
+ from ._uploads import upload
485
+
486
+ return upload(self, op, query=kwargs.get("query"), form=kwargs.get("form"), progress=progress)
487
+
488
+ def close(self) -> None:
489
+ if self._owns_http:
490
+ self._http.close()
491
+
492
+
493
+ class AsyncCore(_BaseClient):
494
+ """The asynchronous transport (`AsyncTaskadence`)."""
495
+
496
+ def __init__(self, *args: Any, http_client: httpx.AsyncClient | None = None, **kwargs: Any) -> None:
497
+ super().__init__(*args, **kwargs)
498
+ import asyncio
499
+
500
+ self._owns_http = http_client is None
501
+ self._http = http_client or httpx.AsyncClient(timeout=self.timeout, follow_redirects=False)
502
+ self._asleep: Callable[[float], Any] = asyncio.sleep
503
+ from ._generated.client import Client as _ParseClient
504
+
505
+ self._parse_client = _ParseClient(base_url=self.base_url, raise_on_unexpected_status=False)
506
+
507
+ async def _send(self, request: _Request) -> httpx.Response:
508
+ attempt = 0
509
+ while True:
510
+ started = time.monotonic()
511
+ try:
512
+ response = await self._http.request(
513
+ request.method,
514
+ self.base_url + request.url,
515
+ params=tuple(request.params),
516
+ headers={**self._headers, **request.headers},
517
+ content=request.content,
518
+ files=request.files,
519
+ data=request.data,
520
+ )
521
+ except (httpx.ConnectError, httpx.ConnectTimeout, httpx.RemoteProtocolError) as exc:
522
+ self._log(request, None, started, attempt, exc)
523
+ delay = self._retry_delay(attempt, None) if request.retryable else None
524
+ if delay is None:
525
+ raise (
526
+ errors.APITimeoutError if isinstance(exc, httpx.TimeoutException) else errors.APIConnectionError
527
+ )(f"{request.method} {request.url}: {exc}") from exc
528
+ except httpx.TimeoutException as exc:
529
+ self._log(request, None, started, attempt, exc)
530
+ raise errors.APITimeoutError(f"{request.method} {request.url}: {exc}") from exc
531
+ else:
532
+ self._log(request, response, started, attempt)
533
+ delay = (
534
+ self._retry_delay(attempt, response)
535
+ if request.retryable and response.status_code in RETRY_STATUSES
536
+ else None
537
+ )
538
+ if delay is None:
539
+ return response
540
+ await response.aclose()
541
+ await self._asleep(delay)
542
+ attempt += 1
543
+
544
+ async def _call(self, op: Operation, **kwargs: Any) -> Any:
545
+ request = self._build(op, **kwargs)
546
+ response = await self._send(request)
547
+
548
+ async def again(cursor: str) -> Any:
549
+ return await self._call(op, **{**kwargs, "query": {**(kwargs.get("query") or {}), "cursor": cursor}})
550
+
551
+ return self._answer(op, response, self._parse_client, again, AsyncPage)
552
+
553
+ async def _upload(
554
+ self, op: Operation, *, progress: Callable[[int, int], object] | None = None, **kwargs: Any
555
+ ) -> Any:
556
+ """A file upload: direct to storage, else the operation's multipart route (`_uploads`)."""
557
+ from ._uploads import aupload
558
+
559
+ return await aupload(self, op, query=kwargs.get("query"), form=kwargs.get("form"), progress=progress)
560
+
561
+ async def aclose(self) -> None:
562
+ if self._owns_http:
563
+ await self._http.aclose()
@@ -0,0 +1,11 @@
1
+ # GENERATED by scripts/generate.py from spec/openapi.public.json — DO NOT EDIT BY HAND.
2
+ # Change the spec snapshot (or the generator) and run `python scripts/generate.py`.
3
+
4
+ """A client library for accessing TasKadence API"""
5
+
6
+ from .client import AuthenticatedClient, Client
7
+
8
+ __all__ = (
9
+ "AuthenticatedClient",
10
+ "Client",
11
+ )
@@ -0,0 +1,3 @@
1
+ # GENERATED by scripts/generate.py from spec/openapi.public.json — DO NOT EDIT BY HAND.
2
+ # Change the spec snapshot (or the generator) and run `python scripts/generate.py`.
3
+ """Contains methods for accessing the API"""
@@ -0,0 +1,3 @@
1
+ # GENERATED by scripts/generate.py from spec/openapi.public.json — DO NOT EDIT BY HAND.
2
+ # Change the spec snapshot (or the generator) and run `python scripts/generate.py`.
3
+ """Contains endpoint functions for accessing the API"""