glpi-python-client 0.6.0__tar.gz → 0.6.1__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 (169) hide show
  1. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/CHANGELOG.md +128 -0
  2. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/PKG-INFO +4 -4
  3. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/docs/api_reference.rst +12 -6
  4. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/docs/development.md +7 -3
  5. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/docs/installation.rst +6 -1
  6. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/docs/user_guide.rst +231 -19
  7. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/__init__.py +1 -1
  8. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_errors.py +2 -2
  9. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/content/conversion.py +179 -22
  10. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/_content.py +13 -2
  11. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/pyproject.toml +21 -5
  12. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/skills/glpi-asset-workflow/SKILL.md +1 -1
  13. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/skills/glpi-client-setup/SKILL.md +1 -1
  14. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/skills/glpi-contract-workflow/SKILL.md +1 -1
  15. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/skills/glpi-document-workflow/SKILL.md +1 -1
  16. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/skills/glpi-knowledge-base/SKILL.md +2 -2
  17. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/skills/glpi-plugin-fields/SKILL.md +2 -2
  18. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/skills/glpi-reporting-and-context/SKILL.md +1 -1
  19. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/skills/glpi-team-members/SKILL.md +1 -1
  20. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/skills/glpi-ticket-timeline/SKILL.md +3 -3
  21. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/skills/glpi-ticket-workflow/SKILL.md +3 -3
  22. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/skills/glpi-user-location-provisioning/SKILL.md +1 -1
  23. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/.gitignore +0 -0
  24. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/.pre-commit-config.yaml +0 -0
  25. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/.readthedocs.yaml +0 -0
  26. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/CONTRIBUTING.md +0 -0
  27. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/LICENSE +0 -0
  28. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/README.md +0 -0
  29. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/docs/_static/.gitkeep +0 -0
  30. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/docs/conf.py +0 -0
  31. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/docs/development_rtd.rst +0 -0
  32. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/docs/index.rst +0 -0
  33. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/docs/publishing.md +0 -0
  34. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/docs/publishing_rtd.rst +0 -0
  35. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/docs/sponsoring.rst +0 -0
  36. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/__init__.py +0 -0
  37. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/_concurrency.py +0 -0
  38. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/_testing.py +0 -0
  39. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/auth/__init__.py +0 -0
  40. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/auth/_v1_session.py +0 -0
  41. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/auth/auth.py +0 -0
  42. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/__init__.py +0 -0
  43. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/_base_client.py +0 -0
  44. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/__init__.py +0 -0
  45. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/administration/__init__.py +0 -0
  46. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/administration/_entity.py +0 -0
  47. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/administration/_user.py +0 -0
  48. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/assets/__init__.py +0 -0
  49. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/assets/_computer.py +0 -0
  50. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/assistance/__init__.py +0 -0
  51. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/assistance/_team.py +0 -0
  52. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/assistance/_ticket.py +0 -0
  53. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/assistance/timeline/__init__.py +0 -0
  54. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/assistance/timeline/_document.py +0 -0
  55. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/assistance/timeline/_followup.py +0 -0
  56. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/assistance/timeline/_solution.py +0 -0
  57. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/assistance/timeline/_task.py +0 -0
  58. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/dropdowns/__init__.py +0 -0
  59. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/dropdowns/_contract_type.py +0 -0
  60. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/dropdowns/_location.py +0 -0
  61. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/knowledgebase/__init__.py +0 -0
  62. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/knowledgebase/_article.py +0 -0
  63. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/knowledgebase/_category.py +0 -0
  64. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/knowledgebase/_comment.py +0 -0
  65. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/knowledgebase/_revision.py +0 -0
  66. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/management/__init__.py +0 -0
  67. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/management/_contract.py +0 -0
  68. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/management/_document.py +0 -0
  69. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/plugins/__init__.py +0 -0
  70. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/api/plugins/_fields.py +0 -0
  71. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/client.py +0 -0
  72. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/commons/__init__.py +0 -0
  73. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/commons/_config.py +0 -0
  74. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/commons/_constants.py +0 -0
  75. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/commons/_filters.py +0 -0
  76. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/commons/_http.py +0 -0
  77. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/commons/_payloads.py +0 -0
  78. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/commons/_transport.py +0 -0
  79. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/custom/__init__.py +0 -0
  80. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/custom/_statistics.py +0 -0
  81. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_async/clients/custom/_ticket_context.py +0 -0
  82. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/__init__.py +0 -0
  83. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/_concurrency.py +0 -0
  84. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/_testing.py +0 -0
  85. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/auth/__init__.py +0 -0
  86. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/auth/_v1_session.py +0 -0
  87. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/auth/auth.py +0 -0
  88. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/__init__.py +0 -0
  89. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/_base_client.py +0 -0
  90. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/__init__.py +0 -0
  91. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/administration/__init__.py +0 -0
  92. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/administration/_entity.py +0 -0
  93. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/administration/_user.py +0 -0
  94. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/assets/__init__.py +0 -0
  95. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/assets/_computer.py +0 -0
  96. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/assistance/__init__.py +0 -0
  97. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/assistance/_team.py +0 -0
  98. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/assistance/_ticket.py +0 -0
  99. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/assistance/timeline/__init__.py +0 -0
  100. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/assistance/timeline/_document.py +0 -0
  101. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/assistance/timeline/_followup.py +0 -0
  102. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/assistance/timeline/_solution.py +0 -0
  103. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/assistance/timeline/_task.py +0 -0
  104. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/dropdowns/__init__.py +0 -0
  105. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/dropdowns/_contract_type.py +0 -0
  106. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/dropdowns/_location.py +0 -0
  107. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/knowledgebase/__init__.py +0 -0
  108. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/knowledgebase/_article.py +0 -0
  109. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/knowledgebase/_category.py +0 -0
  110. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/knowledgebase/_comment.py +0 -0
  111. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/knowledgebase/_revision.py +0 -0
  112. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/management/__init__.py +0 -0
  113. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/management/_contract.py +0 -0
  114. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/management/_document.py +0 -0
  115. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/plugins/__init__.py +0 -0
  116. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/api/plugins/_fields.py +0 -0
  117. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/client.py +0 -0
  118. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/commons/__init__.py +0 -0
  119. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/commons/_config.py +0 -0
  120. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/commons/_constants.py +0 -0
  121. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/commons/_filters.py +0 -0
  122. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/commons/_http.py +0 -0
  123. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/commons/_payloads.py +0 -0
  124. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/commons/_transport.py +0 -0
  125. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/custom/__init__.py +0 -0
  126. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/custom/_statistics.py +0 -0
  127. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/_sync/clients/custom/_ticket_context.py +0 -0
  128. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/content/__init__.py +0 -0
  129. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/__init__.py +0 -0
  130. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/_base.py +0 -0
  131. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/__init__.py +0 -0
  132. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/_common.py +0 -0
  133. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/administration/__init__.py +0 -0
  134. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/administration/_entity.py +0 -0
  135. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/administration/_user.py +0 -0
  136. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/assets/__init__.py +0 -0
  137. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/assets/_computer.py +0 -0
  138. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/assets/_contract_item.py +0 -0
  139. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/assistance/__init__.py +0 -0
  140. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/assistance/_team.py +0 -0
  141. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/assistance/_ticket.py +0 -0
  142. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/assistance/timeline/__init__.py +0 -0
  143. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/assistance/timeline/_document.py +0 -0
  144. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/assistance/timeline/_followup.py +0 -0
  145. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/assistance/timeline/_solution.py +0 -0
  146. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/assistance/timeline/_task.py +0 -0
  147. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/dropdowns/__init__.py +0 -0
  148. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/dropdowns/_contract_type.py +0 -0
  149. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/dropdowns/_location.py +0 -0
  150. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/enums.py +0 -0
  151. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/knowledgebase/__init__.py +0 -0
  152. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/knowledgebase/_article.py +0 -0
  153. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/knowledgebase/_category.py +0 -0
  154. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/knowledgebase/_comment.py +0 -0
  155. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/knowledgebase/_revision.py +0 -0
  156. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/management/__init__.py +0 -0
  157. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/management/_contract.py +0 -0
  158. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/management/_contract_cost.py +0 -0
  159. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/management/_document.py +0 -0
  160. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/plugins/__init__.py +0 -0
  161. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/api_schema/plugins/_fields.py +0 -0
  162. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/custom_schema/__init__.py +0 -0
  163. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/models/custom_schema/_ticket_context.py +0 -0
  164. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/py.typed +0 -0
  165. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/rsql.py +0 -0
  166. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/testing/__init__.py +0 -0
  167. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/testing/fixtures.py +0 -0
  168. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/glpi_python_client/testing/utils.py +0 -0
  169. {glpi_python_client-0.6.0 → glpi_python_client-0.6.1}/skills/README.md +0 -0
@@ -4,6 +4,134 @@ All notable changes to this project are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ ## 0.6.1 — 2026-10-02
8
+
9
+ The reader now gives the same Markdown as `easyvista-python-client` 0.4.1,
10
+ whose converter is this package's 0.6.0 converter plus fifteen fixes and a
11
+ correction to one of them. This release ports those fixes and the
12
+ correction, with their tests. The two conversion modules now differ only in
13
+ names, error messages, docstrings and that package's optional-dependency
14
+ import guard. The converter's writing (`to_transport`) is unchanged; a
15
+ write model now sends a lone surrogate as U+FFFD.
16
+
17
+ ### Fixed
18
+
19
+ - **Image alt text lost its escapes**: `a_b_c.png` read back as `abc.png`.
20
+ Escapes and character references in alt text are kept.
21
+ - **A line opening with `~~~` became a code fence** that swallowed the rest
22
+ of the body. It reads as `\~~~`.
23
+ - **A `!` right before a link turned the link into an image**
24
+ (`Attention!` then a link). It reads as `\!`.
25
+ - **An image whose alt text starts with `^` stopped being an image** once
26
+ rendered, since cmark-gfm never opens one on `![^`. The `^` is escaped.
27
+ - **`<<a@example.org>` became an autolink** behind an escaped `<`. The second
28
+ `<` is escaped too.
29
+ - **A line break inside inline code showed as a literal backslash.**
30
+ `<code>`, `<kbd>` and `<samp>` holding a `<br>` become one span per line;
31
+ a `|` in `<kbd>` or `<samp>` in a table cell no longer splits the row.
32
+ - **A table inside a heading or a link showed its pipes as text**, and
33
+ inside a link did not read back as itself. It is written as its cells'
34
+ text, as a table inside a cell already was.
35
+ - **A block inside such a flattened table broke its holder's line.** It
36
+ stays on that line.
37
+ - **Bold ending in punctuation at a flattened cell's edge came back as raw
38
+ `<strong>`**, which read back as `**`. It stays Markdown bold.
39
+ - **Two `<center>` blocks ran together.** `<center>` is a block, as `<div>`
40
+ is, except inside `<pre>`.
41
+ - **Underline, highlight and inserted text lost their formatting.** `<u>`,
42
+ `<mark>` and `<ins>` are kept as raw tags, as `<s>` already was. Round a
43
+ block (a table, a list, a heading, a quote, a code block, a rule or
44
+ paragraphs) the tag is dropped and the blocks read as 0.6.0 read them.
45
+ Markdown has no inline tag round blocks: `easyvista-python-client` 0.4.0,
46
+ which kept it, showed a table as pipe text and a list or a heading as its
47
+ Markdown source, and round a `<pre>` left a fence open to the end of the
48
+ body. Inside a table cell or a heading, whose blocks are one line, the tag
49
+ stays.
50
+ - **A long ordered list was quadratic to read**: 5,000 items took 3.1 s,
51
+ and take 0.6 s now (CPython 3.12.3). An `<ol start>` such as `²` or `½`
52
+ counts from 1, as a browser counts a start holding no digit, instead of
53
+ failing the body.
54
+ - **A run of spaces or line breaks at the edge of bold, italic or a link was
55
+ quadratic to read.** It is linear.
56
+ - **A `colspan` or `start` markdownify cannot read as a number raised
57
+ `GlpiContentError`** from `.content` (`colspan="²"`, or 5,000 digits). The
58
+ body degrades to its text instead, every word kept. Any other
59
+ `ValueError` raised while converting takes the same path.
60
+ - **A lone surrogate in a write model's Markdown failed the whole write**
61
+ with `GlpiContentError`: cmark-gfm renders UTF-8, which cannot encode
62
+ one. Each lone surrogate is written as U+FFFD instead, as CommonMark
63
+ replaces a NUL, and the rest of the body keeps its Markdown. Calling
64
+ `GlpiContentConverter.to_transport` directly still raises, as
65
+ `easyvista-python-client`'s converter does.
66
+ - **A run of unfinished tags at the end of a body was quadratic to read** on
67
+ CPython before 3.11.14, 3.12.12 and 3.13.6 (CVE-2025-6069). Every `<`
68
+ after the body's last `>` is read as text, so `x <a b` at the very end
69
+ reads as `x \<a b` on every interpreter, where a patched one dropped it.
70
+
71
+ ### Changed
72
+
73
+ - **`.content` changes once** for a body holding any shape above, so stored
74
+ digests of such bodies change once.
75
+ - **Deeply nested HTML degrades to text sooner.** The reader spends one more
76
+ stack frame per level: from a shallow stack, the deepest `<div>` document
77
+ read with its structure went from about 490 levels to about 326 (CPython
78
+ 3.12.3 and 3.13.14, default recursion limit). `<blockquote>` stays at
79
+ about 194. The words are kept either way.
80
+ - **Dependency bounds** are now those of `easyvista-python-client[content]`
81
+ 0.4.0, so the two packages resolve to the same libraries side by side:
82
+ `markdown-it-py>=3.0,<4` (the alt-text fix relies on 3.x's `text_special`
83
+ tokens, and `mdformat` 0.7.22 requires `<4` anyway),
84
+ `markdownify>=1.2.3,<1.3` and `mdformat-tables>=1.0,<1.1`.
85
+ `mdformat>=0.7.22,<0.8` is unchanged. The reader overrides private
86
+ surfaces of `markdownify` and `mdformat`; `pyproject.toml` gives the
87
+ reason for each bound.
88
+
89
+ ### Documentation
90
+
91
+ - The user guide's rich-text section now says what each element becomes,
92
+ that neither direction sanitises, what survives a round trip, and the
93
+ known holes, all reproduced on 2026-10-02. Its claim that a read always
94
+ displays the same and reads back as itself is now stated as the aim it
95
+ is, as in the API reference and the module docstring.
96
+ - The deep-nesting note gives the measured depths, and that a read started
97
+ within about 30 frames of the recursion limit can still raise.
98
+ - `GlpiContentError`'s docstring named python-markdown as the writer; it is
99
+ cmark-gfm.
100
+ - The `glpi-ticket-timeline`, `glpi-ticket-workflow` and
101
+ `glpi-knowledge-base` skills describe raw `<u>`, `<mark>`, `<ins>` and
102
+ `<s>`, the write-model rule exactly, and the new depth.
103
+ - Corrected after review: a write model keeps the caller's Markdown
104
+ stripped at both ends, not verbatim; the `glpi-knowledge-base` and
105
+ `glpi-plugin-fields` skills no longer say a deep body never raises; any
106
+ `ValueError` degrades to text, not only a `colspan` or `start`; a
107
+ `start` counts as a browser counts it only when it holds no digit; and
108
+ the CVE-2025-6069 note says that everything after a body's last `>`,
109
+ an unterminated comment or a cut-short closing tag included, is shown.
110
+
111
+ ### Tests
112
+
113
+ - `content/tests/test_fixes.py` pins each of the fifteen fixes; run against
114
+ 0.6.0, 36 of the 54 tests first ported fail on CPython 3.12.3 and 37 on
115
+ 3.13.14, the rest being controls and pins. Section 11 also pins the
116
+ correction to fix 11 with 42 tests, 40 of which fail on
117
+ `easyvista-python-client` 0.4.0's converter. Reverting any one fix alone fails at least
118
+ one test of the content suite on both.
119
+ - `content/tests/test_properties.py` holds seeded generators to three
120
+ properties: a body displays what its HTML displayed, is a fixed point,
121
+ and keeps its words in order. Against 0.6.0 all 16 tests fail.
122
+ - `content/tests/test_cost.py` replaces the 20-second budgets on long bodies
123
+ with growth tests, `time(4n) / time(n) < 8`, which a quadratic pass fails
124
+ at a few thousand items.
125
+ - Four guards that no test caught are pinned, each by a test that fails
126
+ without it: a second `<` before a space keeps one escape, a header cell
127
+ escapes a `|` in `<kbd>` or `<samp>` and splits a line break in inline
128
+ code, a table inside an `<a>` without `href` stays a table, and an ordered
129
+ item numbered 10 or more indents its content by its bullet's width. A
130
+ count pins that the walk finding blocks inside `<u>`, `<mark>` and `<ins>`
131
+ checks each tag about once.
132
+ - The display oracle treats a flattened cell's edge as a word boundary, as a
133
+ browser does, and reads `start` with `isdecimal`.
134
+
7
135
  ## 0.6.0 — 2026-10-01
8
136
 
9
137
  ### Changed (breaking)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: glpi-python-client
3
- Version: 0.6.0
3
+ Version: 0.6.1
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/
@@ -27,9 +27,9 @@ Requires-Dist: beautifulsoup4>=4.15
27
27
  Requires-Dist: cmarkgfm>=2025.10
28
28
  Requires-Dist: httpx>=0.28
29
29
  Requires-Dist: lxml>=4.9
30
- Requires-Dist: markdown-it-py>=3.0
31
- Requires-Dist: markdownify>=1.2
32
- Requires-Dist: mdformat-tables>=1.0
30
+ Requires-Dist: markdown-it-py<4,>=3.0
31
+ Requires-Dist: markdownify<1.3,>=1.2.3
32
+ Requires-Dist: mdformat-tables<1.1,>=1.0
33
33
  Requires-Dist: mdformat<0.8,>=0.7.22
34
34
  Requires-Dist: pydantic>=2.8
35
35
  Requires-Dist: sniffio>=1.3
@@ -115,17 +115,23 @@ its page being read.
115
115
 
116
116
  The Markdown is CommonMark with GFM tables, rendered by cmark-gfm. It
117
117
  spells a body's text as literal text -- ``\_\_init\_\_``, ``\\\serveur``,
118
- ``\# pas un titre`` -- so rendering it displays what GLPI displayed and
119
- reading that back gives the same Markdown. A write model keeps the caller's
120
- Markdown verbatim unless it starts with an HTML tag. See
121
- :ref:`content-conversion`.
118
+ ``\# pas un titre`` -- so that rendering it displays what GLPI displayed and
119
+ reading that back gives the same Markdown. That is the aim; the user guide
120
+ lists the shapes where it falls short. Underline, highlight and struck text
121
+ stay as raw ``<u>``, ``<mark>``, ``<ins>`` and ``<s>``. Neither direction
122
+ sanitises: raw HTML and ``javascript:`` links pass through. A write model
123
+ keeps the caller's Markdown as written, stripped at both ends, unless it
124
+ starts with ``<`` and holds an HTML element, and sends a lone surrogate as
125
+ U+FFFD. See :ref:`content-conversion`.
122
126
 
123
127
  Very deeply nested HTML is the case worth knowing about.
124
128
  ``markdownify`` walks the document recursively and runs out of stack at
125
129
  a few hundred levels of nesting. The converter attempts the conversion
126
130
  and, if the walk does not fit, reads the body's text instead, a line per
127
- block: the words survive, the structure does not. Anything else that goes
128
- wrong in either direction raises :class:`GlpiContentError`.
131
+ block: the words survive, the structure does not. Any ``ValueError`` from
132
+ the conversion degrades the same way: markdownify raises one for a
133
+ ``colspan`` or ``start`` it cannot read as a number. Anything else that
134
+ goes wrong in either direction raises :class:`GlpiContentError`.
129
135
 
130
136
  Because the conversion is cached on first read, a read model should be
131
137
  treated as immutable afterwards: assigning to ``content_html``, or
@@ -86,13 +86,17 @@ python -m pytest
86
86
  articles (`content` and `description`) and article revisions. It is
87
87
  wired into the models by `models/api_schema/_content.py`, eagerly on
88
88
  the write models and through a cached property on the read ones.
89
- Inbound HTML too deep for `markdownify` to walk is tag-stripped
89
+ Inbound HTML too deep for `markdownify` to walk, or on which it
90
+ raises `ValueError` (a `colspan` or `start` it cannot read as a
91
+ number, among others), is tag-stripped
90
92
  rather than parsed. The depth is not predicted: the conversion is
91
93
  attempted and the `RecursionError` answered, because the budget is the
92
94
  caller's remaining stack and no bound computed in advance can know it.
93
- The module docstring carries the derivation and the rejected
95
+ The 0.5.0 changelog entry carries the derivation and the rejected
94
96
  alternatives (`sys.setrecursionlimit`, and a thread with a larger
95
- stack, which needs the same global). The prohibition is enforced by
97
+ stack, which needs the same global). The reader is kept identical, but
98
+ for names, messages, docstrings and an import guard, to
99
+ `easyvista-python-client`'s; change the two together. The prohibition is enforced by
96
100
  `testing/tests/test_raise_site_audit.py`, not just written down.
97
101
  - `glpi_python_client.testing` exposes `make_client` and
98
102
  `make_async_client` factories that produce in-memory clients with no
@@ -6,7 +6,12 @@ Requirements
6
6
 
7
7
  ``glpi-python-client`` supports Python 3.11 and newer. Runtime dependencies are installed
8
8
  from the package metadata and include ``httpx``, ``tenacity``,
9
- ``beautifulsoup4``, ``lxml``, and ``pydantic``.
9
+ ``beautifulsoup4``, ``lxml``, and ``pydantic``, plus the libraries the
10
+ rich-text converter is built on: ``markdownify``, ``mdformat``,
11
+ ``mdformat-tables`` and ``markdown-it-py`` to read, and ``cmarkgfm`` to
12
+ write. The reader overrides private surfaces of ``markdownify`` and
13
+ ``mdformat``, so those four carry upper bounds; ``pyproject.toml`` gives the
14
+ reason for each.
10
15
 
11
16
  Install from PyPI
12
17
  -----------------
@@ -1758,11 +1758,13 @@ The whole page is built in one pass, so a single unconvertible record used
1758
1758
  to make its page-mates unreadable too. The failure is now scoped to the
1759
1759
  record whose body you actually read.
1760
1760
 
1761
- The Markdown is CommonMark with GFM tables. Rendering it -- as the package
1762
- does on the way back to GLPI, with cmark-gfm -- displays what GLPI
1763
- displayed, and reading that rendering back gives the same Markdown. Text in
1764
- a body is literal: Markdown has one spelling for ``__init__`` typed by a
1765
- user and for bold ``init``, so text that would read as syntax is escaped:
1761
+ The Markdown is CommonMark with GFM tables. The aim is that rendering it --
1762
+ as the package does on the way back to GLPI, with cmark-gfm -- displays what
1763
+ GLPI displayed, and that reading that rendering back gives the same
1764
+ Markdown. It is an aim, not a guarantee for every body: `What survives a
1765
+ round trip`_ lists where it falls short. Text in a body is literal: Markdown
1766
+ has one spelling for ``__init__`` typed by a user and for bold ``init``, so
1767
+ text that would read as syntax is escaped:
1766
1768
 
1767
1769
  .. code-block:: python
1768
1770
 
@@ -1781,28 +1783,106 @@ back as ``\`` and a newline, a nested list is indented by its bullet's width,
1781
1783
  and a table comes back unpadded. A body with no HTML element is plain text
1782
1784
  and is read as GLPI shows it, its lines as lines.
1783
1785
 
1786
+ More of what GLPI displays, and the Markdown it reads as:
1787
+
1788
+ .. code-block:: text
1789
+
1790
+ GLPI displays Markdown
1791
+ ---------------------------- ----------------------------
1792
+ __init__ \_\_init\_\_
1793
+ *important* \*important\*
1794
+ # 4521 (at a line start) \# 4521
1795
+ 1. pas une liste 1\. pas une liste
1796
+ - pas une liste \- pas une liste
1797
+ > pas une citation \> pas une citation
1798
+ [lien](x) \[lien\](x)
1799
+ <Entrée> \<Entrée>
1800
+ ~~~ (at a line start) \~~~
1801
+ Attention! then a link Attention\![lien](https://example.org)
1802
+
1803
+ Text GLPI displays as markup is read back as that text, so ``&lt;b&gt;``
1804
+ reads as ``\<b>``, never as a live ``<b>``. What each kind of element
1805
+ becomes:
1806
+
1807
+ * **Bold and italic** become ``**`` and ``*``, or raw ``<strong>`` and
1808
+ ``<em>`` where CommonMark would not close the markers, for example bold
1809
+ that ends in punctuation directly followed by a letter:
1810
+ ``prix:<b>(10)</b>euros`` reads as ``prix:<strong>(10)</strong>euros``.
1811
+ * **Underline, highlight and inserted text** (``<u>``, ``<mark>``,
1812
+ ``<ins>``) stay as those raw tags, and **struck text** (``<s>``,
1813
+ ``<del>``, ``<strike>``) as a raw ``<s>``. CommonMark has no spelling for
1814
+ any of them, and writing passes the tags through, so the formatting
1815
+ survives. The cost is raw HTML in the Markdown. Markdown has no inline tag
1816
+ round blocks, so a ``<u>``, ``<mark>`` or ``<ins>`` holding a table, a
1817
+ list, a heading, a quote, a ``<pre>``, a rule or paragraphs is dropped and
1818
+ its blocks kept; inside a table cell or a heading, which hold one line, it
1819
+ stays.
1820
+ * **Links** become ``[text](https://... "title")``, and a link whose text is
1821
+ its own URL, a pasted link, becomes the autolink ``<https://...>``. No
1822
+ link target is filtered, ``javascript:`` included (see `It is not a
1823
+ sanitiser`_). An **image** becomes ``![alt](src "title")``, its alt text
1824
+ escaped like any other text.
1825
+ * **Lists** nest and keep their numbers, including an ``<ol start>``. A
1826
+ ``start`` that is not a decimal number counts from 1, as a browser counts
1827
+ one holding no digit, such as ``²``; a browser reads ``" 3"``, ``"+3"``
1828
+ or ``"3abc"`` as 3. **Tables** become GFM tables, and a ``<br>`` inside a cell
1829
+ stays a raw ``<br>``, since a GFM cell is one line. A table inside a cell,
1830
+ a heading or a link is written as its cells' text, spaced apart.
1831
+ * **Preformatted blocks** become fences that keep the ``language-`` class
1832
+ cmark-gfm writes, with a fence longer than any run of backticks in the
1833
+ code. **Inline code** holding a line break becomes one code span per line.
1834
+ * ``<div>`` and ``<center>`` are blocks. ``<head>``, ``<script>``,
1835
+ ``<style>``, ``<template>`` and ``<title>`` are dropped, as a browser does
1836
+ not display them. Styling such as ``<font>`` colours or ``<span style>``
1837
+ keeps its text and loses the style.
1838
+
1784
1839
  Writing, your Markdown is rendered by cmark-gfm. A newline is a line break,
1785
1840
  GFM tables work, and raw HTML passes through, so put a placeholder such as
1786
- ``<login>`` in backticks. A write model keeps your Markdown verbatim unless
1787
- it starts with an HTML tag, in which case it is read as HTML. A Markdown
1788
- table needs a header row, so a header-less HTML table reads back with an
1789
- empty one, and struck-through text stays as raw ``<s>``.
1841
+ ``<login>`` in backticks. A write model keeps your Markdown as written,
1842
+ stripped at both ends, unless it starts with ``<`` and holds an HTML
1843
+ element anywhere, in which case it is read as HTML. Stripping unindents the
1844
+ first line of a body that opens with an indented code block, which then
1845
+ reads as a paragraph, so open such a body with a fence. Read as HTML,
1846
+ Markdown that opens with an autolink and carries an inline ``<br>`` further
1847
+ on loses that autolink, so start such a value with something else. A
1848
+ Markdown table needs a header row, so a header-less HTML table reads back
1849
+ with an empty one, and struck-through text stays as raw ``<s>``. A lone
1850
+ UTF-16 surrogate in a write model's Markdown, which UTF-8 cannot encode, is
1851
+ sent as U+FFFD; ``GlpiContentConverter.to_transport`` called directly raises
1852
+ on one.
1790
1853
 
1791
1854
  .. note::
1792
1855
 
1793
1856
  Deeply nested HTML is the case worth knowing about. The HTML-to-Markdown
1794
- converter walks the document recursively and exhausts the interpreter's
1795
- stack at around 494 levels of nesting. ``.content`` does not try to
1796
- predict that -- it attempts the conversion and, when the walk does not
1797
- fit, strips the tags instead. **It degrades, it never truncates, and it
1798
- does not raise**: every character the normal rendering would have
1799
- produced still appears, so a body never says less because of how deeply
1800
- it happened to nest. What you lose is structure,
1801
- not words — link targets and image alt text, code-block fencing and
1802
- ``<pre>`` indentation, and ``&nbsp;``-padded alignment. Anything else
1803
- that goes wrong raises
1857
+ converter walks the document recursively, so a deeply nested body can
1858
+ exhaust the interpreter's stack. From a shallow stack, the deepest
1859
+ ``<div>`` document that still converts with its structure is about 326
1860
+ levels, and the deepest ``<blockquote>`` one about 194 (measured
1861
+ 2026-10-02 on CPython 3.12.3 and 3.13.14 with the default recursion
1862
+ limit; 0.6.0 reached about 490 ``<div>`` levels, and 0.6.1's reader
1863
+ spends one more frame per level). ``.content`` does not try to predict
1864
+ that -- it attempts the conversion and, when the walk does not fit,
1865
+ strips the tags instead. **It degrades and never truncates**: every
1866
+ character the normal rendering would have produced still appears, so a
1867
+ body never says less because of how deeply it happened to nest. What you
1868
+ lose is structure, not words — link targets and image alt text,
1869
+ code-block fencing and ``<pre>`` indentation, and ``&nbsp;``-padded
1870
+ alignment. Any ``ValueError`` from the conversion takes the same path:
1871
+ markdownify raises one for a ``colspan`` or ``start`` it cannot read as
1872
+ a number, such as a ``colspan`` of ``"²"`` or a value of 5,000 digits.
1873
+ So does a document ``html.parser`` refuses outright. Anything else that
1874
+ goes wrong raises
1804
1875
  :class:`~glpi_python_client.GlpiContentError`.
1805
1876
 
1877
+ Because the budget is whatever stack is left when the call starts, the
1878
+ same body can convert from one call site and degrade from a deeper one,
1879
+ and a caller already close to the recursion limit can get an error after
1880
+ all. Measured 2026-10-02 with the default limit of 1000: an ordinary body
1881
+ converted when read from up to about 969 frames deep and degraded to text
1882
+ from about 970. From about 975 frames it raised
1883
+ :class:`~glpi_python_client.GlpiContentError`, and from about 994 a bare
1884
+ :class:`RecursionError` escaped.
1885
+
1806
1886
  Because the result is cached on first read, treat a read model as
1807
1887
  immutable afterwards. Assigning to ``content_html`` -- or
1808
1888
  ``model_copy(update={"content_html": ...})`` -- leaves the cached
@@ -1815,6 +1895,138 @@ emits ``content_html`` holding HTML where it used to emit ``content``
1815
1895
  holding Markdown. Pass ``by_alias=True`` for a dump keyed the way GLPI
1816
1896
  keys it.
1817
1897
 
1898
+ **CVE-2025-6069.** CPython's ``html.parser`` before 3.11.14, 3.12.12 and
1899
+ 3.13.6 takes quadratic time on some unfinished markup
1900
+ (`python/cpython#135462 <https://github.com/python/cpython/issues/135462>`_).
1901
+ Many unfinished tags after a body's last ``>`` is the shape that reaches the
1902
+ converter, and a body is outside data, so reading spells every ``<`` after
1903
+ the last ``>`` as ``&lt;`` before parsing: no ``<`` there can finish a tag.
1904
+ As a side effect, whatever follows the body's last ``>`` reads as text on
1905
+ every interpreter, where a patched CPython drops some of it: an unfinished
1906
+ tag at the very end, ``x <a``, reads as ``x \<a``; an unterminated
1907
+ comment, ``<!-- note``, as ``\<!-- note``; and a closing tag cut short
1908
+ inside a link, ``<a href="...">lien</a``, leaves ``\</a`` in the link's
1909
+ text.
1910
+ Prefer a patched interpreter anyway: the guard covers the converter's input,
1911
+ and the CPython fix covers the parser itself.
1912
+
1913
+ It is not a sanitiser
1914
+ ^^^^^^^^^^^^^^^^^^^^^
1915
+
1916
+ Spelling literal text as text is not sanitising. Neither direction
1917
+ neutralises anything, by design: the Markdown is the caller's own, and
1918
+ cmark-gfm runs with its ``UNSAFE`` option, which is what lets ``<u>`` or a
1919
+ ``<br>`` in a table cell through. So:
1920
+
1921
+ * raw HTML in the Markdown is rendered verbatim, and
1922
+ ``<script>alert(1)</script>`` goes out as a live ``<script>``;
1923
+ * a ``javascript:`` link target is rendered as a live ``href``;
1924
+ * ``<javascript:alert(1)>`` is a CommonMark autolink, which accepts any
1925
+ scheme, so it too goes out as a live ``href``.
1926
+
1927
+ Reading does not filter either: a body's ``javascript:`` link reads back as
1928
+ a ``javascript:`` link. What reading does guarantee is that text a body
1929
+ *displays* stays text: ``&lt;script&gt;`` reads back as ``\<script>``, and is
1930
+ written back as the same ``&lt;script&gt;``.
1931
+
1932
+ A caller relaying Markdown it did not write must neutralise raw HTML and
1933
+ executable link schemes before writing it. A sync between two ITSMs is one
1934
+ such caller.
1935
+
1936
+ What survives a round trip
1937
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^
1938
+
1939
+ **Markdown written and read back.** Paragraphs, ``*emphasis*`` and
1940
+ ``**strong**``, ATX headings, nested lists, numbered lists, block quotes,
1941
+ fences with their language, links with titles, autolinks, raw ``<u>`` and
1942
+ ``<s>`` spans, accents and escaped literal text all come back as they were
1943
+ written. Other spellings come back in mdformat's canonical form, and display
1944
+ the same:
1945
+
1946
+ * ``_em_`` and ``__strong__`` as ``*em*`` and ``**strong**``;
1947
+ * ``+`` and ``*`` bullets as ``-``, ``1)`` as ``1.``, and repeated ``1.``
1948
+ items numbered ``1.``, ``2.``, ``3.``;
1949
+ * a setext heading as an ATX one, and ``***`` as ``---``;
1950
+ * a table's delimiter row as ``| -- |``;
1951
+ * a lone newline, or two trailing spaces, as a backslash hard break;
1952
+ * a link target holding parentheses wrapped in ``<...>``;
1953
+ * runs of blank lines as one, and a character reference such as ``&copy;``
1954
+ as the character.
1955
+
1956
+ A few things do not come back:
1957
+
1958
+ * text in angle brackets that is not a URL, ``use the <Enter> key``, is sent
1959
+ as a live unknown tag, and the browser and the reader both drop it;
1960
+ * an e-mail autolink comes back as an inline ``mailto:`` link;
1961
+ * a ``[`` or ``]`` in a link target comes back percent-encoded, as ``%5B``
1962
+ and ``%5D``;
1963
+ * a no-break space or hard break at the end of a paragraph is dropped;
1964
+ * a line holding only ``*`` is an empty list item in CommonMark, and reads
1965
+ back as nothing.
1966
+
1967
+ After that first cycle, a further one changes nothing more, with two
1968
+ exceptions found with generated Markdown (2026-10-02, 23 of 4,500 bodies):
1969
+ two adjacent lists with different bullets read as one loose list, then as
1970
+ one tight list; and a fence whose info string holds a character reference,
1971
+ such as ``&amp;amp;``, loses one level of it on each cycle.
1972
+
1973
+ **A body read, written back and read again.** This direction is held to
1974
+ more. The aim is that what the Markdown displays is what GLPI displayed, and
1975
+ that the Markdown is a fixed point from the first read. The converter's
1976
+ tests check both, by comparing what the HTML and the rendered Markdown
1977
+ display, on realistic and generated bodies. It is an aim, not a guarantee
1978
+ for every body.
1979
+
1980
+ A few structures have no Markdown spelling. They keep their words and lose
1981
+ only their shape:
1982
+
1983
+ * **a table nested in a table** has its grid flattened: each inner cell
1984
+ becomes text in the outer cell, spaced apart, with every word kept and in
1985
+ order. E-mail signatures and notification templates are often laid out
1986
+ so. A table inside a heading or a link is flattened the same way;
1987
+ * **a table without a header row** gains an empty one, because GFM requires
1988
+ a header;
1989
+ * a table cell holds one line, so the paragraphs inside a cell join, while a
1990
+ ``<br>`` in a cell is kept as a raw ``<br>``;
1991
+ * a heading holds one line, so a ``<br>`` in a heading becomes a space;
1992
+ * trailing spaces at the end of a ``<pre>`` are dropped;
1993
+ * underline, highlight, insertion and strike are kept only as raw ``<u>``,
1994
+ ``<mark>``, ``<ins>`` and ``<s>``, so a renderer that drops raw HTML shows
1995
+ their text without the formatting;
1996
+ * two code spans with nothing between them become one span, which shows the
1997
+ two backticks that joined them.
1998
+
1999
+ **Known holes.** These shapes lose words, or display other than GLPI did.
2000
+ Each was found with synthetic input and reproduced on 2026-10-02:
2001
+
2002
+ * a table row with more cells than the table's first row loses the extra
2003
+ cells, with their words, at the first read;
2004
+ * a ``|`` inside a ``<pre>``, a link target or a title in a table cell
2005
+ splits the row: the rest of the cell is lost or shows as Markdown source;
2006
+ * a ``<caption>`` or ``<colgroup>`` directly inside a ``<table>``, with no
2007
+ ``<tbody>``, turns the table into lines of literal pipes;
2008
+ * an image whose title holds a ``"`` is lost, and shows as its Markdown
2009
+ source;
2010
+ * a list or an ``<hr>`` inside a table cell shows as ``-`` or ``---`` text;
2011
+ * a heading followed by text in the same cell, or text sitting directly in
2012
+ a nested table after its rows, runs into the next word (``titresuite``);
2013
+ * a table whose ``<td>`` and ``<tr>`` are never closed folds into one cell,
2014
+ keeping its words;
2015
+ * a definition list (``<dl>``) reads as a ``term`` line and a
2016
+ ``: definition`` line, so its display gains the colon;
2017
+ * bold, italic or struck text (``<b>``, ``<em>``, ``<s>`` and their
2018
+ synonyms) wrapped round blocks, other than a single paragraph, shows its
2019
+ markers or its tag as text, and a list, a table or a heading inside it as
2020
+ Markdown source; round a ``<pre>`` it leaves a fence open, so the rest of
2021
+ the body shows as code. 0.6.0 read these the same way.
2022
+
2023
+ Some shapes are not fixed points at the first read, and settle after one
2024
+ more cycle. Adjacent lists read as one loose list, then as one tight list. A
2025
+ definition list's newline becomes a hard break. Bold inside bold
2026
+ (``<b><b>gras</b></b>``) reads as ``****gras****``, then as ``**gras**``. A
2027
+ link or image title loses a backslash before punctuation. The caption,
2028
+ colgroup and image-title shapes above settle too, on what they already lost.
2029
+
1818
2030
  Retry behaviour
1819
2031
  ~~~~~~~~~~~~~~~
1820
2032
 
@@ -131,7 +131,7 @@ from glpi_python_client.rsql import (
131
131
  date_window,
132
132
  )
133
133
 
134
- __version__ = "0.6.0"
134
+ __version__ = "0.6.1"
135
135
 
136
136
  __all__ = [
137
137
  "AsyncGlpiClient",
@@ -149,8 +149,8 @@ class GlpiContentError(GlpiError):
149
149
 
150
150
  This exists so that no failure of the content layer escapes the
151
151
  package's taxonomy. The conversion runs third-party parsers
152
- (``markdownify`` inbound, ``markdown`` outbound), and a parser fault
153
- used to reach the caller as a bare builtin — most visibly a
152
+ (``markdownify`` and ``mdformat`` inbound, ``cmark-gfm`` outbound), and a
153
+ parser fault used to reach the caller as a bare builtin — most visibly a
154
154
  ``RecursionError``, which ``except GlpiError`` does not catch and which
155
155
  a caller reading a ticket has no reason to expect from
156
156
  ``get_ticket``. Deeply nested HTML is caught and answered with the