python-etcd3-async 0.1.5__tar.gz → 0.2.0__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 (173) hide show
  1. python_etcd3_async-0.2.0/.cnb.yml +127 -0
  2. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/.gitignore +1 -0
  3. python_etcd3_async-0.2.0/AGENTS.md +240 -0
  4. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/CLAUDE.md +58 -19
  5. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/PKG-INFO +1 -1
  6. python_etcd3_async-0.2.0/docs/en/getting-started/installation.md +73 -0
  7. python_etcd3_async-0.2.0/docs/en/guide/advanced_queries.md +78 -0
  8. python_etcd3_async-0.2.0/docs/en/guide/authentication.md +116 -0
  9. python_etcd3_async-0.2.0/docs/en/guide/cluster_maintenance.md +130 -0
  10. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/en/guide/configuration.md +52 -0
  11. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/en/guide/leader_election.md +1 -1
  12. python_etcd3_async-0.2.0/docs/en/guide/leases_locks.md +189 -0
  13. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/en/guide/service_discovery.md +1 -1
  14. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/en/guide/service_registration.md +1 -1
  15. python_etcd3_async-0.2.0/docs/en/guide/sync_client.md +145 -0
  16. python_etcd3_async-0.2.0/docs/en/guide/transactions.md +136 -0
  17. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/en/guide/usage.md +13 -7
  18. python_etcd3_async-0.2.0/docs/en/guide/watching.md +145 -0
  19. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/en/index.md +6 -0
  20. python_etcd3_async-0.2.0/docs/en/operations/auto_reconnection.md +108 -0
  21. python_etcd3_async-0.2.0/docs/en/operations/error_handling.md +88 -0
  22. python_etcd3_async-0.2.0/docs/en/operations/failover.md +78 -0
  23. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/en/operations/monitoring.md +20 -13
  24. python_etcd3_async-0.2.0/docs/en/resources/contributing.md +46 -0
  25. python_etcd3_async-0.2.0/docs/en/resources/history.md +72 -0
  26. python_etcd3_async-0.2.0/docs/en/resources/releasing.md +65 -0
  27. python_etcd3_async-0.2.0/docs/zh/getting-started/installation.md +73 -0
  28. python_etcd3_async-0.2.0/docs/zh/guide/advanced_queries.md +77 -0
  29. python_etcd3_async-0.2.0/docs/zh/guide/authentication.md +112 -0
  30. python_etcd3_async-0.2.0/docs/zh/guide/cluster_maintenance.md +129 -0
  31. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/zh/guide/configuration.md +50 -0
  32. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/zh/guide/leader_election.md +1 -1
  33. python_etcd3_async-0.2.0/docs/zh/guide/leases_locks.md +182 -0
  34. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/zh/guide/service_discovery.md +1 -1
  35. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/zh/guide/service_registration.md +1 -1
  36. python_etcd3_async-0.2.0/docs/zh/guide/sync_client.md +142 -0
  37. python_etcd3_async-0.2.0/docs/zh/guide/transactions.md +133 -0
  38. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/zh/guide/usage.md +13 -7
  39. python_etcd3_async-0.2.0/docs/zh/guide/watching.md +140 -0
  40. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/zh/index.md +6 -0
  41. python_etcd3_async-0.2.0/docs/zh/operations/auto_reconnection.md +96 -0
  42. python_etcd3_async-0.2.0/docs/zh/operations/error_handling.md +80 -0
  43. python_etcd3_async-0.2.0/docs/zh/operations/failover.md +70 -0
  44. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/zh/operations/monitoring.md +20 -13
  45. python_etcd3_async-0.2.0/docs/zh/resources/contributing.md +45 -0
  46. python_etcd3_async-0.2.0/docs/zh/resources/history.md +63 -0
  47. python_etcd3_async-0.2.0/docs/zh/resources/releasing.md +63 -0
  48. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/__init__.py +3 -1
  49. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/async_client.py +21 -0
  50. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/async_client_extensions.py +132 -58
  51. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/async_endpoint.py +17 -3
  52. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/async_managers.py +37 -7
  53. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/async_multi_client.py +265 -110
  54. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/async_single_client.py +18 -3
  55. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/async_watch.py +125 -113
  56. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/client.py +20 -0
  57. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/endpoint.py +16 -0
  58. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/errors.py +99 -18
  59. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/locks.py +49 -13
  60. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/multi_client.py +260 -64
  61. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/shared.py +10 -62
  62. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/sync_cluster_operations.py +7 -2
  63. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/sync_kv_operations.py +53 -30
  64. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/utils.py +0 -35
  65. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/pyproject.toml +23 -1
  66. python_etcd3_async-0.2.0/scripts/etcd-test.sh +191 -0
  67. python_etcd3_async-0.2.0/tests/conftest.py +244 -0
  68. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_async_client.py +15 -35
  69. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_async_client_integration.py +19 -67
  70. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_async_error_handling.py +26 -42
  71. python_etcd3_async-0.2.0/tests/test_async_health_check.py +178 -0
  72. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_async_kv_operations_integration.py +14 -59
  73. python_etcd3_async-0.2.0/tests/test_async_watch.py +87 -0
  74. python_etcd3_async-0.2.0/tests/test_async_watch_cancel.py +90 -0
  75. python_etcd3_async-0.2.0/tests/test_auth.py +186 -0
  76. python_etcd3_async-0.2.0/tests/test_auth_integration.py +427 -0
  77. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_batch_operations.py +101 -38
  78. python_etcd3_async-0.2.0/tests/test_cluster_integration.py +69 -0
  79. python_etcd3_async-0.2.0/tests/test_endpoint_selection.py +201 -0
  80. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_kv_operations.py +75 -74
  81. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_lease_keepalive_retry.py +79 -35
  82. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_leases_integration.py +36 -58
  83. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_locks.py +41 -0
  84. python_etcd3_async-0.2.0/tests/test_locks_integration.py +100 -0
  85. python_etcd3_async-0.2.0/tests/test_maintenance_integration.py +40 -0
  86. python_etcd3_async-0.2.0/tests/test_return_semantics.py +170 -0
  87. python_etcd3_async-0.2.0/tests/test_sync_health_check.py +245 -0
  88. python_etcd3_async-0.2.0/tests/test_tls_integration.py +58 -0
  89. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_transactions_integration.py +24 -74
  90. python_etcd3_async-0.2.0/tests/test_watch_integration.py +117 -0
  91. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/uv.lock +0 -1
  92. python_etcd3_async-0.1.5/AGENTS.md +0 -172
  93. python_etcd3_async-0.1.5/docs/en/getting-started/installation.md +0 -35
  94. python_etcd3_async-0.1.5/docs/en/guide/advanced_queries.md +0 -43
  95. python_etcd3_async-0.1.5/docs/en/operations/auto_reconnection.md +0 -34
  96. python_etcd3_async-0.1.5/docs/en/operations/error_handling.md +0 -37
  97. python_etcd3_async-0.1.5/docs/en/operations/failover.md +0 -54
  98. python_etcd3_async-0.1.5/docs/en/resources/contributing.md +0 -37
  99. python_etcd3_async-0.1.5/docs/en/resources/history.md +0 -18
  100. python_etcd3_async-0.1.5/docs/zh/getting-started/installation.md +0 -35
  101. python_etcd3_async-0.1.5/docs/zh/guide/advanced_queries.md +0 -43
  102. python_etcd3_async-0.1.5/docs/zh/operations/auto_reconnection.md +0 -34
  103. python_etcd3_async-0.1.5/docs/zh/operations/error_handling.md +0 -37
  104. python_etcd3_async-0.1.5/docs/zh/operations/failover.md +0 -54
  105. python_etcd3_async-0.1.5/docs/zh/resources/contributing.md +0 -37
  106. python_etcd3_async-0.1.5/docs/zh/resources/history.md +0 -18
  107. python_etcd3_async-0.1.5/tests/test_auth.py +0 -86
  108. python_etcd3_async-0.1.5/tests/test_auth_integration.py +0 -291
  109. python_etcd3_async-0.1.5/tests/test_locks_integration.py +0 -147
  110. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/.coverage +0 -0
  111. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/.pylintrc +0 -0
  112. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/.python-version +0 -0
  113. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/README.md +0 -0
  114. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/README_CN.md +0 -0
  115. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/SKILL.md +0 -0
  116. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/assets/stylesheets/extra.css +0 -0
  117. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/en/operations/async_error_handling.md +0 -0
  118. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/en/operations/observability.md +0 -0
  119. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/en/resources/authors.md +0 -0
  120. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/superpowers/plans/2026-04-03-high-cohesion-low-coupling-plan.md +0 -0
  121. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/superpowers/specs/2026-04-03-high-cohesion-low-coupling-design.md +0 -0
  122. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/zh/operations/async_error_handling.md +0 -0
  123. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/zh/operations/observability.md +0 -0
  124. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/docs/zh/resources/authors.md +0 -0
  125. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/async_config.py +0 -0
  126. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/async_monitor.py +0 -0
  127. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/auth.py +0 -0
  128. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/config.py +0 -0
  129. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/events.py +0 -0
  130. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/exceptions.py +0 -0
  131. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/exporters/__init__.py +0 -0
  132. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/exporters/prometheus.py +0 -0
  133. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/leases.py +0 -0
  134. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/log.py +0 -0
  135. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/log_formatters.py +0 -0
  136. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/log_sampling.py +0 -0
  137. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/members.py +0 -0
  138. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/observability.py +0 -0
  139. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/single_client.py +0 -0
  140. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/sync_lock_operations.py +0 -0
  141. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/sync_maintenance_operations.py +0 -0
  142. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/trace.py +0 -0
  143. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/trace_attributes.py +0 -0
  144. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/transactions.py +0 -0
  145. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcd3/watch.py +0 -0
  146. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcdrpc/__init__.py +0 -0
  147. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcdrpc/auth.proto +0 -0
  148. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcdrpc/kv.proto +0 -0
  149. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/etcdrpc/rpc.proto +0 -0
  150. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/examples/leader_election.py +0 -0
  151. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/examples/leader_election_sync.py +0 -0
  152. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/examples/service_discovery.py +0 -0
  153. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/examples/service_discovery_sync.py +0 -0
  154. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/examples/service_discovery_v2.py +0 -0
  155. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/examples/service_registration.py +0 -0
  156. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/examples/service_registration_sync.py +0 -0
  157. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/examples/service_registry_v2.py +0 -0
  158. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/gen_protos.py +0 -0
  159. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/mkdocs.yml +0 -0
  160. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/__init__.py +0 -0
  161. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_async_managers.py +0 -0
  162. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_client.py +0 -0
  163. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_events.py +0 -0
  164. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_leases.py +0 -0
  165. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_log_formatters.py +0 -0
  166. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_log_sampling.py +0 -0
  167. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_members.py +0 -0
  168. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_observability_integration.py +0 -0
  169. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_trace_attributes.py +0 -0
  170. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_transactions.py +0 -0
  171. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_utils.py +0 -0
  172. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/tests/test_watch.py +0 -0
  173. {python_etcd3_async-0.1.5 → python_etcd3_async-0.2.0}/usage_guide.md +0 -0
@@ -0,0 +1,127 @@
1
+ # CNB pipeline for python-etcd3-async (Phase-2 M5).
2
+ #
3
+ # Four pipelines run on every push (and reuse the same list for pull requests):
4
+ # lint - ruff check + format gate (fast, always required)
5
+ # unit - pytest -m unit, no etcd needed
6
+ # integration - spins an etcd container via dind, runs pytest -m integration
7
+ # and reports etcd3 / etcdrpc coverage as separate numbers
8
+ # auth - opt-in auth-forced etcd instance (allowFailure: best-effort)
9
+ #
10
+ # The etcd image is pinned to match scripts/etcd-test.sh. Integration-only
11
+ # opt-in suites (member round-trip, restart, TLS) stay off here: they require
12
+ # extra containers/env and remain local via scripts/etcd-test.sh.
13
+ #
14
+ # Coverage policy: report-only, no --cov-fail-under gate (we track regressions,
15
+ # we do not chase the number). Baselines are recorded in the integration job.
16
+
17
+ $:
18
+ push: &pipelines
19
+ - name: lint
20
+ docker:
21
+ image: python:3.12
22
+ env:
23
+ PIP_INDEX_URL: https://mirrors.cloud.tencent.com/pypi/simple
24
+ # Overrides the tsinghua default index from pyproject (mirrors.tuna
25
+ # rejects CNB build-node traffic); Tencent mirror is same-region.
26
+ UV_DEFAULT_INDEX: https://mirrors.cloud.tencent.com/pypi/simple
27
+ stages:
28
+ - name: setup
29
+ script:
30
+ - python -m pip install --quiet --no-cache-dir uv
31
+ - uv sync
32
+ - name: ruff check
33
+ script:
34
+ - uv run --no-sync ruff check etcd3/ etcdrpc/ tests/
35
+ - name: ruff format
36
+ script:
37
+ - uv run --no-sync ruff format --check etcd3/ etcdrpc/ tests/
38
+
39
+ - name: unit
40
+ docker:
41
+ image: python:3.12
42
+ env:
43
+ PIP_INDEX_URL: https://mirrors.cloud.tencent.com/pypi/simple
44
+ UV_DEFAULT_INDEX: https://mirrors.cloud.tencent.com/pypi/simple
45
+ stages:
46
+ - name: setup
47
+ script:
48
+ - python -m pip install --quiet --no-cache-dir uv
49
+ - uv sync
50
+ - name: pytest
51
+ script:
52
+ - uv run --no-sync pytest -m unit
53
+
54
+ - name: integration
55
+ services:
56
+ - docker
57
+ docker:
58
+ image: python:3.12
59
+ env:
60
+ ETCD_TEST_IMAGE: public.ecr.aws/eks-distro/etcd-io/etcd:v3.6.12-eks-1-37-latest
61
+ PIP_INDEX_URL: https://mirrors.cloud.tencent.com/pypi/simple
62
+ UV_DEFAULT_INDEX: https://mirrors.cloud.tencent.com/pypi/simple
63
+ stages:
64
+ - name: setup
65
+ script:
66
+ - python -m pip install --quiet --no-cache-dir uv
67
+ - uv sync
68
+ - name: start etcd
69
+ script:
70
+ - >-
71
+ docker run -d --rm --name etcd-ci -p 2379:2379 "$ETCD_TEST_IMAGE"
72
+ etcd --name ci
73
+ --listen-client-urls http://0.0.0.0:2379
74
+ --advertise-client-urls http://localhost:2379
75
+ --watch-progress-notify-interval=1s
76
+ - for i in $(seq 1 30); do docker exec etcd-ci etcdctl endpoint health >/dev/null 2>&1 && break; sleep 2; done
77
+ - docker exec etcd-ci etcdctl endpoint health
78
+ - name: pytest + coverage
79
+ script:
80
+ - ETCD_ENDPOINT=localhost:2379 uv run --no-sync pytest -m integration --cov=etcd3 --cov=etcdrpc
81
+ # Coverage floor is report-only; refresh these numbers when they move
82
+ # intentionally. The gate is a human reading this, not a hard fail.
83
+ # Baselines below are for THIS integration job only (unit tests run in
84
+ # the `unit` job and do not add to these figures).
85
+ # etcd3: 56% (floor; combined with unit job the local full-suite
86
+ # figure is higher — stage target is 70% full-suite)
87
+ # etcdrpc: 36% (generated protobuf code, tracked but not gated)
88
+ - name: coverage report
89
+ script:
90
+ - uv run --no-sync coverage report --include="etcd3/*" -m
91
+ - uv run --no-sync coverage report --include="etcdrpc/*" -m
92
+
93
+ - name: auth
94
+ allowFailure: true
95
+ services:
96
+ - docker
97
+ docker:
98
+ image: python:3.12
99
+ env:
100
+ ETCD_TEST_IMAGE: public.ecr.aws/eks-distro/etcd-io/etcd:v3.6.12-eks-1-37-latest
101
+ PIP_INDEX_URL: https://mirrors.cloud.tencent.com/pypi/simple
102
+ UV_DEFAULT_INDEX: https://mirrors.cloud.tencent.com/pypi/simple
103
+ stages:
104
+ - name: setup
105
+ script:
106
+ - python -m pip install --quiet --no-cache-dir uv
107
+ - uv sync
108
+ - name: start auth etcd
109
+ script:
110
+ - >-
111
+ docker run -d --rm --name etcd-auth -p 22379:2379 "$ETCD_TEST_IMAGE"
112
+ etcd --name auth
113
+ --listen-client-urls http://0.0.0.0:2379
114
+ --advertise-client-urls http://localhost:2379
115
+ - for i in $(seq 1 30); do docker exec etcd-auth etcdctl endpoint health >/dev/null 2>&1 && break; sleep 2; done
116
+ - docker exec etcd-auth etcdctl user add "root:etcd-test-root-pw"
117
+ - docker exec etcd-auth etcdctl user grant-role root root
118
+ - docker exec etcd-auth etcdctl auth enable
119
+ - name: pytest -m auth
120
+ script:
121
+ - >-
122
+ ETCD_ENDPOINT=localhost:22379
123
+ ETCD_AUTH_ENDPOINT=localhost:22379
124
+ ETCD_AUTH_USER=root
125
+ ETCD_AUTH_PASSWORD=etcd-test-root-pw
126
+ uv run --no-sync pytest -m auth
127
+ pull_request: *pipelines
@@ -15,3 +15,4 @@ htmlcov/
15
15
  # Virtual environments
16
16
  .venv
17
17
  /site
18
+ .etcd-tls/
@@ -0,0 +1,240 @@
1
+ # AGENTS.md - python-etcd3-async
2
+
3
+ AI coding agent guidelines for the python-etcd3-async project.
4
+
5
+ ## Project Overview
6
+ - **Name**: python-etcd3-async
7
+ - **Python**: 3.12+
8
+ - **Package Manager**: uv
9
+ - **License**: Apache 2.0
10
+
11
+ ## Build & Development Commands
12
+
13
+ ### Setup
14
+ ```bash
15
+ uv sync # Install dependencies (dev group included by default)
16
+ uv pip install -e . # Editable install
17
+ ```
18
+
19
+ ### Testing
20
+ ```bash
21
+ # Unit tests only — no etcd needed; integration tests auto-skip when no endpoint is configured
22
+ uv run pytest -m "not integration"
23
+
24
+ # Full suite — requires a running etcd (integration tests FAIL if an endpoint
25
+ # is configured but unreachable; that is intentional)
26
+ ./scripts/etcd-test.sh up # podman: etcd-test @ localhost:12379
27
+ ETCD_ENDPOINT=localhost:12379 uv run pytest # all 396 tests
28
+ ETCD_ENDPOINT=localhost:12379 uv run pytest -m integration # integration only
29
+ ETCD_ENDPOINT=localhost:12379 uv run pytest --cov=etcd3 --cov=etcdrpc
30
+ ETCD_ENDPOINT=localhost:12379 uv run pytest -v -k "test_name" # Run tests matching pattern
31
+
32
+ # Restart-automated tests (opt-in; restarts the etcd-test container mid-run)
33
+ ETCD_ENDPOINT=localhost:12379 ETCD_TEST_RESTART=1 uv run pytest -m restart
34
+ ./scripts/etcd-test.sh restart [primary|auth] # manual restart helper
35
+
36
+ # Member add/remove round-trip (opt-in; mutates cluster membership via a learner)
37
+ ETCD_ENDPOINT=localhost:12379 ETCD_TEST_MEMBER=1 uv run pytest tests/test_cluster_integration.py
38
+
39
+ # Auth-enabled instance (optional; tests/test_auth_integration.py routes through it)
40
+ ./scripts/etcd-test.sh up-auth
41
+ ETCD_ENDPOINT=localhost:12379 ETCD_AUTH_ENDPOINT=localhost:22379 \
42
+ ETCD_AUTH_USER=root ETCD_AUTH_PASSWORD=etcd-test-root-pw uv run pytest tests/test_auth_integration.py
43
+
44
+ # TLS+auth instance (optional; tests/test_tls_integration.py; certs auto-generated)
45
+ ./scripts/etcd-test.sh up-tls
46
+ ETCD_TLS_ENDPOINT=localhost:24379 uv run pytest tests/test_tls_integration.py
47
+
48
+ ./scripts/etcd-test.sh down # remove all test containers + cert dir
49
+ ```
50
+ - Integration tests write keys under a per-session random prefix cleaned up automatically
51
+ - Shared fixtures/markers live in `tests/conftest.py` (`etcd`, `key_prefix`, `etcd_auth`, `etcd_tls`)
52
+
53
+ ### CI
54
+ - Pipeline: `.cnb.yml` (CNB), runs on push / pull_request (any branch):
55
+ - `lint`: `ruff check` + `ruff format --check`
56
+ - `unit`: `pytest -m unit` (no etcd)
57
+ - `integration`: etcd container via dind (same pinned image as `etcd-test.sh`) → `pytest -m integration` + etcd3/etcdrpc coverage
58
+ - `auth`: auth-enforced etcd instance, `pytest -m auth` (allowFailure)
59
+ - Member/restart/TLS suites stay local-only (opt-in env gates, see `scripts/etcd-test.sh`)
60
+ - Coverage is report-only (no fail-under gate); baselines recorded in `.cnb.yml` comments
61
+
62
+ ### Linting
63
+ ```bash
64
+ uv run ruff check etcd3/ etcdrpc/ # Check (etcdrpc/ is excluded as generated)
65
+ uv run ruff format etcd3/ etcdrpc/ # Format
66
+ ```
67
+
68
+ ### Other
69
+ ```bash
70
+ uv run python gen_protos.py # Regenerate protobuf
71
+ uv run mkdocs serve # Serve docs
72
+ ```
73
+
74
+ ### Versioning
75
+ - Single source of truth: `__version__` in `etcd3/__init__.py`
76
+ - `pyproject.toml` uses `dynamic = ["version"]` via
77
+ `[tool.hatch.version] path = "etcd3/__init__.py"` — never add a static
78
+ `version` back; `uv.lock` refreshes automatically on the next `uv lock`
79
+ - Release checklist: `docs/en/resources/releasing.md`
80
+
81
+ ## Code Style
82
+
83
+ ### Formatting
84
+ - **Max line length**: 100 characters
85
+ - **Formatter**: ruff
86
+ - Follow PEP 8
87
+ - 4 spaces for indentation (no tabs)
88
+ - Trailing commas for multi-line structures
89
+ - Single blank line between top-level definitions
90
+
91
+ ### Naming
92
+ - Classes: `PascalCase` (e.g., `AsyncEtcd3Client`)
93
+ - Functions/Methods: `snake_case` (e.g., `async_client()`, `get_prefix()`)
94
+ - Constants: `UPPER_CASE` (e.g., `FILTER_PUT`)
95
+ - Private: `_leading_underscore` (e.g., `_get_channel()`)
96
+ - Protected: `_leading_underscore` for internal use
97
+ - Type variables: `PascalCase` (e.g., `T`, `TKey`, `TValue`)
98
+
99
+ ### Imports (3 groups, sorted)
100
+ 1. Standard library (`asyncio`, `functools`, `typing`, etc.)
101
+ 2. Third-party (`grpc`, `etcdrpc`, `pytest`)
102
+ 3. Internal modules (`etcd3.*`, `etcdrpc.*`)
103
+ - Use absolute imports within package
104
+ - Sort alphabetically within groups
105
+
106
+ ### Type Hints
107
+ - Use type hints for all parameters and returns
108
+ - Use `Optional[]` for nullable types, `Literal[]` for enums
109
+ - Use `|` syntax for unions (e.g., `str | bytes`)
110
+ - Use `X | None` over `Optional[X]`
111
+ - Generics: `list[int]`, `dict[str, Any]` over `List[int]`, `Dict[str, Any]`
112
+
113
+ ### Docstrings
114
+ - Google-style
115
+ - Document all public methods/classes
116
+ - Include type info in params and returns
117
+ - First line should be a concise summary
118
+ - Use `:raises:` for exceptions that may be raised
119
+
120
+ ### Error Handling
121
+ - Use custom exceptions from `etcd3/exceptions.py`:
122
+ - `Etcd3Exception` (base), `ConnectionFailedError`, `ConnectionTimeoutError`
123
+ - `RevisionCompactedError`, `LockReleaseError`, `BatchOperationError`
124
+ - Decorators: `_handle_errors`, `_async_handle_errors`
125
+ - Never catch broad `Exception` unless re-raising
126
+ - Always include context in error messages
127
+
128
+ ## Architecture
129
+
130
+ ### Dual Client Design
131
+ - **Sync**: `Etcd3Client` in `etcd3/single_client.py` (extends
132
+ `MultiEndpointEtcd3Client` in `etcd3/multi_client.py`, which also defines `Endpoint`)
133
+ - **Async**: `AsyncEtcd3Client` in `etcd3/async_single_client.py` (extends
134
+ `AsyncMultiEndpointEtcd3Client` in `etcd3/async_multi_client.py`; `AsyncEndpoint`
135
+ lives in `etcd3/async_endpoint.py`)
136
+ - `etcd3/client.py` / `etcd3/async_client.py` are compatibility shims re-exporting the above
137
+ - Factories: `client()` (sync, single endpoint) and `async_client()` (async, single endpoint)
138
+
139
+ ### Extension Pattern (`etcd3/async_client_extensions.py`)
140
+ - `AsyncKVOperations`, `AsyncLeaseOperations`, `AsyncLockOperations`
141
+ - `AsyncClusterOperations`, `AsyncMaintenanceOperations`, `AsyncWatchOperations`
142
+
143
+ ### Load Balancing
144
+ - `LoadBalancerStrategy` enum: `PRIORITY`, `ROUND_ROBIN`, `RANDOM`, `STICKY`
145
+
146
+ ### Package Structure
147
+ ```
148
+ etcd3/
149
+ __init__.py # Public exports (client, async_client, exceptions, etc.)
150
+ single_client.py # Etcd3Client (sync single-endpoint)
151
+ multi_client.py # MultiEndpointEtcd3Client + sync Endpoint
152
+ async_single_client.py # AsyncEtcd3Client (async single-endpoint)
153
+ async_multi_client.py # AsyncMultiEndpointEtcd3Client (main async client)
154
+ async_endpoint.py # AsyncEndpoint (async gRPC channel handler)
155
+ client.py # Compat shim (re-exports sync clients)
156
+ async_client.py # Compat shim (re-exports async clients)
157
+ async_client_extensions.py # Async operation mixins (KV/Lease/Lock/Cluster/Maintenance/Watch)
158
+ sync_kv_operations.py # Sync KV operations mixin
159
+ sync_lock_operations.py # Sync lock operations mixin
160
+ sync_cluster_operations.py # Sync cluster operations mixin
161
+ sync_maintenance_operations.py # Sync maintenance operations mixin
162
+ async_managers.py # Async watch/cluster/maintenance managers
163
+ config.py # ClientConfig, MultiEndpointConfig, RangeRequestConfig
164
+ async_config.py # AsyncClientConfig, AsyncMultiEndpointConfig
165
+ endpoint.py # BaseEndpoint (shared fail-state / health logic)
166
+ errors.py # Error-handler decorator factories
167
+ exceptions.py # Custom exceptions
168
+ auth.py # AuthOperations / AsyncAuthOperations
169
+ leases.py # Lease / AsyncLease objects
170
+ locks.py # Lock / AsyncLock objects
171
+ watch.py # Sync watch builder + iterator
172
+ async_watch.py # Async watch builder + event iterator
173
+ events.py # PutEvent / DeleteEvent classes
174
+ transactions.py # Transaction compare/put/get builders
175
+ members.py # Member class for cluster representation
176
+ shared.py # Shared code between sync/async (Status, KVMetadata, ...)
177
+ utils.py # Key encoding, prefixes, filter constants, LoadBalancerStrategy
178
+ trace.py / trace_attributes.py / monitor.py / async_monitor.py # Tracing & monitoring
179
+ log.py / log_formatters.py / log_sampling.py # Logging
180
+ observability.py # ObservabilityConfig / configure_from_env / auto_init
181
+ exporters/ # Prometheus exporter
182
+ etcdrpc/ # Generated protobuf/gRPC code
183
+ tests/ # Test files
184
+ ```
185
+
186
+ ## Observability (Environment Variables)
187
+
188
+ All prefixed with `ETCD_OBS_`:
189
+
190
+ | Variable | Default | Description |
191
+ |----------|---------|-------------|
192
+ | `ETCD_OBS_AUTO_INIT` | true | Auto-init on client create |
193
+ | `ETCD_OBS_LOG_LEVEL` | INFO | DEBUG/INFO/WARNING/ERROR |
194
+ | `ETCD_OBS_LOG_FORMAT` | text | text/json/colored |
195
+ | `ETCD_OBS_LOG_SAMPLE_RATE` | 1.0 | 0.0-1.0 |
196
+ | `ETCD_OBS_LOG_SAMPLE_STRATEGY` | always | always/rate/dynamic/level_based |
197
+ | `ETCD_OBS_TRACING_ENABLED` | true | Enable OpenTelemetry |
198
+ | `ETCD_OBS_TRACING_SERVICE_NAME` | etcd3-client | Service name |
199
+ | `ETCD_OBS_TRACING_SAMPLE_RATE` | 1.0 | Trace sampling |
200
+ | `ETCD_OBS_OTLP_ENDPOINT` | - | OTLP collector endpoint |
201
+ | `ETCD_OBS_PROMETHEUS_ENABLED` | false | Enable Prometheus |
202
+
203
+ ## Testing
204
+ - Plain `uv run pytest` never fails for lack of etcd: integration tests
205
+ auto-skip unless an endpoint is configured (`ETCD_ENDPOINT` or `ETCD_HOST`/`ETCD_PORT`)
206
+ - Configured endpoint unreachable => integration tests fail (intentional signal)
207
+ - Markers: `-m unit` / `-m integration` / `-m auth`; shared fixtures in `tests/conftest.py`
208
+ - Local test server: `./scripts/etcd-test.sh up` (podman, localhost:12379)
209
+ - Integration keys live under a per-session random prefix, cleaned automatically
210
+ - Test files: `tests/test_*.py`
211
+
212
+ ## Common Patterns
213
+ ```python
214
+ # Async client (auto-initializes observability)
215
+ from etcd3 import async_client
216
+ etcd = await async_client(host='localhost', port=2379)
217
+
218
+ # Sync client
219
+ from etcd3 import client
220
+ etcd = client(host='localhost', port=2379)
221
+
222
+ # Manual observability config
223
+ from etcd3 import ObservabilityConfig, configure_from_env
224
+ config = configure_from_env()
225
+ config.apply()
226
+
227
+ # Context tracking
228
+ from etcd3 import get_request_context
229
+ async with etcd.context(operation="batch_put", source="worker_1"):
230
+ await etcd.kv.batch_put(items)
231
+ ```
232
+
233
+ ## Security
234
+ - TLS, username/password, token-based auth supported
235
+ - Never commit credentials
236
+
237
+ ## References
238
+ - [python-etcd3](https://github.com/kragniz/python-etcd3)
239
+ - [Go etcd client v3](https://pkg.go.dev/go.etcd.io/etcd/client/v3)
240
+ - [etcd docs](https://etcd.io/docs/)
@@ -10,12 +10,9 @@ python-etcd3-async is an async Python client library for the etcd v3 API using `
10
10
 
11
11
  ### Development Setup
12
12
  ```bash
13
- # Install dependencies
13
+ # Install dependencies (includes the dev group)
14
14
  uv sync
15
15
 
16
- # Install dev dependencies
17
- uv sync --extra dev
18
-
19
16
  # Run Python commands
20
17
  uv run python <script.py>
21
18
 
@@ -25,22 +22,55 @@ uv pip install -e .
25
22
 
26
23
  ### Testing
27
24
  ```bash
28
- # Run all tests (requires ETCD_ENDPOINT environment variable)
29
- ETCD_ENDPOINT=127.0.0.1:2379 uv run pytest
25
+ # Unit tests only — no etcd needed; integration tests auto-skip without an endpoint
26
+ uv run pytest -m "not integration"
27
+
28
+ # Start the local test etcd (podman, localhost:12379)
29
+ ./scripts/etcd-test.sh up
30
+
31
+ # Run all tests against it
32
+ ETCD_ENDPOINT=localhost:12379 uv run pytest
30
33
 
31
34
  # Run a single test file
32
- ETCD_ENDPOINT=192.168.31.15:12379 uv run pytest tests/test_async_client.py
35
+ ETCD_ENDPOINT=localhost:12379 uv run pytest tests/test_async_client.py
33
36
 
34
37
  # Run a specific test method
35
- ETCD_ENDPOINT=192.168.31.15:12379 uv run pytest tests/test_locks.py::TestAsyncLock::test_lock_acquire -v
38
+ ETCD_ENDPOINT=localhost:12379 uv run pytest tests/test_locks.py::TestAsyncLock::test_async_lock_acquire_succeeds_after_wait -v
39
+
40
+ # Integration tests only / unit tests only
41
+ ETCD_ENDPOINT=localhost:12379 uv run pytest -m integration
42
+ uv run pytest -m unit
43
+
44
+ # Restart-automated tests (opt-in; restarts the etcd-test container mid-run)
45
+ ETCD_ENDPOINT=localhost:12379 ETCD_TEST_RESTART=1 uv run pytest -m restart
46
+ ./scripts/etcd-test.sh restart [primary|auth]
47
+
48
+ # Member add/remove round-trip (opt-in; mutates cluster membership via a learner)
49
+ ETCD_ENDPOINT=localhost:12379 ETCD_TEST_MEMBER=1 uv run pytest tests/test_cluster_integration.py
36
50
 
37
51
  # Run with coverage
38
- ETCD_ENDPOINT=192.168.31.15:12379 uv run pytest --cov=etcd3 --cov=etcdrpc
52
+ ETCD_ENDPOINT=localhost:12379 uv run pytest --cov=etcd3 --cov=etcdrpc
53
+
54
+ # Auth-enabled instance (optional)
55
+ ./scripts/etcd-test.sh up-auth
56
+ ETCD_ENDPOINT=localhost:12379 ETCD_AUTH_ENDPOINT=localhost:22379 \
57
+ ETCD_AUTH_USER=root ETCD_AUTH_PASSWORD=etcd-test-root-pw uv run pytest tests/test_auth_integration.py
39
58
 
40
- # Run with verbose output
41
- ETCD_ENDPOINT=192.168.31.15:12379 uv run pytest -v
59
+ # TLS+auth instance (optional; certs generated into .etcd-tls/)
60
+ ./scripts/etcd-test.sh up-tls
61
+ ETCD_TLS_ENDPOINT=localhost:24379 uv run pytest tests/test_tls_integration.py
42
62
  ```
43
63
 
64
+ ### CI (`.cnb.yml`)
65
+ Runs on push / pull_request (any branch):
66
+ - `lint` — ruff check + format --check
67
+ - `unit` — `pytest -m unit` (no etcd)
68
+ - `integration` — etcd via dind (pinned image matches etcd-test.sh) → `pytest -m integration` + etcd3/etcdrpc coverage
69
+ - `auth` — auth-enforced etcd, `pytest -m auth` (allowFailure)
70
+
71
+ Member/restart/TLS suites stay local-only (opt-in env gates). Coverage is
72
+ report-only (no fail-under gate); baselines are recorded in `.cnb.yml`.
73
+
44
74
  ### Linting & Formatting
45
75
  ```bash
46
76
  # Check with ruff
@@ -48,9 +78,6 @@ uv run ruff check etcd3/ etcdrpc/
48
78
 
49
79
  # Format with ruff
50
80
  uv run ruff format etcd3/ etcdrpc/
51
-
52
- # Run pylint
53
- pylint etcd3/ etcdrpc/
54
81
  ```
55
82
 
56
83
  ### Protobuf Generation
@@ -63,7 +90,12 @@ uv run python gen_protos.py
63
90
  ### Environment Variables
64
91
  | Variable | Description |
65
92
  |----------|-------------|
66
- | `ETCD_ENDPOINT` | etcd server address for testing (e.g., 192.168.31.15:12379) |
93
+ | `ETCD_ENDPOINT` | etcd server address for testing (e.g., localhost:12379 via scripts/etcd-test.sh) |
94
+ | `ETCD_AUTH_ENDPOINT` | optional auth-enabled etcd address (e.g., localhost:22379) |
95
+ | `ETCD_AUTH_USER` / `ETCD_AUTH_PASSWORD` | credentials for the auth-enabled test instance |
96
+ | `ETCD_TLS_ENDPOINT` | TLS+auth etcd address for testing (e.g., localhost:24379 via scripts/etcd-test.sh up-tls) |
97
+ | `ETCD_TLS_CA` | CA cert path used to verify the TLS server (default: `.etcd-tls/ca.crt`) |
98
+ | `ETCD_TLS_USER` / `ETCD_TLS_PASSWORD` | credentials for the TLS+auth test instance |
67
99
  | `ETCD_OBS_LOG_LEVEL` | Logging level (DEBUG, INFO, WARNING, ERROR) |
68
100
  | `ETCD_OBS_LOG_FORMAT` | Log format (text, json, colored) |
69
101
  | `ETCD_OBS_TRACING_ENABLED` | Enable OpenTelemetry tracing |
@@ -73,7 +105,7 @@ uv run python gen_protos.py
73
105
 
74
106
  1. Use `uv` for Python environment management, `hatchling` as build tool
75
107
  2. Use `uv run python` to execute Python commands
76
- 3. Follow PEP8规范, use `pylint` for static analysis
108
+ 3. Follow PEP8; use `ruff` for linting and formatting (etcdrpc/ is excluded as generated)
77
109
  4. Use `pytest` and `pytest-asyncio` for sync and async tests
78
110
  5. Use context7 MCP to query official documentation
79
111
 
@@ -234,7 +266,11 @@ auto_init() # Auto-initialize at import time
234
266
  - Error handlers are created via `create_sync_error_handlers()` and `create_async_error_handlers()` factories from `errors.py`
235
267
  - `_sync_handle_keepalive_errors` / `_async_handle_keepalive_errors` - Lease keepalive with `propagate_errors=True`
236
268
 
237
- 3. **Endpoint failover:** `MultiEndpointEtcd3Client` tracks failed endpoints with retry on `failover=True`
269
+ 3. **Endpoint failover:** Both `MultiEndpointEtcd3Client` and
270
+ `AsyncMultiEndpointEtcd3Client` track failed endpoints. With
271
+ `failover=True` a failed endpoint is bypassed and another healthy
272
+ endpoint is picked; with `failover=False` the client stays on the
273
+ endpoint it holds and rebuilds that endpoint's channel in place.
238
274
 
239
275
  4. **Load balancing:** Multiple strategies supported via `LoadBalancerStrategy`:
240
276
  - `PRIORITY` (default): Keep current endpoint if healthy, failover on failure
@@ -242,11 +278,14 @@ auto_init() # Auto-initialize at import time
242
278
  - `RANDOM`: Random endpoint selection
243
279
  - `STICKY`: Maintain connection to the same endpoint
244
280
 
245
- 5. **Auto-reconnection:** Async has `_get_channel()`, sync has matching implementation with health check
281
+ 5. **Auto-reconnection:** Async has `_get_channel()` / `_acquire_channel()`,
282
+ sync has a matching `_get_channel()`, both with a background health probe
246
283
 
247
284
  6. **Lazy authentication:** Async uses `_init_auth()` with `asyncio.Lock()`, sync initializes in `__init__`
248
285
 
249
- 7. **Watch pattern:** Iterator + cancel function returns for both sync and async
286
+ 7. **Watch pattern:** Iterator + cancel function returns for both sync and
287
+ async. Cancelling sends a real cancel request to the server and unblocks
288
+ a pending iteration.
250
289
 
251
290
  8. **Request context tracking:** Use `client.context()` to track operations across async tasks:
252
291
  ```python
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: python-etcd3-async
3
- Version: 0.1.5
3
+ Version: 0.2.0
4
4
  Summary: etcd3 async sdk
5
5
  Requires-Python: <4,>=3.12
6
6
  Requires-Dist: grpcio==1.76.0
@@ -0,0 +1,73 @@
1
+ ---
2
+ title: Installation
3
+ ---
4
+
5
+ # Installation
6
+
7
+ ## Stable Release
8
+
9
+ To install python-etcd3-async, run this command in your terminal:
10
+
11
+ ```bash
12
+ uv pip install python-etcd3-async
13
+ ```
14
+
15
+ This is the preferred method to install, as it will always install the most recent stable release.
16
+
17
+ ## From Sources
18
+
19
+ The sources for python-etcd3-async can be downloaded from the [CNB repo](https://cnb.cool/owasd/python3-etcd-async).
20
+
21
+ ```bash
22
+ git clone https://cnb.cool/owasd/python3-etcd-async.git
23
+ cd python3-etcd-async
24
+ uv pip install -e .
25
+ ```
26
+
27
+ ## Requirements
28
+
29
+ - Python 3.12+
30
+ - etcd v3 API server (v3.1+ recommended)
31
+
32
+ ## Virtual Environments with uv
33
+
34
+ For an application project managed by [uv](https://docs.astral.sh/uv/):
35
+
36
+ ```bash
37
+ uv init myapp && cd myapp
38
+ uv add python-etcd3-async
39
+ ```
40
+
41
+ uv creates and syncs `.venv` automatically; run code with `uv run python ...`.
42
+ With pip/venv instead:
43
+
44
+ ```bash
45
+ python -m venv .venv && source .venv/bin/activate
46
+ pip install python-etcd3-async
47
+ ```
48
+
49
+ ## Verify the Installation
50
+
51
+ ```bash
52
+ python -c "import etcd3; print(etcd3.__version__)"
53
+ ```
54
+
55
+ Then connect to a running etcd:
56
+
57
+ ```python
58
+ import asyncio
59
+ from etcd3 import async_client
60
+
61
+ async def main():
62
+ client = await async_client(host='localhost', port=2379)
63
+ print(await client.status())
64
+ await client.close()
65
+
66
+ asyncio.run(main())
67
+ ```
68
+
69
+ ## See Also
70
+
71
+ - [API Usage](../guide/usage.md)
72
+ - [Configuration](../guide/configuration.md)
73
+ - [Sync Client](../guide/sync_client.md)
@@ -0,0 +1,78 @@
1
+ ---
2
+ title: Advanced Queries
3
+ ---
4
+
5
+ # Advanced Queries
6
+
7
+ ## Range Options
8
+
9
+ Range options are passed as keyword arguments to any range-based call
10
+ (`get`, `get_prefix`, `get_range`, `get_all` and their `*_response` variants);
11
+ they map to the fields of `RangeRequestConfig`.
12
+
13
+ ```python
14
+ # Range options as keyword arguments
15
+ resp = await client.get_prefix_response(
16
+ '/app/',
17
+ limit=100, # Limit number of results
18
+ revision=5, # Historical revision
19
+ count_only=True, # Only return count
20
+ sort_order='ascend', # 'ascend' or 'descend'
21
+ sort_target='key', # 'key' | 'version' | 'create' | 'mod'
22
+ )
23
+ print(f'{resp.count} keys under /app/')
24
+
25
+ # Serializable read (hit the follower, lower latency)
26
+ value, meta = await client.get('/mykey', serializable=True)
27
+
28
+ # Keys only (no values transferred)
29
+ async for value, meta in client.get_prefix('/app/', keys_only=True):
30
+ print(meta.key)
31
+ ```
32
+
33
+ Other available options: `min_mod_revision`, `max_mod_revision`,
34
+ `min_create_revision`, `max_create_revision`.
35
+
36
+ ## Prefix Query
37
+
38
+ ```python
39
+ # Get all keys with prefix
40
+ async for value, meta in client.get_prefix('/app/'):
41
+ print(f'{meta.key}: {value}')
42
+ ```
43
+
44
+ ## Range Query
45
+
46
+ ```python
47
+ # Get keys in range [start, end)
48
+ async for value, meta in client.get_range('/start', '/end'):
49
+ print(f'{meta.key}: {value}')
50
+ ```
51
+
52
+ ## Batch Operations
53
+
54
+ The three batch entry points share one concurrency and failure policy:
55
+ items run in parallel, the concurrency ceiling is controlled by
56
+ `concurrency`, and a failing item never aborts the whole batch.
57
+
58
+ ```python
59
+ # Atomic transaction: all succeed or none do
60
+ count = await client.kv.batch_put([('/a', '1'), ('/b', '2')])
61
+
62
+ # Parallel execution: at most 10 at a time, failures only affect the count
63
+ count = await client.kv.batch_put(items, atomic=False, concurrency=10)
64
+
65
+ # Returns (success_count, [(failed_key, error), ...])
66
+ success, failed = await client.kv.put_many(items, concurrency=10)
67
+ ```
68
+
69
+ `batch_put` and `put_many` accept either an iterable of `(key, value)`
70
+ tuples or a mapping; `batch_delete` accepts an iterable of keys.
71
+
72
+ The effective ceiling for `concurrency` is the client's `max_concurrent`
73
+ (default 100): callers may lower it, never raise it.
74
+
75
+ ## See Also
76
+
77
+ - [API Usage](usage.md)
78
+ - [Configuration](configuration.md)