405 lines
15 KiB
Python
405 lines
15 KiB
Python
|
"""
|
||
|
codegen.py generates pika/spec.py
|
||
|
|
||
|
"""
|
||
|
from __future__ import nested_scopes
|
||
|
|
||
|
import os
|
||
|
import re
|
||
|
import sys
|
||
|
|
||
|
RABBITMQ_PUBLIC_UMBRELLA = '../../rabbitmq-public-umbrella'
|
||
|
RABBITMQ_CODEGEN = 'rabbitmq-codegen'
|
||
|
PIKA_SPEC = '../pika/spec.py'
|
||
|
|
||
|
CODEGEN_PATH = os.path.realpath('%s/%s' % (RABBITMQ_PUBLIC_UMBRELLA,
|
||
|
RABBITMQ_CODEGEN))
|
||
|
print('codegen-path: %s' % CODEGEN_PATH)
|
||
|
sys.path.append(CODEGEN_PATH)
|
||
|
|
||
|
import amqp_codegen
|
||
|
|
||
|
DRIVER_METHODS = {
|
||
|
"Exchange.Bind": ["Exchange.BindOk"],
|
||
|
"Exchange.Unbind": ["Exchange.UnbindOk"],
|
||
|
"Exchange.Declare": ["Exchange.DeclareOk"],
|
||
|
"Exchange.Delete": ["Exchange.DeleteOk"],
|
||
|
"Queue.Declare": ["Queue.DeclareOk"],
|
||
|
"Queue.Bind": ["Queue.BindOk"],
|
||
|
"Queue.Purge": ["Queue.PurgeOk"],
|
||
|
"Queue.Delete": ["Queue.DeleteOk"],
|
||
|
"Queue.Unbind": ["Queue.UnbindOk"],
|
||
|
"Basic.Qos": ["Basic.QosOk"],
|
||
|
"Basic.Get": ["Basic.GetOk", "Basic.GetEmpty"],
|
||
|
"Basic.Ack": [],
|
||
|
"Basic.Reject": [],
|
||
|
"Basic.Recover": ["Basic.RecoverOk"],
|
||
|
"Basic.RecoverAsync": [],
|
||
|
"Tx.Select": ["Tx.SelectOk"],
|
||
|
"Tx.Commit": ["Tx.CommitOk"],
|
||
|
"Tx.Rollback": ["Tx.RollbackOk"]
|
||
|
}
|
||
|
|
||
|
|
||
|
def fieldvalue(v):
|
||
|
if isinstance(v, unicode):
|
||
|
return repr(v.encode('ascii'))
|
||
|
else:
|
||
|
return repr(v)
|
||
|
|
||
|
|
||
|
def normalize_separators(s):
|
||
|
s = s.replace('-', '_')
|
||
|
s = s.replace(' ', '_')
|
||
|
return s
|
||
|
|
||
|
|
||
|
def pyize(s):
|
||
|
s = normalize_separators(s)
|
||
|
if s in ('global', 'class'):
|
||
|
s += '_'
|
||
|
return s
|
||
|
|
||
|
|
||
|
def camel(s):
|
||
|
return normalize_separators(s).title().replace('_', '')
|
||
|
|
||
|
|
||
|
amqp_codegen.AmqpMethod.structName = lambda m: camel(
|
||
|
m.klass.name) + '.' + camel(m.name)
|
||
|
amqp_codegen.AmqpClass.structName = lambda c: camel(c.name) + "Properties"
|
||
|
|
||
|
|
||
|
def constantName(s):
|
||
|
return '_'.join(re.split('[- ]', s.upper()))
|
||
|
|
||
|
|
||
|
def flagName(c, f):
|
||
|
if c:
|
||
|
return c.structName() + '.' + constantName('flag_' + f.name)
|
||
|
else:
|
||
|
return constantName('flag_' + f.name)
|
||
|
|
||
|
|
||
|
def generate(specPath):
|
||
|
spec = amqp_codegen.AmqpSpec(specPath)
|
||
|
|
||
|
def genSingleDecode(prefix, cLvalue, unresolved_domain):
|
||
|
type = spec.resolveDomain(unresolved_domain)
|
||
|
if type == 'shortstr':
|
||
|
print(prefix + "%s, offset = data.decode_short_string(encoded, offset)" % cLvalue)
|
||
|
elif type == 'longstr':
|
||
|
print(prefix +
|
||
|
"length = struct.unpack_from('>I', encoded, offset)[0]")
|
||
|
print(prefix + "offset += 4")
|
||
|
print(prefix + "%s = encoded[offset:offset + length]" % cLvalue)
|
||
|
print(prefix + "try:")
|
||
|
print(prefix + " %s = str(%s)" % (cLvalue, cLvalue))
|
||
|
print(prefix + "except UnicodeEncodeError:")
|
||
|
print(prefix + " pass")
|
||
|
print(prefix + "offset += length")
|
||
|
elif type == 'octet':
|
||
|
print(prefix + "%s = struct.unpack_from('B', encoded, offset)[0]" %
|
||
|
cLvalue)
|
||
|
print(prefix + "offset += 1")
|
||
|
elif type == 'short':
|
||
|
print(prefix + "%s = struct.unpack_from('>H', encoded, offset)[0]" %
|
||
|
cLvalue)
|
||
|
print(prefix + "offset += 2")
|
||
|
elif type == 'long':
|
||
|
print(prefix + "%s = struct.unpack_from('>I', encoded, offset)[0]" %
|
||
|
cLvalue)
|
||
|
print(prefix + "offset += 4")
|
||
|
elif type == 'longlong':
|
||
|
print(prefix + "%s = struct.unpack_from('>Q', encoded, offset)[0]" %
|
||
|
cLvalue)
|
||
|
print(prefix + "offset += 8")
|
||
|
elif type == 'timestamp':
|
||
|
print(prefix + "%s = struct.unpack_from('>Q', encoded, offset)[0]" %
|
||
|
cLvalue)
|
||
|
print(prefix + "offset += 8")
|
||
|
elif type == 'bit':
|
||
|
raise Exception("Can't decode bit in genSingleDecode")
|
||
|
elif type == 'table':
|
||
|
print(Exception(prefix + "(%s, offset) = data.decode_table(encoded, offset)" %
|
||
|
cLvalue))
|
||
|
else:
|
||
|
raise Exception("Illegal domain in genSingleDecode", type)
|
||
|
|
||
|
def genSingleEncode(prefix, cValue, unresolved_domain):
|
||
|
type = spec.resolveDomain(unresolved_domain)
|
||
|
if type == 'shortstr':
|
||
|
print(prefix +
|
||
|
"assert isinstance(%s, str_or_bytes),\\\n%s 'A non-string value was supplied for %s'"
|
||
|
% (cValue, prefix, cValue))
|
||
|
print(prefix + "data.encode_short_string(pieces, %s)" % cValue)
|
||
|
elif type == 'longstr':
|
||
|
print(prefix +
|
||
|
"assert isinstance(%s, str_or_bytes),\\\n%s 'A non-string value was supplied for %s'"
|
||
|
% (cValue, prefix, cValue))
|
||
|
print(
|
||
|
prefix +
|
||
|
"value = %s.encode('utf-8') if isinstance(%s, unicode_type) else %s"
|
||
|
% (cValue, cValue, cValue))
|
||
|
print(prefix + "pieces.append(struct.pack('>I', len(value)))")
|
||
|
print(prefix + "pieces.append(value)")
|
||
|
elif type == 'octet':
|
||
|
print(prefix + "pieces.append(struct.pack('B', %s))" % cValue)
|
||
|
elif type == 'short':
|
||
|
print(prefix + "pieces.append(struct.pack('>H', %s))" % cValue)
|
||
|
elif type == 'long':
|
||
|
print(prefix + "pieces.append(struct.pack('>I', %s))" % cValue)
|
||
|
elif type == 'longlong':
|
||
|
print(prefix + "pieces.append(struct.pack('>Q', %s))" % cValue)
|
||
|
elif type == 'timestamp':
|
||
|
print(prefix + "pieces.append(struct.pack('>Q', %s))" % cValue)
|
||
|
elif type == 'bit':
|
||
|
raise Exception("Can't encode bit in genSingleEncode")
|
||
|
elif type == 'table':
|
||
|
print(Exception(prefix + "data.encode_table(pieces, %s)" % cValue))
|
||
|
else:
|
||
|
raise Exception("Illegal domain in genSingleEncode", type)
|
||
|
|
||
|
def genDecodeMethodFields(m):
|
||
|
print(" def decode(self, encoded, offset=0):")
|
||
|
bitindex = None
|
||
|
for f in m.arguments:
|
||
|
if spec.resolveDomain(f.domain) == 'bit':
|
||
|
if bitindex is None:
|
||
|
bitindex = 0
|
||
|
if bitindex >= 8:
|
||
|
bitindex = 0
|
||
|
if not bitindex:
|
||
|
print(
|
||
|
" bit_buffer = struct.unpack_from('B', encoded, offset)[0]")
|
||
|
print(" offset += 1")
|
||
|
print(" self.%s = (bit_buffer & (1 << %d)) != 0" %
|
||
|
(pyize(f.name), bitindex))
|
||
|
bitindex += 1
|
||
|
else:
|
||
|
bitindex = None
|
||
|
genSingleDecode(" ", "self.%s" % (pyize(f.name),),
|
||
|
f.domain)
|
||
|
print(" return self")
|
||
|
print('')
|
||
|
|
||
|
def genDecodeProperties(c):
|
||
|
print(" def decode(self, encoded, offset=0):")
|
||
|
print(" flags = 0")
|
||
|
print(" flagword_index = 0")
|
||
|
print(" while True:")
|
||
|
print(
|
||
|
" partial_flags = struct.unpack_from('>H', encoded, offset)[0]")
|
||
|
print(" offset += 2")
|
||
|
print(
|
||
|
" flags = flags | (partial_flags << (flagword_index * 16))")
|
||
|
print(" if not (partial_flags & 1):")
|
||
|
print(" break")
|
||
|
print(" flagword_index += 1")
|
||
|
for f in c.fields:
|
||
|
if spec.resolveDomain(f.domain) == 'bit':
|
||
|
print(" self.%s = (flags & %s) != 0" %
|
||
|
(pyize(f.name), flagName(c, f)))
|
||
|
else:
|
||
|
print(" if flags & %s:" % (flagName(c, f),))
|
||
|
genSingleDecode(" ", "self.%s" % (pyize(f.name),),
|
||
|
f.domain)
|
||
|
print(" else:")
|
||
|
print(" self.%s = None" % (pyize(f.name),))
|
||
|
print(" return self")
|
||
|
print('')
|
||
|
|
||
|
def genEncodeMethodFields(m):
|
||
|
print(" def encode(self):")
|
||
|
print(" pieces = list()")
|
||
|
bitindex = None
|
||
|
|
||
|
def finishBits():
|
||
|
if bitindex is not None:
|
||
|
print(" pieces.append(struct.pack('B', bit_buffer))")
|
||
|
|
||
|
for f in m.arguments:
|
||
|
if spec.resolveDomain(f.domain) == 'bit':
|
||
|
if bitindex is None:
|
||
|
bitindex = 0
|
||
|
print(" bit_buffer = 0")
|
||
|
if bitindex >= 8:
|
||
|
finishBits()
|
||
|
print(" bit_buffer = 0")
|
||
|
bitindex = 0
|
||
|
print(" if self.%s:" % pyize(f.name))
|
||
|
print(" bit_buffer = bit_buffer | (1 << %d)" %
|
||
|
bitindex)
|
||
|
bitindex += 1
|
||
|
else:
|
||
|
finishBits()
|
||
|
bitindex = None
|
||
|
genSingleEncode(" ", "self.%s" % (pyize(f.name),),
|
||
|
f.domain)
|
||
|
finishBits()
|
||
|
print(" return pieces")
|
||
|
print('')
|
||
|
|
||
|
def genEncodeProperties(c):
|
||
|
print(" def encode(self):")
|
||
|
print(" pieces = list()")
|
||
|
print(" flags = 0")
|
||
|
for f in c.fields:
|
||
|
if spec.resolveDomain(f.domain) == 'bit':
|
||
|
print(" if self.%s: flags = flags | %s" %
|
||
|
(pyize(f.name), flagName(c, f)))
|
||
|
else:
|
||
|
print(" if self.%s is not None:" % (pyize(f.name),))
|
||
|
print(" flags = flags | %s" % (flagName(c, f),))
|
||
|
genSingleEncode(" ", "self.%s" % (pyize(f.name),),
|
||
|
f.domain)
|
||
|
print(" flag_pieces = list()")
|
||
|
print(" while True:")
|
||
|
print(" remainder = flags >> 16")
|
||
|
print(" partial_flags = flags & 0xFFFE")
|
||
|
print(" if remainder != 0:")
|
||
|
print(" partial_flags |= 1")
|
||
|
print(
|
||
|
" flag_pieces.append(struct.pack('>H', partial_flags))")
|
||
|
print(" flags = remainder")
|
||
|
print(" if not flags:")
|
||
|
print(" break")
|
||
|
print(" return flag_pieces + pieces")
|
||
|
print('')
|
||
|
|
||
|
def fieldDeclList(fields):
|
||
|
return ''.join([", %s=%s" % (pyize(f.name), fieldvalue(f.defaultvalue))
|
||
|
for f in fields])
|
||
|
|
||
|
def fieldInitList(prefix, fields):
|
||
|
if fields:
|
||
|
return ''.join(["%sself.%s = %s\n" % (prefix, pyize(f.name), pyize(f.name)) \
|
||
|
for f in fields])
|
||
|
else:
|
||
|
return '%spass\n' % (prefix,)
|
||
|
|
||
|
print("""\"\"\"
|
||
|
AMQP Specification
|
||
|
==================
|
||
|
This module implements the constants and classes that comprise AMQP protocol
|
||
|
level constructs. It should rarely be directly referenced outside of Pika's
|
||
|
own internal use.
|
||
|
|
||
|
.. note:: Auto-generated code by codegen.py, do not edit directly. Pull
|
||
|
requests to this file without accompanying ``utils/codegen.py`` changes will be
|
||
|
rejected.
|
||
|
|
||
|
\"\"\"
|
||
|
|
||
|
import struct
|
||
|
from pika import amqp_object
|
||
|
from pika import data
|
||
|
from pika.compat import str_or_bytes, unicode_type
|
||
|
|
||
|
# Python 3 support for str object
|
||
|
str = bytes
|
||
|
""")
|
||
|
|
||
|
print("PROTOCOL_VERSION = (%d, %d, %d)" % (spec.major, spec.minor,
|
||
|
spec.revision))
|
||
|
print("PORT = %d" % spec.port)
|
||
|
print('')
|
||
|
|
||
|
# Append some constants that arent in the spec json file
|
||
|
spec.constants.append(('FRAME_MAX_SIZE', 131072, ''))
|
||
|
spec.constants.append(('FRAME_HEADER_SIZE', 7, ''))
|
||
|
spec.constants.append(('FRAME_END_SIZE', 1, ''))
|
||
|
spec.constants.append(('TRANSIENT_DELIVERY_MODE', 1, ''))
|
||
|
spec.constants.append(('PERSISTENT_DELIVERY_MODE', 2, ''))
|
||
|
|
||
|
constants = {}
|
||
|
for c, v, cls in spec.constants:
|
||
|
constants[constantName(c)] = v
|
||
|
|
||
|
for key in sorted(constants.keys()):
|
||
|
print("%s = %s" % (key, constants[key]))
|
||
|
print('')
|
||
|
|
||
|
for c in spec.allClasses():
|
||
|
print('')
|
||
|
print('class %s(amqp_object.Class):' % (camel(c.name),))
|
||
|
print('')
|
||
|
print(" INDEX = 0x%.04X # %d" % (c.index, c.index))
|
||
|
print(" NAME = %s" % (fieldvalue(camel(c.name)),))
|
||
|
print('')
|
||
|
|
||
|
for m in c.allMethods():
|
||
|
print(' class %s(amqp_object.Method):' % (camel(m.name),))
|
||
|
print('')
|
||
|
methodid = m.klass.index << 16 | m.index
|
||
|
print(" INDEX = 0x%.08X # %d, %d; %d" %
|
||
|
(methodid,
|
||
|
m.klass.index,
|
||
|
m.index,
|
||
|
methodid))
|
||
|
print(" NAME = %s" % (fieldvalue(m.structName(),)))
|
||
|
print('')
|
||
|
print(" def __init__(self%s):" %
|
||
|
(fieldDeclList(m.arguments),))
|
||
|
print(fieldInitList(' ', m.arguments))
|
||
|
print(" @property")
|
||
|
print(" def synchronous(self):")
|
||
|
print(" return %s" % m.isSynchronous)
|
||
|
print('')
|
||
|
genDecodeMethodFields(m)
|
||
|
genEncodeMethodFields(m)
|
||
|
|
||
|
for c in spec.allClasses():
|
||
|
if c.fields:
|
||
|
print('')
|
||
|
print('class %s(amqp_object.Properties):' % (c.structName(),))
|
||
|
print('')
|
||
|
print(" CLASS = %s" % (camel(c.name),))
|
||
|
print(" INDEX = 0x%.04X # %d" % (c.index, c.index))
|
||
|
print(" NAME = %s" % (fieldvalue(c.structName(),)))
|
||
|
print('')
|
||
|
|
||
|
index = 0
|
||
|
if c.fields:
|
||
|
for f in c.fields:
|
||
|
if index % 16 == 15:
|
||
|
index += 1
|
||
|
shortnum = index / 16
|
||
|
partialindex = 15 - (index % 16)
|
||
|
bitindex = shortnum * 16 + partialindex
|
||
|
print(' %s = (1 << %d)' % (flagName(None, f), bitindex))
|
||
|
index += 1
|
||
|
print('')
|
||
|
|
||
|
print(" def __init__(self%s):" % (fieldDeclList(c.fields),))
|
||
|
print(fieldInitList(' ', c.fields))
|
||
|
genDecodeProperties(c)
|
||
|
genEncodeProperties(c)
|
||
|
|
||
|
print("methods = {")
|
||
|
print(',\n'.join([" 0x%08X: %s" % (m.klass.index << 16 | m.index, m.structName())
|
||
|
for m in spec.allMethods()]))
|
||
|
print("}")
|
||
|
print('')
|
||
|
|
||
|
print("props = {")
|
||
|
print(',\n'.join([" 0x%04X: %s" % (c.index, c.structName())
|
||
|
for c in spec.allClasses()
|
||
|
if c.fields]))
|
||
|
print("}")
|
||
|
print('')
|
||
|
print('')
|
||
|
|
||
|
print("def has_content(methodNumber):")
|
||
|
print(' return methodNumber in (')
|
||
|
for m in spec.allMethods():
|
||
|
if m.hasContent:
|
||
|
print(' %s.INDEX,' % m.structName())
|
||
|
print(' )')
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
with open(PIKA_SPEC, 'w') as handle:
|
||
|
sys.stdout = handle
|
||
|
generate(['%s/amqp-rabbitmq-0.9.1.json' % CODEGEN_PATH])
|