1+ # mypy: disable-error-code="attr-defined,union-attr,assignment"
2+ from __future__ import annotations
3+
14import copy
25import numbers
36import warnings
47from functools import partial
8+ from typing import TYPE_CHECKING , Any
59
610import pymongo
711from bson import SON , DBRef , ObjectId , json_util
12+ from typing_extensions import Self
813
914from mongoengine import signals
1015from mongoengine .base .common import _DocumentRegistry
1520 LazyReference ,
1621 StrictDict ,
1722)
18- from mongoengine .base .fields import ComplexBaseField
23+ from mongoengine .base .fields import BaseField , ComplexBaseField
1924from mongoengine .common import _import_class
2025from mongoengine .errors import (
2126 FieldDoesNotExist ,
2631)
2732from mongoengine .pymongo_support import LEGACY_JSON_OPTIONS
2833
34+ if TYPE_CHECKING :
35+ from mongoengine .fields import DynamicField
36+
2937__all__ = ("BaseDocument" , "NON_FIELD_ERRORS" )
3038
3139NON_FIELD_ERRORS = "__all__"
3240
3341try :
34- GEOHAYSTACK = pymongo .GEOHAYSTACK
42+ GEOHAYSTACK = pymongo .GEOHAYSTACK # type: ignore[attr-defined]
3543except AttributeError :
3644 GEOHAYSTACK = None
3745
@@ -62,7 +70,12 @@ class BaseDocument:
6270 _dynamic_lock = True
6371 STRICT = False
6472
65- def __init__ (self , * args , ** values ):
73+ # Fields, added by metaclass
74+ _class_name : str
75+ _fields : dict [str , BaseField ]
76+ _meta : dict [str , Any ]
77+
78+ def __init__ (self , * args , ** values ) -> None :
6679 """
6780 Initialise a document or an embedded document.
6881
@@ -103,7 +116,7 @@ def __init__(self, *args, **values):
103116 else :
104117 self ._data = {}
105118
106- self ._dynamic_fields = SON ()
119+ self ._dynamic_fields : SON [ str , DynamicField ] = SON ()
107120
108121 # Assign default values for fields
109122 # not set in the constructor
@@ -329,13 +342,15 @@ def get_text_score(self):
329342
330343 return self ._data ["_text_score" ]
331344
332- def to_mongo (self , use_db_field = True , fields = None ):
345+ def to_mongo (
346+ self , use_db_field : bool = True , fields : list [str ] | None = None
347+ ) -> SON [Any , Any ]:
333348 """
334349 Return as SON data ready for use with MongoDB.
335350 """
336351 fields = fields or []
337352
338- data = SON ()
353+ data : SON [ str , Any ] = SON ()
339354 data ["_id" ] = None
340355 data ["_cls" ] = self ._class_name
341356
@@ -354,7 +369,7 @@ def to_mongo(self, use_db_field=True, fields=None):
354369
355370 if value is not None :
356371 f_inputs = field .to_mongo .__code__ .co_varnames
357- ex_vars = {}
372+ ex_vars : dict [ str , Any ] = {}
358373 if fields and "fields" in f_inputs :
359374 key = "%s." % field_name
360375 embedded_fields = [
@@ -370,7 +385,7 @@ def to_mongo(self, use_db_field=True, fields=None):
370385
371386 # Handle self generating fields
372387 if value is None and field ._auto_gen :
373- value = field .generate ()
388+ value = field .generate () # type: ignore[attr-defined]
374389 self ._data [field_name ] = value
375390
376391 if value is not None or field .null :
@@ -385,7 +400,7 @@ def to_mongo(self, use_db_field=True, fields=None):
385400
386401 return data
387402
388- def validate (self , clean = True ):
403+ def validate (self , clean : bool = True ) -> None :
389404 """Ensure that all fields' values are valid and that required fields
390405 are present.
391406
@@ -439,7 +454,7 @@ def validate(self, clean=True):
439454 message = f"ValidationError ({ self ._class_name } :{ pk } ) "
440455 raise ValidationError (message , errors = errors )
441456
442- def to_json (self , * args , ** kwargs ) :
457+ def to_json (self , * args : Any , ** kwargs : Any ) -> str :
443458 """Convert this document to JSON.
444459
445460 :param use_db_field: Serialize field names as they appear in
@@ -461,7 +476,7 @@ def to_json(self, *args, **kwargs):
461476 return json_util .dumps (self .to_mongo (use_db_field ), * args , ** kwargs )
462477
463478 @classmethod
464- def from_json (cls , json_data , created = False , ** kwargs ) :
479+ def from_json (cls , json_data : str , created : bool = False , ** kwargs : Any ) -> Self :
465480 """Converts json data to a Document instance.
466481
467482 :param str json_data: The json data to load into the Document.
@@ -687,7 +702,7 @@ def _get_changed_fields(self):
687702 self ._nestable_types_changed_fields (changed_fields , key , data )
688703 return changed_fields
689704
690- def _delta (self ):
705+ def _delta (self ) -> tuple [ dict [ str , Any ], dict [ str , Any ]] :
691706 """Returns the delta (set, unset) of the changes for a document.
692707 Gets any values that have been explicitly changed.
693708 """
@@ -771,14 +786,16 @@ def _delta(self):
771786 return set_data , unset_data
772787
773788 @classmethod
774- def _get_collection_name (cls ):
789+ def _get_collection_name (cls ) -> str | None :
775790 """Return the collection name for this class. None for abstract
776791 class.
777792 """
778793 return cls ._meta .get ("collection" , None )
779794
780795 @classmethod
781- def _from_son (cls , son , _auto_dereference = True , created = False ):
796+ def _from_son (
797+ cls , son : dict [str , Any ], _auto_dereference : bool = True , created : bool = False
798+ ) -> Self :
782799 """Create an instance of a Document (subclass) from a PyMongo SON (dict)"""
783800 if son and not isinstance (son , dict ):
784801 raise ValueError (
0 commit comments