Skip to content

Commit 47a7a06

Browse files
Do not map ClassVar attributes (#1936)
* Do not map ClassVar attributes Fixes #1927 * document ClassVar usage
1 parent 601bea7 commit 47a7a06

File tree

4 files changed

+43
-4
lines changed

4 files changed

+43
-4
lines changed

docs/persistence.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,18 @@ field needs to be referenced, such as when specifying sort options in a
271271
When specifying sorting order, the ``+`` and ``-`` unary operators can be used
272272
on the class field attributes to indicate ascending and descending order.
273273

274+
Finally, the ``ClassVar`` annotation can be used to define a regular class
275+
attribute that should not be mapped to the Elasticsearch index::
276+
277+
.. code:: python
278+
279+
from typing import ClassVar
280+
281+
class MyDoc(Document):
282+
title: M[str]
283+
created_at: M[datetime] = mapped_field(default_factory=datetime.now)
284+
my_var: ClassVar[str] # regular class variable, ignored by Elasticsearch
285+
274286
Note on dates
275287
~~~~~~~~~~~~~
276288

elasticsearch_dsl/document_base.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
TYPE_CHECKING,
2222
Any,
2323
Callable,
24+
ClassVar,
2425
Dict,
2526
Generic,
2627
List,
@@ -159,7 +160,10 @@ def __init__(self, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]):
159160
# field8: M[str] = mapped_field(MyCustomText(), default="foo")
160161
#
161162
# # legacy format without Python typing
162-
# field8 = Text()
163+
# field9 = Text()
164+
#
165+
# # ignore attributes
166+
# field10: ClassVar[string] = "a regular class variable"
163167
annotations = attrs.get("__annotations__", {})
164168
fields = set([n for n in attrs if isinstance(attrs[n], Field)])
165169
fields.update(annotations.keys())
@@ -172,10 +176,14 @@ def __init__(self, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]):
172176
# the field has a type annotation, so next we try to figure out
173177
# what field type we can use
174178
type_ = annotations[name]
179+
skip = False
175180
required = True
176181
multi = False
177182
while hasattr(type_, "__origin__"):
178-
if type_.__origin__ == Mapped:
183+
if type_.__origin__ == ClassVar:
184+
skip = True
185+
break
186+
elif type_.__origin__ == Mapped:
179187
# M[type] -> extract the wrapped type
180188
type_ = type_.__args__[0]
181189
elif type_.__origin__ == Union:
@@ -192,6 +200,9 @@ def __init__(self, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]):
192200
type_ = type_.__args__[0]
193201
else:
194202
break
203+
if skip or type_ == ClassVar:
204+
# skip ClassVar attributes
205+
continue
195206
field = None
196207
field_args: List[Any] = []
197208
field_kwargs: Dict[str, Any] = {}

tests/_async/test_document.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import pickle
2727
from datetime import datetime
2828
from hashlib import md5
29-
from typing import Any, Dict, List, Optional
29+
from typing import Any, ClassVar, Dict, List, Optional
3030

3131
import pytest
3232
from pytest import raises
@@ -675,6 +675,8 @@ class TypedDoc(AsyncDocument):
675675
s4: M[Optional[Secret]] = mapped_field(
676676
SecretField(), default_factory=lambda: "foo"
677677
)
678+
i1: ClassVar
679+
i2: ClassVar[int]
678680

679681
props = TypedDoc._doc_type.mapping.to_dict()["properties"]
680682
assert props == {
@@ -708,6 +710,9 @@ class TypedDoc(AsyncDocument):
708710
"s4": {"type": "text"},
709711
}
710712

713+
TypedDoc.i1 = "foo"
714+
TypedDoc.i2 = 123
715+
711716
doc = TypedDoc()
712717
assert doc.k3 == "foo"
713718
assert doc.s4 == "foo"
@@ -723,6 +728,9 @@ class TypedDoc(AsyncDocument):
723728
"s3",
724729
}
725730

731+
assert TypedDoc.i1 == "foo"
732+
assert TypedDoc.i2 == 123
733+
726734
doc.st = "s"
727735
doc.li = [1, 2, 3]
728736
doc.k1 = "k1"

tests/_sync/test_document.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import pickle
2727
from datetime import datetime
2828
from hashlib import md5
29-
from typing import Any, Dict, List, Optional
29+
from typing import Any, ClassVar, Dict, List, Optional
3030

3131
import pytest
3232
from pytest import raises
@@ -675,6 +675,8 @@ class TypedDoc(Document):
675675
s4: M[Optional[Secret]] = mapped_field(
676676
SecretField(), default_factory=lambda: "foo"
677677
)
678+
i1: ClassVar
679+
i2: ClassVar[int]
678680

679681
props = TypedDoc._doc_type.mapping.to_dict()["properties"]
680682
assert props == {
@@ -708,6 +710,9 @@ class TypedDoc(Document):
708710
"s4": {"type": "text"},
709711
}
710712

713+
TypedDoc.i1 = "foo"
714+
TypedDoc.i2 = 123
715+
711716
doc = TypedDoc()
712717
assert doc.k3 == "foo"
713718
assert doc.s4 == "foo"
@@ -723,6 +728,9 @@ class TypedDoc(Document):
723728
"s3",
724729
}
725730

731+
assert TypedDoc.i1 == "foo"
732+
assert TypedDoc.i2 == 123
733+
726734
doc.st = "s"
727735
doc.li = [1, 2, 3]
728736
doc.k1 = "k1"

0 commit comments

Comments
 (0)