Skip to content

Commit d25918f

Browse files
committed
Remove _(async|sync) top-level imports, use absolute instead
1 parent 9eff645 commit d25918f

24 files changed

+581
-347
lines changed

elasticsearch_dsl/_async/__init__.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,3 @@
1414
# KIND, either express or implied. See the License for the
1515
# specific language governing permissions and limitations
1616
# under the License.
17-
18-
try:
19-
from .document import AsyncDocument, AsyncIndexMeta
20-
from .faceted_search import AsyncFacetedSearch
21-
from .index import AsyncIndex, AsyncIndexTemplate
22-
from .search import AsyncMultiSearch, AsyncSearch
23-
from .update_by_query import AsyncUpdateByQuery
24-
25-
__all__ = [
26-
"AsyncDocument",
27-
"AsyncIndexMeta",
28-
"AsyncFacetedSearch",
29-
"AsyncIndex",
30-
"AsyncIndexTemplate",
31-
"AsyncSearch",
32-
"AsyncMultiSearch",
33-
"AsyncUpdateByQuery",
34-
]
35-
except (ImportError, SyntaxError):
36-
pass

elasticsearch_dsl/_async/document.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525
from elasticsearch.exceptions import NotFoundError, RequestError
2626
from six import add_metaclass
2727

28+
from .._base.document import DocumentMeta
2829
from ..connections import get_connection
29-
from ..document import DocumentMeta
3030
from ..exceptions import IllegalOperation, ValidationException
3131
from ..utils import DOC_META_FIELDS, META_FIELDS, ObjectBase, merge
3232
from .search import AsyncSearch

elasticsearch_dsl/_async/faceted_search.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
from six import iteritems, itervalues
1919

20-
from .._base import FacetedResponse
20+
from .._base.faceted_search import FacetedResponse
2121
from ..query import MatchAll
2222
from .search import AsyncSearch
2323

elasticsearch_dsl/_async/index.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
from .. import analysis
1919
from ..connections import get_connection
2020
from ..exceptions import IllegalOperation
21-
from ..mapping import Mapping
2221
from ..utils import merge
22+
from .mapping import AsyncMapping
2323
from .search import AsyncSearch
2424
from .update_by_query import AsyncUpdateByQuery
2525
from .utils import ASYNC_IS_ASYNC
@@ -73,7 +73,7 @@ def __init__(self, name, using="default"):
7373

7474
def get_or_create_mapping(self):
7575
if self._mapping is None:
76-
self._mapping = Mapping()
76+
self._mapping = AsyncMapping()
7777
return self._mapping
7878

7979
def as_template(self, template_name, pattern=None, order=None):

elasticsearch_dsl/_async/mapping.py

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# Licensed to Elasticsearch B.V. under one or more contributor
2+
# license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright
4+
# ownership. Elasticsearch B.V. licenses this file to you under
5+
# the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
try:
19+
import collections.abc as collections_abc # only works on python 3.3+
20+
except ImportError:
21+
import collections as collections_abc
22+
23+
from itertools import chain
24+
25+
from six import iteritems
26+
27+
from .._base.mapping import Properties
28+
from ..connections import get_connection
29+
from ..field import Nested, Text
30+
from .utils import ASYNC_IS_ASYNC
31+
32+
33+
class AsyncMapping(object):
34+
def __init__(self):
35+
self.properties = Properties()
36+
self._meta = {}
37+
38+
def __repr__(self):
39+
return str(type(self.__name__)) + "()"
40+
41+
def _clone(self):
42+
m = AsyncMapping()
43+
m.properties._params = self.properties._params.copy()
44+
return m
45+
46+
@classmethod
47+
async def from_es(cls, index, using="default"):
48+
m = cls()
49+
await m.update_from_es(index, using)
50+
return m
51+
52+
def resolve_nested(self, field_path):
53+
field = self
54+
nested = []
55+
parts = field_path.split(".")
56+
for i, step in enumerate(parts):
57+
try:
58+
field = field[step]
59+
except KeyError:
60+
return (), None
61+
if isinstance(field, Nested):
62+
nested.append(".".join(parts[: i + 1]))
63+
return nested, field
64+
65+
def resolve_field(self, field_path):
66+
field = self
67+
for step in field_path.split("."):
68+
try:
69+
field = field[step]
70+
except KeyError:
71+
return
72+
return field
73+
74+
def _collect_analysis(self):
75+
analysis = {}
76+
fields = []
77+
if "_all" in self._meta:
78+
fields.append(Text(**self._meta["_all"]))
79+
80+
for f in chain(fields, self.properties._collect_fields()):
81+
for analyzer_name in (
82+
"analyzer",
83+
"normalizer",
84+
"search_analyzer",
85+
"search_quote_analyzer",
86+
):
87+
if not hasattr(f, analyzer_name):
88+
continue
89+
analyzer = getattr(f, analyzer_name)
90+
d = analyzer.get_analysis_definition()
91+
# empty custom analyzer, probably already defined out of our control
92+
if not d:
93+
continue
94+
95+
# merge the definition
96+
# TODO: conflict detection/resolution
97+
for key in d:
98+
analysis.setdefault(key, {}).update(d[key])
99+
100+
return analysis
101+
102+
async def save(self, index, using="default"):
103+
from .index import AsyncIndex
104+
105+
index = AsyncIndex(index, using=using)
106+
index.mapping(self)
107+
return await index.save()
108+
109+
async def update_from_es(self, index, using="default"):
110+
es = get_connection(using, is_async=ASYNC_IS_ASYNC)
111+
raw = await es.indices.get_mapping(index=index)
112+
_, raw = raw.popitem()
113+
self._update_from_dict(raw["mappings"])
114+
115+
def _update_from_dict(self, raw):
116+
for name, definition in iteritems(raw.get("properties", {})):
117+
self.field(name, definition)
118+
119+
# metadata like _all etc
120+
for name, value in iteritems(raw):
121+
if name != "properties":
122+
if isinstance(value, collections_abc.Mapping):
123+
self.meta(name, **value)
124+
else:
125+
self.meta(name, value)
126+
127+
def update(self, mapping, update_only=False):
128+
for name in mapping:
129+
if update_only and name in self:
130+
# nested and inner objects, merge recursively
131+
if hasattr(self[name], "update"):
132+
# FIXME only merge subfields, not the settings
133+
self[name].update(mapping[name], update_only)
134+
continue
135+
self.field(name, mapping[name])
136+
137+
if update_only:
138+
for name in mapping._meta:
139+
if name not in self._meta:
140+
self._meta[name] = mapping._meta[name]
141+
else:
142+
self._meta.update(mapping._meta)
143+
144+
def __contains__(self, name):
145+
return name in self.properties.properties
146+
147+
def __getitem__(self, name):
148+
return self.properties.properties[name]
149+
150+
def __iter__(self):
151+
return iter(self.properties.properties)
152+
153+
def field(self, *args, **kwargs):
154+
self.properties.field(*args, **kwargs)
155+
return self
156+
157+
def meta(self, name, params=None, **kwargs):
158+
from ..mapping import META_FIELDS
159+
160+
if not name.startswith("_") and name not in META_FIELDS:
161+
name = "_" + name
162+
163+
if params and kwargs:
164+
raise ValueError("Meta configs cannot have both value and a dictionary.")
165+
166+
self._meta[name] = kwargs if params is None else params
167+
return self
168+
169+
def to_dict(self):
170+
meta = self._meta
171+
172+
# hard coded serialization of analyzers in _all
173+
if "_all" in meta:
174+
meta = meta.copy()
175+
_all = meta["_all"] = meta["_all"].copy()
176+
for f in ("analyzer", "search_analyzer", "search_quote_analyzer"):
177+
if hasattr(_all.get(f, None), "to_dict"):
178+
_all[f] = _all[f].to_dict()
179+
meta.update(self.properties.to_dict())
180+
return meta

elasticsearch_dsl/_async/search.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from elasticsearch.helpers import async_scan
2222
from six import iteritems, string_types
2323

24-
from .._base import AggsProxy, ProxyDescriptor, QueryProxy, Request
24+
from .._base.search import AggsProxy, ProxyDescriptor, QueryProxy, Request
2525
from ..aggs import A
2626
from ..connections import get_connection
2727
from ..exceptions import IllegalOperation

elasticsearch_dsl/_async/update_by_query.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717

18-
from .._base import ProxyDescriptor, QueryProxy, Request
18+
from .._base.search import ProxyDescriptor, QueryProxy, Request
1919
from ..connections import get_connection
2020
from ..query import Bool, Q
2121
from ..response import UpdateByQueryResponse

elasticsearch_dsl/_base/__init__.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,3 @@
1414
# KIND, either express or implied. See the License for the
1515
# specific language governing permissions and limitations
1616
# under the License.
17-
18-
from .faceted_search import FacetedResponse
19-
from .search import AggsProxy, ProxyDescriptor, QueryProxy, Request, Response
20-
21-
__all__ = [
22-
"FacetedResponse",
23-
"AggsProxy",
24-
"ProxyDescriptor",
25-
"QueryProxy",
26-
"Request",
27-
"Response",
28-
]

elasticsearch_dsl/_base/document.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Licensed to Elasticsearch B.V. under one or more contributor
2+
# license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright
4+
# ownership. Elasticsearch B.V. licenses this file to you under
5+
# the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
from six import add_metaclass, iteritems
19+
20+
from ..field import Field
21+
from ..utils import ObjectBase, iscoroutinefunction
22+
23+
24+
class MetaField(object):
25+
def __init__(self, *args, **kwargs):
26+
self.args, self.kwargs = args, kwargs
27+
28+
29+
class DocumentMeta(type):
30+
def __new__(cls, name, bases, attrs):
31+
# DocumentMeta filters attrs in place
32+
attrs["_doc_type"] = DocumentOptions(name, bases, attrs)
33+
return super(DocumentMeta, cls).__new__(cls, name, bases, attrs)
34+
35+
36+
class DocumentOptions(object):
37+
def __init__(self, name, bases, attrs):
38+
meta = attrs.pop("Meta", None)
39+
40+
# Decide whether we should use an 'AsyncMapping' or sync 'Mapping'
41+
# class based on whether the document.save() function is async or not.
42+
if "init" in attrs and iscoroutinefunction(attrs["init"]):
43+
from ..mapping import AsyncMapping
44+
45+
default_mapping_cls = AsyncMapping
46+
else:
47+
from ..mapping import Mapping
48+
49+
default_mapping_cls = Mapping
50+
51+
# create the mapping instance
52+
try:
53+
meta_mapping = meta.mapping
54+
55+
# If a synchronous 'Mapping' is defined on an
56+
# 'AsyncDocument' or the reverse we can correct
57+
# the definition by updating a new instance
58+
# with the proper I/O flavoring.
59+
if not isinstance(meta_mapping, default_mapping_cls):
60+
meta_mapping = default_mapping_cls()
61+
meta_mapping.update(meta_mapping)
62+
63+
self.mapping = meta_mapping
64+
except AttributeError:
65+
self.mapping = default_mapping_cls()
66+
67+
# register all declared fields into the mapping
68+
for name, value in list(iteritems(attrs)):
69+
if isinstance(value, Field):
70+
self.mapping.field(name, value)
71+
del attrs[name]
72+
73+
# add all the mappings for meta fields
74+
for name in dir(meta):
75+
if isinstance(getattr(meta, name, None), MetaField):
76+
params = getattr(meta, name)
77+
self.mapping.meta(name, *params.args, **params.kwargs)
78+
79+
# document inheritance - include the fields from parents' mappings
80+
for b in bases:
81+
if hasattr(b, "_doc_type") and hasattr(b._doc_type, "mapping"):
82+
self.mapping.update(b._doc_type.mapping, update_only=True)
83+
84+
@property
85+
def name(self):
86+
return self.mapping.properties.name
87+
88+
89+
@add_metaclass(DocumentMeta)
90+
class InnerDoc(ObjectBase):
91+
"""
92+
Common class for inner documents like Object or Nested
93+
"""
94+
95+
@classmethod
96+
def from_es(cls, data, data_only=False):
97+
if data_only:
98+
data = {"_source": data}
99+
return super(InnerDoc, cls).from_es(data)

0 commit comments

Comments
 (0)