Skip to content

Commit

Permalink
add handel null value as gogoproto does while modelizing response data (
Browse files Browse the repository at this point in the history
  • Loading branch information
Revolution1 committed May 7, 2019
1 parent 14dd395 commit e305fd4
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 105 deletions.
115 changes: 61 additions & 54 deletions etcd3/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,65 @@ class AlarmRequestAlarmAction(EtcdModel, enum.Enum):
default: GET
"""
GET = 'GET'
ACTIVATE = 'ACTIVATE'
DEACTIVATE = 'DEACTIVATE'
GET = 'GET'


class CompareCompareResult(EtcdModel, enum.Enum):
class authpbPermissionType(EtcdModel, enum.Enum):
"""
ref: #/definitions/CompareCompareResult
ref: #/definitions/authpbPermissionType
default: EQUAL
default: READ
"""
EQUAL = 'EQUAL'
GREATER = 'GREATER'
LESS = 'LESS'
NOT_EQUAL = 'NOT_EQUAL'
READ = 'READ'
READWRITE = 'READWRITE'
WRITE = 'WRITE'


class CompareCompareTarget(EtcdModel, enum.Enum):
class EventEventType(EtcdModel, enum.Enum):
"""
ref: #/definitions/CompareCompareTarget
ref: #/definitions/EventEventType
default: VERSION
default: PUT
"""
DELETE = 'DELETE'
PUT = 'PUT'


class RangeRequestSortTarget(EtcdModel, enum.Enum):
"""
ref: #/definitions/RangeRequestSortTarget
default: KEY
"""
VERSION = 'VERSION'
CREATE = 'CREATE'
KEY = 'KEY'
MOD = 'MOD'
VALUE = 'VALUE'
VERSION = 'VERSION'


class EventEventType(EtcdModel, enum.Enum):
class etcdserverpbAlarmType(EtcdModel, enum.Enum):
"""
ref: #/definitions/EventEventType
ref: #/definitions/etcdserverpbAlarmType
default: PUT
default: NONE
"""
PUT = 'PUT'
DELETE = 'DELETE'
NONE = 'NONE'
NOSPACE = 'NOSPACE'


class CompareCompareResult(EtcdModel, enum.Enum):
"""
ref: #/definitions/CompareCompareResult
default: EQUAL
"""
EQUAL = 'EQUAL'
GREATER = 'GREATER'
LESS = 'LESS'
NOT_EQUAL = 'NOT_EQUAL'


class RangeRequestSortOrder(EtcdModel, enum.Enum):
Expand All @@ -58,22 +80,21 @@ class RangeRequestSortOrder(EtcdModel, enum.Enum):
default: NONE
"""
NONE = 'NONE'
ASCEND = 'ASCEND'
DESCEND = 'DESCEND'
NONE = 'NONE'


class RangeRequestSortTarget(EtcdModel, enum.Enum):
class CompareCompareTarget(EtcdModel, enum.Enum):
"""
ref: #/definitions/RangeRequestSortTarget
ref: #/definitions/CompareCompareTarget
default: KEY
default: VERSION
"""
KEY = 'KEY'
VERSION = 'VERSION'
CREATE = 'CREATE'
MOD = 'MOD'
VALUE = 'VALUE'
VERSION = 'VERSION'


class WatchCreateRequestFilterType(EtcdModel, enum.Enum):
Expand All @@ -82,45 +103,31 @@ class WatchCreateRequestFilterType(EtcdModel, enum.Enum):
default: NOPUT
"""
NOPUT = 'NOPUT'
NODELETE = 'NODELETE'


class authpbPermissionType(EtcdModel, enum.Enum):
"""
ref: #/definitions/authpbPermissionType
default: READ
"""
READ = 'READ'
WRITE = 'WRITE'
READWRITE = 'READWRITE'


class etcdserverpbAlarmType(EtcdModel, enum.Enum):
"""
ref: #/definitions/etcdserverpbAlarmType
default: NONE
"""
NONE = 'NONE'
NOSPACE = 'NOSPACE'
NOPUT = 'NOPUT'


name_to_model = {
'AlarmRequestAlarmAction': AlarmRequestAlarmAction,
'CompareCompareResult': CompareCompareResult,
'CompareCompareTarget': CompareCompareTarget,
'authpbPermissionType': authpbPermissionType,
'EventEventType': EventEventType,
'RangeRequestSortOrder': RangeRequestSortOrder,
'RangeRequestSortTarget': RangeRequestSortTarget,
'WatchCreateRequestFilterType': WatchCreateRequestFilterType,
'authpbPermissionType': authpbPermissionType,
'etcdserverpbAlarmType': etcdserverpbAlarmType,
'CompareCompareResult': CompareCompareResult,
'RangeRequestSortOrder': RangeRequestSortOrder,
'CompareCompareTarget': CompareCompareTarget,
'WatchCreateRequestFilterType': WatchCreateRequestFilterType,
}
__all__ = [
'AlarmRequestAlarmAction', 'CompareCompareResult', 'CompareCompareTarget',
'EventEventType', 'RangeRequestSortOrder', 'RangeRequestSortTarget',
'WatchCreateRequestFilterType', 'authpbPermissionType',
'etcdserverpbAlarmType', 'name_to_model'
'AlarmRequestAlarmAction',
'authpbPermissionType',
'EventEventType',
'RangeRequestSortTarget',
'etcdserverpbAlarmType',
'CompareCompareResult',
'RangeRequestSortOrder',
'CompareCompareTarget',
'WatchCreateRequestFilterType',
'name_to_model',
'EtcdModel',
]
10 changes: 7 additions & 3 deletions etcd3/stateful/watch.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from collections import deque

import logging
import re
import six
import socket
import threading
import time
from collections import deque

import six
from requests import ConnectionError
from requests.exceptions import ChunkedEncodingError

Expand Down Expand Up @@ -60,6 +60,10 @@ class Event(KeyValue):
"""

def __init__(self, data, header=None):
"""
:param data: dict data of a etcdserverpbWatchResponse.events[<mvccpbEvent>]
:param header: the header of etcdserverpbWatchResponse
"""
super(Event, self).__init__(data.kv._data)
self.header = header
self.type = data.type or EventType.PUT # default is PUT
Expand Down
85 changes: 50 additions & 35 deletions etcd3/swagger_helper.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from collections import OrderedDict

import base64
import copy
import enum
Expand All @@ -6,8 +8,6 @@
import keyword
import os
import re
from collections import OrderedDict

import six

from .utils import memoize_in_object, cached_property
Expand Down Expand Up @@ -230,61 +230,61 @@ def __repr__(self):
PROP_ENCODERS = {
None: lambda x: x,
'string': lambda x: x,
'integer': lambda x: int(x) if x is not None else x,
'int64': lambda x: int(x) if x is not None else x,
'int32': lambda x: int(x) if x is not None else x,
'uint64': lambda x: abs(int(x)) if x is not None else x,
'boolean': lambda x: bool(x) if x is not None else x,
'integer': lambda x: None if x is None else int(x),
'int64': lambda x: None if x is None else int(x),
'int32': lambda x: None if x is None else int(x),
'uint64': lambda x: None if x is None else abs(int(x)),
'boolean': lambda x: None if x is None else bool(x),
}

if six.PY3:
def _encode(data):
def _encode_bytes(data):
"""
Encode the given data using base-64
:param data:
:return: base-64 encoded string
"""
if data is None:
if not data:
return
if not isinstance(data, six.binary_type):
data = six.b(str(data))
return base64.b64encode(data).decode("utf-8")


# noqa: E303
PROP_ENCODERS['byte'] = _encode
PROP_ENCODERS['byte'] = _encode_bytes
else:
PROP_ENCODERS['byte'] = lambda x: base64.b64encode(x) if x is not None else x

PROP_DECODERS = {
None: lambda x: x,
'string': lambda x: x,
'integer': lambda x: int(x) if x is not None else x,
'int64': lambda x: int(x) if x is not None else x,
'int32': lambda x: int(x) if x is not None else x,
'uint64': lambda x: abs(int(x)) if x is not None else x,
'boolean': lambda x: bool(x) if x is not None else x,
'byte': lambda x: base64.b64decode(six.binary_type(x, encoding='utf-8')) if x is not None else x
'string': lambda x: x or '',
'integer': lambda x: 0 if x is None else int(x),
'int64': lambda x: 0 if x is None else int(x),
'int32': lambda x: 0 if x is None else int(x),
'uint64': lambda x: 0 if x is None else abs(int(x)),
'boolean': lambda x: False if x is None else bool(x),
'byte': lambda x: None if x is None else base64.b64decode(six.binary_type(x, encoding='utf-8'))
}

if six.PY3:
def _decode(data):
def _decode_bytes(data):
"""
Decode the base-64 encoded string
Decode the base64 encoded string
:param data:
:param data: base64 decodeable bytes of ascii string
:return: decoded string
"""
if data is None:
if not data:
return
if not isinstance(data, six.binary_type):
data = six.b(str(data))
return base64.b64decode(data.decode("utf-8"))
# if not isinstance(data, six.binary_type):
# data = six.b(str(data))
return base64.b64decode(data)


# noqa: E303
PROP_DECODERS['byte'] = _decode
PROP_DECODERS['byte'] = _decode_bytes
else:
PROP_DECODERS['byte'] = lambda x: base64.b64decode(x) if x is not None else x

Expand Down Expand Up @@ -360,7 +360,7 @@ def encode(self, data):
else:
if isinstance(data, enum.Enum):
data = data.value
rt = PROP_ENCODERS[self.type](PROP_ENCODERS[self.format](data))
rt = PROP_ENCODERS[self.format or self.type](data)
if self.default and rt is None:
rt = copy.copy(self.default)
if isinstance(rt, six.binary_type):
Expand All @@ -369,7 +369,7 @@ def encode(self, data):

def decode(self, data):
"""
decode the data to as the schema defined
decode the data as the schema defined
:param data: data to decode
:return: decoded data
Expand All @@ -383,14 +383,15 @@ def decode(self, data):
for k, v in six.iteritems(data):
if k not in self.properties:
continue
rt[k] = self.properties._get(k).decode(v)
_decoder = self.properties._get(k)
rt[k] = _decoder.decode(v)
return rt
elif self.type == 'array':
if data is None:
return
return [self.items.decode(i) for i in data]
else:
r = PROP_DECODERS[self.format](PROP_DECODERS[self.type](data))
r = PROP_DECODERS[self.format or self.type](data)
if self._is_enum:
m = name_to_model.get(self._path[-1])
if m:
Expand All @@ -400,24 +401,36 @@ def decode(self, data):
def getModel(self):
"""
get the model of the schema
Null handling:
Since etcd's swagger spec is converted from gogoproto files,
modelizing will follow the gogoprotobuf deserialization rule:
int -> 0
bool -> False
object -> None
array -> None
string -> ""
"""
if not hasattr(self, 'type') and self.type:
raise NotImplementedError
if self.type == 'object':
def init(this, data):
if data is None:
return
if not isinstance(data, dict):
raise TypeError("A dict expected, got a '%s' instead" % type(data))
this._node = self
this._data = data
for k in self.properties._keys():
if k not in data:
setattr(this, k, None)
continue
v = data[k]
v = data.get(k)
m = self.properties._get(k)
if m._is_schema:
m = m.getModel()
v = m(v)
if v is None and m.type not in ('object', 'array'):
v = PROP_DECODERS[m.format or m.type](None)
else:
m = m.getModel()
v = m(v)
setattr(this, k, v)

name = self._path[-1]
Expand All @@ -434,6 +447,8 @@ def init(this, data):
})
elif self.type == 'array':
def init(data):
if data is None:
return
if not isinstance(data, (list, tuple)):
raise TypeError("A list or tuple expected, got a '%s' instead" % type(data))
m = self.items.getModel()
Expand Down

0 comments on commit e305fd4

Please sign in to comment.