kover 2.7.2__tar.gz → 2.7.3__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.
- kover-2.7.3/PKG-INFO +239 -0
- kover-2.7.3/README.md +209 -0
- {kover-2.7.2 → kover-2.7.3}/kover/__init__.py +1 -1
- {kover-2.7.2 → kover-2.7.3}/kover/bulk_write_builder.py +11 -13
- {kover-2.7.2 → kover-2.7.3}/kover/client.py +88 -4
- {kover-2.7.2 → kover-2.7.3}/kover/database.py +24 -1
- {kover-2.7.2 → kover-2.7.3}/kover/network/compressors.py +1 -0
- {kover-2.7.2 → kover-2.7.3}/kover/session.py +1 -1
- {kover-2.7.2 → kover-2.7.3}/kover/uri_parser.py +1 -4
- {kover-2.7.2 → kover-2.7.3}/pyproject.toml +2 -5
- kover-2.7.2/PKG-INFO +0 -350
- kover-2.7.2/README.md +0 -320
- {kover-2.7.2 → kover-2.7.3}/.gitignore +0 -0
- {kover-2.7.2 → kover-2.7.3}/CODE_OF_CONDUCT.md +0 -0
- {kover-2.7.2 → kover-2.7.3}/LICENSE +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/_internals/__init__.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/_internals/_mixins.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/_internals/_serializers.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/__init__.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/_helpers.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/binary.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/code.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/codec_options.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/datetime_ms.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/dbref.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/decimal128.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/errors.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/int64.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/json_util.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/max_key.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/min_key.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/objectid.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/raw_bson.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/regex.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/son.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/timestamp.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/typings.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/bson/tz_util.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/codes.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/collection.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/cursor.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/enums.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/exceptions.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/gridfs/__init__.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/gridfs/exceptions.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/gridfs/gridfs.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/gridfs/models.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/helpers.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/metadata/__init__.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/metadata/definitions.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/models/__init__.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/models/operations.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/models/other.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/models/replset.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/network/__init__.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/network/auth.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/network/resolver.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/network/transport.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/network/wirehelper.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/py.typed +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/schema.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/transaction.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/kover/typings.py +0 -0
- {kover-2.7.2 → kover-2.7.3}/requirements.txt +0 -0
kover-2.7.3/PKG-INFO
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kover
|
|
3
|
+
Version: 2.7.3
|
|
4
|
+
Summary: Fully async mongodb driver for mongod and replica sets.
|
|
5
|
+
Project-URL: Repository, https://github.com/megawattka/kover
|
|
6
|
+
Project-URL: Documentation, https://github.com/megawattka/kover
|
|
7
|
+
Author-email: megawattka <gunter56741@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: async driver,kover,kover mongodb,mongo async,mongod driver,mongodb
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Topic :: Database
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: dnspython>=2.7.0
|
|
23
|
+
Requires-Dist: pydantic>=2.10.6
|
|
24
|
+
Requires-Dist: typing-extensions>=4.12.2
|
|
25
|
+
Provides-Extra: snappy
|
|
26
|
+
Requires-Dist: python-snappy; extra == 'snappy'
|
|
27
|
+
Provides-Extra: zstd
|
|
28
|
+
Requires-Dist: zstd; extra == 'zstd'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# kover
|
|
32
|
+

|
|
33
|
+

|
|
34
|
+

|
|
35
|
+

|
|
36
|
+

|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
Kover is a model-oriented, strictly-typed, and asynchronous Object-Document Mapper (ODM) for MongoDB. It was built from the ground up using `asyncio` to provide a clean and high-performance alternative to traditional database drivers that rely on thread pools.
|
|
40
|
+
|
|
41
|
+
This library is inspired by `aiomongo` but is modernized for recent versions of Python and MongoDB, with a strong emphasis on type safety and developer experience. Kover is linted with Ruff and supports `pyright`'s strict type-checking mode.
|
|
42
|
+
|
|
43
|
+
## Features
|
|
44
|
+
|
|
45
|
+
* **Fully Asynchronous:** Uses `asyncio` for non-blocking database operations, avoiding the use of thread pool executors.
|
|
46
|
+
* **Pydantic Integration:** Define your document schemas using Pydantic-style models for automatic validation and serialization.
|
|
47
|
+
* **Strictly Typed:** Designed for modern Python, with full type hinting support for better static analysis and code completion.
|
|
48
|
+
* **Modern MongoDB Support:** Built for MongoDB 6.0+ and omits deprecated features for a cleaner API.
|
|
49
|
+
* **Comprehensive API:** Supports nearly all of PyMongo's features, including CRUD operations, bulk writes, transactions, and GridFS.
|
|
50
|
+
* **Authentication:** Supports all standard MongoDB authentication mechanisms.
|
|
51
|
+
|
|
52
|
+
**Note:** The `kover.bson` package is adapted from the `pymongo` source code.
|
|
53
|
+
|
|
54
|
+
## Dependencies
|
|
55
|
+
- `Python 3.10+`
|
|
56
|
+
- `pydantic>=2.10.6`
|
|
57
|
+
- `dnspython>=2.7.0`
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install kover
|
|
63
|
+
```
|
|
64
|
+
Optional dependencies for compression can be installed with:
|
|
65
|
+
```bash
|
|
66
|
+
pip install kover[snappy,zstd]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Quick Start
|
|
70
|
+
|
|
71
|
+
Connect to MongoDB, create a client, and perform a simple query.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
import asyncio
|
|
75
|
+
import logging
|
|
76
|
+
|
|
77
|
+
from kover import Kover
|
|
78
|
+
|
|
79
|
+
log = logging.getLogger(__name__)
|
|
80
|
+
logging.basicConfig(level=logging.INFO)
|
|
81
|
+
|
|
82
|
+
async def main():
|
|
83
|
+
# Connect using a connection string
|
|
84
|
+
client = await Kover.from_uri("mongodb://user:pass@host:port/?tls=false")
|
|
85
|
+
|
|
86
|
+
# Or, create a client programmatically
|
|
87
|
+
# from kover import AuthCredentials
|
|
88
|
+
# credentials = AuthCredentials(username="<user>", password="<pass>")
|
|
89
|
+
# client = await Kover.make_client(credentials=credentials)
|
|
90
|
+
|
|
91
|
+
db = client.testdb
|
|
92
|
+
collection = db.test_collection
|
|
93
|
+
|
|
94
|
+
# Insert a document
|
|
95
|
+
await collection.insert_one({"message": "Hello, Kover!"})
|
|
96
|
+
|
|
97
|
+
# Find documents
|
|
98
|
+
found = await collection.find().to_list()
|
|
99
|
+
log.info(found)
|
|
100
|
+
|
|
101
|
+
await client.close()
|
|
102
|
+
|
|
103
|
+
if __name__ == "__main__":
|
|
104
|
+
asyncio.run(main())
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Examples
|
|
108
|
+
|
|
109
|
+
### Defining Schemas with Pydantic
|
|
110
|
+
|
|
111
|
+
Kover leverages Pydantic for defining document structures. This provides data validation, serialization, and a clear, explicit schema.
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from uuid import UUID
|
|
115
|
+
from kover import Document
|
|
116
|
+
|
|
117
|
+
class User(Document):
|
|
118
|
+
"""A user document schema."""
|
|
119
|
+
|
|
120
|
+
uuid: UUID
|
|
121
|
+
name: str
|
|
122
|
+
age: int
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
You can even generate and enforce a JSON schema on the collection in MongoDB.
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from kover import SchemaGenerator
|
|
129
|
+
|
|
130
|
+
# Generate a schema from the User model
|
|
131
|
+
schema = client.generate_schema(User)
|
|
132
|
+
|
|
133
|
+
# Apply the schema to the collection
|
|
134
|
+
collection = await client.db.users.create_if_not_exists()
|
|
135
|
+
await collection.set_validator(schema)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Inserting Documents
|
|
139
|
+
|
|
140
|
+
You can insert Pydantic model instances directly into a collection.
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from uuid import uuid4
|
|
144
|
+
|
|
145
|
+
user = User(name="Jane Doe", age=30, uuid=uuid4())
|
|
146
|
+
result = await client.db.users.insert_one(user)
|
|
147
|
+
log.info("Inserted user with ID: %s", result)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Querying Documents
|
|
151
|
+
|
|
152
|
+
Kover's cursor provides a powerful and flexible way to retrieve data.
|
|
153
|
+
|
|
154
|
+
**Iterating over a Cursor:**
|
|
155
|
+
```python
|
|
156
|
+
# The cursor asynchronously yields `User` objects
|
|
157
|
+
async with client.db.users.find(cls=User).limit(100) as cursor:
|
|
158
|
+
async for user in cursor:
|
|
159
|
+
log.info("User: %s, Age: %d", user.name, user.age)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Fetching all results into a list:**
|
|
163
|
+
```python
|
|
164
|
+
users_list = await client.db.users.find(cls=User).to_list()
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Updating and Deleting
|
|
168
|
+
|
|
169
|
+
Use `Update` and `Delete` models to construct operations. This approach makes it clear which documents are being targeted and what modifications are being made.
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from kover import Update, Delete
|
|
173
|
+
|
|
174
|
+
# Update a user's age
|
|
175
|
+
update = Update({"name": "Jane Doe"}, {"$set": {"age": 31}})
|
|
176
|
+
await client.db.users.update(update)
|
|
177
|
+
|
|
178
|
+
# Delete a user
|
|
179
|
+
delete = Delete({"name": "Jane Doe"}, limit=1)
|
|
180
|
+
n_deleted = await client.db.users.delete(delete)
|
|
181
|
+
log.info("Documents deleted: %d", n_deleted)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Bulk Writes
|
|
185
|
+
|
|
186
|
+
Perform multiple operations in a single request for efficiency.
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
from kover import BulkWriteBuilder, Update, Delete
|
|
190
|
+
|
|
191
|
+
builder = BulkWriteBuilder()
|
|
192
|
+
builder.add_insert([{"product": "A"}], ns="testdb.inventory")
|
|
193
|
+
builder.add_update(
|
|
194
|
+
Update({"product": "A"}, {"$set": {"quantity": 10}}),
|
|
195
|
+
ns="testdb.inventory",
|
|
196
|
+
)
|
|
197
|
+
builder.add_delete(Delete({"product": "A"}, limit=1), ns="testdb.inventory")
|
|
198
|
+
|
|
199
|
+
await client.bulk_write(builder.build())
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Transactions
|
|
203
|
+
|
|
204
|
+
Kover supports ACID transactions for operations that require atomicity.
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
session = await client.start_session()
|
|
208
|
+
collection = client.db.test
|
|
209
|
+
|
|
210
|
+
async with session.start_transaction() as transaction:
|
|
211
|
+
await collection.insert_one({"step": 1}, transaction=transaction)
|
|
212
|
+
await collection.insert_one({"step": 2}, transaction=transaction)
|
|
213
|
+
# The transaction will be automatically committed on successful exit.
|
|
214
|
+
# If an exception occurs, it will be aborted.
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### GridFS for Large Files
|
|
218
|
+
|
|
219
|
+
Store and retrieve large files (e.g., images, videos) seamlessly with GridFS.
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
from kover.gridfs import GridFS
|
|
223
|
+
|
|
224
|
+
# Get a GridFS instance for a database
|
|
225
|
+
fs = await GridFS(client.get_database("files")).indexed()
|
|
226
|
+
|
|
227
|
+
# Put a file into GridFS
|
|
228
|
+
file_id = await fs.put(b"Hello, large world!", filename="greeting.txt")
|
|
229
|
+
|
|
230
|
+
# Retrieve the file
|
|
231
|
+
file_info, file_bytes_io = await fs.get_by_file_id(file_id)
|
|
232
|
+
|
|
233
|
+
log.info(file_info)
|
|
234
|
+
log.info(file_bytes_io.read())
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Found a Bug?
|
|
238
|
+
|
|
239
|
+
If you find a bug, please [open an issue](https://github.com/megawattka/kover/issues). Better yet, create a pull request with a fix. Contributions are welcome! ❤️
|
kover-2.7.3/README.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# kover
|
|
2
|
+

|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
Kover is a model-oriented, strictly-typed, and asynchronous Object-Document Mapper (ODM) for MongoDB. It was built from the ground up using `asyncio` to provide a clean and high-performance alternative to traditional database drivers that rely on thread pools.
|
|
10
|
+
|
|
11
|
+
This library is inspired by `aiomongo` but is modernized for recent versions of Python and MongoDB, with a strong emphasis on type safety and developer experience. Kover is linted with Ruff and supports `pyright`'s strict type-checking mode.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
* **Fully Asynchronous:** Uses `asyncio` for non-blocking database operations, avoiding the use of thread pool executors.
|
|
16
|
+
* **Pydantic Integration:** Define your document schemas using Pydantic-style models for automatic validation and serialization.
|
|
17
|
+
* **Strictly Typed:** Designed for modern Python, with full type hinting support for better static analysis and code completion.
|
|
18
|
+
* **Modern MongoDB Support:** Built for MongoDB 6.0+ and omits deprecated features for a cleaner API.
|
|
19
|
+
* **Comprehensive API:** Supports nearly all of PyMongo's features, including CRUD operations, bulk writes, transactions, and GridFS.
|
|
20
|
+
* **Authentication:** Supports all standard MongoDB authentication mechanisms.
|
|
21
|
+
|
|
22
|
+
**Note:** The `kover.bson` package is adapted from the `pymongo` source code.
|
|
23
|
+
|
|
24
|
+
## Dependencies
|
|
25
|
+
- `Python 3.10+`
|
|
26
|
+
- `pydantic>=2.10.6`
|
|
27
|
+
- `dnspython>=2.7.0`
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install kover
|
|
33
|
+
```
|
|
34
|
+
Optional dependencies for compression can be installed with:
|
|
35
|
+
```bash
|
|
36
|
+
pip install kover[snappy,zstd]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
Connect to MongoDB, create a client, and perform a simple query.
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import asyncio
|
|
45
|
+
import logging
|
|
46
|
+
|
|
47
|
+
from kover import Kover
|
|
48
|
+
|
|
49
|
+
log = logging.getLogger(__name__)
|
|
50
|
+
logging.basicConfig(level=logging.INFO)
|
|
51
|
+
|
|
52
|
+
async def main():
|
|
53
|
+
# Connect using a connection string
|
|
54
|
+
client = await Kover.from_uri("mongodb://user:pass@host:port/?tls=false")
|
|
55
|
+
|
|
56
|
+
# Or, create a client programmatically
|
|
57
|
+
# from kover import AuthCredentials
|
|
58
|
+
# credentials = AuthCredentials(username="<user>", password="<pass>")
|
|
59
|
+
# client = await Kover.make_client(credentials=credentials)
|
|
60
|
+
|
|
61
|
+
db = client.testdb
|
|
62
|
+
collection = db.test_collection
|
|
63
|
+
|
|
64
|
+
# Insert a document
|
|
65
|
+
await collection.insert_one({"message": "Hello, Kover!"})
|
|
66
|
+
|
|
67
|
+
# Find documents
|
|
68
|
+
found = await collection.find().to_list()
|
|
69
|
+
log.info(found)
|
|
70
|
+
|
|
71
|
+
await client.close()
|
|
72
|
+
|
|
73
|
+
if __name__ == "__main__":
|
|
74
|
+
asyncio.run(main())
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Examples
|
|
78
|
+
|
|
79
|
+
### Defining Schemas with Pydantic
|
|
80
|
+
|
|
81
|
+
Kover leverages Pydantic for defining document structures. This provides data validation, serialization, and a clear, explicit schema.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from uuid import UUID
|
|
85
|
+
from kover import Document
|
|
86
|
+
|
|
87
|
+
class User(Document):
|
|
88
|
+
"""A user document schema."""
|
|
89
|
+
|
|
90
|
+
uuid: UUID
|
|
91
|
+
name: str
|
|
92
|
+
age: int
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
You can even generate and enforce a JSON schema on the collection in MongoDB.
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from kover import SchemaGenerator
|
|
99
|
+
|
|
100
|
+
# Generate a schema from the User model
|
|
101
|
+
schema = client.generate_schema(User)
|
|
102
|
+
|
|
103
|
+
# Apply the schema to the collection
|
|
104
|
+
collection = await client.db.users.create_if_not_exists()
|
|
105
|
+
await collection.set_validator(schema)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Inserting Documents
|
|
109
|
+
|
|
110
|
+
You can insert Pydantic model instances directly into a collection.
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from uuid import uuid4
|
|
114
|
+
|
|
115
|
+
user = User(name="Jane Doe", age=30, uuid=uuid4())
|
|
116
|
+
result = await client.db.users.insert_one(user)
|
|
117
|
+
log.info("Inserted user with ID: %s", result)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Querying Documents
|
|
121
|
+
|
|
122
|
+
Kover's cursor provides a powerful and flexible way to retrieve data.
|
|
123
|
+
|
|
124
|
+
**Iterating over a Cursor:**
|
|
125
|
+
```python
|
|
126
|
+
# The cursor asynchronously yields `User` objects
|
|
127
|
+
async with client.db.users.find(cls=User).limit(100) as cursor:
|
|
128
|
+
async for user in cursor:
|
|
129
|
+
log.info("User: %s, Age: %d", user.name, user.age)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Fetching all results into a list:**
|
|
133
|
+
```python
|
|
134
|
+
users_list = await client.db.users.find(cls=User).to_list()
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Updating and Deleting
|
|
138
|
+
|
|
139
|
+
Use `Update` and `Delete` models to construct operations. This approach makes it clear which documents are being targeted and what modifications are being made.
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from kover import Update, Delete
|
|
143
|
+
|
|
144
|
+
# Update a user's age
|
|
145
|
+
update = Update({"name": "Jane Doe"}, {"$set": {"age": 31}})
|
|
146
|
+
await client.db.users.update(update)
|
|
147
|
+
|
|
148
|
+
# Delete a user
|
|
149
|
+
delete = Delete({"name": "Jane Doe"}, limit=1)
|
|
150
|
+
n_deleted = await client.db.users.delete(delete)
|
|
151
|
+
log.info("Documents deleted: %d", n_deleted)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Bulk Writes
|
|
155
|
+
|
|
156
|
+
Perform multiple operations in a single request for efficiency.
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from kover import BulkWriteBuilder, Update, Delete
|
|
160
|
+
|
|
161
|
+
builder = BulkWriteBuilder()
|
|
162
|
+
builder.add_insert([{"product": "A"}], ns="testdb.inventory")
|
|
163
|
+
builder.add_update(
|
|
164
|
+
Update({"product": "A"}, {"$set": {"quantity": 10}}),
|
|
165
|
+
ns="testdb.inventory",
|
|
166
|
+
)
|
|
167
|
+
builder.add_delete(Delete({"product": "A"}, limit=1), ns="testdb.inventory")
|
|
168
|
+
|
|
169
|
+
await client.bulk_write(builder.build())
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Transactions
|
|
173
|
+
|
|
174
|
+
Kover supports ACID transactions for operations that require atomicity.
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
session = await client.start_session()
|
|
178
|
+
collection = client.db.test
|
|
179
|
+
|
|
180
|
+
async with session.start_transaction() as transaction:
|
|
181
|
+
await collection.insert_one({"step": 1}, transaction=transaction)
|
|
182
|
+
await collection.insert_one({"step": 2}, transaction=transaction)
|
|
183
|
+
# The transaction will be automatically committed on successful exit.
|
|
184
|
+
# If an exception occurs, it will be aborted.
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### GridFS for Large Files
|
|
188
|
+
|
|
189
|
+
Store and retrieve large files (e.g., images, videos) seamlessly with GridFS.
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
from kover.gridfs import GridFS
|
|
193
|
+
|
|
194
|
+
# Get a GridFS instance for a database
|
|
195
|
+
fs = await GridFS(client.get_database("files")).indexed()
|
|
196
|
+
|
|
197
|
+
# Put a file into GridFS
|
|
198
|
+
file_id = await fs.put(b"Hello, large world!", filename="greeting.txt")
|
|
199
|
+
|
|
200
|
+
# Retrieve the file
|
|
201
|
+
file_info, file_bytes_io = await fs.get_by_file_id(file_id)
|
|
202
|
+
|
|
203
|
+
log.info(file_info)
|
|
204
|
+
log.info(file_bytes_io.read())
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Found a Bug?
|
|
208
|
+
|
|
209
|
+
If you find a bug, please [open an issue](https://github.com/megawattka/kover/issues). Better yet, create a pull request with a fix. Contributions are welcome! ❤️
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
# SOFTWARE.
|
|
22
22
|
|
|
23
|
-
__version__ = "2.7.
|
|
23
|
+
__version__ = "2.7.3"
|
|
24
24
|
__author__ = "megawattka"
|
|
25
25
|
__license__ = "MIT"
|
|
26
26
|
__copyright__ = "Copyright (C) 2024-present megawattka"
|
|
@@ -24,7 +24,7 @@ class BulkWriteBuilder:
|
|
|
24
24
|
let: xJsonT | None = None,
|
|
25
25
|
errors_only: bool = False,
|
|
26
26
|
cursor_batch_size: int | None = None,
|
|
27
|
-
write_concern: str = "majority",
|
|
27
|
+
write_concern: str | int = "majority",
|
|
28
28
|
) -> None:
|
|
29
29
|
self.ordered = ordered
|
|
30
30
|
self.bypass_document_validation = bypass_document_validation
|
|
@@ -66,29 +66,27 @@ class BulkWriteBuilder:
|
|
|
66
66
|
|
|
67
67
|
def add_update(
|
|
68
68
|
self,
|
|
69
|
-
|
|
69
|
+
update: Update,
|
|
70
70
|
ns: str,
|
|
71
71
|
) -> None:
|
|
72
72
|
"""Adds an Update operations into the builder."""
|
|
73
73
|
idx = self._get_ns_idx(ns)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
})
|
|
74
|
+
self._operations.append({
|
|
75
|
+
"update": idx,
|
|
76
|
+
**update.as_bulk_write_op(),
|
|
77
|
+
})
|
|
79
78
|
|
|
80
79
|
def add_delete(
|
|
81
80
|
self,
|
|
82
|
-
|
|
81
|
+
delete: Delete,
|
|
83
82
|
ns: str,
|
|
84
83
|
) -> None:
|
|
85
84
|
"""Adds an Delete operations into the builder."""
|
|
86
85
|
idx = self._get_ns_idx(ns)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
})
|
|
86
|
+
self._operations.append({
|
|
87
|
+
"delete": idx,
|
|
88
|
+
**delete.as_bulk_write_op(),
|
|
89
|
+
})
|
|
92
90
|
|
|
93
91
|
def build(self) -> xJsonT:
|
|
94
92
|
"""Build the command.
|
|
@@ -14,8 +14,9 @@ from .helpers import (
|
|
|
14
14
|
filter_non_null,
|
|
15
15
|
maybe_to_dict,
|
|
16
16
|
)
|
|
17
|
-
from .models import BuildInfo, WriteConcern
|
|
17
|
+
from .models import BuildInfo, ReadConcern, WriteConcern
|
|
18
18
|
from .network import MongoTransport, SrvResolver
|
|
19
|
+
from .schema import SchemaGenerator
|
|
19
20
|
from .session import Session
|
|
20
21
|
from .typings import DEFAULT_MONGODB_PORT
|
|
21
22
|
from .uri_parser import parse_uri
|
|
@@ -23,11 +24,12 @@ from .uri_parser import parse_uri
|
|
|
23
24
|
if TYPE_CHECKING:
|
|
24
25
|
from .models import ReplicaSetConfig
|
|
25
26
|
from .network import AuthCredentials
|
|
27
|
+
from .schema import Document
|
|
26
28
|
from .transaction import Transaction
|
|
27
29
|
from .typings import COMPRESSION_T, DocumentT, xJsonT
|
|
28
30
|
|
|
29
31
|
|
|
30
|
-
def
|
|
32
|
+
def _create_connection_pool(
|
|
31
33
|
host: str,
|
|
32
34
|
port: int,
|
|
33
35
|
size: int,
|
|
@@ -64,6 +66,7 @@ class Kover:
|
|
|
64
66
|
self._credentials = credentials
|
|
65
67
|
self._compression = compression
|
|
66
68
|
self._application = application
|
|
69
|
+
self._schema_generator = SchemaGenerator()
|
|
67
70
|
|
|
68
71
|
async def __aenter__(self) -> Self:
|
|
69
72
|
return self
|
|
@@ -115,6 +118,17 @@ class Kover:
|
|
|
115
118
|
self._write_concern = WriteConcern(w=w, j=j, wtimeout=wtimeout)
|
|
116
119
|
return self
|
|
117
120
|
|
|
121
|
+
def generate_schema(self, cls: type[Document]) -> xJsonT:
|
|
122
|
+
"""Generate a JSON schema for the provided Document class.
|
|
123
|
+
|
|
124
|
+
Parameters:
|
|
125
|
+
cls : The Document class for which to generate the schema.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
A JSON schema as a dictionary.
|
|
129
|
+
"""
|
|
130
|
+
return self._schema_generator.generate(cls)
|
|
131
|
+
|
|
118
132
|
@classmethod
|
|
119
133
|
async def from_uri(
|
|
120
134
|
cls,
|
|
@@ -154,7 +168,7 @@ class Kover:
|
|
|
154
168
|
nodes = [host]
|
|
155
169
|
|
|
156
170
|
args = (nodes[0], parsed.port, parsed.max_pool_size)
|
|
157
|
-
pool =
|
|
171
|
+
pool = _create_connection_pool(*args, tls=parsed.tls, loop=loop)
|
|
158
172
|
|
|
159
173
|
return cls(
|
|
160
174
|
w=parsed.write_concern,
|
|
@@ -196,7 +210,7 @@ class Kover:
|
|
|
196
210
|
Returns:
|
|
197
211
|
An instance of the Kover client.
|
|
198
212
|
"""
|
|
199
|
-
pool =
|
|
213
|
+
pool = _create_connection_pool(
|
|
200
214
|
host, port, max_pool_size, tls=tls, loop=loop)
|
|
201
215
|
|
|
202
216
|
return cls(
|
|
@@ -316,6 +330,25 @@ class Kover:
|
|
|
316
330
|
"""
|
|
317
331
|
await self.request({"dropDatabase": 1.0}, db_name=name)
|
|
318
332
|
|
|
333
|
+
# https://www.mongodb.com/docs/manual/reference/command/dropConnections
|
|
334
|
+
async def drop_connections(
|
|
335
|
+
self,
|
|
336
|
+
hosts: list[str],
|
|
337
|
+
comment: str | None = None,
|
|
338
|
+
) -> None:
|
|
339
|
+
"""Drop connections to the specified hosts.
|
|
340
|
+
|
|
341
|
+
Parameters:
|
|
342
|
+
hosts : A list of hostnames whose connections should be dropped.
|
|
343
|
+
comment : Optional comment for the operation.
|
|
344
|
+
"""
|
|
345
|
+
document: xJsonT = filter_non_null({
|
|
346
|
+
"dropConnections": 1.0,
|
|
347
|
+
"hostAndPort": hosts,
|
|
348
|
+
"comment": comment,
|
|
349
|
+
})
|
|
350
|
+
await self.request(document)
|
|
351
|
+
|
|
319
352
|
# https://www.mongodb.com/docs/manual/reference/command/replSetInitiate/
|
|
320
353
|
async def replica_set_initiate(
|
|
321
354
|
self,
|
|
@@ -487,3 +520,54 @@ class Kover:
|
|
|
487
520
|
"comment": comment,
|
|
488
521
|
})
|
|
489
522
|
await self.request(command)
|
|
523
|
+
|
|
524
|
+
# https://www.mongodb.com/docs/manual/reference/command/getDefaultRWConcern
|
|
525
|
+
async def get_default_rw_concern(
|
|
526
|
+
self,
|
|
527
|
+
*,
|
|
528
|
+
in_memory: bool = False,
|
|
529
|
+
comment: str | None = None,
|
|
530
|
+
) -> tuple[ReadConcern, WriteConcern]:
|
|
531
|
+
"""Get the default read and write concern settings for the database.
|
|
532
|
+
|
|
533
|
+
Parameters:
|
|
534
|
+
in_memory : If True, returns a WriteConcern instance without
|
|
535
|
+
querying the server, by default False.
|
|
536
|
+
comment : Optional comment for the operation.
|
|
537
|
+
|
|
538
|
+
Returns:
|
|
539
|
+
An instance of WriteConcern representing the default settings.
|
|
540
|
+
"""
|
|
541
|
+
command = filter_non_null({
|
|
542
|
+
"getDefaultRWConcern": 1.0,
|
|
543
|
+
"inMemory": in_memory,
|
|
544
|
+
"comment": comment,
|
|
545
|
+
})
|
|
546
|
+
resp = await self.request(command)
|
|
547
|
+
read_concern = ReadConcern(level=resp["defaultReadConcern"]["level"])
|
|
548
|
+
write_concern = WriteConcern(w=resp["defaultWriteConcern"]["w"])
|
|
549
|
+
|
|
550
|
+
return read_concern, write_concern
|
|
551
|
+
|
|
552
|
+
# https://www.mongodb.com/docs/manual/reference/command/getParameter
|
|
553
|
+
async def get_parameter(self, name: str) -> object:
|
|
554
|
+
"""Get the value of a specific server parameter.
|
|
555
|
+
|
|
556
|
+
Parameters:
|
|
557
|
+
name : The name of the server parameter to retrieve.
|
|
558
|
+
|
|
559
|
+
Returns:
|
|
560
|
+
A Parameter itself.
|
|
561
|
+
"""
|
|
562
|
+
resp = await self.request({"getParameter": 1.0, name: 1.0})
|
|
563
|
+
return resp[name]
|
|
564
|
+
|
|
565
|
+
# https://www.mongodb.com/docs/manual/reference/command/setParameter
|
|
566
|
+
async def set_parameter(self, name: str, value: object) -> None:
|
|
567
|
+
"""Set the server parameter at runtime.
|
|
568
|
+
|
|
569
|
+
Parameters:
|
|
570
|
+
name : The name of the server parameter to set.
|
|
571
|
+
value : The value of that parameter.
|
|
572
|
+
"""
|
|
573
|
+
await self.request({"setParameter": 1.0, name: value})
|
|
@@ -5,13 +5,14 @@ from __future__ import annotations
|
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
from .collection import Collection
|
|
8
|
-
from .helpers import classrepr, filter_non_null
|
|
8
|
+
from .helpers import classrepr, filter_non_null, maybe_to_dict
|
|
9
9
|
from .models import User
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
12
|
from collections.abc import Sequence
|
|
13
13
|
|
|
14
14
|
from .client import Kover
|
|
15
|
+
from .models import WriteConcern
|
|
15
16
|
from .session import Transaction
|
|
16
17
|
from .typings import xJsonT
|
|
17
18
|
|
|
@@ -214,6 +215,28 @@ class Database:
|
|
|
214
215
|
})
|
|
215
216
|
await self.command(command)
|
|
216
217
|
|
|
218
|
+
async def drop_all_users_from_database(
|
|
219
|
+
self,
|
|
220
|
+
write_concern: WriteConcern | None = None,
|
|
221
|
+
comment: str | None = None,
|
|
222
|
+
) -> int:
|
|
223
|
+
"""Drops all users from the database.
|
|
224
|
+
|
|
225
|
+
Parameters:
|
|
226
|
+
write_concern : Optional write concern for the operation.
|
|
227
|
+
comment : Optional comment for the operation.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
The number of users dropped.
|
|
231
|
+
"""
|
|
232
|
+
command = filter_non_null({
|
|
233
|
+
"dropAllUsersFromDatabase": 1.0,
|
|
234
|
+
"writeConcern": maybe_to_dict(write_concern),
|
|
235
|
+
"comment": comment,
|
|
236
|
+
})
|
|
237
|
+
resp = await self.command(command)
|
|
238
|
+
return resp["n"]
|
|
239
|
+
|
|
217
240
|
async def grant_roles_to_user(
|
|
218
241
|
self,
|
|
219
242
|
username: str,
|