glpi-python-client 0.3.4__tar.gz → 0.4.2__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 (212) hide show
  1. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/.gitignore +9 -1
  2. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/.pre-commit-config.yaml +10 -0
  3. glpi_python_client-0.4.2/CHANGELOG.md +341 -0
  4. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/CONTRIBUTING.md +8 -5
  5. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/PKG-INFO +24 -17
  6. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/README.md +19 -13
  7. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/docs/api_reference.rst +132 -3
  8. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/docs/conf.py +0 -1
  9. glpi_python_client-0.4.2/docs/development.md +147 -0
  10. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/docs/development_rtd.rst +6 -6
  11. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/docs/installation.rst +2 -2
  12. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/docs/publishing_rtd.rst +1 -1
  13. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/docs/user_guide.rst +268 -79
  14. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/__init__.py +56 -6
  15. glpi_python_client-0.4.2/glpi_python_client/_async/__init__.py +15 -0
  16. glpi_python_client-0.4.2/glpi_python_client/_async/_concurrency.py +59 -0
  17. glpi_python_client-0.4.2/glpi_python_client/_async/_testing.py +146 -0
  18. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_async}/auth/__init__.py +3 -3
  19. glpi_python_client-0.4.2/glpi_python_client/_async/auth/_v1_session.py +466 -0
  20. glpi_python_client-0.4.2/glpi_python_client/_async/auth/auth.py +417 -0
  21. glpi_python_client-0.4.2/glpi_python_client/_async/clients/__init__.py +17 -0
  22. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_async}/clients/_base_client.py +12 -13
  23. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/__init__.py +54 -0
  24. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/administration/__init__.py +13 -0
  25. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/administration/_entity.py +217 -0
  26. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/administration/_user.py +224 -0
  27. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/assistance/__init__.py +8 -0
  28. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/assistance/_team.py +121 -0
  29. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/assistance/_ticket.py +238 -0
  30. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/assistance/timeline/__init__.py +23 -0
  31. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/assistance/timeline/_document.py +225 -0
  32. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/assistance/timeline/_followup.py +204 -0
  33. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/assistance/timeline/_solution.py +201 -0
  34. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/assistance/timeline/_task.py +194 -0
  35. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/dropdowns/__init__.py +7 -0
  36. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/dropdowns/_location.py +170 -0
  37. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/knowledgebase/__init__.py +21 -0
  38. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/knowledgebase/_article.py +214 -0
  39. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/knowledgebase/_category.py +120 -0
  40. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/knowledgebase/_comment.py +109 -0
  41. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/knowledgebase/_revision.py +70 -0
  42. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/management/__init__.py +7 -0
  43. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/management/_document.py +280 -0
  44. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/plugins/__init__.py +10 -0
  45. glpi_python_client-0.4.2/glpi_python_client/_async/clients/api/plugins/_fields.py +426 -0
  46. glpi_python_client-0.4.2/glpi_python_client/_async/clients/client.py +135 -0
  47. glpi_python_client-0.4.2/glpi_python_client/_async/clients/commons/__init__.py +9 -0
  48. glpi_python_client-0.4.2/glpi_python_client/_async/clients/commons/_config.py +317 -0
  49. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_async}/clients/commons/_constants.py +10 -0
  50. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_async}/clients/commons/_filters.py +12 -1
  51. glpi_python_client-0.4.2/glpi_python_client/_async/clients/commons/_http.py +394 -0
  52. glpi_python_client-0.4.2/glpi_python_client/_async/clients/commons/_transport.py +573 -0
  53. glpi_python_client-0.4.2/glpi_python_client/_async/clients/custom/__init__.py +23 -0
  54. glpi_python_client-0.4.2/glpi_python_client/_async/clients/custom/_statistics.py +973 -0
  55. glpi_python_client-0.4.2/glpi_python_client/_async/clients/custom/_ticket_context.py +75 -0
  56. glpi_python_client-0.4.2/glpi_python_client/_errors.py +176 -0
  57. glpi_python_client-0.4.2/glpi_python_client/_sync/__init__.py +15 -0
  58. glpi_python_client-0.4.2/glpi_python_client/_sync/_concurrency.py +41 -0
  59. glpi_python_client-0.4.2/glpi_python_client/_sync/_testing.py +146 -0
  60. glpi_python_client-0.4.2/glpi_python_client/_sync/auth/__init__.py +13 -0
  61. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/auth/_v1_session.py +86 -35
  62. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/auth/auth.py +162 -55
  63. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/__init__.py +17 -0
  64. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/_base_client.py +177 -0
  65. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/api/__init__.py +54 -0
  66. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/api/administration/__init__.py +13 -0
  67. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/api/administration/_entity.py +14 -11
  68. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/api/administration/_user.py +12 -11
  69. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/api/assistance/__init__.py +8 -0
  70. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/api/assistance/_team.py +12 -10
  71. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/api/assistance/_ticket.py +14 -11
  72. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/api/assistance/timeline/__init__.py +23 -0
  73. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/api/assistance/timeline/_document.py +14 -11
  74. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/api/assistance/timeline/_followup.py +15 -12
  75. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/api/assistance/timeline/_solution.py +15 -12
  76. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/api/assistance/timeline/_task.py +15 -12
  77. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/api/dropdowns/__init__.py +7 -0
  78. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/api/dropdowns/_location.py +17 -11
  79. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/api/knowledgebase/__init__.py +21 -0
  80. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/api/knowledgebase/_article.py +214 -0
  81. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/api/knowledgebase/_category.py +120 -0
  82. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/api/knowledgebase/_comment.py +109 -0
  83. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/api/knowledgebase/_revision.py +70 -0
  84. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/api/management/__init__.py +7 -0
  85. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/api/management/_document.py +22 -19
  86. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/api/plugins/__init__.py +2 -2
  87. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/api/plugins/_fields.py +35 -20
  88. glpi_python_client-0.3.4/glpi_python_client/clients/sync_client.py → glpi_python_client-0.4.2/glpi_python_client/_sync/clients/client.py +33 -27
  89. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/commons/__init__.py +9 -0
  90. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/commons/_config.py +129 -46
  91. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/commons/_constants.py +60 -0
  92. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/commons/_filters.py +105 -0
  93. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/commons/_http.py +165 -25
  94. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/commons/_payloads.py +42 -0
  95. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/commons/_transport.py +103 -81
  96. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/custom/__init__.py +23 -0
  97. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_sync}/clients/custom/_statistics.py +340 -46
  98. glpi_python_client-0.4.2/glpi_python_client/_sync/clients/custom/_ticket_context.py +75 -0
  99. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/__init__.py +28 -0
  100. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/_content.py +1 -1
  101. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/enums.py +18 -1
  102. glpi_python_client-0.4.2/glpi_python_client/models/api_schema/knowledgebase/__init__.py +45 -0
  103. glpi_python_client-0.4.2/glpi_python_client/models/api_schema/knowledgebase/_article.py +123 -0
  104. glpi_python_client-0.4.2/glpi_python_client/models/api_schema/knowledgebase/_category.py +78 -0
  105. glpi_python_client-0.4.2/glpi_python_client/models/api_schema/knowledgebase/_comment.py +77 -0
  106. glpi_python_client-0.4.2/glpi_python_client/models/api_schema/knowledgebase/_revision.py +35 -0
  107. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/plugins/_fields.py +2 -2
  108. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/testing/__init__.py +2 -0
  109. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/testing/fixtures.py +9 -0
  110. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/testing/utils.py +17 -5
  111. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/pyproject.toml +49 -5
  112. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/skills/README.md +14 -3
  113. glpi_python_client-0.4.2/skills/glpi-client-setup/SKILL.md +213 -0
  114. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/skills/glpi-document-workflow/SKILL.md +7 -6
  115. glpi_python_client-0.4.2/skills/glpi-knowledge-base/SKILL.md +168 -0
  116. glpi_python_client-0.4.2/skills/glpi-plugin-fields/SKILL.md +199 -0
  117. glpi_python_client-0.4.2/skills/glpi-reporting-and-context/SKILL.md +125 -0
  118. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/skills/glpi-team-members/SKILL.md +6 -5
  119. glpi_python_client-0.4.2/skills/glpi-ticket-timeline/SKILL.md +124 -0
  120. glpi_python_client-0.4.2/skills/glpi-ticket-workflow/SKILL.md +77 -0
  121. glpi_python_client-0.4.2/skills/glpi-user-location-provisioning/SKILL.md +106 -0
  122. glpi_python_client-0.3.4/docs/development.md +0 -109
  123. glpi_python_client-0.3.4/docs/glpi_api_contract.json +0 -188668
  124. glpi_python_client-0.3.4/glpi_python_client/auth/tests/test_auth.py +0 -189
  125. glpi_python_client-0.3.4/glpi_python_client/auth/tests/test_v1_session.py +0 -446
  126. glpi_python_client-0.3.4/glpi_python_client/clients/__init__.py +0 -20
  127. glpi_python_client-0.3.4/glpi_python_client/clients/api/__init__.py +0 -42
  128. glpi_python_client-0.3.4/glpi_python_client/clients/api/administration/__init__.py +0 -13
  129. glpi_python_client-0.3.4/glpi_python_client/clients/api/assistance/__init__.py +0 -8
  130. glpi_python_client-0.3.4/glpi_python_client/clients/api/assistance/timeline/__init__.py +0 -23
  131. glpi_python_client-0.3.4/glpi_python_client/clients/api/dropdowns/__init__.py +0 -7
  132. glpi_python_client-0.3.4/glpi_python_client/clients/api/management/__init__.py +0 -7
  133. glpi_python_client-0.3.4/glpi_python_client/clients/api/plugins/tests/__init__.py +0 -0
  134. glpi_python_client-0.3.4/glpi_python_client/clients/api/plugins/tests/test_fields_mixin.py +0 -377
  135. glpi_python_client-0.3.4/glpi_python_client/clients/async_client.py +0 -167
  136. glpi_python_client-0.3.4/glpi_python_client/clients/commons/__init__.py +0 -9
  137. glpi_python_client-0.3.4/glpi_python_client/clients/commons/_async_bridge.py +0 -191
  138. glpi_python_client-0.3.4/glpi_python_client/clients/commons/_errors.py +0 -27
  139. glpi_python_client-0.3.4/glpi_python_client/clients/commons/tests/__init__.py +0 -5
  140. glpi_python_client-0.3.4/glpi_python_client/clients/commons/tests/test_errors.py +0 -41
  141. glpi_python_client-0.3.4/glpi_python_client/clients/commons/tests/test_filters.py +0 -49
  142. glpi_python_client-0.3.4/glpi_python_client/clients/commons/tests/test_http.py +0 -131
  143. glpi_python_client-0.3.4/glpi_python_client/clients/commons/tests/test_payloads.py +0 -64
  144. glpi_python_client-0.3.4/glpi_python_client/clients/commons/tests/test_transport.py +0 -136
  145. glpi_python_client-0.3.4/glpi_python_client/clients/custom/__init__.py +0 -34
  146. glpi_python_client-0.3.4/glpi_python_client/clients/custom/_pagination_async.py +0 -164
  147. glpi_python_client-0.3.4/glpi_python_client/clients/custom/_statistics_async.py +0 -500
  148. glpi_python_client-0.3.4/glpi_python_client/clients/custom/_ticket_context.py +0 -67
  149. glpi_python_client-0.3.4/glpi_python_client/clients/custom/_ticket_context_async.py +0 -73
  150. glpi_python_client-0.3.4/glpi_python_client/clients/custom/tests/test_statistics.py +0 -562
  151. glpi_python_client-0.3.4/glpi_python_client/clients/custom/tests/test_statistics_async.py +0 -232
  152. glpi_python_client-0.3.4/glpi_python_client/clients/custom/tests/test_ticket_context.py +0 -57
  153. glpi_python_client-0.3.4/glpi_python_client/clients/tests/__init__.py +0 -1
  154. glpi_python_client-0.3.4/glpi_python_client/clients/tests/test_api_coverage.py +0 -882
  155. glpi_python_client-0.3.4/glpi_python_client/clients/tests/test_async_branches.py +0 -701
  156. glpi_python_client-0.3.4/glpi_python_client/clients/tests/test_async_smoke.py +0 -150
  157. glpi_python_client-0.3.4/glpi_python_client/clients/tests/test_glpi_client.py +0 -266
  158. glpi_python_client-0.3.4/glpi_python_client/clients/tests/test_parity.py +0 -70
  159. glpi_python_client-0.3.4/glpi_python_client/clients/tests/test_smoke.py +0 -259
  160. glpi_python_client-0.3.4/glpi_python_client/content/tests/__init__.py +0 -5
  161. glpi_python_client-0.3.4/glpi_python_client/content/tests/test_conversion.py +0 -13
  162. glpi_python_client-0.3.4/glpi_python_client/models/api_schema/administration/tests/__init__.py +0 -1
  163. glpi_python_client-0.3.4/glpi_python_client/models/api_schema/administration/tests/test_administration_schemas.py +0 -145
  164. glpi_python_client-0.3.4/glpi_python_client/models/api_schema/assistance/tests/__init__.py +0 -1
  165. glpi_python_client-0.3.4/glpi_python_client/models/api_schema/assistance/tests/test_assistance_schemas.py +0 -108
  166. glpi_python_client-0.3.4/glpi_python_client/models/api_schema/assistance/tests/test_content_roundtrip.py +0 -102
  167. glpi_python_client-0.3.4/glpi_python_client/models/api_schema/assistance/timeline/tests/__init__.py +0 -1
  168. glpi_python_client-0.3.4/glpi_python_client/models/api_schema/assistance/timeline/tests/test_timeline_schemas.py +0 -147
  169. glpi_python_client-0.3.4/glpi_python_client/models/api_schema/dropdowns/tests/__init__.py +0 -1
  170. glpi_python_client-0.3.4/glpi_python_client/models/api_schema/dropdowns/tests/test_dropdowns_schemas.py +0 -61
  171. glpi_python_client-0.3.4/glpi_python_client/models/api_schema/management/tests/__init__.py +0 -1
  172. glpi_python_client-0.3.4/glpi_python_client/models/api_schema/management/tests/test_management_schemas.py +0 -50
  173. glpi_python_client-0.3.4/glpi_python_client/models/api_schema/plugins/tests/__init__.py +0 -0
  174. glpi_python_client-0.3.4/glpi_python_client/models/api_schema/plugins/tests/test_fields_schemas.py +0 -82
  175. glpi_python_client-0.3.4/glpi_python_client/models/custom_schema/tests/__init__.py +0 -1
  176. glpi_python_client-0.3.4/glpi_python_client/models/custom_schema/tests/test_ticket_context.py +0 -398
  177. glpi_python_client-0.3.4/skills/glpi-client-setup/SKILL.md +0 -157
  178. glpi_python_client-0.3.4/skills/glpi-reporting-and-context/SKILL.md +0 -110
  179. glpi_python_client-0.3.4/skills/glpi-ticket-timeline/SKILL.md +0 -72
  180. glpi_python_client-0.3.4/skills/glpi-ticket-workflow/SKILL.md +0 -76
  181. glpi_python_client-0.3.4/skills/glpi-user-location-provisioning/SKILL.md +0 -77
  182. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/.readthedocs.yaml +0 -0
  183. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/LICENSE +0 -0
  184. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/docs/_static/.gitkeep +0 -0
  185. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/docs/index.rst +0 -0
  186. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/docs/publishing.md +0 -0
  187. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/docs/sponsoring.rst +0 -0
  188. {glpi_python_client-0.3.4/glpi_python_client → glpi_python_client-0.4.2/glpi_python_client/_async}/clients/commons/_payloads.py +0 -0
  189. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/content/__init__.py +0 -0
  190. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/content/conversion.py +0 -0
  191. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/_base.py +0 -0
  192. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/__init__.py +0 -0
  193. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/_common.py +0 -0
  194. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/administration/__init__.py +0 -0
  195. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/administration/_entity.py +0 -0
  196. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/administration/_user.py +0 -0
  197. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/assistance/__init__.py +0 -0
  198. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/assistance/_team.py +0 -0
  199. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/assistance/_ticket.py +0 -0
  200. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/assistance/timeline/__init__.py +0 -0
  201. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/assistance/timeline/_document.py +0 -0
  202. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/assistance/timeline/_followup.py +0 -0
  203. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/assistance/timeline/_solution.py +0 -0
  204. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/assistance/timeline/_task.py +0 -0
  205. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/dropdowns/__init__.py +0 -0
  206. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/dropdowns/_location.py +0 -0
  207. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/management/__init__.py +0 -0
  208. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/management/_document.py +0 -0
  209. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/api_schema/plugins/__init__.py +0 -0
  210. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/custom_schema/__init__.py +0 -0
  211. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/models/custom_schema/_ticket_context.py +0 -0
  212. {glpi_python_client-0.3.4 → glpi_python_client-0.4.2}/glpi_python_client/py.typed +0 -0
@@ -28,4 +28,12 @@ secrets/*
28
28
 
29
29
  .claude/
30
30
 
31
- CLAUDE.md
31
+ CLAUDE.md
32
+
33
+ docs/superpowers/*
34
+
35
+ # Large generated GLPI API contract (kept locally, not tracked)
36
+ docs/glpi_api_contract.json
37
+ # Coverage data: rewritten by every test run (and by the venv .pth hook).
38
+ .coverage
39
+ .coverage.*
@@ -17,6 +17,16 @@ repos:
17
17
  types: [python]
18
18
  pass_filenames: false
19
19
 
20
+ # Fails on a _sync/ tree left stale by an edit to _async/. Runs at
21
+ # commit time rather than pre-push so the generated tree is never
22
+ # committed out of step with its source.
23
+ - id: unasync-check
24
+ name: generated sync tree is up to date
25
+ entry: python unasync_build.py --check
26
+ language: system
27
+ types: [python]
28
+ pass_filenames: false
29
+
20
30
  - id: pytest-coverage
21
31
  name: pytest with coverage (>=95%)
22
32
  entry: python -m pytest -m "not integration" --cov=glpi_python_client --cov-fail-under=95 -q
@@ -0,0 +1,341 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
+
7
+ ## Unreleased
8
+
9
+ ### Fixed
10
+
11
+ - **The unit test suite was published inside the wheel and the sdist.** Both
12
+ artefacts carried 56 test modules, so every install shipped the project's
13
+ own tests into the consuming environment. `[tool.hatch.build] exclude` now
14
+ drops `tests/` and `conftest.py`, taking the wheel from 180 entries to 126
15
+ and the sdist from 204 to 150. Nothing a downstream consumer imports was
16
+ removed: `glpi_python_client.testing` — the documented factories, fixtures
17
+ and fake responses — still ships in full, and the package now imports
18
+ cleanly in an environment with no `pytest` installed.
19
+
20
+ - **A `Major` priority ticket made the whole search fail.** GLPI's priority
21
+ scale has six levels; the published contract advertises five, and
22
+ `GlpiPriority` followed the contract. Since `GetTicket.priority` is typed
23
+ with that enum and validation runs per record, a single escalated ticket
24
+ anywhere in a result set raised `ValidationError` and took the entire
25
+ query down with it — most likely to bite exactly the reporting queries
26
+ that filter on high priority. `GlpiPriority.MAJOR = 6` is now defined.
27
+ The five existing members keep their identifiers, so stored filters are
28
+ unaffected. `urgency` and `impact` genuinely do stop at 5 and now accept a
29
+ value GLPI will never send, which is harmless in the direction that
30
+ matters.
31
+
32
+ - **The statistics layer sent GLPI v1 field names to the v2 API, which
33
+ silently ignored them and returned unfiltered results.** v2 drops a
34
+ `filter=` conjunct whose field it does not recognise, honours the rest,
35
+ and answers 200/206 with no error — so the aggregations narrowed by date
36
+ and looked plausible while ignoring the user and entity selection
37
+ entirely. Measured against a live GLPI 11 instance,
38
+ `get_user_activity` reported `tickets_as_technician == tickets_as_recipient
39
+ == 963` — the window's *total* ticket count — for every user regardless of
40
+ who they were. Corrected:
41
+ - `entities_id==N` → `entity.id==N` (v2 types `entity` as an object).
42
+ - `users_id_lastupdater==N` → `user_editor.id==N`.
43
+ - `users_id_requester==N` (as `user_recipient_id`) → `user_recipient.id==N`,
44
+ which is what that parameter's *name* has always meant. Note the v2
45
+ `user_recipient` field is `users_id_recipient` — who *recorded* the
46
+ ticket — not the requester link; the two are different people.
47
+ - `users_id_assign` / `users_id_requester` (as `user_id`) have **no v2
48
+ equivalent at all**: the v2 `team` array cannot be joined by the RSQL
49
+ engine (the four contract-declared subfields answer HTTP 500 and every
50
+ other spelling is silently ignored — 19 spellings were tested). These
51
+ now resolve through the legacy v1 search engine, whose searchOption 5
52
+ (`Technicien`) and 4 (`Demandeur`) map exactly onto the
53
+ `glpi_tickets_users` link types, and which fails **loudly** (HTTP 400)
54
+ on an unknown field instead of silently returning everything.
55
+ - **`rsql_any_filter` produced an unparenthesised OR group**, and RSQL binds
56
+ `;` (AND) tighter than `,` (OR). `get_ticket_statistics(entity_name=...)`
57
+ matching several entities emitted `date;e==1,e==2`, which the server reads
58
+ as `(date AND e==1) OR e==2` — the date window stopped applying to every
59
+ entity after the first. Measured live: 16,245 tickets returned where the
60
+ correct answer was 1,552. OR groups are now wrapped in parentheses.
61
+ - **v2 ticket searches counted soft-deleted tickets.** The v2 search includes
62
+ trashed tickets by default while v1 excludes them (59,690 live + 258
63
+ trashed = 59,948), so every aggregation was inflated by the trash bin — for
64
+ one user 92% of matches were deleted tickets. All v2 ticket queries in the
65
+ statistics layer now pin `is_deleted==false`.
66
+ - Actor identifiers are validated before reaching the v1 search, which fails
67
+ *open* rather than rejecting bad input: `equals 0` matched 20,905 tickets
68
+ (a LEFT-JOIN-NULL "has no actor" match), an empty value matched the entire
69
+ baseline, and a non-numeric value returned the same arbitrary 3 rows
70
+ whatever the string. A non-positive or non-`int` id now raises
71
+ `GlpiValidationError`.
72
+ - **The per-ticket task fan-out is replaced by one bulk sweep.** The v2 API
73
+ publishes tasks only under `/Assistance/Ticket/{id}/Timeline/Task`, so
74
+ aggregating N tickets cost N requests. The v1 `TicketTask` *collection*
75
+ returns whole rows including `tickets_id`, paged 1000 at a time, so the
76
+ same aggregate now costs one page per 1000 tasks created since the window
77
+ opened. Measured live on a 120-ticket set: **120 requests / 11.7 s -> 2
78
+ requests / 0.4 s**, with `ticket_count`, `task_count`, `total_duration`,
79
+ `duration_by_ticket` and `duration_by_user` all byte-identical between the
80
+ two paths. Below 25 tickets the per-ticket path is cheaper and is kept, so
81
+ clients without a v1 session are unaffected.
82
+
83
+ Note v1 `search/TicketTask` is *not* usable for this: its searchOptions
84
+ expose the task id, content, category, date, privacy, technician, duration
85
+ and state, but no parent ticket id, so results cannot be attributed back
86
+ to a ticket. The plain collection endpoint is what carries `tickets_id`.
87
+
88
+ - `get_user_activity` walks the date window **once** for all users instead of
89
+ twice per user. Combined with the corrected filters this took one user over
90
+ 90 days from **979 requests / 120 s to 9 requests / 5.1 s**, verified live.
91
+
92
+ Actor-based statistics now require the legacy v1 session (`v1_base_url` +
93
+ `v1_user_token`) and raise `RuntimeError` naming the missing options when
94
+ it is absent, rather than returning a wrong number.
95
+
96
+ - `GLPITokenManager._refresh_access_token`'s retry decorator no longer
97
+ retries a `GlpiServerError` from its fall-through to the nested
98
+ `_acquire_token()` call. That nested call already carries its own
99
+ independent 3-attempt retry decorator for `GlpiServerError`, so the
100
+ outer decorator retrying it too meant a persistent 5xx during token
101
+ refresh cost 3 (outer attempts) × (1 refresh POST + 3 nested acquire
102
+ POSTs) = 12 POST requests and ~33s of `wait_fixed(3)` sleep, instead of
103
+ the 3 attempts the retry configuration alone would suggest. The outer
104
+ decorator now only retries `requests.RequestException` (a genuine
105
+ network fault on the refresh POST itself), which is not covered by the
106
+ nested call at all. A persistent 5xx now costs exactly 1 refresh POST +
107
+ 3 nested acquire POSTs = 4 POST requests. A persistent 401 (2 POSTs) and
108
+ a network error on the refresh POST (3 POSTs) are unaffected.
109
+ - `AsyncGlpiClient.create_kb_article` / `update_kb_article` no longer
110
+ silently drop `categories`. Both methods called the public
111
+ `set_kb_article_categories` through `self` from inside a synchronous
112
+ method body; `AsyncBridge.__init_subclass__` wraps every public sync
113
+ method into a coroutine, so that call returned an un-awaited coroutine
114
+ instead of performing the write. The article was created (or updated)
115
+ successfully, a valid id was returned, and no exception was raised —
116
+ the category assignment simply never happened. Fixed with hand-written
117
+ async overrides in `_article_async.py` that strip `categories` from the
118
+ v2 body, run the v2 write in a worker thread, and apply the category
119
+ fallback through an awaited call.
120
+ - `AsyncGlpiClient.get_ticket_custom_fields` / `set_ticket_custom_fields`
121
+ raised `TypeError: 'coroutine' object is not iterable` and were
122
+ unusable. Same root cause as above: a sync method reaching a sibling
123
+ public method through `self` received a coroutine instead of a result.
124
+ Fixed with hand-written async overrides in `_fields_async.py`.
125
+ - The integration suite is runnable end-to-end again. Two defects, both in
126
+ `integration_tests/` only (no library code involved):
127
+ - `test_iter_search_tickets_multi_page` walked *every* matching ticket in
128
+ batches of 3 with no upper bound — it was the only one of the suite's
129
+ seven `iter_search` loops missing a `break`. Against a real instance
130
+ (59,879 matching tickets) that is ~19,960 requests and several hours,
131
+ which stalled the whole suite. It now stops after 3 pages and asserts
132
+ that ids do not repeat across pages, which actually verifies that the
133
+ `start` offset advances — the old unbounded loop asserted only
134
+ `isinstance(collected, list)` and so could not have detected a stuck
135
+ offset.
136
+ - The three GLPI Fields plugin tests failed rather than skipped when the
137
+ plugin is not installed. `_skip_when_no_v1` only checked that v1
138
+ *credentials were configured*, never that the *plugin existed*; an
139
+ absent plugin makes GLPI reject the `PluginFieldsContainer` itemtype
140
+ with a 400 rather than return an empty list. A new `fields_containers`
141
+ fixture skips on exactly that signature (400 +
142
+ `ERROR_RESOURCE_NOT_FOUND_NOR_COMMONDBTM`) and re-raises anything else.
143
+ - `parse_optional_env_int` (environment/config parsing) and
144
+ `StatisticsMixin._resolve_window` (the date-window helper behind
145
+ `get_ticket_statistics` / `get_task_durations` / `get_user_activity`)
146
+ no longer let a malformed value escape as a bare stdlib `ValueError`
147
+ from `int()` / `date.fromisoformat()` (e.g. `GLPI_TIMEOUT=abc` or
148
+ `get_ticket_statistics(start_date="2026-13-45")`). Both now raise
149
+ `GlpiValidationError`, chaining the original error via `from` rather
150
+ than swallowing it. Non-breaking: `GlpiValidationError` inherits
151
+ `ValueError`, so `except ValueError` still catches it.
152
+
153
+ ### Added
154
+
155
+ - `glpi_python_client/clients/tests/test_async_selfcall_guard.py`: a
156
+ structural AST guard that fails the suite if any public method on
157
+ `GlpiClient` transitively reaches another public method through a
158
+ literal `self.name(...)` call (directly, or via a private helper)
159
+ without a corresponding hand-written async override on
160
+ `AsyncGlpiClient`. This prevents the same bug class — silent data loss
161
+ or a `TypeError` at call time, depending on how the dropped coroutine is
162
+ used — from being reintroduced by a future endpoint.
163
+ - A public exception hierarchy, exported from the package root:
164
+ `GlpiError`, `GlpiTransportError`, `GlpiTimeoutError`, `GlpiStatusError`,
165
+ `GlpiAuthError`, `GlpiNotFoundError`, `GlpiServerError`,
166
+ `GlpiValidationError` and `GlpiProtocolError`. `GlpiStatusError` and its
167
+ subclasses carry `.status_code`, `.url` and `.response_text`. A GLPI 404
168
+ and a bad argument were previously both a bare `ValueError` and could not
169
+ be told apart.
170
+ - `FakeResponse` (in the public `glpi_python_client.testing` module) gained
171
+ a `url` attribute.
172
+ - A user-guide "Error handling" section documenting the exception
173
+ hierarchy and the retry behaviour for both the transport layer and OAuth
174
+ token acquisition/refresh.
175
+
176
+ ### Changed
177
+
178
+ - **`AsyncGlpiClient` now performs real non-blocking I/O.** It was a facade
179
+ that wrapped each synchronous method in `asyncio.to_thread`, so "async"
180
+ meant "blocking call on a worker thread". It is now genuinely
181
+ asynchronous, built on `httpx.AsyncClient`, with no thread pool and no
182
+ executor. The `executor` constructor keyword is gone, as is
183
+ `AsyncBridge`.
184
+ - The two clients are now one codebase. `glpi_python_client/_async/` is
185
+ hand-written and `glpi_python_client/_sync/` is generated from it by
186
+ `unasync_build.py`, committed, and diffed in CI. Endpoint logic exists
187
+ exactly once, so the two surfaces cannot drift.
188
+ - This deletes the six hand-written async override modules the bridge
189
+ forced into existence — including the 500-line `_statistics_async.py`,
190
+ which duplicated the most intricate logic in the package with no test
191
+ asserting the two copies agreed.
192
+ - Aggregating helpers keep their concurrency through a shared `gather`
193
+ helper that is `asyncio.gather` on the async surface and sequential
194
+ evaluation on the generated one, written once at the call site.
195
+ - **Public imports are unchanged**: `from glpi_python_client import
196
+ GlpiClient, AsyncGlpiClient` still works. Code importing private
197
+ module paths (`glpi_python_client.clients.*`, `glpi_python_client.auth.*`)
198
+ must add the tree segment, e.g.
199
+ `glpi_python_client._sync.clients.commons._transport`.
200
+ - **Breaking: the HTTP transport moved from `requests` to `httpx`.**
201
+ `requests` and `urllib3` are no longer dependencies. The v2 transport, the
202
+ legacy v1 session, and the OAuth token manager were swapped together in a
203
+ single change because they share `_http.py`; splitting them would have left
204
+ the shared code validated only by tests exercising the old transport.
205
+ Behaviour is preserved, which took three deliberate corrections where the
206
+ two libraries disagree and the difference is silent:
207
+ - **Query parameters with a `None` value are dropped**, as `requests` did.
208
+ `httpx` encodes them as a valueless `key=`, and GLPI treats an empty
209
+ filter or search value as *match everything* — so the swap would have
210
+ silently widened queries rather than leaving them unconstrained.
211
+ - **`bytes` and `bool` parameter values keep their previous rendering**
212
+ (`b"x"` → `x`, `True` → `True`). `httpx` would emit the Python repr
213
+ `b'x'` and a lowercase `true`.
214
+ - **Redirects are still followed.** `requests` follows them by default and
215
+ `httpx` does not, so a followed redirect would have started surfacing as
216
+ a bare 3xx response.
217
+ - **Breaking: network-level faults now raise `GlpiTransportError`** (or its
218
+ `GlpiTimeoutError` subclass) instead of propagating the HTTP library's own
219
+ exception. This completes the promise the previous release documented:
220
+ catching `GlpiError` is now sufficient for the library's whole failure
221
+ surface, and you never need to import the HTTP library. The originating
222
+ exception is attached as `__cause__`. Code doing
223
+ `except requests.RequestException` should now catch `GlpiTransportError`.
224
+ Note it does *not* inherit `ValueError` — nothing was passed in wrongly and
225
+ no value came back.
226
+ - The retry predicates were retargeted onto this library-owned type in the
227
+ same change. This is the failure mode that made the swap risky: the
228
+ exception trees of the two libraries are completely disjoint, so a
229
+ predicate left naming the old one stops matching and **every retry
230
+ silently disappears** — no error, no warning, and a green test suite.
231
+ Naming a type the library itself raises makes that impossible to
232
+ reintroduce. A mutation test confirms the suite catches it: reverting the
233
+ predicate fails 7 tests across all three transports.
234
+ - **Breaking:** a persistent 5xx now raises `GlpiServerError` instead of
235
+ `tenacity.RetryError`. The retry decorators gained `reraise=True`. Code
236
+ doing `except tenacity.RetryError` and digging out
237
+ `.last_attempt.exception()` should now catch `GlpiServerError` directly.
238
+ - **Breaking:** unexpected HTTP statuses raise a `GlpiStatusError` subclass;
239
+ rejected arguments and configuration raise `GlpiValidationError`; 2xx
240
+ responses with an unusable body raise `GlpiProtocolError`. All three
241
+ inherit `ValueError`, so existing `except ValueError` handlers keep
242
+ working.
243
+ - **Breaking:** a non-2xx OAuth token response raises `GlpiAuthError` (401/403)
244
+ or `GlpiServerError` (5xx). The token retry decorators had no `retry=`
245
+ predicate and therefore retried every failure, including a rejected
246
+ credential; a wrong `client_secret` cost 3 attempts and 6 seconds. OAuth
247
+ 4xx is now final, matching the rest of the library. OAuth 5xx is still
248
+ retried.
249
+ - **Breaking:** the private `glpi_python_client.clients.commons._errors`
250
+ module and its `remote_error_message` helper are removed. It had no
251
+ library call sites, and `reraise=True` leaves it nothing to unwrap.
252
+
253
+ ### Performance
254
+
255
+ - **`sniffio` is now a dependency, and the async client is ~2.6x faster at
256
+ wide fan-out because of it.** `httpcore` decides whether it is running
257
+ under asyncio or trio by probing for `sniffio` on every async request,
258
+ falling back to `"asyncio"` when the import fails. Nothing in the
259
+ dependency chain required it — `httpx` pulls in `anyio`, and `anyio` 4.14
260
+ dropped `sniffio` — so a fresh install had no `sniffio`, and because
261
+ Python never caches a failed import, every single request re-walked
262
+ `sys.path` doing filesystem stats. Measured against a local server with
263
+ 50 ms latency, a fan-out of 128 took **3354 ms without `sniffio` and
264
+ 1304 ms with it**. `pip check` reports no broken requirements either way,
265
+ which is why this went unnoticed: nothing declares the package, nothing
266
+ imports it, and the only symptom is that every request is slower.
267
+
268
+ - **Bounding a wide fan-out is now a documented requirement, not a
269
+ suggestion.** `httpcore` rescans its entire connection pool on every
270
+ request assignment, calling `has_expired()` per connection — profiled at
271
+ 9040 such calls for a 64-request fan-out. The cost is quadratic in the
272
+ width of the fan-out and it saturates the event loop, so server-observed
273
+ concurrency *falls* as the fan-out widens. Raising `httpx.Limits` does not
274
+ help. At a fan-out of 16 against a 50 ms server, an unbounded
275
+ `gather` took 350 ms while the same work capped at 16 with an
276
+ `asyncio.Semaphore` took 108 ms. See "Bounding concurrency" in the user
277
+ guide. This is a property of `httpx` 0.28 / `httpcore` 1.0.9, which are
278
+ the current releases; there is no version to upgrade to.
279
+
280
+ ### Documentation
281
+
282
+ - **The documentation still described the deleted bridge**, in the places
283
+ users actually read: the README, the user guide, the API reference, the
284
+ package docstring, and two skills all said `AsyncGlpiClient` wraps each
285
+ synchronous method into a coroutine dispatched to a worker thread via
286
+ `asyncio.to_thread`. None of that has been true since the codegen
287
+ rewrite. The worst of it was live API: the user guide's *Custom thread
288
+ pools* section and step 8 of `glpi-client-setup` documented an
289
+ `executor=` constructor argument that no longer exists, so anyone
290
+ copying either example got a `TypeError`. That section is now
291
+ *Bounding concurrency* and shows an `asyncio.Semaphore`, which is what
292
+ actually bounds a fan-out now.
293
+ - **Fourteen docstring cross-references pointed at modules the rewrite had
294
+ renamed or deleted** (`clients.async_client`, `clients.sync_client`,
295
+ `custom._ticket_context_async`). Nothing caught them: Sphinx runs with
296
+ `nitpicky` off, so an unresolvable target renders as plain text rather
297
+ than failing the build, and these private modules are not autodoc'd in
298
+ the first place. `tests/test_docstring_references.py` now resolves every
299
+ qualified reference in the package against the live modules.
300
+ - **The generated sync tree documented itself in terms of the async one.**
301
+ unasync repoints imports, where `_async` is its own NAME token, but a
302
+ dotted path inside a docstring is a single string token and passes
303
+ through untouched — so 47 cross-references in the shipped sync client
304
+ pointed into `_async/`. The diff gate is blind to this for the same
305
+ reason it is blind to a token collision: the omission is deterministic,
306
+ so regeneration reproduces it and the diff stays clean. `unasync_build`
307
+ now repoints the qualified prefix, and a test asserts the generated tree
308
+ never names `_async` at all.
309
+ - `TicketContextMixin` claimed its five calls ran sequentially and that an
310
+ async override fanned them out, contradicting both the code and its own
311
+ method docstring. The development guide still described the deleted
312
+ `_ticket_context_async.py` / `_statistics_async.py` and the retired
313
+ `test_parity.py` / `test_async_selfcall_guard.py` suites.
314
+ - The `requests` intersphinx mapping is removed; it survived the transport
315
+ swap and made every docs build fetch an inventory nothing referenced.
316
+
317
+ ### Unchanged (deliberately)
318
+
319
+ - Retry semantics: 5xx retried 3 times with a 3-second fixed wait, 4xx never
320
+ retried.
321
+ - Tolerant search endpoints still return `[]` rather than raising on a 4xx.
322
+ - The `TypeError` sites in environment parsing and the `RuntimeError` sites
323
+ for closed clients, missing v1 sessions and partial KB failures still
324
+ raise those types. `GlpiValidationError` inherits `ValueError`, not
325
+ `TypeError`, so converting them would break `except TypeError` callers.
326
+ - The transport is still `requests`. Network faults (connection reset, DNS,
327
+ timeout) still surface as `requests` exceptions; they become
328
+ `GlpiTransportError` / `GlpiTimeoutError` when the transport moves to
329
+ httpx, with no change to the class names above.
330
+
331
+ ### Notes
332
+
333
+ - Both fixed bugs shared one root cause: `AsyncBridge` wraps every public
334
+ sync method into a coroutine, so a sync method body calling a sibling
335
+ public method through `self` (rather than through a hand-written async
336
+ override) silently receives a coroutine instead of the real return
337
+ value.
338
+ - This is a documentation-only release note; **no version was released**
339
+ from this branch. The next release is planned as 0.4.0, an httpx +
340
+ unasync rewrite that removes `AsyncBridge` entirely, making this class
341
+ of bug structurally impossible rather than merely guarded against.
@@ -38,7 +38,7 @@ python -m sphinx -W --keep-going -b html docs docs/_build/html
38
38
 
39
39
  ## GitHub Actions
40
40
 
41
- - `.github/workflows/ci.yml` runs tests for Python 3.10 through 3.13 on pull
41
+ - `.github/workflows/ci.yml` runs tests for Python 3.10 through 3.14 on pull
42
42
  requests and pushes to `main`.
43
43
  - The same workflow runs `ruff`, `mypy`, and a warning-free Sphinx build on
44
44
  Python 3.12.
@@ -51,10 +51,13 @@ python -m sphinx -W --keep-going -b html docs docs/_build/html
51
51
  ## Design Guidelines
52
52
 
53
53
  - Keep API calls behind `GlpiClient` / `AsyncGlpiClient` methods. Add
54
- new endpoints to a sync endpoint mixin only; `AsyncGlpiClient`
55
- exposes them as coroutines automatically through `AsyncBridge`. Only
56
- add a dedicated async override when the method needs concurrent
57
- fan-out via `asyncio.gather`.
54
+ new endpoints to the async endpoint mixin under
55
+ `glpi_python_client/_async/`, then run `python unasync_build.py` to
56
+ regenerate `glpi_python_client/_sync/` and commit both. Never edit
57
+ `_sync/` by hand; CI regenerates it and fails on any difference. For
58
+ concurrent fan-out use `gather` from `_async/_concurrency.py` rather
59
+ than `asyncio.gather` directly, so the generated sync code stays
60
+ correct.
58
61
  - Prefer field-validated Pydantic models for request and response payloads.
59
62
  - Avoid organization-specific category, entity, or profile defaults in the
60
63
  library core.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glpi-python-client
3
- Version: 0.3.4
3
+ Version: 0.4.2
4
4
  Summary: A typed Python client for GLPI ITSM APIs.
5
5
  Project-URL: Homepage, https://github.com/baraline/glpi_python_client
6
6
  Project-URL: Documentation, https://glpi-python-client.readthedocs.io/en/latest/
@@ -19,19 +19,20 @@ Classifier: Programming Language :: Python :: 3.10
19
19
  Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
21
  Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
22
23
  Classifier: Topic :: Internet :: WWW/HTTP
23
24
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
25
  Classifier: Typing :: Typed
25
26
  Requires-Python: >=3.10
26
27
  Requires-Dist: beautifulsoup4>=4.12
28
+ Requires-Dist: httpx>=0.28
27
29
  Requires-Dist: lxml>=4.9
28
30
  Requires-Dist: markdown>=3.6
29
31
  Requires-Dist: markdownify>=0.13
30
32
  Requires-Dist: pydantic>=2.8
31
- Requires-Dist: requests>=2.31
33
+ Requires-Dist: sniffio>=1.3
32
34
  Requires-Dist: tenacity>=8.2
33
35
  Requires-Dist: typing-extensions>=4.7; python_version < '3.11'
34
- Requires-Dist: urllib3>=2.0
35
36
  Provides-Extra: dev
36
37
  Requires-Dist: build>=1.2; extra == 'dev'
37
38
  Requires-Dist: mypy>=1.11; extra == 'dev'
@@ -45,7 +46,7 @@ Requires-Dist: sphinx-rtd-theme>=2.0; extra == 'dev'
45
46
  Requires-Dist: sphinx<8.2,>=7.2; extra == 'dev'
46
47
  Requires-Dist: tomli>=2.0; (python_version < '3.11') and extra == 'dev'
47
48
  Requires-Dist: twine>=5.1; extra == 'dev'
48
- Requires-Dist: types-requests>=2.32; extra == 'dev'
49
+ Requires-Dist: unasync>=0.6; extra == 'dev'
49
50
  Requires-Dist: vulture>=2.11; extra == 'dev'
50
51
  Provides-Extra: docs
51
52
  Requires-Dist: numpydoc>=1.8; extra == 'docs'
@@ -73,10 +74,12 @@ back to HTML for outgoing payloads.
73
74
  It currently focuses on ticket-centric workflows and exposes two high-level
74
75
  clients built on top of the GLPI v2 REST API:
75
76
 
76
- - `GlpiClient` — synchronous, blocking client (single source of truth for
77
- endpoint behaviour).
78
- - `AsyncGlpiClient` — asynchronous facade that wraps every synchronous
79
- method into a coroutine and dispatches it to a worker thread.
77
+ - `GlpiClient` — synchronous, blocking client.
78
+ - `AsyncGlpiClient` — asynchronous client doing real non-blocking I/O on the
79
+ event loop.
80
+
81
+ Neither wraps the other. The async tree is hand-written and the sync one is
82
+ generated from it, so the two surfaces cannot drift apart.
80
83
 
81
84
  Note that all integration tests using this package are made on GLPI 11.
82
85
  I cannot make any guarantee of the behaviour on previous versions.
@@ -162,15 +165,19 @@ variables.
162
165
  ### Sync or async?
163
166
 
164
167
  Both clients expose the exact same endpoint surface and accept the same
165
- constructor arguments. The async client is a thin facade that wraps each
166
- synchronous method into a coroutine dispatched to a worker thread via
167
- `asyncio.to_thread` (or a caller-supplied `concurrent.futures.Executor`).
168
- A shared `threading.Lock` serialises OAuth token acquisition so concurrent
169
- `asyncio.gather(...)` fan-outs cannot race. Pick `GlpiClient` for plain
170
- scripts, CLI tools, and synchronous services; pick `AsyncGlpiClient` when
171
- your application already runs an event loop or when you need concurrent
172
- fan-out (the aggregated `get_ticket_context` and per-ticket
173
- `get_task_statistics` helpers use `asyncio.gather` on the async client).
168
+ constructor arguments, because both are generated from one source: the
169
+ async tree is hand-written and the sync one is produced from it by a build
170
+ step that strips `async`/`await`. `AsyncGlpiClient` performs real
171
+ non-blocking I/O on the event loop — there is no worker thread and no
172
+ executor — and `GlpiClient` performs real blocking I/O with no coroutine
173
+ scheduling. An auth lock serialises OAuth token acquisition on both
174
+ surfaces, so concurrent fan-outs cannot race.
175
+
176
+ Pick `GlpiClient` for plain scripts, CLI tools, and synchronous services;
177
+ pick `AsyncGlpiClient` when your application already runs an event loop or
178
+ when you need concurrent fan-out (the aggregated `get_ticket_context` and
179
+ per-ticket `get_task_statistics` helpers overlap their calls there, and run
180
+ them one after another on the sync client).
174
181
 
175
182
  ## Documentation
176
183
 
@@ -17,10 +17,12 @@ back to HTML for outgoing payloads.
17
17
  It currently focuses on ticket-centric workflows and exposes two high-level
18
18
  clients built on top of the GLPI v2 REST API:
19
19
 
20
- - `GlpiClient` — synchronous, blocking client (single source of truth for
21
- endpoint behaviour).
22
- - `AsyncGlpiClient` — asynchronous facade that wraps every synchronous
23
- method into a coroutine and dispatches it to a worker thread.
20
+ - `GlpiClient` — synchronous, blocking client.
21
+ - `AsyncGlpiClient` — asynchronous client doing real non-blocking I/O on the
22
+ event loop.
23
+
24
+ Neither wraps the other. The async tree is hand-written and the sync one is
25
+ generated from it, so the two surfaces cannot drift apart.
24
26
 
25
27
  Note that all integration tests using this package are made on GLPI 11.
26
28
  I cannot make any guarantee of the behaviour on previous versions.
@@ -106,15 +108,19 @@ variables.
106
108
  ### Sync or async?
107
109
 
108
110
  Both clients expose the exact same endpoint surface and accept the same
109
- constructor arguments. The async client is a thin facade that wraps each
110
- synchronous method into a coroutine dispatched to a worker thread via
111
- `asyncio.to_thread` (or a caller-supplied `concurrent.futures.Executor`).
112
- A shared `threading.Lock` serialises OAuth token acquisition so concurrent
113
- `asyncio.gather(...)` fan-outs cannot race. Pick `GlpiClient` for plain
114
- scripts, CLI tools, and synchronous services; pick `AsyncGlpiClient` when
115
- your application already runs an event loop or when you need concurrent
116
- fan-out (the aggregated `get_ticket_context` and per-ticket
117
- `get_task_statistics` helpers use `asyncio.gather` on the async client).
111
+ constructor arguments, because both are generated from one source: the
112
+ async tree is hand-written and the sync one is produced from it by a build
113
+ step that strips `async`/`await`. `AsyncGlpiClient` performs real
114
+ non-blocking I/O on the event loop — there is no worker thread and no
115
+ executor — and `GlpiClient` performs real blocking I/O with no coroutine
116
+ scheduling. An auth lock serialises OAuth token acquisition on both
117
+ surfaces, so concurrent fan-outs cannot race.
118
+
119
+ Pick `GlpiClient` for plain scripts, CLI tools, and synchronous services;
120
+ pick `AsyncGlpiClient` when your application already runs an event loop or
121
+ when you need concurrent fan-out (the aggregated `get_ticket_context` and
122
+ per-ticket `get_task_statistics` helpers overlap their calls there, and run
123
+ them one after another on the sync client).
118
124
 
119
125
  ## Documentation
120
126