Skip to content

Commit 2652c5a

Browse files
authored
Feature: optional batteries (#575)
* poetry -> flit * feature | merge indexes * version | 1.20.0b0
1 parent 02111b0 commit 2652c5a

33 files changed

+296
-180
lines changed

.github/workflows/github-actions-lint.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
python-version: [ 3.10.9 ]
1010
runs-on: ubuntu-latest
1111
steps:
12-
- uses: actions/checkout@v2
12+
- uses: actions/checkout@v3
1313
- uses: actions/setup-python@v2
1414
with:
1515
python-version: ${{ matrix.python-version }}

.github/workflows/github-actions-mypy.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
fail-fast: false
88
runs-on: ubuntu-latest
99
steps:
10-
- uses: actions/checkout@v2
10+
- uses: actions/checkout@v3
1111
- uses: actions/setup-python@v2
1212
with:
1313
python-version: 3.10.9

.github/workflows/github-actions-publish-docs.yml

+2-4
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@ jobs:
77
publish_docs:
88
runs-on: ubuntu-latest
99
steps:
10-
- uses: actions/checkout@v2
10+
- uses: actions/checkout@v3
1111
- uses: actions/setup-python@v2
1212
with:
1313
python-version: 3.10.9
14-
- name: install poetry
15-
run: pip install poetry
1614
- name: install dependencies
17-
run: poetry install
15+
run: pip3 instll .[doc]
1816
- name: publish docs
1917
run: bash scripts/publish_docs.sh

.github/workflows/github-actions-publish-project.yml

+4-9
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,8 @@ jobs:
77
publish_project:
88
runs-on: ubuntu-latest
99
steps:
10-
- uses: actions/checkout@v2
11-
- uses: actions/setup-python@v2
12-
with:
13-
python-version: 3.10.9
14-
- name: install poetry
15-
run: pip install poetry
16-
- name: install dependencies
17-
run: poetry install
10+
- uses: actions/checkout@v3
11+
- name: install flit
12+
run: pip3 install flit
1813
- name: publish project
19-
run: poetry publish --build --username __token__ --password ${{ secrets.PYPI_TOKEN }}
14+
run: flit publish

.github/workflows/github-actions-tests.yml

+4-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
pydantic-version: [ 1.10.0 ]
1212
runs-on: ubuntu-latest
1313
steps:
14-
- uses: actions/checkout@v2
14+
- uses: actions/checkout@v3
1515
- uses: actions/setup-python@v2
1616
with:
1717
python-version: ${{ matrix.python-version }}
@@ -20,11 +20,9 @@ jobs:
2020
with:
2121
mongodb-version: ${{ matrix.mongodb-version }}
2222
mongodb-replica-set: test-rs
23-
- name: install poetry
24-
run: pip install poetry
2523
- name: install dependencies
26-
run: poetry install
24+
run: pip install .[test]
2725
- name: specify pydantic
28-
run: poetry add pydantic==${{ matrix.pydantic-version }}
26+
run: pip3 install pydantic==${{ matrix.pydantic-version }}
2927
- name: run tests
30-
run: poetry run pytest
28+
run: pytest

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,5 @@ docker-compose-aws.yml
184184
tilt_modules
185185

186186
# Poetry stuff
187-
poetry.lock
187+
poetry.lock
188+
.pdm-python

.pypirc

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[distutils]
2+
index-servers =
3+
pypi
4+
testpypi
5+
6+
[pypi]
7+
repository = https://upload.pypi.org/legacy/
8+
username = __token__
9+
password = ${PYPI_TOKEN}
10+
11+
[testpypi]
12+
repository = https://test.pypi.org/legacy/
13+
username = roman-right
14+
password = =$C[wT}^]5EWvX(p#9Po

beanie/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from beanie.odm.views import View
2929
from beanie.odm.union_doc import UnionDoc
3030

31-
__version__ = "1.19.2"
31+
__version__ = "1.20.0b0"
3232
__all__ = [
3333
# ODM
3434
"Document",

beanie/odm/bulk.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ async def __aenter__(self):
4242
async def __aexit__(self, exc_type, exc, tb):
4343
await self.commit()
4444

45-
async def commit(self) -> BulkWriteResult:
45+
async def commit(self) -> Optional[BulkWriteResult]:
46+
"""
47+
Commit all the operations to the database
48+
:return:
49+
"""
4650
obj_class = None
4751
requests = []
4852
if self.operations:
@@ -55,16 +59,17 @@ async def commit(self) -> BulkWriteResult:
5559
"All the operations should be for a single document model"
5660
)
5761
if op.operation in [InsertOne, DeleteOne]:
58-
query = op.operation(op.first_query, **op.pymongo_kwargs)
62+
query = op.operation(op.first_query, **op.pymongo_kwargs) # type: ignore
5963
else:
6064
query = op.operation(
61-
op.first_query, op.second_query, **op.pymongo_kwargs
65+
op.first_query, op.second_query, **op.pymongo_kwargs # type: ignore
6266
)
6367
requests.append(query)
6468

6569
return await obj_class.get_motor_collection().bulk_write( # type: ignore
6670
requests, session=self.session
6771
)
72+
return None
6873

6974
def add_operation(self, operation: Operation):
7075
self.operations.append(operation)

beanie/odm/fields.py

+83
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
In,
3434
)
3535
from beanie.odm.utils.parsing import parse_obj
36+
from pymongo import IndexModel
3637

3738
if TYPE_CHECKING:
3839
from beanie.odm.documents import DocType
@@ -293,3 +294,85 @@ def to_dict(self):
293294

294295

295296
ENCODERS_BY_TYPE[BackLink] = lambda o: o.to_dict()
297+
298+
299+
class IndexModelField:
300+
def __init__(self, index: IndexModel):
301+
self.index = index
302+
self.name = index.document["name"]
303+
304+
self.fields = tuple(sorted(self.index.document["key"]))
305+
self.options = tuple(
306+
sorted(
307+
(k, v)
308+
for k, v in self.index.document.items()
309+
if k not in ["key", "v"]
310+
)
311+
)
312+
313+
def __eq__(self, other):
314+
return self.fields == other.fields and self.options == other.options
315+
316+
def __repr__(self):
317+
return f"IndexModelField({self.name}, {self.fields}, {self.options})"
318+
319+
@classmethod
320+
def __get_validators__(cls):
321+
yield cls.validate
322+
323+
@classmethod
324+
def validate(cls, v):
325+
if isinstance(v, IndexModel):
326+
return IndexModelField(v)
327+
else:
328+
return IndexModelField(IndexModel(v))
329+
330+
@staticmethod
331+
def list_difference(
332+
left: List["IndexModelField"], right: List["IndexModelField"]
333+
):
334+
result = []
335+
for index in left:
336+
if index not in right:
337+
result.append(index)
338+
return result
339+
340+
@staticmethod
341+
def list_to_index_model(left: List["IndexModelField"]):
342+
return [index.index for index in left]
343+
344+
@classmethod
345+
def from_motor_index_information(cls, index_info: dict):
346+
result = []
347+
for name, details in index_info.items():
348+
fields = details["key"]
349+
if ("_id", 1) in fields:
350+
continue
351+
352+
options = {k: v for k, v in details.items() if k != "key"}
353+
index_model = IndexModelField(
354+
IndexModel(fields, name=name, **options)
355+
)
356+
result.append(index_model)
357+
return result
358+
359+
def same_fields(self, other: "IndexModelField"):
360+
return self.fields == other.fields
361+
362+
@staticmethod
363+
def find_index_with_the_same_fields(
364+
indexes: List["IndexModelField"], index: "IndexModelField"
365+
):
366+
for i in indexes:
367+
if i.same_fields(index):
368+
return i
369+
return None
370+
371+
@staticmethod
372+
def merge_indexes(
373+
left: List["IndexModelField"], right: List["IndexModelField"]
374+
):
375+
left_dict = {index.fields: index for index in left}
376+
right_dict = {index.fields: index for index in right}
377+
left_dict.update(right_dict)
378+
return list(left_dict.values())

beanie/odm/settings/document.py

+2-62
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,12 @@
11
from typing import Optional, List
22

33
from pydantic import Field
4-
from pymongo import IndexModel
54

5+
from beanie.odm.fields import IndexModelField
66
from beanie.odm.settings.base import ItemSettings
77
from beanie.odm.settings.timeseries import TimeSeriesConfig
88

99

10-
class IndexModelField:
11-
def __init__(self, index: IndexModel):
12-
self.index = index
13-
self.name = index.document["name"]
14-
15-
self.fields = tuple(sorted(self.index.document["key"]))
16-
self.options = tuple(
17-
sorted(
18-
(k, v)
19-
for k, v in self.index.document.items()
20-
if k not in ["key", "v"]
21-
)
22-
)
23-
24-
def __eq__(self, other):
25-
return self.fields == other.fields and self.options == other.options
26-
27-
def __repr__(self):
28-
return f"IndexModelField({self.name}, {self.fields}, {self.options})"
29-
30-
@classmethod
31-
def __get_validators__(cls):
32-
yield cls.validate
33-
34-
@classmethod
35-
def validate(cls, v):
36-
if isinstance(v, IndexModel):
37-
return IndexModelField(v)
38-
else:
39-
return IndexModelField(IndexModel(v))
40-
41-
@staticmethod
42-
def list_difference(
43-
left: List["IndexModelField"], right: List["IndexModelField"]
44-
):
45-
result = []
46-
for index in left:
47-
if index not in right:
48-
result.append(index)
49-
return result
50-
51-
@staticmethod
52-
def list_to_index_model(left: List["IndexModelField"]):
53-
return [index.index for index in left]
54-
55-
@classmethod
56-
def from_motor_index_information(cls, index_info: dict):
57-
result = []
58-
for name, details in index_info.items():
59-
fields = details["key"]
60-
if ("_id", 1) in fields:
61-
continue
62-
63-
options = {k: v for k, v in details.items() if k != "key"}
64-
index_model = IndexModelField(
65-
IndexModel(fields, name=name, **options)
66-
)
67-
result.append(index_model)
68-
return result
69-
70-
7110
class DocumentSettings(ItemSettings):
7211
use_state_management: bool = False
7312
state_management_replace_objects: bool = False
@@ -77,6 +16,7 @@ class DocumentSettings(ItemSettings):
7716
single_root_inheritance: bool = False
7817

7918
indexes: List[IndexModelField] = Field(default_factory=list)
19+
merge_indexes: bool = False
8020
timeseries: Optional[TimeSeriesConfig] = None
8121

8222
lazy_parsing: bool = False

beanie/odm/utils/find.py

+8-12
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ def construct_query(
4040
lookup_steps = [
4141
{
4242
"$lookup": {
43-
"from": link_info.model_class.get_motor_collection().name,
44-
# type: ignore
43+
"from": link_info.model_class.get_motor_collection().name, # type: ignore
4544
"localField": f"{link_info.lookup_field_name}.$id",
4645
"foreignField": "_id",
4746
"as": f"_link_{link_info.field_name}",
@@ -85,7 +84,7 @@ def construct_query(
8584
lookup_steps = [
8685
{
8786
"$lookup": {
88-
"from": link_info.model_class.get_motor_collection().name,
87+
"from": link_info.model_class.get_motor_collection().name, # type: ignore
8988
"let": {
9089
"link_id": f"${link_info.lookup_field_name}.$id"
9190
},
@@ -139,8 +138,7 @@ def construct_query(
139138
lookup_steps = [
140139
{
141140
"$lookup": {
142-
"from": link_info.model_class.get_motor_collection().name,
143-
# type: ignore
141+
"from": link_info.model_class.get_motor_collection().name, # type: ignore
144142
"localField": "_id",
145143
"foreignField": f"{link_info.lookup_field_name}.$id",
146144
"as": f"_link_{link_info.field_name}",
@@ -184,7 +182,7 @@ def construct_query(
184182
lookup_steps = [
185183
{
186184
"$lookup": {
187-
"from": link_info.model_class.get_motor_collection().name,
185+
"from": link_info.model_class.get_motor_collection().name, # type: ignore
188186
"let": {"link_id": "$_id"},
189187
"as": f"_link_{link_info.field_name}",
190188
"pipeline": [
@@ -241,8 +239,7 @@ def construct_query(
241239
queries.append(
242240
{
243241
"$lookup": {
244-
"from": link_info.model_class.get_motor_collection().name,
245-
# type: ignore
242+
"from": link_info.model_class.get_motor_collection().name, # type: ignore
246243
"localField": f"{link_info.lookup_field_name}.$id",
247244
"foreignField": "_id",
248245
"as": link_info.field_name,
@@ -261,7 +258,7 @@ def construct_query(
261258
else:
262259
lookup_step = {
263260
"$lookup": {
264-
"from": link_info.model_class.get_motor_collection().name,
261+
"from": link_info.model_class.get_motor_collection().name, # type: ignore
265262
"let": {"link_id": f"${link_info.lookup_field_name}.$id"},
266263
"as": link_info.field_name,
267264
"pipeline": [
@@ -286,8 +283,7 @@ def construct_query(
286283
queries.append(
287284
{
288285
"$lookup": {
289-
"from": link_info.model_class.get_motor_collection().name,
290-
# type: ignore
286+
"from": link_info.model_class.get_motor_collection().name, # type: ignore
291287
"localField": "_id",
292288
"foreignField": f"{link_info.lookup_field_name}.$id",
293289
"as": link_info.field_name,
@@ -306,7 +302,7 @@ def construct_query(
306302
else:
307303
lookup_step = {
308304
"$lookup": {
309-
"from": link_info.model_class.get_motor_collection().name,
305+
"from": link_info.model_class.get_motor_collection().name, # type: ignore
310306
"let": {"link_id": "$_id"},
311307
"as": link_info.field_name,
312308
"pipeline": [

0 commit comments

Comments
 (0)