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

Reply via email to