Skip to content

Commit

Permalink
- return an OrderedDict if possible
Browse files Browse the repository at this point in the history
- use json.dumps in tests
- upgrade version
  • Loading branch information
hubo1016 committed Aug 21, 2017
1 parent 53aa764 commit 4af9862
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 7 deletions.
106 changes: 103 additions & 3 deletions namedstruct/namedstruct.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
import struct
import logging
import warnings
try:
from collections import OrderedDict as OrderedDict
except Exception:
_has_ordered_dict = False
OrderedDict = dict
else:
_has_ordered_dict = True


class ParseError(ValueError):
'''
Expand Down Expand Up @@ -177,6 +185,14 @@ def _gettype(self):
if tn is not None:
lastname = tn
return lastname
def _getbasetype(self):
'''
Return base type of this struct
:returns: a typedef object (e.g. nstruct)
'''
return getattr(self._parser, 'typedef', None)
def _setextra(self, extradata):
'''
Set the _extra field in the struct, which stands for the additional ("extra") data after the
Expand Down Expand Up @@ -334,7 +350,7 @@ def _create_struct(parser, inlineparent = None):
DUMPTYPE_KEY = 'key'
DUMPTYPE_NONE = 'none'

def dump(val, humanread = True, dumpextra = False, typeinfo = DUMPTYPE_FLAT):
def dump(val, humanread = True, dumpextra = False, typeinfo = DUMPTYPE_FLAT, ordered=True):
'''
Convert a parsed NamedStruct (probably with additional NamedStruct as fields) into a
JSON-friendly format, with only Python primitives (dictionaries, lists, bytes, integers etc.)
Expand Down Expand Up @@ -368,14 +384,19 @@ def dump(val, humanread = True, dumpextra = False, typeinfo = DUMPTYPE_FLAT):
r = dict((k, dump(v, humanread, dumpextra, typeinfo)) for k, v in val.__dict__.items() if not k[:1] != '_')
else:
if humanread:
r = t.formatdump(dict((k, dump(v, humanread, dumpextra, typeinfo)) for k, v in val.__dict__.items() if k[:1] != '_'), val)
r = dict((k, dump(v, humanread, dumpextra, typeinfo)) for k, v in val.__dict__.items() if k[:1] != '_')
if ordered:
r = t.reorderdump(r, val)
r = t.formatdump(r, val)
if hasattr(t, 'extraformatter'):
try:
r = t.extraformatter(r)
except:
NamedStruct._logger.log(logging.DEBUG, 'A formatter thrown an exception', exc_info = True)
else:
r = dict((k, dump(v, humanread, dumpextra, typeinfo)) for k, v in val.__dict__.items() if k[:1] != '_')
if ordered:
r = t.reorderdump(r, val)
if dumpextra:
extra = val._getextra()
if extra:
Expand Down Expand Up @@ -1328,6 +1349,11 @@ def formatdump(self, dumpvalue, v):
'''
return dumpvalue
def reorderdump(self, dumpvalue, v):
'''
Reorder the dict to match the original field order
'''
return dumpvalue

class arraytype(typedef):
'''
Expand Down Expand Up @@ -1451,6 +1477,32 @@ def array(self, size):

char = chartype()


def _merge_to(path, from_dict, to_dict):
current = from_dict
for p in path[:-1]:
if p not in current:
return
current = current[p]
if path[-1] not in current:
return
v = current.pop(path[-1])
current = to_dict
for p in path[:-1]:
if p not in to_dict:
to_dict[p] = OrderedDict()
current = to_dict[p]
current[path[-1]] = v


def _merge_dict(from_dict, to_dict):
for k,v in from_dict.items():
if isinstance(v, dict):
_merge_dict(v, to_dict.setdefault(k, OrderedDict()))
else:
to_dict[k] = v


class fixedstruct(typedef):
'''
A type with fixed structure. Do not define this kind of type directly; nstruct will automatically create
Expand Down Expand Up @@ -1518,6 +1570,10 @@ def __repr__(self, *args, **kwargs):
return str(self.readablename)
else:
return 'fixed(%r)' % (self.format,)
def _reorder_properties(self, unordered_dict, ordered_dict, val):
for p in self.properties:
property_path = p[0]
_merge_to(property_path, unordered_dict, ordered_dict)

class StructDefWarning(Warning):
pass
Expand Down Expand Up @@ -2034,6 +2090,7 @@ def __init__(self, *members, **arguments):
self.readablename, self.inlineself, self.initfunc, self, self.classifier, self.classifyby)
self._inline = self.fixedstruct.inline()
self.lastextra = False
self.propertynames = []
else:
self.seqs = seqs
if self.lastextra is None:
Expand Down Expand Up @@ -2140,6 +2197,30 @@ def _formatdump(ns, dumpvalue, val):
except:
NamedStruct._logger.log(logging.DEBUG, 'A formatter thrown an exception', exc_info = True)
return dumpvalue
def _reorder_properties(self, unordered_dict, ordered_dict, val):
_basetype = val._getbasetype()
while _basetype is not self:
if hasattr(_basetype, '_reorder_properties'):
_basetype._reorder_properties(unordered_dict, ordered_dict, val)
val = val._sub
_basetype = val._getbasetype()
if hasattr(self, 'fixedstruct'):
self.fixedstruct._reorder_properties(unordered_dict, ordered_dict, val)
else:
_seqindex = 0
for s, name in self.seqs:
if name is None:
t = val._seqs[_seqindex]._gettype()
if hasattr(t, '_reorder_properties'):
t._reorder_properties(unordered_dict, ordered_dict, val._seqs[_seqindex])
_seqindex += 1
else:
_merge_to((name[0],), unordered_dict, ordered_dict)
def reorderdump(self, dumpvalue, v):
to_dict = OrderedDict()
self._reorder_properties(dumpvalue, to_dict, v)
_merge_dict(dumpvalue, to_dict)
return to_dict

class enum(prim):
'''
Expand Down Expand Up @@ -2505,6 +2586,8 @@ def formatdump(self, dumpvalue, val):
except:
NamedStruct._logger.log(logging.DEBUG, 'A formatter thrown an exception', exc_info = True)
return dumpvalue
def _reorder_properties(self, unordered_dict, ordered_dict, val):
_merge_to((self.name,), unordered_dict, ordered_dict)

class DArrayParser(Parser):
'''
Expand Down Expand Up @@ -2825,6 +2908,15 @@ def formatdump(self, dumpvalue, val):
except:
NamedStruct._logger.log(logging.DEBUG, 'A formatter thrown an exception', exc_info = True)
return dumpvalue
def _reorder_properties(self, unordered_dict, ordered_dict, val):
for _, name in self.fields:
_merge_to((name,), unordered_dict, ordered_dict)
def reorderdump(self, dumpvalue, v):
to_dict = OrderedDict()
self._reorder_properties(dumpvalue, to_dict, v)
_merge_dict(dumpvalue, to_dict)
return to_dict


class VariantParser(Parser):
'''
Expand Down Expand Up @@ -2968,4 +3060,12 @@ def derive(self, newchild):
self.subclasses.append(newchild)
if hasattr(self, '_parser'):
newchild.parser()

def _reorder_properties(self, unordered_dict, ordered_dict, val):
t = val._seqs[0]._gettype()
if t is not None and hasattr(t, '_reorder_properties'):
t._reorder_properties(unordered_dict, ordered_dict, val._seqs[0])
def reorderdump(self, dumpvalue, v):
to_dict = OrderedDict()
self._reorder_properties(dumpvalue, to_dict, v)
_merge_dict(dumpvalue, to_dict)
return to_dict
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
pass
from setuptools import setup, find_packages

VERSION = '1.1.1'
VERSION = '1.2.0'

setup(name='nstruct',
version=VERSION,
Expand Down
6 changes: 3 additions & 3 deletions tests/testOpenflowDefs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import unittest
from misc.openflow import common, openflow10, openflow13
from namedstruct import nstruct, dump
from pprint import pprint
import json
import misc.ethernet as ethernet

class Test(unittest.TestCase):
Expand Down Expand Up @@ -63,8 +63,8 @@ def testOxm(self):
self.assertTrue(r is not None, 'Cannot parse message')
obj2, size = r
self.assertEqual(size, len(s), 'Cannot parse message')
pprint(dump(fm))
pprint(dump(obj2))
print(json.dumps(dump(fm), indent=2))
print(json.dumps(dump(obj2), indent=2))
self.assertEqual(dump(fm), dump(obj2), 'message changed after parsing')
def testDefs13Size(self):
# From openflow.h
Expand Down

0 comments on commit 4af9862

Please sign in to comment.