glpi-python-client 0.3.1__tar.gz → 0.3.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.
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/PKG-INFO +1 -1
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/docs/user_guide.rst +34 -10
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/__init__.py +1 -1
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/async_client.py +3 -1
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/_async_bridge.py +17 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/custom/__init__.py +2 -0
- glpi_python_client-0.3.2/glpi_python_client/clients/custom/_pagination_async.py +164 -0
- glpi_python_client-0.3.2/glpi_python_client/clients/custom/_statistics_async.py +495 -0
- glpi_python_client-0.3.2/glpi_python_client/clients/tests/test_async_branches.py +701 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/pyproject.toml +1 -1
- glpi_python_client-0.3.1/glpi_python_client/clients/custom/_statistics_async.py +0 -250
- glpi_python_client-0.3.1/glpi_python_client/clients/tests/test_async_branches.py +0 -195
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/.gitignore +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/.pre-commit-config.yaml +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/.readthedocs.yaml +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/CONTRIBUTING.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/LICENSE +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/README.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/docs/_static/.gitkeep +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/docs/api_reference.rst +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/docs/conf.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/docs/development.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/docs/development_rtd.rst +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/docs/glpi_api_contract.json +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/docs/index.rst +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/docs/installation.rst +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/docs/publishing.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/docs/publishing_rtd.rst +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/docs/sponsoring.rst +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/auth/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/auth/_v1_session.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/auth/auth.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/auth/tests/test_auth.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/auth/tests/test_v1_session.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/administration/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/administration/_entity.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/administration/_user.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/assistance/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/assistance/_team.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/assistance/_ticket.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/assistance/timeline/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/assistance/timeline/_document.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/assistance/timeline/_followup.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/assistance/timeline/_solution.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/assistance/timeline/_task.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/dropdowns/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/dropdowns/_location.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/management/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/api/management/_document.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/_config.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/_constants.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/_errors.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/_filters.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/_http.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/_payloads.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/_transport.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/tests/test_errors.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/tests/test_filters.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/tests/test_http.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/tests/test_payloads.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/commons/tests/test_transport.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/custom/_statistics.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/custom/_ticket_context.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/custom/_ticket_context_async.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/custom/tests/test_statistics.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/custom/tests/test_statistics_async.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/custom/tests/test_ticket_context.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/sync_client.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/tests/test_api_coverage.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/tests/test_async_smoke.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/tests/test_glpi_client.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/tests/test_parity.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/tests/test_smoke.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/content/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/content/conversion.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/content/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/content/tests/test_conversion.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/_base.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/_common.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/_content.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/administration/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/administration/_entity.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/administration/_user.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/administration/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/administration/tests/test_administration_schemas.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/assistance/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/assistance/_team.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/assistance/_ticket.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/assistance/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/assistance/tests/test_assistance_schemas.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/assistance/tests/test_content_roundtrip.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/assistance/timeline/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/assistance/timeline/_document.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/assistance/timeline/_followup.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/assistance/timeline/_solution.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/assistance/timeline/_task.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/assistance/timeline/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/assistance/timeline/tests/test_timeline_schemas.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/dropdowns/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/dropdowns/_location.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/dropdowns/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/dropdowns/tests/test_dropdowns_schemas.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/enums.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/management/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/management/_document.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/management/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/api_schema/management/tests/test_management_schemas.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/custom_schema/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/custom_schema/_ticket_context.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/custom_schema/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/models/custom_schema/tests/test_ticket_context.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/py.typed +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/testing/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/testing/fixtures.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/testing/utils.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/skills/README.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/skills/glpi-client-setup/SKILL.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/skills/glpi-document-workflow/SKILL.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/skills/glpi-reporting-and-context/SKILL.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/skills/glpi-team-members/SKILL.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/skills/glpi-ticket-timeline/SKILL.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/skills/glpi-ticket-workflow/SKILL.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/skills/glpi-user-location-provisioning/SKILL.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: glpi-python-client
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.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/
|
|
@@ -179,16 +179,40 @@ fan-outs cannot race the auth manager, while the HTTP requests
|
|
|
179
179
|
themselves execute outside the lock through the thread-safe
|
|
180
180
|
:class:`requests.Session`.
|
|
181
181
|
|
|
182
|
-
A
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
182
|
+
A number of helpers ship with hand-written async overrides rather than
|
|
183
|
+
relying solely on the bridge. There are two reasons a method needs its
|
|
184
|
+
own async variant:
|
|
185
|
+
|
|
186
|
+
1. **Concurrency** — the method benefits from fanning multiple GLPI
|
|
187
|
+
calls out concurrently with :func:`asyncio.gather`.
|
|
188
|
+
2. **Internal self-calls** — the method calls another public method
|
|
189
|
+
through ``self`` (e.g. ``self.search_tickets(...)`` inside a
|
|
190
|
+
pagination loop). When the bridge runs the synchronous body in a
|
|
191
|
+
worker thread, ``self.method`` resolves to the bridge-wrapped
|
|
192
|
+
*coroutine*, which returns a coroutine object instead of data when
|
|
193
|
+
called without ``await``. The async override replaces the body so
|
|
194
|
+
every internal call is properly awaited on the event loop.
|
|
195
|
+
|
|
196
|
+
Helpers with async overrides:
|
|
197
|
+
|
|
198
|
+
* :meth:`AsyncGlpiClient.get_ticket_context` — fans the five underlying
|
|
199
|
+
GLPI calls out concurrently (reason: concurrency).
|
|
200
|
+
* :meth:`AsyncGlpiClient.get_task_statistics` — fans the per-ticket
|
|
201
|
+
task-list calls out concurrently (reason: concurrency).
|
|
202
|
+
* :meth:`AsyncGlpiClient.get_task_durations` — fans the per-ticket task
|
|
203
|
+
fetches out concurrently when ``return_task_details=True``, and
|
|
204
|
+
properly awaits ``iter_search_tickets`` and ``search_entities``
|
|
205
|
+
internally (reasons: concurrency + internal self-calls).
|
|
206
|
+
* :meth:`AsyncGlpiClient.get_ticket_statistics` — properly awaits
|
|
207
|
+
``search_tickets`` and ``search_entities`` internally (reason:
|
|
208
|
+
internal self-calls).
|
|
209
|
+
* :meth:`AsyncGlpiClient.get_user_activity` — properly awaits
|
|
210
|
+
``search_users``, ``iter_search_tickets``, and ``get_task_durations``
|
|
211
|
+
internally (reason: internal self-calls).
|
|
212
|
+
* ``iter_search_tickets``, ``iter_search_users``,
|
|
213
|
+
``iter_search_entities`` — each pagination loop body calls
|
|
214
|
+
``self.search_*(...)``; the async variants are native async generators
|
|
215
|
+
that ``await`` those calls directly (reason: internal self-calls).
|
|
192
216
|
|
|
193
217
|
Pagination helpers (``iter_search_tickets``, ``iter_search_users``,
|
|
194
218
|
``iter_search_entities``) are exposed as **async generators** on the
|
{glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/async_client.py
RENAMED
|
@@ -50,6 +50,7 @@ from glpi_python_client.clients.commons._config import (
|
|
|
50
50
|
build_client_resources,
|
|
51
51
|
)
|
|
52
52
|
from glpi_python_client.clients.commons._transport import TransportMixin
|
|
53
|
+
from glpi_python_client.clients.custom._pagination_async import AsyncPaginationMixin
|
|
53
54
|
from glpi_python_client.clients.custom._statistics_async import AsyncStatisticsMixin
|
|
54
55
|
from glpi_python_client.clients.custom._ticket_context_async import (
|
|
55
56
|
AsyncTicketContextMixin,
|
|
@@ -61,8 +62,9 @@ if TYPE_CHECKING:
|
|
|
61
62
|
logger = logging.getLogger(__name__)
|
|
62
63
|
|
|
63
64
|
|
|
64
|
-
class AsyncGlpiClient(
|
|
65
|
+
class AsyncGlpiClient( # type: ignore[misc]
|
|
65
66
|
AsyncBridge,
|
|
67
|
+
AsyncPaginationMixin,
|
|
66
68
|
TicketMixin,
|
|
67
69
|
TicketTaskMixin,
|
|
68
70
|
FollowupMixin,
|
|
@@ -22,6 +22,23 @@ Concurrency notes
|
|
|
22
22
|
releases the awaiter immediately, but the in-flight HTTP request keeps
|
|
23
23
|
running on the worker thread until ``requests`` returns. This matches
|
|
24
24
|
the behaviour of the original async client.
|
|
25
|
+
|
|
26
|
+
Known limitation — internal ``self``-calls
|
|
27
|
+
------------------------------------------
|
|
28
|
+
The bridge wraps *every* public method on the async client class. As a
|
|
29
|
+
result, when a synchronous body that is running inside a worker thread
|
|
30
|
+
calls another public method through ``self`` (e.g.
|
|
31
|
+
``self.search_tickets(...)``), it resolves to the *bridge-wrapped*
|
|
32
|
+
coroutine, not the synchronous function. Calling a coroutine without
|
|
33
|
+
``await`` produces a dangling coroutine object, not data.
|
|
34
|
+
|
|
35
|
+
Any sync method (or generator) that internally calls other public
|
|
36
|
+
methods through ``self`` must therefore be given a hand-written async
|
|
37
|
+
override that ``await``s (or ``async for``s) those calls on the event
|
|
38
|
+
loop. The convention used in this codebase is to place such overrides
|
|
39
|
+
in a ``_*_async.py`` companion module (e.g.
|
|
40
|
+
``clients/custom/_statistics_async.py``) and wire them into the async
|
|
41
|
+
client's MRO *before* the sync mixin that defines the original method.
|
|
25
42
|
"""
|
|
26
43
|
|
|
27
44
|
from __future__ import annotations
|
{glpi_python_client-0.3.1 → glpi_python_client-0.3.2}/glpi_python_client/clients/custom/__init__.py
RENAMED
|
@@ -17,6 +17,7 @@ composed into :class:`~glpi_python_client.clients.AsyncGlpiClient`.
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
+
from glpi_python_client.clients.custom._pagination_async import AsyncPaginationMixin
|
|
20
21
|
from glpi_python_client.clients.custom._statistics import StatisticsMixin
|
|
21
22
|
from glpi_python_client.clients.custom._statistics_async import AsyncStatisticsMixin
|
|
22
23
|
from glpi_python_client.clients.custom._ticket_context import TicketContextMixin
|
|
@@ -25,6 +26,7 @@ from glpi_python_client.clients.custom._ticket_context_async import (
|
|
|
25
26
|
)
|
|
26
27
|
|
|
27
28
|
__all__ = [
|
|
29
|
+
"AsyncPaginationMixin",
|
|
28
30
|
"AsyncStatisticsMixin",
|
|
29
31
|
"AsyncTicketContextMixin",
|
|
30
32
|
"StatisticsMixin",
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Asynchronous overrides for the three paginated search generators.
|
|
2
|
+
|
|
3
|
+
Each of the three ``iter_search_*`` generators in the API mixins
|
|
4
|
+
delegates its pagination loop to a ``self.search_*()`` call. When the
|
|
5
|
+
:class:`~glpi_python_client.clients.commons._async_bridge.AsyncBridge`
|
|
6
|
+
wraps a sync generator it drives ``next()`` on the generator object inside
|
|
7
|
+
a worker thread. Inside that thread ``self`` is still the
|
|
8
|
+
:class:`~glpi_python_client.clients.AsyncGlpiClient` instance, so
|
|
9
|
+
``self.search_tickets(...)`` resolves to the bridge-wrapped coroutine
|
|
10
|
+
function and calling it without ``await`` returns a coroutine object
|
|
11
|
+
instead of the expected list — triggering a
|
|
12
|
+
``RuntimeWarning: coroutine … was never awaited`` and a 500 response.
|
|
13
|
+
|
|
14
|
+
This mixin replaces all three generators with native async generators that
|
|
15
|
+
``await self.search_*(...)`` directly on the event loop.
|
|
16
|
+
|
|
17
|
+
The mixin must be positioned **before** ``TicketMixin``, ``UserMixin``, and
|
|
18
|
+
``EntityMixin`` in the :class:`~glpi_python_client.clients.AsyncGlpiClient`
|
|
19
|
+
base list so that the bridge's ``__init_subclass__`` hook finds the async
|
|
20
|
+
generator via ``getattr`` before it would otherwise wrap the sync version.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from collections.abc import AsyncIterator
|
|
26
|
+
|
|
27
|
+
from glpi_python_client.models.api_schema.administration._entity import GetEntity
|
|
28
|
+
from glpi_python_client.models.api_schema.administration._user import GetUser
|
|
29
|
+
from glpi_python_client.models.api_schema.assistance._ticket import GetTicket
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AsyncPaginationMixin:
|
|
33
|
+
"""Async generator overrides for the three paginated search helpers.
|
|
34
|
+
|
|
35
|
+
Each override re-implements the simple ``start``-advancing loop of
|
|
36
|
+
the synchronous counterpart but ``await``\\ s the underlying
|
|
37
|
+
``search_*`` call so it runs on the event loop rather than inside a
|
|
38
|
+
worker-thread-dispatched ``next()`` call where the coroutine would
|
|
39
|
+
be dropped on the floor.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
async def iter_search_tickets(
|
|
43
|
+
self,
|
|
44
|
+
rsql_filter: str = "",
|
|
45
|
+
*,
|
|
46
|
+
batch_size: int = 50,
|
|
47
|
+
sort: str | None = None,
|
|
48
|
+
fields: tuple[str, ...] = (),
|
|
49
|
+
) -> AsyncIterator[list[GetTicket]]:
|
|
50
|
+
"""Yield successive pages of GLPI tickets until exhausted.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
rsql_filter : str, optional
|
|
55
|
+
Raw RSQL filter forwarded as the ``filter`` query parameter.
|
|
56
|
+
Empty by default, which lists every visible ticket.
|
|
57
|
+
batch_size : int, optional
|
|
58
|
+
Number of records requested per page (default 50).
|
|
59
|
+
sort : str | None, optional
|
|
60
|
+
``sort`` query parameter forwarded as-is to each page request.
|
|
61
|
+
fields : tuple[str, ...], optional
|
|
62
|
+
Restricted set of contract field names to request.
|
|
63
|
+
|
|
64
|
+
Yields
|
|
65
|
+
------
|
|
66
|
+
list[GetTicket]
|
|
67
|
+
One page of tickets per iteration. The last yielded batch may
|
|
68
|
+
be shorter than ``batch_size``.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
start = 0
|
|
72
|
+
while True:
|
|
73
|
+
batch: list[GetTicket] = await self.search_tickets( # type: ignore[attr-defined]
|
|
74
|
+
rsql_filter,
|
|
75
|
+
limit=batch_size,
|
|
76
|
+
start=start,
|
|
77
|
+
sort=sort,
|
|
78
|
+
fields=fields,
|
|
79
|
+
)
|
|
80
|
+
if batch:
|
|
81
|
+
yield batch
|
|
82
|
+
if len(batch) < batch_size:
|
|
83
|
+
break
|
|
84
|
+
start += batch_size
|
|
85
|
+
|
|
86
|
+
async def iter_search_users(
|
|
87
|
+
self,
|
|
88
|
+
rsql_filter: str = "",
|
|
89
|
+
*,
|
|
90
|
+
batch_size: int = 50,
|
|
91
|
+
skip_entity: bool = False,
|
|
92
|
+
) -> AsyncIterator[list[GetUser]]:
|
|
93
|
+
"""Yield successive pages of GLPI users until exhausted.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
rsql_filter : str, optional
|
|
98
|
+
Raw RSQL filter forwarded as the ``filter`` query parameter.
|
|
99
|
+
Empty by default, which lists every visible user.
|
|
100
|
+
batch_size : int, optional
|
|
101
|
+
Number of records requested per page (default 50).
|
|
102
|
+
skip_entity : bool, optional
|
|
103
|
+
When ``True`` the ``GLPI-Entity`` header is omitted so the
|
|
104
|
+
search spans every entity the caller has access to.
|
|
105
|
+
|
|
106
|
+
Yields
|
|
107
|
+
------
|
|
108
|
+
list[GetUser]
|
|
109
|
+
One page of users per iteration. The last yielded batch may
|
|
110
|
+
be shorter than ``batch_size``.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
start = 0
|
|
114
|
+
while True:
|
|
115
|
+
batch: list[GetUser] = await self.search_users( # type: ignore[attr-defined]
|
|
116
|
+
rsql_filter,
|
|
117
|
+
limit=batch_size,
|
|
118
|
+
start=start,
|
|
119
|
+
skip_entity=skip_entity,
|
|
120
|
+
)
|
|
121
|
+
if batch:
|
|
122
|
+
yield batch
|
|
123
|
+
if len(batch) < batch_size:
|
|
124
|
+
break
|
|
125
|
+
start += batch_size
|
|
126
|
+
|
|
127
|
+
async def iter_search_entities(
|
|
128
|
+
self,
|
|
129
|
+
rsql_filter: str = "",
|
|
130
|
+
*,
|
|
131
|
+
batch_size: int = 50,
|
|
132
|
+
) -> AsyncIterator[list[GetEntity]]:
|
|
133
|
+
"""Yield successive pages of GLPI entities until exhausted.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
rsql_filter : str, optional
|
|
138
|
+
Raw RSQL filter forwarded as the ``filter`` query parameter.
|
|
139
|
+
Empty by default, which lists every accessible entity.
|
|
140
|
+
batch_size : int, optional
|
|
141
|
+
Number of records requested per page (default 50).
|
|
142
|
+
|
|
143
|
+
Yields
|
|
144
|
+
------
|
|
145
|
+
list[GetEntity]
|
|
146
|
+
One page of entities per iteration. The last yielded batch
|
|
147
|
+
may be shorter than ``batch_size``.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
start = 0
|
|
151
|
+
while True:
|
|
152
|
+
batch: list[GetEntity] = await self.search_entities( # type: ignore[attr-defined]
|
|
153
|
+
rsql_filter,
|
|
154
|
+
limit=batch_size,
|
|
155
|
+
start=start,
|
|
156
|
+
)
|
|
157
|
+
if batch:
|
|
158
|
+
yield batch
|
|
159
|
+
if len(batch) < batch_size:
|
|
160
|
+
break
|
|
161
|
+
start += batch_size
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
__all__ = ["AsyncPaginationMixin"]
|