Files
opensearch-pyd/opensearch/serializer.py
T
Rushi Agrawal ea397e0f92 Add support for Elasticsearch api versioning header
Originally added in Elasticsearch in commit
2c5f1d1569, removed in OpenSearch in
3eac282c57

Note that this just adds support of 'elasticsearch' header, there's no
'opensearch' header support as such.

Signed-off-by: Rushi Agrawal <rushi.agr@gmail.com>
2021-08-28 00:23:46 +05:30

181 lines
5.0 KiB
Python

# SPDX-License-Identifier: Apache-2.0
#
# The OpenSearch Contributors require contributions made to
# this file be licensed under the Apache-2.0 license or a
# compatible open source license.
#
# Modifications Copyright OpenSearch Contributors. See
# GitHub history for details.
#
# Licensed to Elasticsearch B.V. under one or more contributor
# license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you under
# the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
try:
import simplejson as json
except ImportError:
import json
import uuid
from datetime import date, datetime
from decimal import Decimal
from .compat import string_types
from .exceptions import ImproperlyConfigured, SerializationError
INTEGER_TYPES = ()
FLOAT_TYPES = (Decimal,)
TIME_TYPES = (date, datetime)
try:
import numpy as np
INTEGER_TYPES += (
np.int_,
np.intc,
np.int8,
np.int16,
np.int32,
np.int64,
np.uint8,
np.uint16,
np.uint32,
np.uint64,
)
FLOAT_TYPES += (
np.float_,
np.float16,
np.float32,
np.float64,
)
except ImportError:
np = None
try:
import pandas as pd
TIME_TYPES += (pd.Timestamp,)
except ImportError:
pd = None
class Serializer(object):
mimetype = ""
def loads(self, s):
raise NotImplementedError()
def dumps(self, data):
raise NotImplementedError()
class TextSerializer(Serializer):
mimetype = "text/plain"
def loads(self, s):
return s
def dumps(self, data):
if isinstance(data, string_types):
return data
raise SerializationError("Cannot serialize %r into text." % data)
class JSONSerializer(Serializer):
mimetype = "application/json"
def default(self, data):
if isinstance(data, TIME_TYPES) and getattr(pd, "NaT", None) is not data:
return data.isoformat()
elif isinstance(data, uuid.UUID):
return str(data)
elif isinstance(data, FLOAT_TYPES):
return float(data)
elif INTEGER_TYPES and isinstance(data, INTEGER_TYPES):
return int(data)
# Special cases for numpy and pandas types
elif np:
if isinstance(data, np.bool_):
return bool(data)
elif isinstance(data, np.datetime64):
return data.item().isoformat()
elif isinstance(data, np.ndarray):
return data.tolist()
if pd:
if isinstance(data, (pd.Series, pd.Categorical)):
return data.tolist()
elif data is getattr(pd, "NA", None):
return None
raise TypeError("Unable to serialize %r (type: %s)" % (data, type(data)))
def loads(self, s):
try:
return json.loads(s)
except (ValueError, TypeError) as e:
raise SerializationError(s, e)
def dumps(self, data):
# don't serialize strings
if isinstance(data, string_types):
return data
try:
return json.dumps(
data, default=self.default, ensure_ascii=False, separators=(",", ":")
)
except (ValueError, TypeError) as e:
raise SerializationError(data, e)
DEFAULT_SERIALIZERS = {
JSONSerializer.mimetype: JSONSerializer(),
TextSerializer.mimetype: TextSerializer(),
}
class Deserializer(object):
def __init__(self, serializers, default_mimetype="application/json"):
try:
self.default = serializers[default_mimetype]
except KeyError:
raise ImproperlyConfigured(
"Cannot find default serializer (%s)" % default_mimetype
)
self.serializers = serializers
def loads(self, s, mimetype=None):
if not mimetype:
deserializer = self.default
else:
# Treat 'application/vnd.elasticsearch+json'
# as application/json for compatibility.
if mimetype == "application/vnd.elasticsearch+json":
mimetype = "application/json"
# split out charset
mimetype, _, _ = mimetype.partition(";")
try:
deserializer = self.serializers[mimetype]
except KeyError:
raise SerializationError(
"Unknown mimetype, unable to deserialize: %s" % mimetype
)
return deserializer.loads(s)