On Wed, Jun 12, 2013 at 07:44:09PM +0900, YAMAMOTO Takashi wrote:
>
> Signed-off-by: YAMAMOTO Takashi <[email protected]>
> ---
> ryu/ofproto/ofproto_parser.py | 151
> +++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 149 insertions(+), 2 deletions(-)
>
> diff --git a/ryu/ofproto/ofproto_parser.py b/ryu/ofproto/ofproto_parser.py
> index 73a6d9e..e88802e 100644
> --- a/ryu/ofproto/ofproto_parser.py
> +++ b/ryu/ofproto/ofproto_parser.py
> @@ -14,8 +14,11 @@
> # See the License for the specific language governing permissions and
> # limitations under the License.
>
> +import base64
> +import collections
> import logging
> import struct
> +import sys
> import functools
> import inspect
>
> @@ -61,17 +64,138 @@ def create_list_of_base_attributes(f):
> return wrapper
>
>
> +# Some arguments to __init__ is mungled in order to avoid name conflicts
> +# with builtin names.
> +# The standard mangling is to append '_' in order to avoid name clashes
> +# with reserved keywords.
> +#
> +# PEP8:
> +# Function and method arguments
> +# If a function argument's name clashes with a reserved keyword,
> +# it is generally better to append a single trailing underscore
> +# rather than use an abbreviation or spelling corruption. Thus
> +# class_ is better than clss. (Perhaps better is to avoid such
> +# clashes by using a synonym.)
> +#
> +# grep __init__ *.py | grep '[^_]_\>' showed that
> +# 'len', 'property', 'set', 'type'
> +# A bit more generic way is adopted
> +import __builtin__
> +_RESERVED_KEYWORD = dir(__builtin__)
> +
> +
> +_mapdict = lambda f, d: dict([(k, f(v)) for k, v in d.items()])
> +_mapdict_key = lambda f, d: dict([(f(k), v) for k, v in d.items()])
> +
> +
> class StringifyMixin(object):
> def __str__(self):
> buf = ''
> sep = ''
> - for k, v in ofp_attrs(self):
> + for k, v in ofp_python_attrs(self):
> buf += sep
> buf += "%s=%s" % (k, repr(v)) # repr() to escape binaries
> sep = ','
> return self.__class__.__name__ + '(' + buf + ')'
> __repr__ = __str__ # note: str(list) uses __repr__ for elements
>
> + @staticmethod
> + def _is_ofp_class(dict_):
> + # we distinguish a dict like OFPSwitchFeatures.ports
> + # from OFPxxx classes using heuristics.
> + # exmples of OFP classes:
> + # {"OFPMatch": { ... }}
> + # {"MTIPv6SRC": { ... }}
> + assert isinstance(dict_, dict)
> + if len(dict_) != 1:
> + return False
> + k = dict_.keys()[0]
> + if not isinstance(k, (bytes, unicode)):
> + return False
> + return k.startswith("OFP") or k.startswith("MT")
> +
> + @staticmethod
> + def _encode_value(v):
> + if isinstance(v, (bytes, unicode)):
> + json_value = base64.b64encode(v)
> + elif isinstance(v, list):
> + json_value = map(StringifyMixin._encode_value, v)
> + elif isinstance(v, dict):
> + json_value = _mapdict(StringifyMixin._encode_value, v)
> + assert not StringifyMixin._is_ofp_class(json_value)
> + else:
> + try:
> + json_value = v.to_jsondict()
> + except:
> + json_value = v
> + return json_value
> +
> + def to_jsondict(self):
> + """returns an object to feed json.dumps()
> + """
> + dict_ = {}
> + for k, v in ofp_attrs(self):
> + dict_[k] = self._encode_value(v)
> + return {self.__class__.__name__: dict_}
> +
> + @classmethod
> + def _decode_value(cls, json_value):
> + if isinstance(json_value, (bytes, unicode)):
> + v = base64.b64decode(json_value)
> + elif isinstance(json_value, list):
> + decode = lambda x: cls._decode_value(x)
> + v = map(decode, json_value)
> + elif isinstance(json_value, dict):
> + if cls._is_ofp_class(json_value):
> + import sys
> + parser = sys.modules[cls.__module__]
> + v = ofp_from_jsondict(parser, json_value)
> + else:
> + decode = lambda x: cls._decode_value(x)
> + v = _mapdict(decode, json_value)
> + else:
> + v = json_value
> + return v
> +
> + @staticmethod
> + def _restore_args(dict_):
> + def restore(k):
> + if k in _RESERVED_KEYWORD:
> + return k + '_'
> + return k
> + return _mapdict_key(restore, dict_)
> +
> + @classmethod
> + def from_jsondict(cls, dict_, **additional_args):
> + """create an instance from a result of json.loads()
> + """
> + kwargs = cls._restore_args(_mapdict(cls._decode_value, dict_))
> + try:
> + return cls(**dict(kwargs, **additional_args))
> + except TypeError:
> + #debug
> + print "CLS", cls
> + print "ARG", dict_
> + print "KWARG", kwargs
> + raise
> +
> +
> +def ofp_from_jsondict(parser, jsondict):
> + assert len(jsondict) == 1
> + for k, v in jsondict.iteritems():
> + cls = getattr(parser, k)
> + assert not MsgBase in inspect.getmro(cls)
Doesn't issubclass(cls, MsgBase) work?
> + return cls.from_jsondict(v)
> +
> +
> +def ofp_msg_from_jsondict(dp, jsondict):
> + parser = dp.ofproto_parser
> + assert len(jsondict) == 1
> + for k, v in jsondict.iteritems():
> + cls = getattr(parser, k)
> + assert MsgBase in inspect.getmro(cls)
ditto.
thanks,
> + return cls.from_jsondict(v, datapath=dp)
> +
>
> class MsgBase(StringifyMixin):
> @create_list_of_base_attributes
> @@ -161,7 +285,14 @@ def msg_pack_into(fmt, buf, offset, *args):
> struct.pack_into(fmt, buf, offset, *args)
>
>
> -def ofp_attrs(msg_):
> +def ofp_python_attrs(msg_):
> + import collections
> + # a special case for namedtuple which seems widely used in
> + # ofp parser implementations.
> + if hasattr(msg_, '_fields'):
> + for k in msg_._fields:
> + yield(k, getattr(msg_, k))
> + return
> base = getattr(msg_, '_base_attributes', [])
> for k, v in inspect.getmembers(msg_):
> if k.startswith('_'):
> @@ -175,6 +306,22 @@ def ofp_attrs(msg_):
> yield (k, v)
>
>
> +def ofp_attrs(msg_):
> + for k, v in ofp_python_attrs(msg_):
> + if k.endswith('_') and k[:-1] in _RESERVED_KEYWORD:
> + # XXX currently only StringifyMixin has restoring logic
> + assert isinstance(msg_, StringifyMixin)
> + k = k[:-1]
> + yield (k, v)
> +
> +
> +def namedtuple(typename, fields, **kwargs):
> + class _namedtuple(StringifyMixin,
> + collections.namedtuple(typename, fields, **kwargs)):
> + pass
> + return _namedtuple
> +
> +
> def msg_str_attr(msg_, buf, attr_list=None):
> if attr_list is None:
> attr_list = ofp_attrs(msg_)
> --
> 1.8.1.5
>
>
> ------------------------------------------------------------------------------
> This SF.net email is sponsored by Windows:
>
> Build for Windows Store.
>
> http://p.sf.net/sfu/windows-dev2dev
> _______________________________________________
> Ryu-devel mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/ryu-devel
>
--
yamahata
------------------------------------------------------------------------------
This SF.net email is sponsored by Windows:
Build for Windows Store.
http://p.sf.net/sfu/windows-dev2dev
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel