mongospec 0.0.1__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.
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2025 Dmitry Belozerov
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
19
+ OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,88 @@
1
+ Metadata-Version: 2.4
2
+ Name: mongospec
3
+ Version: 0.0.1
4
+ Summary: Async MongoDB ODM with msgspec integration and automatic collection binding
5
+ Author: Diprog
6
+ License: MIT
7
+ Project-URL: repository, https://github.com/diprog/mongospec
8
+ Classifier: Development Status :: 2 - Pre-Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Topic :: Database
13
+ Classifier: Typing :: Typed
14
+ Requires-Python: >=3.13
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: mongojet~=0.3.1
18
+ Requires-Dist: msgspec~=0.19.0
19
+ Dynamic: license-file
20
+
21
+ # mongospec
22
+
23
+ Minimal async MongoDB ODM built for **speed** and **simplicity**, featuring automatic collection binding and msgspec
24
+ integration.
25
+
26
+ ## Why mongospec?
27
+
28
+ - ⚡ **Blazing fast** - Uses [msgspec](https://github.com/jcrist/msgspec) (fastest Python serialization)
29
+ and [mongojet](https://github.com/romis2012/mongojet) (fastest async MongoDB wrapper)
30
+ - 🧩 **Dead simple** - No complex abstractions, just clean document handling
31
+ - 🏎️ **Zero overhead** - Optimized for performance-critical applications
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install mongospec
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ```python
42
+ import asyncio
43
+ from datetime import datetime
44
+
45
+ import mongojet
46
+ import msgspec
47
+
48
+ import mongospec
49
+ from mongospec import MongoDocument
50
+
51
+
52
+ class User(MongoDocument):
53
+ __collection_name__ = "users"
54
+ __indexes__ = [{"keys": [("email", 1)], "options": {"unique": True}}]
55
+
56
+ name: str
57
+ email: str
58
+ created_at: datetime = msgspec.field(default_factory=datetime.now)
59
+
60
+
61
+ async def main():
62
+ mongo_client = await mongojet.create_client("mongodb://localhost:27017")
63
+ try:
64
+ await mongospec.init(mongo_client.get_database("db"), document_types=[User])
65
+
66
+ # Create and insert user
67
+ user = User(name="Alice", email="alice@example.com")
68
+ await user.insert()
69
+
70
+ # Find document
71
+ found = await User.find_one({"email": "alice@example.com"})
72
+ print(found.dump())
73
+ finally:
74
+ await mongospec.close()
75
+
76
+
77
+ asyncio.run(main())
78
+ ```
79
+
80
+ ## Performance Features
81
+
82
+ - 🚀 **msgspec-powered** serialization (2-10x faster than alternatives)
83
+ - ⚡ **mongojet backend** (faster than Motor with identical API)
84
+ - 🧬 **Compiled structs** for document models
85
+ - 📉 **Minimal abstraction** overhead
86
+
87
+ ⚠️ **Early Alpha Notice**
88
+ Basic CRUD operations supported. Designed for rapid prototyping. Not production-ready. API will evolve.
@@ -0,0 +1,68 @@
1
+ # mongospec
2
+
3
+ Minimal async MongoDB ODM built for **speed** and **simplicity**, featuring automatic collection binding and msgspec
4
+ integration.
5
+
6
+ ## Why mongospec?
7
+
8
+ - ⚡ **Blazing fast** - Uses [msgspec](https://github.com/jcrist/msgspec) (fastest Python serialization)
9
+ and [mongojet](https://github.com/romis2012/mongojet) (fastest async MongoDB wrapper)
10
+ - 🧩 **Dead simple** - No complex abstractions, just clean document handling
11
+ - 🏎️ **Zero overhead** - Optimized for performance-critical applications
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pip install mongospec
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```python
22
+ import asyncio
23
+ from datetime import datetime
24
+
25
+ import mongojet
26
+ import msgspec
27
+
28
+ import mongospec
29
+ from mongospec import MongoDocument
30
+
31
+
32
+ class User(MongoDocument):
33
+ __collection_name__ = "users"
34
+ __indexes__ = [{"keys": [("email", 1)], "options": {"unique": True}}]
35
+
36
+ name: str
37
+ email: str
38
+ created_at: datetime = msgspec.field(default_factory=datetime.now)
39
+
40
+
41
+ async def main():
42
+ mongo_client = await mongojet.create_client("mongodb://localhost:27017")
43
+ try:
44
+ await mongospec.init(mongo_client.get_database("db"), document_types=[User])
45
+
46
+ # Create and insert user
47
+ user = User(name="Alice", email="alice@example.com")
48
+ await user.insert()
49
+
50
+ # Find document
51
+ found = await User.find_one({"email": "alice@example.com"})
52
+ print(found.dump())
53
+ finally:
54
+ await mongospec.close()
55
+
56
+
57
+ asyncio.run(main())
58
+ ```
59
+
60
+ ## Performance Features
61
+
62
+ - 🚀 **msgspec-powered** serialization (2-10x faster than alternatives)
63
+ - ⚡ **mongojet backend** (faster than Motor with identical API)
64
+ - 🧬 **Compiled structs** for document models
65
+ - 📉 **Minimal abstraction** overhead
66
+
67
+ ⚠️ **Early Alpha Notice**
68
+ Basic CRUD operations supported. Designed for rapid prototyping. Not production-ready. API will evolve.
@@ -0,0 +1 @@
1
+ from .document import MongoDocument
@@ -0,0 +1,112 @@
1
+ """
2
+ Base MongoDB document model with automatic collection binding.
3
+
4
+ Provides collection name resolution and runtime collection access.
5
+ Uses class name as fallback when __collection_name__ is not specified.
6
+ """
7
+ from datetime import datetime
8
+ from typing import Any, ClassVar, Self
9
+
10
+ import mongojet
11
+ import msgspec
12
+ from bson import ObjectId
13
+ from mongojet import IndexModel
14
+
15
+ from .operations.find import FindOperationsMixin
16
+ from .operations.insert import InsertOperationsMixin
17
+
18
+
19
+ class MongoDocument(
20
+ msgspec.Struct,
21
+ InsertOperationsMixin,
22
+ FindOperationsMixin,
23
+ kw_only=True
24
+ ):
25
+ """
26
+ Abstract base document for MongoDB collections with automatic binding.
27
+
28
+ .. rubric:: Class Organization
29
+
30
+ Settings:
31
+ __collection_name__: ClassVar[Optional[str]] = None
32
+ Explicit collection name (optional).
33
+ __preserve_types__: ClassVar[Tuple[Type[Any], ...]] = (ObjectId, datetime)
34
+ Types to preserve in their original form during encoding.
35
+ __indexes__: ClassVar[List[Dict]] = []
36
+ List of MongoDB indexes to ensure on initialization.
37
+
38
+ Runtime:
39
+ __collection__: ClassVar[Optional[mongojet.Collection]] = None
40
+ Set during mongospec.init().
41
+
42
+ Document:
43
+ _id: Optional[ObjectId] = None
44
+ MongoDB document ID field.
45
+ """
46
+
47
+ # Configuration settings
48
+ __collection_name__: ClassVar[str | None] = None
49
+ __preserve_types__: ClassVar[tuple[type[Any], ...]] = (ObjectId, datetime)
50
+ __indexes__: ClassVar[list[IndexModel]] = []
51
+
52
+ # Collection initialized externally
53
+ __collection__: ClassVar[mongojet.Collection | None] = None
54
+
55
+ # Primary key field
56
+ _id: ObjectId | None = None
57
+
58
+ @staticmethod
59
+ def _dec_hook(type_: type[Any], obj: Any) -> Any:
60
+ """Decode custom types like ObjectId and datetime during conversion."""
61
+ if type_ is ObjectId:
62
+ if isinstance(obj, ObjectId):
63
+ return obj
64
+ try:
65
+ return ObjectId(obj)
66
+ except Exception as e:
67
+ raise ValueError(f"Invalid ObjectId: {obj}") from e
68
+ raise NotImplementedError(f"Unsupported type: {type_}")
69
+
70
+ @staticmethod
71
+ def _enc_hook(obj: Any) -> Any:
72
+ """Encode custom types during serialization. Override in subclasses."""
73
+ raise NotImplementedError(f"Type {type(obj)} not supported")
74
+
75
+ @classmethod
76
+ def get_collection(cls) -> mongojet.Collection:
77
+ """Retrieve the bound MongoDB collection."""
78
+ if cls.__collection__ is None:
79
+ raise RuntimeError(
80
+ f"Collection for {cls.__name__} not initialized. "
81
+ "Call mongospec.init() first."
82
+ )
83
+ return cls.__collection__
84
+
85
+ @classmethod
86
+ def get_collection_name(cls) -> str:
87
+ """Determine the collection name from class settings."""
88
+ return cls.__collection_name__ or cls.__name__
89
+
90
+ @classmethod
91
+ def load(cls, data: dict[str, Any]) -> Self:
92
+ """Deserialize a dictionary into a document instance."""
93
+ return msgspec.convert(
94
+ data,
95
+ cls,
96
+ dec_hook=cls._dec_hook,
97
+ from_attributes=True,
98
+ strict=False
99
+ )
100
+
101
+ def dump(self, **kwargs: Any) -> dict[str, Any]:
102
+ """Serialize the document into a MongoDB-compatible dictionary."""
103
+ data = msgspec.to_builtins(
104
+ self,
105
+ enc_hook=self._enc_hook,
106
+ builtin_types=self.__preserve_types__,
107
+ **kwargs
108
+ )
109
+ # Strip None _id to allow MongoDB to generate it
110
+ if data.get("_id") is None:
111
+ del data["_id"]
112
+ return data
@@ -0,0 +1,24 @@
1
+ """
2
+ Base class for MongoDB document operations.
3
+ Handles collection validation and provides utility methods.
4
+ """
5
+ from typing import Any, TypeVar
6
+
7
+ TDocument = TypeVar("TDocument", bound="MongoDocument")
8
+
9
+
10
+ class BaseOperations:
11
+ """Base class for MongoDB operations mixins"""
12
+
13
+ @classmethod
14
+ def _get_collection(cls: type[TDocument]) -> Any:
15
+ """Get collection with type checking"""
16
+ if not hasattr(cls, "get_collection"):
17
+ raise AttributeError("Document model must implement get_collection()")
18
+ return cls.get_collection()
19
+
20
+ @classmethod
21
+ def _validate_document_type(cls: type[TDocument], document: Any) -> None:
22
+ """Ensure document matches collection type"""
23
+ if not isinstance(document, cls):
24
+ raise TypeError(f"Document must be of type {cls.__name__}")
@@ -0,0 +1,20 @@
1
+ """
2
+ CRUD operations mixin for MongoDocument.
3
+ """
4
+
5
+ from .base import BaseOperations, TDocument
6
+
7
+
8
+ class FindOperationsMixin(BaseOperations):
9
+ """Mixin class for query operations"""
10
+
11
+ @classmethod
12
+ async def find_one(cls: type[TDocument], filter: dict | None = None) -> TDocument | None:
13
+ """
14
+ Find single document by query filter
15
+
16
+ :param filter: MongoDB query filter
17
+ :return: Document instance or None
18
+ """
19
+ doc = await cls._get_collection().find_one(filter or {})
20
+ return cls.load(doc) if doc else None
@@ -0,0 +1,46 @@
1
+ from .base import BaseOperations, TDocument
2
+
3
+
4
+ class InsertOperationsMixin(BaseOperations):
5
+ """Mixin class for insert operations"""
6
+
7
+ @classmethod
8
+ async def insert_one(cls: type[TDocument], document: TDocument) -> TDocument:
9
+ """
10
+ Insert single document into collection
11
+
12
+ :param document: Document instance to insert
13
+ :return: Inserted document with _id
14
+ """
15
+ cls._validate_document_type(document)
16
+ result = await cls._get_collection().insert_one(document.dump())
17
+ document._id = result["inserted_id"]
18
+ return document
19
+
20
+ async def insert(self: TDocument) -> TDocument:
21
+ """
22
+ Insert the document instance into collection including existing _id
23
+
24
+ :return: Inserted document with _id (newly generated or existing)
25
+ """
26
+ self._validate_document_type(self)
27
+ result = await self._get_collection().insert_one(self.dump())
28
+ self._id = result["inserted_id"]
29
+ return self
30
+
31
+ @classmethod
32
+ async def insert_many(cls: type[TDocument], documents: list[TDocument]) -> list[TDocument]:
33
+ """
34
+ Batch insert documents
35
+
36
+ :param documents: List of document instances
37
+ :return: List of inserted documents with _ids
38
+ """
39
+ if not all(isinstance(d, cls) for d in documents):
40
+ raise TypeError("All documents must match collection type")
41
+
42
+ result = await cls._get_collection().insert_many([d.dump() for d in documents])
43
+
44
+ for doc, doc_id in zip(documents, result["inserted_ids"]):
45
+ doc._id = doc_id
46
+ return documents
@@ -0,0 +1,88 @@
1
+ Metadata-Version: 2.4
2
+ Name: mongospec
3
+ Version: 0.0.1
4
+ Summary: Async MongoDB ODM with msgspec integration and automatic collection binding
5
+ Author: Diprog
6
+ License: MIT
7
+ Project-URL: repository, https://github.com/diprog/mongospec
8
+ Classifier: Development Status :: 2 - Pre-Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Topic :: Database
13
+ Classifier: Typing :: Typed
14
+ Requires-Python: >=3.13
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: mongojet~=0.3.1
18
+ Requires-Dist: msgspec~=0.19.0
19
+ Dynamic: license-file
20
+
21
+ # mongospec
22
+
23
+ Minimal async MongoDB ODM built for **speed** and **simplicity**, featuring automatic collection binding and msgspec
24
+ integration.
25
+
26
+ ## Why mongospec?
27
+
28
+ - ⚡ **Blazing fast** - Uses [msgspec](https://github.com/jcrist/msgspec) (fastest Python serialization)
29
+ and [mongojet](https://github.com/romis2012/mongojet) (fastest async MongoDB wrapper)
30
+ - 🧩 **Dead simple** - No complex abstractions, just clean document handling
31
+ - 🏎️ **Zero overhead** - Optimized for performance-critical applications
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install mongospec
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ```python
42
+ import asyncio
43
+ from datetime import datetime
44
+
45
+ import mongojet
46
+ import msgspec
47
+
48
+ import mongospec
49
+ from mongospec import MongoDocument
50
+
51
+
52
+ class User(MongoDocument):
53
+ __collection_name__ = "users"
54
+ __indexes__ = [{"keys": [("email", 1)], "options": {"unique": True}}]
55
+
56
+ name: str
57
+ email: str
58
+ created_at: datetime = msgspec.field(default_factory=datetime.now)
59
+
60
+
61
+ async def main():
62
+ mongo_client = await mongojet.create_client("mongodb://localhost:27017")
63
+ try:
64
+ await mongospec.init(mongo_client.get_database("db"), document_types=[User])
65
+
66
+ # Create and insert user
67
+ user = User(name="Alice", email="alice@example.com")
68
+ await user.insert()
69
+
70
+ # Find document
71
+ found = await User.find_one({"email": "alice@example.com"})
72
+ print(found.dump())
73
+ finally:
74
+ await mongospec.close()
75
+
76
+
77
+ asyncio.run(main())
78
+ ```
79
+
80
+ ## Performance Features
81
+
82
+ - 🚀 **msgspec-powered** serialization (2-10x faster than alternatives)
83
+ - ⚡ **mongojet backend** (faster than Motor with identical API)
84
+ - 🧬 **Compiled structs** for document models
85
+ - 📉 **Minimal abstraction** overhead
86
+
87
+ ⚠️ **Early Alpha Notice**
88
+ Basic CRUD operations supported. Designed for rapid prototyping. Not production-ready. API will evolve.
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ mongospec/document/__init__.py
5
+ mongospec/document/document.py
6
+ mongospec/document/operations/__init__.py
7
+ mongospec/document/operations/base.py
8
+ mongospec/document/operations/find.py
9
+ mongospec/document/operations/insert.py
10
+ mongospec/mongospec.egg-info/PKG-INFO
11
+ mongospec/mongospec.egg-info/SOURCES.txt
12
+ mongospec/mongospec.egg-info/dependency_links.txt
13
+ mongospec/mongospec.egg-info/requires.txt
14
+ mongospec/mongospec.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ mongojet~=0.3.1
2
+ msgspec~=0.19.0
@@ -0,0 +1,30 @@
1
+ [project]
2
+ name = "mongospec"
3
+ version = "0.0.1"
4
+ authors = [{name = "Diprog"}]
5
+ description = "Async MongoDB ODM with msgspec integration and automatic collection binding"
6
+ readme = "README.md"
7
+ requires-python = ">=3.13"
8
+ license = {text = "MIT"}
9
+ dependencies = [
10
+ "mongojet~=0.3.1",
11
+ "msgspec~=0.19.0",
12
+ ]
13
+ classifiers = [
14
+ "Development Status :: 2 - Pre-Alpha",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Topic :: Database",
19
+ "Typing :: Typed"
20
+ ]
21
+
22
+ [project.urls]
23
+ repository = "https://github.com/diprog/mongospec"
24
+
25
+ [build-system]
26
+ requires = ["setuptools"]
27
+ build-backend = "setuptools.build_meta"
28
+
29
+ [tool.setuptools.packages.find]
30
+ where = ["mongospec"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+