1+ from collections import ChainMap
2+ import logging
3+
14from django .apps import apps
25from django .conf import settings
36from django .utils import timezone
4- from django .utils .six import add_metaclass
57from elasticsearch .exceptions import NotFoundError
6- from elasticsearch_dsl import Document , connections , Index
8+ from elasticsearch_dsl import Document , connections
79from elasticsearch_dsl .document import IndexMeta , MetaField
8- from elasticsearch_dsl .index import DEFAULT_INDEX
10+ from elasticsearch_dsl .index import Index
911
1012from elasticsearch_metrics import signals
1113from elasticsearch_metrics import exceptions
1719
1820DEFAULT_DATE_FORMAT = "%Y.%m.%d"
1921
22+ logger = logging .getLogger (__name__ )
23+
24+
25+ class ReadonlyAttrMap :
26+ def __init__ (self , inner_obj ):
27+ self .__inner_obj = inner_obj
28+
29+ def __getitem__ (self , key ):
30+ try :
31+ return getattr (self .__inner_obj , key )
32+ except AttributeError as e :
33+ raise KeyError (key ) from e
34+
35+ def __contains__ (self , key ):
36+ return hasattr (self .__inner_obj , key )
37+
2038
2139class MetricMeta (IndexMeta ):
2240 """Metaclass for the base `Metric` class."""
@@ -71,41 +89,31 @@ def __new__(mcls, name, bases, attrs): # noqa: B902
7189
7290 # Override IndexMeta.construct_index so that
7391 # a new Index is created for every metric class
92+ # and Index attrs are inherited
7493 @classmethod
7594 def construct_index (cls , opts , bases ):
76- i = None
77- if opts is None :
78- # Inherit Index from base classes
79- for b in bases :
80- if getattr (b , "_index" , DEFAULT_INDEX ) is not DEFAULT_INDEX :
81- parent_index = b ._index
82- i = Index (
83- parent_index ._name ,
84- doc_type = parent_index ._mapping .doc_type ,
85- using = parent_index ._using ,
86- )
87- i ._settings = parent_index ._settings .copy ()
88- i ._aliases = parent_index ._aliases .copy ()
89- i ._analysis = parent_index ._analysis .copy ()
90- i ._doc_types = parent_index ._doc_types [:]
91- break
92- if i is None :
93- i = Index (
94- getattr (opts , "name" , "*" ),
95- doc_type = getattr (opts , "doc_type" , "doc" ),
96- using = getattr (opts , "using" , "default" ),
97- )
98- i .settings (** getattr (opts , "settings" , {}))
99- i .aliases (** getattr (opts , "aliases" , {}))
100- for a in getattr (opts , "analyzers" , ()):
95+ parent_configs = [
96+ base ._index .to_dict () for base in bases if hasattr (base , "_index" )
97+ ]
98+ if opts :
99+ index_config = ChainMap (ReadonlyAttrMap (opts ), * parent_configs )
100+ else :
101+ index_config = ChainMap (* parent_configs )
102+
103+ i = Index (
104+ index_config .get ("name" , "*" ),
105+ using = index_config .get ("using" , "default" ),
106+ )
107+ i .settings (** index_config .get ("settings" , {}))
108+ i .aliases (** index_config .get ("aliases" , {}))
109+ for a in index_config .get ("analyzers" , ()):
101110 i .analyzer (a )
102111 return i
103112
104113
105114# We need this intermediate BaseMetric class so that
106115# we can run MetricMeta ahead of IndexMeta
107- @add_metaclass (MetricMeta )
108- class BaseMetric (object ):
116+ class BaseMetric (metaclass = MetricMeta ):
109117 """Base metric class with which to define custom metric classes.
110118
111119 Example usage:
@@ -114,6 +122,7 @@ class BaseMetric(object):
114122
115123 from elasticsearch_metrics import metrics
116124
125+
117126 class PageView(metrics.Metric):
118127 user_id = metrics.Integer(index=True, doc_values=True)
119128
@@ -162,7 +171,7 @@ def check_index_template(cls, using=None):
162171 raise exceptions .IndexTemplateNotFoundError (
163172 "{template_name} does not exist for {metric_name}" .format (** locals ()),
164173 client_error = client_error ,
165- )
174+ ) from client_error
166175 else :
167176 current_data = list (template .values ())[0 ]
168177 template_data = cls .get_index_template ().to_dict ()
0 commit comments