python-arango-async 1.2.3__tar.gz → 1.2.5__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 (114) hide show
  1. python_arango_async-1.2.5/.circleci/config.yml +207 -0
  2. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/.github/workflows/codeql.yaml +3 -3
  3. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/.github/workflows/docs.yaml +2 -2
  4. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/.github/workflows/pypi.yaml +2 -2
  5. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/PKG-INFO +2 -2
  6. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/collection.py +62 -20
  7. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/replication.py +19 -3
  8. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/typings.py +12 -0
  9. python_arango_async-1.2.5/arangoasync/version.py +1 -0
  10. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/indexes.rst +39 -0
  11. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/pyproject.toml +1 -1
  12. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/python_arango_async.egg-info/PKG-INFO +2 -2
  13. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/python_arango_async.egg-info/SOURCES.txt +3 -0
  14. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/python_arango_async.egg-info/requires.txt +1 -1
  15. python_arango_async-1.2.5/python_arango_async.egg-info/scm_file_list.json +108 -0
  16. python_arango_async-1.2.5/python_arango_async.egg-info/scm_version.json +8 -0
  17. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/conftest.py +13 -0
  18. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_aql.py +5 -4
  19. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_collection.py +120 -8
  20. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_database.py +0 -15
  21. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_document.py +68 -0
  22. python_arango_async-1.2.5/tests/test_imports.py +21 -0
  23. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_typings.py +26 -0
  24. python_arango_async-1.2.3/.circleci/config.yml +0 -117
  25. python_arango_async-1.2.3/arangoasync/version.py +0 -1
  26. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/.gitignore +0 -0
  27. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/.pre-commit-config.yaml +0 -0
  28. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/.readthedocs.yaml +0 -0
  29. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/CONTRIBUTING.md +0 -0
  30. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/LICENSE +0 -0
  31. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/README.md +0 -0
  32. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/__init__.py +0 -0
  33. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/aql.py +0 -0
  34. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/auth.py +0 -0
  35. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/backup.py +0 -0
  36. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/client.py +0 -0
  37. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/cluster.py +0 -0
  38. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/compression.py +0 -0
  39. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/connection.py +0 -0
  40. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/cursor.py +0 -0
  41. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/database.py +0 -0
  42. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/errno.py +0 -0
  43. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/exceptions.py +0 -0
  44. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/executor.py +0 -0
  45. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/foxx.py +0 -0
  46. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/graph.py +0 -0
  47. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/http.py +0 -0
  48. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/job.py +0 -0
  49. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/logger.py +0 -0
  50. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/request.py +0 -0
  51. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/resolver.py +0 -0
  52. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/response.py +0 -0
  53. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/result.py +0 -0
  54. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/arangoasync/serialization.py +0 -0
  55. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/admin.rst +0 -0
  56. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/analyzer.rst +0 -0
  57. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/aql.rst +0 -0
  58. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/async.rst +0 -0
  59. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/authentication.rst +0 -0
  60. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/backup.rst +0 -0
  61. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/certificates.rst +0 -0
  62. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/cluster.rst +0 -0
  63. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/collection.rst +0 -0
  64. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/compression.rst +0 -0
  65. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/conf.py +0 -0
  66. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/cursor.rst +0 -0
  67. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/database.rst +0 -0
  68. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/document.rst +0 -0
  69. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/errno.rst +0 -0
  70. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/errors.rst +0 -0
  71. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/foxx.rst +0 -0
  72. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/graph.rst +0 -0
  73. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/helpers.rst +0 -0
  74. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/http.rst +0 -0
  75. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/index.rst +0 -0
  76. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/logging.rst +0 -0
  77. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/migration.rst +0 -0
  78. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/overview.rst +0 -0
  79. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/serialization.rst +0 -0
  80. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/specs.rst +0 -0
  81. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/static/logo.png +0 -0
  82. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/task.rst +0 -0
  83. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/transaction.rst +0 -0
  84. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/user.rst +0 -0
  85. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/docs/view.rst +0 -0
  86. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/python_arango_async.egg-info/dependency_links.txt +0 -0
  87. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/python_arango_async.egg-info/top_level.txt +0 -0
  88. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/setup.cfg +0 -0
  89. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/setup.py +0 -0
  90. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/starter.sh +0 -0
  91. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/__init__.py +0 -0
  92. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/helpers.py +0 -0
  93. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/static/cluster-3.12.conf +0 -0
  94. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/static/cluster-4.0.conf +0 -0
  95. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/static/keyfile +0 -0
  96. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/static/service.zip +0 -0
  97. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/static/single-3.12.conf +0 -0
  98. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/static/single-4.0.conf +0 -0
  99. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_analyzer.py +0 -0
  100. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_async.py +0 -0
  101. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_backup.py +0 -0
  102. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_client.py +0 -0
  103. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_cluster.py +0 -0
  104. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_compression.py +0 -0
  105. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_connection.py +0 -0
  106. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_cursor.py +0 -0
  107. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_foxx.py +0 -0
  108. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_graph.py +0 -0
  109. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_http.py +0 -0
  110. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_resolver.py +0 -0
  111. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_task.py +0 -0
  112. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_transaction.py +0 -0
  113. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_user.py +0 -0
  114. {python_arango_async-1.2.3 → python_arango_async-1.2.5}/tests/test_view.py +0 -0
@@ -0,0 +1,207 @@
1
+ version: 2.1
2
+
3
+ executors:
4
+ python-container: # used by lint and import-test jobs
5
+ parameters:
6
+ python_version:
7
+ type: string
8
+ default: "3.14"
9
+ docker:
10
+ - image: cimg/python:<< parameters.python_version >>
11
+ resource_class: arangodb/small-amd64
12
+ arangodb-container: # runs integration tests
13
+ parameters:
14
+ arangodb_image:
15
+ type: string
16
+ docker:
17
+ - image: << parameters.arangodb_image >>
18
+ entrypoint: /bin/sh
19
+ command: -c "while true; do sleep 10; done"
20
+ user: root
21
+ resource_class: arangodb/medium-amd64
22
+
23
+ workflows:
24
+ ci:
25
+ jobs:
26
+ - lint
27
+ - import-test:
28
+ name: Imports (Python << matrix.python_version >>)
29
+ matrix:
30
+ parameters:
31
+ python_version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
32
+ - integration-test:
33
+ name: ArangoDB (enterprise, 3.12 << matrix.arangodb_config >>)
34
+ matrix:
35
+ parameters:
36
+ arangodb_config: ["single", "cluster"]
37
+ arangodb_image: ["arangodb/enterprise:3.12"]
38
+ arangodb_conf_version: ["3.12"]
39
+ - integration-test:
40
+ name: ArangoDB (core-preview, 4.0-nightly << matrix.arangodb_config >>)
41
+ matrix:
42
+ parameters:
43
+ arangodb_config: [ "single", "cluster" ]
44
+ arangodb_image: [ "arangodb/core-preview:4.0-nightly" ]
45
+ arangodb_conf_version: [ "4.0" ]
46
+ - integration-test:
47
+ name: ArangoDB (enterprise-preview, 3.12-nightly << matrix.arangodb_config >>)
48
+ matrix:
49
+ parameters:
50
+ arangodb_config: ["single", "cluster"]
51
+ arangodb_image: ["arangodb/enterprise-preview:3.12-nightly"]
52
+ arangodb_conf_version: ["3.12"]
53
+
54
+ jobs:
55
+ lint:
56
+ executor: python-container
57
+ steps:
58
+ - checkout
59
+ - run:
60
+ name: Install Dependencies
61
+ command: python -m pip install .[dev]
62
+ - run:
63
+ name: Run black
64
+ command: black --check --verbose --diff --color --config=pyproject.toml ./arangoasync ./tests/
65
+ - run:
66
+ name: Run flake8
67
+ command: flake8 ./arangoasync ./tests
68
+ - run:
69
+ name: Run isort
70
+ command: isort --check ./arangoasync ./tests
71
+ - run:
72
+ name: Run mypy
73
+ command: mypy ./arangoasync
74
+ import-test:
75
+ parameters:
76
+ python_version:
77
+ type: string
78
+ executor:
79
+ name: python-container
80
+ python_version: << parameters.python_version >>
81
+ steps:
82
+ - checkout
83
+ - run:
84
+ name: Install Dependencies
85
+ command: |
86
+ python --version
87
+ python -m pip install .[dev]
88
+ - run:
89
+ name: Run Import Tests
90
+ command: python -m pytest tests/test_imports.py --skip-arango-setup --color=yes --code-highlight=yes
91
+ integration-test:
92
+ parameters:
93
+ arangodb_config:
94
+ type: string
95
+ arangodb_image:
96
+ type: string
97
+ arangodb_conf_version:
98
+ type: string
99
+ executor:
100
+ name: arangodb-container
101
+ arangodb_image: << parameters.arangodb_image >>
102
+ steps:
103
+ - run:
104
+ name: Bootstrap CI Tools
105
+ command: |
106
+ apk add --no-cache \
107
+ bash \
108
+ build-base \
109
+ ca-certificates \
110
+ curl \
111
+ git \
112
+ jq \
113
+ openssh-client \
114
+ openssl-dev \
115
+ py3-pip \
116
+ py3-virtualenv \
117
+ python3 \
118
+ python3-dev
119
+ - checkout
120
+ - run:
121
+ name: Setup ArangoDB
122
+ command: |
123
+ mkdir -p /tests
124
+ if [ ! -e /tests/static ]; then
125
+ ln -s "$PWD/tests/static" /tests/static
126
+ fi
127
+
128
+ conf_file="/tests/static/<< parameters.arangodb_config >>-<< parameters.arangodb_conf_version >>.conf"
129
+ if [ ! -f "$conf_file" ]; then
130
+ echo "Missing configuration file: $conf_file"
131
+ exit 1
132
+ fi
133
+
134
+ arangodb --configuration="$conf_file" > arangodb.log 2>&1 &
135
+ echo "$!" > arangodb.pid
136
+
137
+ ports="8529"
138
+ if [ << parameters.arangodb_config >> = "cluster" ]; then
139
+ ports="$ports 8539 8549"
140
+ fi
141
+
142
+ for port in $ports; do
143
+ echo "Waiting for ArangoDB at http://localhost:$port/_api/version"
144
+ for attempt in $(seq 1 120); do
145
+ if curl --fail --silent --show-error \
146
+ --user root:passwd \
147
+ "http://localhost:$port/_api/version" | jq; then
148
+ break
149
+ fi
150
+
151
+ if ! kill -0 "$(cat arangodb.pid)" 2>/dev/null; then
152
+ echo "ArangoDB process exited before port $port became ready"
153
+ exit 1
154
+ fi
155
+
156
+ if [ "$attempt" -eq 120 ]; then
157
+ echo "Timed out waiting for ArangoDB on port $port"
158
+ exit 1
159
+ fi
160
+
161
+ sleep 1
162
+ done
163
+ done
164
+ - run:
165
+ name: Show ArangoDB Logs
166
+ command: tail -n 1000 arangodb.log
167
+ when: always
168
+ - run:
169
+ name: Setup Python
170
+ shell: /bin/bash -eo pipefail
171
+ command: |
172
+ python3 --version
173
+ python3 -m virtualenv .venv
174
+ source .venv/bin/activate
175
+ python --version
176
+ python -m pip --version
177
+ - run:
178
+ name: Install Dependencies
179
+ shell: /bin/bash -eo pipefail
180
+ command: |
181
+ source .venv/bin/activate
182
+ python -m pip install -e .[dev]
183
+ - run:
184
+ name: Run pytest
185
+ shell: /bin/bash -eo pipefail
186
+ command: |
187
+ mkdir test-results
188
+ mkdir htmlcov
189
+
190
+ source .venv/bin/activate
191
+
192
+ args=("--junitxml=test-results/junit.xml" "--log-cli-level=DEBUG" "--host" "localhost" "--port=8529")
193
+ if [ << parameters.arangodb_config >> = "cluster" ]; then
194
+ args+=("--cluster" "--port=8539" "--port=8549")
195
+ fi
196
+
197
+ echo "Running pytest with args: ${args[@]}"
198
+ pytest tests --cov=arangoasync --cov-report=html:htmlcov --color=yes --code-highlight=yes "${args[@]}"
199
+ - store_artifacts:
200
+ path: htmlcov
201
+ destination: coverage-report
202
+ - store_artifacts:
203
+ path: test-results
204
+ - store_artifacts:
205
+ path: arangodb.log
206
+ - store_test_results:
207
+ path: test-results
@@ -14,10 +14,10 @@ jobs:
14
14
 
15
15
  steps:
16
16
  - name: Checkout repository
17
- uses: actions/checkout@v3
17
+ uses: actions/checkout@v7
18
18
 
19
19
  - name: Initialize CodeQL
20
- uses: github/codeql-action/init@v2
20
+ uses: github/codeql-action/init@v4
21
21
 
22
22
  - name: Perform CodeQL Analysis
23
- uses: github/codeql-action/analyze@v2
23
+ uses: github/codeql-action/analyze@v4
@@ -11,13 +11,13 @@ jobs:
11
11
 
12
12
  steps:
13
13
  - name: Checkout repository
14
- uses: actions/checkout@v4
14
+ uses: actions/checkout@v7
15
15
 
16
16
  - name: Fetch all tags and branches
17
17
  run: git fetch --prune --unshallow
18
18
 
19
19
  - name: Set up Python
20
- uses: actions/setup-python@v4
20
+ uses: actions/setup-python@v7
21
21
  with:
22
22
  python-version: '3.12'
23
23
 
@@ -10,12 +10,12 @@ jobs:
10
10
 
11
11
  steps:
12
12
  - name: Checkout repository
13
- uses: actions/checkout@v3
13
+ uses: actions/checkout@v7
14
14
  with:
15
15
  fetch-depth: 0
16
16
  fetch-tags: true
17
17
 
18
- - uses: actions/setup-python@v4
18
+ - uses: actions/setup-python@v7
19
19
  with:
20
20
  python-version: "3.12"
21
21
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-arango-async
3
- Version: 1.2.3
3
+ Version: 1.2.5
4
4
  Summary: Async Python Driver for ArangoDB
5
5
  Author-email: Alexandru Petenchea <alexandru.petenchea@arangodb.com>, Anthony Mahanna <anthony.mahanna@arangodb.com>
6
6
  Maintainer-email: Alexandru Petenchea <alexandru.petenchea@arangodb.com>, Anthony Mahanna <anthony.mahanna@arangodb.com>
@@ -26,7 +26,7 @@ Requires-Dist: multidict>=6.0
26
26
  Requires-Dist: pyjwt>=2.10.0
27
27
  Provides-Extra: dev
28
28
  Requires-Dist: aiofiles>=24.1.0; extra == "dev"
29
- Requires-Dist: black==26.3.1; extra == "dev"
29
+ Requires-Dist: black==26.5.1; extra == "dev"
30
30
  Requires-Dist: flake8==7.3.0; extra == "dev"
31
31
  Requires-Dist: isort>=5.10.1; extra == "dev"
32
32
  Requires-Dist: mypy==1.15.0; extra == "dev"
@@ -194,24 +194,49 @@ class Collection(Generic[T, U, V]):
194
194
 
195
195
  return doc_id
196
196
 
197
- def _build_filter_conditions(self, filters: Optional[Json]) -> str:
197
+ @staticmethod
198
+ def _build_attribute_expression(field: str, prefix: str, bind_vars: Json) -> str:
199
+ """Build a bind-safe AQL document attribute expression."""
200
+ bind_vars[prefix] = field
201
+ field_access = f"doc[@{prefix}]"
202
+
203
+ if "." not in field:
204
+ return field_access
205
+
206
+ nested_access = "doc"
207
+ for field_index, field_part in enumerate(field.split(".")):
208
+ field_var = f"{prefix}_{field_index}"
209
+ bind_vars[field_var] = field_part
210
+ nested_access += f"[@{field_var}]"
211
+
212
+ return f"(HAS(doc, @{prefix}) ? {field_access} : {nested_access})"
213
+
214
+ def _build_filter_conditions(self, filters: Optional[Json]) -> tuple[str, Json]:
198
215
  """Build filter conditions for an AQL query.
199
216
 
200
217
  Args:
201
218
  filters (dict | None): Document filters.
202
219
 
203
220
  Returns:
204
- str: The complete AQL filter condition.
221
+ tuple: The complete AQL filter condition and its bind variables.
205
222
  """
206
223
  if not filters:
207
- return ""
224
+ return "", {}
208
225
 
209
226
  conditions = []
210
- for k, v in filters.items():
211
- field = k if "." in k else f"`{k}`"
212
- conditions.append(f"doc.{field} == {self.serializer.dumps(v)}")
227
+ bind_vars: Json = {}
228
+ for filter_index, (field, value) in enumerate(filters.items()):
229
+ field_access = self._build_attribute_expression(
230
+ field,
231
+ f"filter_field_{filter_index}",
232
+ bind_vars,
233
+ )
213
234
 
214
- return "FILTER " + " AND ".join(conditions)
235
+ value_var = f"filter_value_{filter_index}"
236
+ bind_vars[value_var] = value
237
+ conditions.append(f"{field_access} == @{value_var}")
238
+
239
+ return "FILTER " + " AND ".join(conditions), bind_vars
215
240
 
216
241
  @staticmethod
217
242
  def _is_none_or_int(obj: Any) -> bool:
@@ -259,24 +284,30 @@ class Collection(Generic[T, U, V]):
259
284
  raise SortValidationError("'sort_order' must be either 'ASC' or 'DESC'")
260
285
 
261
286
  @staticmethod
262
- def _build_sort_expression(sort: Optional[Jsons]) -> str:
287
+ def _build_sort_expression(sort: Optional[Jsons]) -> tuple[str, Json]:
263
288
  """Build a sort condition for an AQL query.
264
289
 
265
290
  Args:
266
291
  sort (list | None): Document sort parameters.
267
292
 
268
293
  Returns:
269
- str: The complete AQL sort condition.
294
+ tuple: The complete AQL sort condition and its bind variables.
270
295
  """
271
296
  if not sort:
272
- return ""
297
+ return "", {}
273
298
 
274
299
  sort_chunks = []
275
- for sort_param in sort:
276
- chunk = f"doc.{sort_param['sort_by']} {sort_param['sort_order']}"
300
+ bind_vars: Json = {}
301
+ for sort_index, sort_param in enumerate(sort):
302
+ field_access = Collection._build_attribute_expression(
303
+ sort_param["sort_by"],
304
+ f"sort_field_{sort_index}",
305
+ bind_vars,
306
+ )
307
+ chunk = f"{field_access} {sort_param['sort_order'].upper()}"
277
308
  sort_chunks.append(chunk)
278
309
 
279
- return "SORT " + ", ".join(sort_chunks)
310
+ return "SORT " + ", ".join(sort_chunks), bind_vars
280
311
 
281
312
  @property
282
313
  def name(self) -> str:
@@ -1025,14 +1056,20 @@ class Collection(Generic[T, U, V]):
1025
1056
 
1026
1057
  skip = skip if skip is not None else 0
1027
1058
  limit = limit if limit is not None else "null"
1059
+ filter_conditions, filter_bind_vars = self._build_filter_conditions(filters)
1060
+ sort_expression, sort_bind_vars = self._build_sort_expression(sort)
1028
1061
  query = f"""
1029
1062
  FOR doc IN @@collection
1030
- {self._build_filter_conditions(filters)}
1063
+ {filter_conditions}
1031
1064
  LIMIT {skip}, {limit}
1032
- {self._build_sort_expression(sort)}
1065
+ {sort_expression}
1033
1066
  RETURN doc
1034
1067
  """
1035
- bind_vars = {"@collection": self.name}
1068
+ bind_vars = {
1069
+ "@collection": self.name,
1070
+ **filter_bind_vars,
1071
+ **sort_bind_vars,
1072
+ }
1036
1073
  data: Json = {"query": query, "bindVars": bind_vars, "count": True}
1037
1074
  headers: RequestHeaders = {}
1038
1075
  if allow_dirty_read is not None:
@@ -1095,9 +1132,10 @@ class Collection(Generic[T, U, V]):
1095
1132
  raise ValueError("limit parameter must be a non-negative int")
1096
1133
 
1097
1134
  sync = f", waitForSync: {wait_for_sync}" if wait_for_sync is not None else ""
1135
+ filter_conditions, filter_bind_vars = self._build_filter_conditions(filters)
1098
1136
  query = f"""
1099
1137
  FOR doc IN @@collection
1100
- {self._build_filter_conditions(filters)}
1138
+ {filter_conditions}
1101
1139
  {f"LIMIT {limit}" if limit is not None else ""}
1102
1140
  UPDATE doc WITH @body IN @@collection
1103
1141
  OPTIONS {{ keepNull: @keep_none, mergeObjects: @merge {sync} }}
@@ -1107,6 +1145,7 @@ class Collection(Generic[T, U, V]):
1107
1145
  "body": body,
1108
1146
  "keep_none": keep_none,
1109
1147
  "merge": merge_objects,
1148
+ **filter_bind_vars,
1110
1149
  }
1111
1150
  data = {"query": query, "bindVars": bind_vars}
1112
1151
 
@@ -1151,9 +1190,10 @@ class Collection(Generic[T, U, V]):
1151
1190
  raise ValueError("limit parameter must be a non-negative int")
1152
1191
 
1153
1192
  sync = f"waitForSync: {wait_for_sync}" if wait_for_sync is not None else ""
1193
+ filter_conditions, filter_bind_vars = self._build_filter_conditions(filters)
1154
1194
  query = f"""
1155
1195
  FOR doc IN @@collection
1156
- {self._build_filter_conditions(filters)}
1196
+ {filter_conditions}
1157
1197
  {f"LIMIT {limit}" if limit is not None else ""}
1158
1198
  REPLACE doc WITH @body IN @@collection
1159
1199
  {f"OPTIONS {{ {sync} }}" if sync else ""}
@@ -1161,6 +1201,7 @@ class Collection(Generic[T, U, V]):
1161
1201
  bind_vars = {
1162
1202
  "@collection": self.name,
1163
1203
  "body": body,
1204
+ **filter_bind_vars,
1164
1205
  }
1165
1206
  data = {"query": query, "bindVars": bind_vars}
1166
1207
 
@@ -1203,14 +1244,15 @@ class Collection(Generic[T, U, V]):
1203
1244
  raise ValueError("limit parameter must be a non-negative int")
1204
1245
 
1205
1246
  sync = f"waitForSync: {wait_for_sync}" if wait_for_sync is not None else ""
1247
+ filter_conditions, filter_bind_vars = self._build_filter_conditions(filters)
1206
1248
  query = f"""
1207
1249
  FOR doc IN @@collection
1208
- {self._build_filter_conditions(filters)}
1250
+ {filter_conditions}
1209
1251
  {f"LIMIT {limit}" if limit is not None else ""}
1210
1252
  REMOVE doc IN @@collection
1211
1253
  {f"OPTIONS {{ {sync} }}" if sync else ""}
1212
1254
  """ # noqa: E201 E202
1213
- bind_vars = {"@collection": self.name}
1255
+ bind_vars = {"@collection": self.name, **filter_bind_vars}
1214
1256
  data = {"query": query, "bindVars": bind_vars}
1215
1257
 
1216
1258
  request = Request(
@@ -2,6 +2,7 @@ __all__ = ["Replication"]
2
2
 
3
3
 
4
4
  from typing import Optional
5
+ from warnings import warn
5
6
 
6
7
  from arangoasync.exceptions import (
7
8
  ReplicationApplierConfigError,
@@ -195,7 +196,9 @@ class Replication:
195
196
  return await self._executor.execute(request, response_handler)
196
197
 
197
198
  async def applier_config(self) -> Result[Json]:
198
- """Return the configuration of the replication applier.
199
+ """**Removed** in ArangoDB v3.12.10.
200
+
201
+ Return the configuration of the replication applier.
199
202
 
200
203
  Returns:
201
204
  dict: Configuration of the replication applier.
@@ -206,6 +209,9 @@ class Replication:
206
209
  References:
207
210
  - `get-the-replication-applier-configuration <https://docs.arango.ai/arangodb/stable/develop/http-api/replication/replication-applier/#get-the-replication-applier-configuration>`__
208
211
  """ # noqa: E501
212
+ m = "/_api/replication/applier-config was removed in ArangoDB v3.12.10."
213
+ warn(m, DeprecationWarning, stacklevel=2)
214
+
209
215
  request = Request(
210
216
  method=Method.GET,
211
217
  endpoint="/_api/replication/applier-config",
@@ -220,7 +226,9 @@ class Replication:
220
226
  return await self._executor.execute(request, response_handler)
221
227
 
222
228
  async def applier_state(self) -> Result[Json]:
223
- """Return the state of the replication applier.
229
+ """**Removed** in ArangoDB v3.12.10.
230
+
231
+ Return the state of the replication applier.
224
232
 
225
233
  Returns:
226
234
  dict: State of the replication applier.
@@ -231,6 +239,9 @@ class Replication:
231
239
  References:
232
240
  - `get-the-replication-applier-state <https://docs.arango.ai/arangodb/stable/develop/http-api/replication/replication-applier/#get-the-replication-applier-state>`__
233
241
  """ # noqa: E501
242
+ m = "/_api/replication/applier-state was removed in ArangoDB v3.12.10."
243
+ warn(m, DeprecationWarning, stacklevel=2)
244
+
234
245
  request = Request(
235
246
  method=Method.GET,
236
247
  endpoint="/_api/replication/applier-state",
@@ -245,7 +256,9 @@ class Replication:
245
256
  return await self._executor.execute(request, response_handler)
246
257
 
247
258
  async def server_id(self) -> Result[str]:
248
- """Return the current server's ID.
259
+ """**Removed** in ArangoDB v3.12.10.
260
+
261
+ Return the current server's ID.
249
262
 
250
263
  Returns:
251
264
  str: Server ID.
@@ -256,6 +269,9 @@ class Replication:
256
269
  References:
257
270
  - `get-the-replication-server-id <https://docs.arango.ai/arangodb/stable/develop/http-api/replication/other-replication-commands/#get-the-replication-server-id>`__
258
271
  """ # noqa: E501
272
+ m = "/_api/replication/server-id was removed in ArangoDB v3.12.10."
273
+ warn(m, DeprecationWarning, stacklevel=2)
274
+
259
275
  request = Request(
260
276
  method=Method.GET,
261
277
  endpoint="/_api/replication/server-id",
@@ -1060,6 +1060,10 @@ class IndexProperties(JsonWrapper):
1060
1060
  def legacy_polygons(self) -> Optional[bool]:
1061
1061
  return self._data.get("legacyPolygons")
1062
1062
 
1063
+ @property
1064
+ def figures(self) -> Optional[Json]:
1065
+ return self._data.get("figures")
1066
+
1063
1067
  @property
1064
1068
  def estimates(self) -> Optional[bool]:
1065
1069
  return self._data.get("estimates")
@@ -1140,6 +1144,10 @@ class IndexProperties(JsonWrapper):
1140
1144
  def training_state(self) -> Optional[str]:
1141
1145
  return self._data.get("trainingState")
1142
1146
 
1147
+ @property
1148
+ def shards(self) -> Optional[Json]:
1149
+ return self._data.get("shards")
1150
+
1143
1151
  @staticmethod
1144
1152
  def compatibility_formatter(data: Json) -> Json:
1145
1153
  """python-arango compatibility formatter."""
@@ -1170,6 +1178,8 @@ class IndexProperties(JsonWrapper):
1170
1178
  result["storedValues"] = data["storedValues"]
1171
1179
  if "legacyPolygons" in data:
1172
1180
  result["legacyPolygons"] = data["legacyPolygons"]
1181
+ if "figures" in data:
1182
+ result["figures"] = data["figures"]
1173
1183
  if "estimates" in data:
1174
1184
  result["estimates"] = data["estimates"]
1175
1185
  if "analyzer" in data:
@@ -1202,6 +1212,8 @@ class IndexProperties(JsonWrapper):
1202
1212
  result["error_message"] = data["errorMessage"]
1203
1213
  if "trainingState" in data:
1204
1214
  result["training_state"] = data["trainingState"]
1215
+ if "shards" in data:
1216
+ result["shards"] = data["shards"]
1205
1217
  return result
1206
1218
 
1207
1219
  def format(self, formatter: Optional[Formatter] = None) -> Json:
@@ -0,0 +1 @@
1
+ __version__ = "1.2.5"
@@ -50,4 +50,43 @@ on fields ``_from`` and ``_to``. For more information on indexes, refer to
50
50
  # Delete the last index from the collection.
51
51
  await cities.delete_index(index["id"])
52
52
 
53
+ # Insert documents with vector embeddings.
54
+ await cities.insert_many([
55
+ {
56
+ "_key": f"city{i}",
57
+ "continent": f"continent{i}",
58
+ "country": f"country{i}",
59
+ "population": i,
60
+ "coordinates": [float(i % 180), float(i % 90)],
61
+ "embedding": [float(i), float(i % 7), float(i % 11), 1.0],
62
+ }
63
+ for i in range(100)
64
+ ])
65
+
66
+ # Let ArangoDB determine the number of vector-index centroids.
67
+ vector_index = await cities.add_index(
68
+ type="vector",
69
+ fields=["embedding"],
70
+ options={
71
+ "name": "vector_index",
72
+ "params": {
73
+ "metric": "cosine",
74
+ "dimension": 4,
75
+ },
76
+ },
77
+ )
78
+
79
+ # Index creation may succeed even if vector training fails.
80
+ if vector_index.training_state != "ready":
81
+ raise RuntimeError(
82
+ vector_index.error_message or "Vector index is not ready"
83
+ )
84
+
85
+ Omitted or scaling-object ``nLists``, ``numberOfDocsPerCentroid``, factory
86
+ placeholders such as ``IVF{},Flat``, and successful-but-unusable creation
87
+ behavior require ArangoDB 3.12.10 or later. A successful creation response
88
+ means that the index exists, but callers should check ``training_state`` before
89
+ using it. If training fails permanently, the state is ``"unusable"`` and
90
+ ``error_message`` describes the failure.
91
+
53
92
  See :class:`arangoasync.collection.StandardCollection` for API specification.
@@ -49,7 +49,7 @@ version = { attr = "arangoasync.version.__version__" }
49
49
  [project.optional-dependencies]
50
50
  dev = [
51
51
  "aiofiles>=24.1.0",
52
- "black==26.3.1",
52
+ "black==26.5.1",
53
53
  "flake8==7.3.0",
54
54
  "isort>=5.10.1",
55
55
  "mypy==1.15.0",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-arango-async
3
- Version: 1.2.3
3
+ Version: 1.2.5
4
4
  Summary: Async Python Driver for ArangoDB
5
5
  Author-email: Alexandru Petenchea <alexandru.petenchea@arangodb.com>, Anthony Mahanna <anthony.mahanna@arangodb.com>
6
6
  Maintainer-email: Alexandru Petenchea <alexandru.petenchea@arangodb.com>, Anthony Mahanna <anthony.mahanna@arangodb.com>
@@ -26,7 +26,7 @@ Requires-Dist: multidict>=6.0
26
26
  Requires-Dist: pyjwt>=2.10.0
27
27
  Provides-Extra: dev
28
28
  Requires-Dist: aiofiles>=24.1.0; extra == "dev"
29
- Requires-Dist: black==26.3.1; extra == "dev"
29
+ Requires-Dist: black==26.5.1; extra == "dev"
30
30
  Requires-Dist: flake8==7.3.0; extra == "dev"
31
31
  Requires-Dist: isort>=5.10.1; extra == "dev"
32
32
  Requires-Dist: mypy==1.15.0; extra == "dev"
@@ -75,6 +75,8 @@ python_arango_async.egg-info/PKG-INFO
75
75
  python_arango_async.egg-info/SOURCES.txt
76
76
  python_arango_async.egg-info/dependency_links.txt
77
77
  python_arango_async.egg-info/requires.txt
78
+ python_arango_async.egg-info/scm_file_list.json
79
+ python_arango_async.egg-info/scm_version.json
78
80
  python_arango_async.egg-info/top_level.txt
79
81
  tests/__init__.py
80
82
  tests/conftest.py
@@ -94,6 +96,7 @@ tests/test_document.py
94
96
  tests/test_foxx.py
95
97
  tests/test_graph.py
96
98
  tests/test_http.py
99
+ tests/test_imports.py
97
100
  tests/test_resolver.py
98
101
  tests/test_task.py
99
102
  tests/test_transaction.py
@@ -5,7 +5,7 @@ pyjwt>=2.10.0
5
5
 
6
6
  [dev]
7
7
  aiofiles>=24.1.0
8
- black==26.3.1
8
+ black==26.5.1
9
9
  flake8==7.3.0
10
10
  isort>=5.10.1
11
11
  mypy==1.15.0