diff --git a/coolamqp/framing/compilation/compile_definitions.py b/coolamqp/framing/compilation/compile_definitions.py index 2864dc8813e6ffc28798ffcb2311926517689515..ead887a6ea30a55c7fb8704684dc1bc12d8f4850 100644 --- a/coolamqp/framing/compilation/compile_definitions.py +++ b/coolamqp/framing/compilation/compile_definitions.py @@ -13,17 +13,18 @@ from coolamqp.framing.compilation.utilities import get_constants, get_classes, g frepr, get_size TYPE_TRANSLATOR = { - 'shortstr': 'binary type (max length 255)', - 'longstr': 'binary type', - 'table': 'table. See coolamqp.uplink.framing.field_table', - 'bit': 'bool', - 'octet': 'int, 8 bit unsigned', - 'short': 'int, 16 bit unsigned', - 'long': 'int, 32 bit unsigned', - 'longlong': 'int, 64 bit unsigned', - 'timestamp': '64 bit signed POSIX timestamp (in seconds)', + 'shortstr': 'binary type (max length 255)', + 'longstr': 'binary type', + 'table': 'table. See coolamqp.uplink.framing.field_table', + 'bit': 'bool', + 'octet': 'int, 8 bit unsigned', + 'short': 'int, 16 bit unsigned', + 'long': 'int, 32 bit unsigned', + 'longlong': 'int, 64 bit unsigned', + 'timestamp': '64 bit signed POSIX timestamp (in seconds)', } + def compile_definitions(xml_file='resources/amqp0-9-1.extended.xml', out_file='coolamqp/framing/definitions.py'): """parse resources/amqp-0-9-1.xml into """ @@ -90,7 +91,7 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved line(' # %s\n', lines[0]) if len(lines) > 1: for ln in lines[1:]: - line(u' '*len(g)) + line(u' ' * len(g)) line(u' # %s\n', ln) else: line('\n') @@ -113,8 +114,8 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved class_id_to_contentpropertylist = {} # below are stored as strings! - methods_that_are_reply_reasons_for = {} # eg. ConnectionOpenOk: ConnectionOk - methods_that_are_replies_for = {} # eg. ConnectionOk: [ConnectionOpenOk] + methods_that_are_reply_reasons_for = {} # eg. ConnectionOpenOk: ConnectionOk + methods_that_are_replies_for = {} # eg. ConnectionOk: [ConnectionOpenOk] # Output classes for cls in get_classes(xml): @@ -132,7 +133,7 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved name_class(cls.name), to_docstring(None, cls.docs), frepr(cls.name), cls.index) if len(cls.properties) > 0: - class_id_to_contentpropertylist[cls.index] = name_class(cls.name)+'ContentPropertyList' + class_id_to_contentpropertylist[cls.index] = name_class(cls.name) + 'ContentPropertyList' line('''\nclass %sContentPropertyList(AMQPContentPropertyList): """ @@ -149,7 +150,8 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved if property.basic_type == 'bit': raise ValueError('bit properties are not supported!' ) - line(' Field(%s, %s, %s, %s),\n', frepr(property.name), frepr(property.type), frepr(property.basic_type), repr(property.reserved)) + line(' Field(%s, %s, %s, %s),\n', frepr(property.name), frepr(property.type), + frepr(property.basic_type), repr(property.reserved)) line(''' ] # A dictionary from a zero property list to a class typized with # some fields @@ -168,12 +170,13 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved my_props = [prop for prop in cls.properties if (not prop.reserved)] for property in my_props: line(' :param %s: %s\n', format_field_name(property.name), property.label) - line(' :type %s: %s (AMQP as %s)\n', format_field_name(property.name), TYPE_TRANSLATOR[property.basic_type], property.basic_type) + line(' :type %s: %s (AMQP as %s)\n', format_field_name(property.name), + TYPE_TRANSLATOR[property.basic_type], property.basic_type) line(' """\n') zpf_len = int(math.ceil(len(cls.properties) // 15)) - first_byte = True # in 2-byte group - piece_index = 7 # from 7 downto 0 + first_byte = True # in 2-byte group + piece_index = 7 # from 7 downto 0 fields_remaining = len(cls.properties) byte_chunk = [] @@ -183,18 +186,18 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved # a bit if piece_index > 0: if field.reserved or field.basic_type == 'bit': - pass # zero anyway + pass # zero anyway else: byte_chunk.append(u"(('%s' in kwargs) << %s)" % (format_field_name(field.name), piece_index)) piece_index -= 1 else: if first_byte: if field.reserved or field.basic_type == 'bit': - pass # zero anyway + pass # zero anyway else: byte_chunk.append(u"int('%s' in kwargs)" % (format_field_name(field.name),)) else: - # this is the "do we need moar flags" section + # this is the "do we need moar flags" section byte_chunk.append(u"kwargs['%s']" % ( int(fields_remaining > 1) )) @@ -207,7 +210,7 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved fields_remaining -= 1 if len(byte_chunk) > 0: - line(u' %s\n', u' | '.join(byte_chunk)) # We did not finish + line(u' %s\n', u' | '.join(byte_chunk)) # We did not finish line(u' ])\n zpf = six.binary_type(zpf)\n') line(u''' @@ -255,14 +258,14 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved # a bit if piece_index > 0: if field.reserved or field.basic_type == 'bit': - pass # zero + pass # zero else: byte_chunk.append(u"(('%s' in fields) << %s)" % (format_field_name(field.name), piece_index)) piece_index -= 1 else: if first_byte: if field.reserved or field.basic_type == 'bit': - pass #zero + pass # zero else: byte_chunk.append(u"int('%s' in kwargs)" % (format_field_name(field.name),)) else: @@ -336,7 +339,7 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved if len(non_reserved_fields) == 0: slots = u'' else: - slots = (u', '.join(map(lambda f: frepr(format_field_name(f.name)), non_reserved_fields)))+u', ' + slots = (u', '.join(map(lambda f: frepr(format_field_name(f.name)), non_reserved_fields))) + u', ' line('''\nclass %s(AMQPMethodPayload): """ @@ -375,7 +378,6 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved methods_that_are_replies_for[full_class_name].append(_namify(response)) if is_content_static: - line(''' STATIC_CONTENT = %s # spans LENGTH, CLASS ID, METHOD ID, ....., FRAME_END ''', to_code_binary(struct.pack('!LHH', static_size + 4, cls.index, method.index) + \ @@ -388,12 +390,11 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved line(' FIELDS = [ \n') for field in method.fields: - line(' Field(%s, %s, %s, reserved=%s),\n', frepr(field.name), frepr(field.type), frepr(field.basic_type), repr(field.reserved)) + line(' Field(%s, %s, %s, reserved=%s),\n', frepr(field.name), frepr(field.type), + frepr(field.basic_type), repr(field.reserved)) line(' ]\n') - - # constructor line('''\n def __init__(%s): """ @@ -411,9 +412,8 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved line(' :param %s: %s\n', format_field_name(field.name), to_docstring(field.label, field.docs, prefix=12, blank=False)) - - - line(' :type %s: %s (%s in AMQP)\n', format_field_name(field.name), TYPE_TRANSLATOR[field.basic_type], field.type) + line(' :type %s: %s (%s in AMQP)\n', format_field_name(field.name), + TYPE_TRANSLATOR[field.basic_type], field.type) line(' """\n') @@ -449,7 +449,8 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved dct = {} for cls in get_classes(xml): for method in cls.methods: - dct[((cls.index, method.index))] = '%s%s' % (name_class(cls.name), format_method_class_name(method.name)) + dct[((cls.index, method.index))] = '%s%s' % ( + name_class(cls.name), format_method_class_name(method.name)) line('\nIDENT_TO_METHOD = {\n') for k, v in dct.items(): @@ -462,7 +463,7 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved line('}\n\n') line('\nCLASS_ID_TO_CONTENT_PROPERTY_LIST = {\n') - for k,v in class_id_to_contentpropertylist.items(): + for k, v in class_id_to_contentpropertylist.items(): line(' %s: %s,\n', k, v) line('}\n\n') @@ -470,7 +471,7 @@ Field = collections.namedtuple('Field', ('name', 'type', 'basic_type', 'reserved # if a method is NOT a reply, it will not be in this dict # a method may be a reply for AT MOST one method REPLY_REASONS_FOR = {\n''') - for k,v in methods_that_are_reply_reasons_for.items(): + for k, v in methods_that_are_reply_reasons_for.items(): line(u' %s: %s,\n' % (k, v)) line(u'''} @@ -480,7 +481,7 @@ REPLY_REASONS_FOR = {\n''') # if a method has no replies, it will have an empty list as value here REPLIES_FOR= {\n''') - for k,v in methods_that_are_replies_for.items(): + for k, v in methods_that_are_replies_for.items(): line(u' %s: [%s],\n' % (k, u', '.join(map(str, v)))) line(u'}\n') diff --git a/coolamqp/framing/compilation/textcode_fields.py b/coolamqp/framing/compilation/textcode_fields.py index 5fb44d82b40f3b17b9614bbdeb4e48317b3c1fad..a20969c3ec812dccad2fcb876c8085b5e1d079f3 100644 --- a/coolamqp/framing/compilation/textcode_fields.py +++ b/coolamqp/framing/compilation/textcode_fields.py @@ -21,6 +21,7 @@ import math from coolamqp.framing.base import BASIC_TYPES, DYNAMIC_BASIC_TYPES from coolamqp.framing.compilation.utilities import format_field_name, get_size + def get_counter(fields, prefix=u'', indent_level=2): """ Emit code that counts how long this struct is. @@ -36,7 +37,7 @@ def get_counter(fields, prefix=u'', indent_level=2): bits = 0 for field in fields: bt = field.basic_type - nam = prefix+format_field_name(field.name) + nam = prefix + format_field_name(field.name) if (bits > 0) and (bt != 'bit'): # sync bits if not accumulator += int(math.ceil(bits / 8)) @@ -49,21 +50,21 @@ def get_counter(fields, prefix=u'', indent_level=2): elif BASIC_TYPES[bt][0] is not None: accumulator += BASIC_TYPES[field.basic_type][0] elif bt == 'shortstr': - parts.append('len('+nam+')') + parts.append('len(' + nam + ')') accumulator += 1 elif bt == 'longstr': parts.append('len(' + nam + ')') accumulator += 4 elif bt == 'table': parts.append('frame_table_size(' + nam + ')') - accumulator += 0 # because frame_table_size accounts for that 4 leading bytes + accumulator += 0 # because frame_table_size accounts for that 4 leading bytes else: raise Exception() if bits > 0: # sync bits accumulator += int(math.ceil(bits / 8)) - return (u' '*indent_level)+u'return '+(u' + '.join([str(accumulator)]+parts))+u'\n' + return (u' ' * indent_level) + u'return ' + (u' + '.join([str(accumulator)] + parts)) + u'\n' def get_from_buffer(fields, prefix='', indent_level=2, remark=False): @@ -72,16 +73,16 @@ def get_from_buffer(fields, prefix='', indent_level=2, remark=False): :param remark: BE FUCKING VERBOSE! #DEBUG """ code = [] + def emit(fmt, *args): args = list(args) - code.append(u' '*indent_level) + code.append(u' ' * indent_level) assert fmt.count('%s') == len(args) for arg in args: fmt = fmt.replace('%s', str(arg), 1) code.append(fmt) code.append('\n') - # actually go and load it bits = [] @@ -112,7 +113,7 @@ def get_from_buffer(fields, prefix='', indent_level=2, remark=False): emit_bits() if len(to_struct) == 0: return - fffnames = [a for a, b in to_struct if a != u'_'] # skip reserved + fffnames = [a for a, b in to_struct if a != u'_'] # skip reserved ffffmts = [b for a, b in to_struct] emit("%s, = struct.unpack_from('!%s', buf, offset)", u', '.join(fffnames), u''.join(ffffmts)) emit("offset += %s", ln['ln']) @@ -120,7 +121,7 @@ def get_from_buffer(fields, prefix='', indent_level=2, remark=False): del to_struct[:] for field in fields: - fieldname = prefix+format_field_name(field.name) + fieldname = prefix + format_field_name(field.name) if (len(bits) > 0) and (field.basic_type != u'bit'): emit_bits() @@ -143,7 +144,7 @@ def get_from_buffer(fields, prefix='', indent_level=2, remark=False): ln['ln'] += BASIC_TYPES[field.basic_type][0] elif field.basic_type == u'bit': bits.append('_' if field.reserved else fieldname) - elif field.basic_type == u'table': # oh my god + elif field.basic_type == u'table': # oh my god emit_structures() assert len(bits) == 0 @@ -151,7 +152,7 @@ def get_from_buffer(fields, prefix='', indent_level=2, remark=False): emit("%s, delta = deframe_table(buf, offset)", fieldname) emit("offset += delta") - else: # longstr or shortstr + else: # longstr or shortstr f_q, f_l = ('L', 4) if field.basic_type == u'longstr' else ('B', 1) to_struct.append(('s_len', f_q)) ln['ln'] += f_l @@ -183,7 +184,7 @@ def get_serializer(fields, prefix='', indent_level=2): def emit(fmt, *args): args = list(args) - code.append(u' '*indent_level) + code.append(u' ' * indent_level) while len(args) > 0: fmt = fmt.replace('%s', args[0], 1) del args[0] @@ -202,7 +203,7 @@ def get_serializer(fields, prefix='', indent_level=2): else: for bit_name, modif in zip(bits, range(8)): if bit_name != 'False': - p.append('('+bit_name+' << %s)' % (modif, )) # yes you can << bools + p.append('(' + bit_name + ' << %s)' % (modif,)) # yes you can << bools format_args.append(u' | '.join(p)) del bits[:] @@ -212,7 +213,7 @@ def get_serializer(fields, prefix='', indent_level=2): del format_args[:] for field in fields: - nam = prefix+format_field_name(field.name) + nam = prefix + format_field_name(field.name) if (len(bits) == 8) or ((len(bits) > 0) and field.basic_type != 'bit'): emit_bits() @@ -228,7 +229,7 @@ def get_serializer(fields, prefix='', indent_level=2): else: if field.basic_type in ('shortstr', 'longstr'): formats.append('B' if field.basic_type == 'shortstr' else 'I') - format_args.append('len('+nam+')') + format_args.append('len(' + nam + ')') emit_single_struct_pack() emit('buf.write(%s)', nam) elif field.basic_type == 'table': @@ -246,7 +247,6 @@ def get_serializer(fields, prefix='', indent_level=2): if len(formats) > 0: emit_single_struct_pack() - emit('') # eol + emit('') # eol return u''.join(code) - diff --git a/coolamqp/framing/compilation/utilities.py b/coolamqp/framing/compilation/utilities.py index 4227efed4d347f72c92bf8d67cf0113c5a18c9ea..d332441c90b8f86f9fc81aa0d3f610bd70c29655 100644 --- a/coolamqp/framing/compilation/utilities.py +++ b/coolamqp/framing/compilation/utilities.py @@ -10,16 +10,12 @@ from coolamqp.framing.base import BASIC_TYPES, DYNAMIC_BASIC_TYPES # docs may be None - - Constant = namedtuple('Constant', ('name', 'value', 'kind', 'docs')) # kind is AMQP constant class # value is int -Field = namedtuple('Field', ('name', 'type', 'label', 'docs', 'reserved', 'basic_type')) # reserved is bool -Method = namedtuple('Method', ('name', 'synchronous', 'index', 'label', 'docs', 'fields', 'response', - 'sent_by_client', 'sent_by_server', 'constant')) - # synchronous is bool, constant is bool - # repponse is a list of method.name -Class_ = namedtuple('Class_', ('name', 'index', 'docs', 'methods', 'properties')) # label is int -Domain = namedtuple('Domain', ('name', 'type', 'elementary')) # elementary is bool +Field = namedtuple('Field', ('name', 'type', 'label', 'docs', 'reserved', 'basic_type')) # reserved is bool +# synchronous is bool, constant is bool +# repponse is a list of method.name +Class_ = namedtuple('Class_', ('name', 'index', 'docs', 'methods', 'properties')) # label is int +Domain = namedtuple('Domain', ('name', 'type', 'elementary')) # elementary is bool class Method(object): @@ -51,14 +47,15 @@ class Method(object): body.append(eval(BASIC_TYPES[field.basic_type][2])) return b''.join(body) - def is_static(self, domain_to_type=None): # is size constant? + def is_static(self, domain_to_type=None): # is size constant? for field in self.fields: if field.basic_type in DYNAMIC_BASIC_TYPES: return False return True -def get_size(fields): # assume all fields have static length +def get_size(fields): # assume all fields have static length + """Assuming all fields have static length, return their length together. Supports bits""" size = 0 bits = 0 for field in fields: @@ -71,17 +68,18 @@ def get_size(fields): # assume all fields have static length if field.basic_type == 'bit': bits += 1 else: - size += len(BASIC_TYPES[field.basic_type][2]) # default minimum entry + size += len(BASIC_TYPES[field.basic_type][2]) # default minimum entry else: size += BASIC_TYPES[field.basic_type][0] - if bits > 0: # sync bits + if bits > 0: # sync bits size += int(math.ceil(bits / 8)) return size def get_docs(elem): + """Parse an XML element. Return documentation""" for kid in elem.getchildren(): if kid.tag == 'rule': @@ -94,11 +92,13 @@ def get_docs(elem): def for_domain(elem): + """Parse XML document. Return domains""" a = elem.attrib return Domain(six.text_type(a['name']), a['type'], a['type'] == a['name']) -def for_field(elem): # for <field> in <method> +def for_field(elem): # for <field> in <method> + """Parse method. Return fields""" a = elem.attrib return Field(six.text_type(a['name']), a['domain'] if 'domain' in a else a['type'], a.get('label', None), @@ -106,9 +106,12 @@ def for_field(elem): # for <field> in <method> a.get('reserved', '0') == '1', None) -def for_method(elem): # for <method> + +def for_method(elem): # for <method> + """Parse class, return methods""" a = elem.attrib - return Method(six.text_type(a['name']), bool(int(a.get('synchronous', '0'))), int(a['index']), a.get('label', None), get_docs(elem), + return Method(six.text_type(a['name']), bool(int(a.get('synchronous', '0'))), int(a['index']), a.get('label', None), + get_docs(elem), [for_field(fie) for fie in elem.getchildren() if fie.tag == 'field'], [e.attrib['name'] for e in elem.findall('response')], # if chassis=server that means server has to accept it @@ -116,13 +119,18 @@ def for_method(elem): # for <method> any([e.attrib.get('name', '') == 'client' for e in elem.getchildren() if e.tag == 'chassis']) ) -def for_class(elem): # for <class> + +def for_class(elem): # for <class> + """Parse XML, return classes""" a = elem.attrib - methods = sorted([for_method(me) for me in elem.getchildren() if me.tag == 'method'], key=lambda m: (m.name.strip('-')[0], -len(m.response))) + methods = sorted([for_method(me) for me in elem.getchildren() if me.tag == 'method'], + key=lambda m: (m.name.strip('-')[0], -len(m.response))) return Class_(six.text_type(a['name']), int(a['index']), get_docs(elem) or a['label'], methods, [for_field(e) for e in elem.getchildren() if e.tag == 'field']) -def for_constant(elem): # for <constant> + +def for_constant(elem): # for <constant> + """Parse XML, return constants""" a = elem.attrib return Constant(a['name'], int(a['value']), a.get('class', ''), get_docs(elem)) @@ -130,9 +138,11 @@ def for_constant(elem): # for <constant> def get_constants(xml): return [for_constant(e) for e in xml.findall('constant')] + def get_classes(xml): return [for_class(e) for e in xml.findall('class')] + def get_domains(xml): return [for_domain(e) for e in xml.findall('domain')] @@ -140,30 +150,36 @@ def get_domains(xml): def as_unicode(callable): def roll(*args, **kwargs): return six.text_type(callable(*args, **kwargs)) + return roll + def to_dict_by_name(list_of_things): return dict((a.name, a) for a in list_of_things) + @as_unicode def name_class(classname): """Change AMQP class name to Python class name""" return classname.capitalize() + @as_unicode def format_method_class_name(methodname): if '-' in methodname: i = methodname.find('-') - return methodname[0:i].capitalize() + methodname[i+1].upper() + methodname[i+2:] + return methodname[0:i].capitalize() + methodname[i + 1].upper() + methodname[i + 2:] else: return methodname.capitalize() + @as_unicode def format_field_name(field): if field in (u'global', u'type'): field = field + '_' return field.replace('-', '_') + def frepr(p, sop=six.text_type): if isinstance(p, (six.binary_type, six.text_type)): p = sop(p) @@ -174,6 +190,7 @@ def frepr(p, sop=six.text_type): else: return s + def to_code_binary(p): body = [] for q in p: @@ -183,21 +200,24 @@ def to_code_binary(p): if len(z) == 1: z = u'0' + z body.append(u'\\x' + z) - return u"b'"+(u''.join(body))+u"'" + return u"b'" + (u''.join(body)) + u"'" + def pythonify_name(p): return p.strip().replace('-', '_').upper() + def try_to_int(p): try: return int(p) except ValueError: return p -def to_docstring(label, doc, prefix=4, blank=True): # output a full docstring section + +def to_docstring(label, doc, prefix=4, blank=True): # output a full docstring section label = [] if label is None else [label] doc = [] if doc is None else [q.strip() for q in doc.split(u'\n') if len(q.strip()) > 0] - pre = u' '*prefix + pre = u' ' * prefix doc = label + doc @@ -217,6 +237,7 @@ def to_docstring(label, doc, prefix=4, blank=True): # output a full docstring se f = (u'\n'.join(pre + lin for lin in doc))[prefix:] return f + def ffmt(data, *args, **kwargs): for arg in args: op = str if kwargs.get('sane', True) else frepr diff --git a/coolamqp/framing/field_table.py b/coolamqp/framing/field_table.py index 04cb39a1c57be0b9421eb0736704e4173cb5c93d..e03d144ea4fc5eb09b46826143726d82d8ac9176 100644 --- a/coolamqp/framing/field_table.py +++ b/coolamqp/framing/field_table.py @@ -16,11 +16,11 @@ import struct import six -def enframe_decimal(buf, v): # convert decimal to bytes +def enframe_decimal(buf, v): # convert decimal to bytes dps = 0 for k in six.moves.xrange(20): k = v * (10 ** dps) - if abs(k-int(k)) < 0.00001: # epsilon + if abs(k - int(k)) < 0.00001: # epsilon return buf.write(struct.pack('!BI', dps, k)) raise ValueError('Could not convert %s to decimal', v) @@ -31,9 +31,9 @@ def deframe_decimal(buf, offset): return val / (10 ** scale), 5 -def deframe_shortstr(buf, offset): # -> value, bytes_eaten +def deframe_shortstr(buf, offset): # -> value, bytes_eaten ln, = struct.unpack_from('!B', buf, offset) - return buf[offset+1:offset+1+ln], 1+ln + return buf[offset + 1:offset + 1 + ln], 1 + ln def enframe_shortstr(buf, value): @@ -43,7 +43,7 @@ def enframe_shortstr(buf, value): def deframe_longstr(buf, offset): # -> value, bytes_eaten ln, = struct.unpack_from('!I', buf, offset) - return buf[offset+4:offset+4+ln], 4 + ln + return buf[offset + 4:offset + 4 + ln], 4 + ln def enframe_longstr(buf, value): @@ -52,10 +52,10 @@ def enframe_longstr(buf, value): FIELD_TYPES = { - # length, struct, (option)to_bytes (callable(buffer, value)), - # (option)from_bytes (callable(buffer, offset) -> value, bytes_consumed), - # (option)get_len (callable(value) -> length in bytes) - 't': (1, '!?'), # boolean + # length, struct, (option)to_bytes (callable(buffer, value)), + # (option)from_bytes (callable(buffer, offset) -> value, bytes_consumed), + # (option)get_len (callable(value) -> length in bytes) + 't': (1, '!?'), # boolean 'b': (1, '!b'), 'B': (1, '!B'), 'U': (2, '!H'), @@ -66,11 +66,11 @@ FIELD_TYPES = { 'l': (8, '!q'), 'f': (4, '!f'), 'd': (8, '!d'), - 'D': (5, None, enframe_decimal, deframe_decimal), # decimal-value - 's': (None, None, enframe_shortstr, deframe_shortstr, lambda val: len(val)+1), # shortstr - 'S': (None, None, enframe_longstr, deframe_longstr, lambda val: len(val)+4), # longstr + 'D': (5, None, enframe_decimal, deframe_decimal), # decimal-value + 's': (None, None, enframe_shortstr, deframe_shortstr, lambda val: len(val) + 1), # shortstr + 'S': (None, None, enframe_longstr, deframe_longstr, lambda val: len(val) + 4), # longstr 'T': (8, '!Q'), - 'V': (0, None, lambda buf, v: None, lambda buf, ofs: None, 0), # rendered as None + 'V': (0, None, lambda buf, v: None, lambda buf, ofs: None, 0), # rendered as None } @@ -114,20 +114,20 @@ def deframe_array(buf, offset): offset += 4 values = [] - while offset < (start_offset+1+ln): + while offset < (start_offset + 1 + ln): v, t, delta = deframe_field_value(buf, offset) offset += delta - values.append((v,t)) + values.append((v, t)) - if offset != start_offset+4+ln: + if offset != start_offset + 4 + ln: raise ValueError('Array longer than expected, took %s, expected %s bytes', - (offset-(start_offset+ln+4), ln+4)) + (offset - (start_offset + ln + 4), ln + 4)) - return values, ln+4 + return values, ln + 4 def enframe_array(buf, array): - buf.write(struct.pack('!I', frame_array_size(array)-4)) + buf.write(struct.pack('!I', frame_array_size(array) - 4)) for fv in array: enframe_field_value(buf, fv) @@ -139,7 +139,7 @@ def enframe_table(buf, table): :param table: :return: """ - buf.write(struct.pack('!I', frame_table_size(table)-4)) + buf.write(struct.pack('!I', frame_table_size(table) - 4)) for name, fv in table: buf.write(struct.pack('!B', len(name))) @@ -147,7 +147,7 @@ def enframe_table(buf, table): enframe_field_value(buf, fv) -def deframe_table(buf, start_offset): # -> (table, bytes_consumed) +def deframe_table(buf, start_offset): # -> (table, bytes_consumed) """:return: tuple (table, bytes consumed)""" offset = start_offset table_length, = struct.unpack_from('!L', buf, start_offset) @@ -156,22 +156,22 @@ def deframe_table(buf, start_offset): # -> (table, bytes_consumed) # we will check if it's really so. fields = [] - while offset < (start_offset+table_length+4): + while offset < (start_offset + table_length + 4): field_name, ln = deframe_shortstr(buf, offset) offset += ln fv, delta = deframe_field_value(buf, offset) offset += delta fields.append((field_name.tobytes(), fv)) - if offset > (start_offset+table_length+4): + if offset > (start_offset + table_length + 4): raise ValueError('Table turned out longer than expected! Found %s bytes expected %s', - (offset-start_offset, table_length)) + (offset - start_offset, table_length)) - return fields, table_length+4 + return fields, table_length + 4 def frame_field_value_size(fv): - v,t=fv + v, t = fv if FIELD_TYPES[t][0] is None: return FIELD_TYPES[t][4](v) + 1 else: