2011-01-19 22:00:43 +08:00
|
|
|
## The contents of this file are subject to the Mozilla Public License
|
|
|
|
## Version 1.1 (the "License"); you may not use this file except in
|
|
|
|
## compliance with the License. You may obtain a copy of the License
|
|
|
|
## at http://www.mozilla.org/MPL/
|
2008-07-03 20:35:10 +08:00
|
|
|
##
|
2011-01-19 22:00:43 +08:00
|
|
|
## Software distributed under the License is distributed on an "AS IS"
|
|
|
|
## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
|
|
|
## the License for the specific language governing rights and
|
|
|
|
## limitations under the License.
|
2008-07-03 20:35:10 +08:00
|
|
|
##
|
2011-01-19 22:00:43 +08:00
|
|
|
## The Original Code is RabbitMQ.
|
2008-07-03 20:35:10 +08:00
|
|
|
##
|
2013-07-01 17:49:11 +08:00
|
|
|
## The Initial Developer of the Original Code is GoPivotal, Inc.
|
2014-03-18 01:25:20 +08:00
|
|
|
## Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved.
|
2008-07-03 20:35:10 +08:00
|
|
|
##
|
|
|
|
|
|
|
|
from __future__ import nested_scopes
|
|
|
|
import re
|
2008-10-10 23:26:06 +08:00
|
|
|
import sys
|
2011-01-13 19:23:01 +08:00
|
|
|
import os
|
2010-07-05 22:18:46 +08:00
|
|
|
from optparse import OptionParser
|
2008-10-10 23:26:06 +08:00
|
|
|
|
|
|
|
try:
|
2008-11-20 21:32:47 +08:00
|
|
|
try:
|
|
|
|
import simplejson as json
|
2015-12-17 21:00:53 +08:00
|
|
|
except ImportError as e:
|
2008-11-20 21:32:47 +08:00
|
|
|
if sys.hexversion >= 0x20600f0:
|
|
|
|
import json
|
|
|
|
else:
|
|
|
|
raise e
|
2008-10-10 23:26:06 +08:00
|
|
|
except ImportError:
|
2008-11-20 21:32:47 +08:00
|
|
|
print >> sys.stderr , " You don't appear to have simplejson.py installed"
|
2008-10-10 23:26:06 +08:00
|
|
|
print >> sys.stderr , " (an implementation of a JSON reader and writer in Python)."
|
|
|
|
print >> sys.stderr , " You can install it:"
|
2008-11-20 21:32:47 +08:00
|
|
|
print >> sys.stderr , " - by running 'apt-get install python-simplejson' on Debian-based systems,"
|
|
|
|
print >> sys.stderr , " - by running 'yum install python-simplejson' on Fedora/Red Hat system,"
|
2008-12-11 05:18:36 +08:00
|
|
|
print >> sys.stderr , " - by running 'port install py25-simplejson' on Macports on OS X"
|
|
|
|
print >> sys.stderr , " (you may need to say 'make PYTHON=python2.5', as well),"
|
2008-11-20 21:32:47 +08:00
|
|
|
print >> sys.stderr , " - from sources from 'http://pypi.python.org/pypi/simplejson'"
|
|
|
|
print >> sys.stderr , " - simplejson is a standard json library in the Python core since 2.6"
|
2008-10-10 23:26:06 +08:00
|
|
|
sys.exit(1)
|
2008-07-03 20:35:10 +08:00
|
|
|
|
|
|
|
def insert_base_types(d):
|
|
|
|
for t in ['octet', 'shortstr', 'longstr', 'short', 'long',
|
|
|
|
'longlong', 'bit', 'table', 'timestamp']:
|
|
|
|
d[t] = t
|
2009-10-07 21:55:02 +08:00
|
|
|
|
2010-07-05 22:18:46 +08:00
|
|
|
class AmqpSpecFileMergeConflict(Exception): pass
|
|
|
|
|
2010-07-07 20:43:36 +08:00
|
|
|
# If ignore_conflicts is true, then we allow acc and new to conflict,
|
2010-07-07 20:03:24 +08:00
|
|
|
# with whatever's already in acc winning and new being ignored. If
|
2010-07-07 20:43:36 +08:00
|
|
|
# ignore_conflicts is false, acc and new must not conflict.
|
2010-07-07 20:03:24 +08:00
|
|
|
|
2010-07-07 20:43:36 +08:00
|
|
|
def default_spec_value_merger(key, acc, new, ignore_conflicts):
|
|
|
|
if acc is None or acc == new or ignore_conflicts:
|
2009-10-07 21:55:02 +08:00
|
|
|
return new
|
2010-06-24 22:26:11 +08:00
|
|
|
else:
|
2010-07-07 20:03:24 +08:00
|
|
|
raise AmqpSpecFileMergeConflict(key, acc, new)
|
2009-10-07 21:55:02 +08:00
|
|
|
|
2010-07-07 20:43:36 +08:00
|
|
|
def extension_info_merger(key, acc, new, ignore_conflicts):
|
2010-07-07 20:03:24 +08:00
|
|
|
return acc + [new]
|
2009-10-07 23:11:54 +08:00
|
|
|
|
2010-07-07 20:43:36 +08:00
|
|
|
def domains_merger(key, acc, new, ignore_conflicts):
|
2010-07-07 23:07:45 +08:00
|
|
|
merged = dict((k, v) for [k, v] in acc)
|
|
|
|
for [k, v] in new:
|
2015-12-17 21:00:53 +08:00
|
|
|
if k in merged:
|
2010-07-07 20:43:36 +08:00
|
|
|
if not ignore_conflicts:
|
2010-07-07 20:03:24 +08:00
|
|
|
raise AmqpSpecFileMergeConflict(key, acc, new)
|
2010-07-05 22:18:46 +08:00
|
|
|
else:
|
2010-07-07 20:03:24 +08:00
|
|
|
merged[k] = v
|
2010-07-05 22:18:46 +08:00
|
|
|
|
2015-12-17 21:00:53 +08:00
|
|
|
return [[k, v] for (k, v) in merged.items()]
|
2009-10-07 23:11:54 +08:00
|
|
|
|
2010-07-07 20:43:36 +08:00
|
|
|
def merge_dict_lists_by(dict_key, acc, new, ignore_conflicts):
|
2010-07-07 21:42:46 +08:00
|
|
|
acc_index = set(v[dict_key] for v in acc)
|
|
|
|
result = list(acc) # shallow copy
|
|
|
|
for v in new:
|
|
|
|
if v[dict_key] in acc_index:
|
2010-07-07 20:43:36 +08:00
|
|
|
if not ignore_conflicts:
|
2010-07-07 20:03:24 +08:00
|
|
|
raise AmqpSpecFileMergeConflict(description, acc, new)
|
2010-07-05 22:18:46 +08:00
|
|
|
else:
|
2010-07-06 00:18:33 +08:00
|
|
|
result.append(v)
|
2010-06-21 10:44:33 +08:00
|
|
|
return result
|
|
|
|
|
2010-07-07 20:43:36 +08:00
|
|
|
def constants_merger(key, acc, new, ignore_conflicts):
|
|
|
|
return merge_dict_lists_by("name", acc, new, ignore_conflicts)
|
2009-10-07 23:11:54 +08:00
|
|
|
|
2010-07-07 20:43:36 +08:00
|
|
|
def methods_merger(classname, acc, new, ignore_conflicts):
|
|
|
|
return merge_dict_lists_by("name", acc, new, ignore_conflicts)
|
2009-10-07 23:11:54 +08:00
|
|
|
|
2010-07-07 20:43:36 +08:00
|
|
|
def properties_merger(classname, acc, new, ignore_conflicts):
|
|
|
|
return merge_dict_lists_by("name", acc, new, ignore_conflicts)
|
2010-07-05 23:30:28 +08:00
|
|
|
|
2010-07-07 20:43:36 +08:00
|
|
|
def class_merger(acc, new, ignore_conflicts):
|
2010-07-07 20:03:24 +08:00
|
|
|
acc["methods"] = methods_merger(acc["name"],
|
|
|
|
acc["methods"],
|
2010-07-05 22:18:46 +08:00
|
|
|
new["methods"],
|
2010-07-07 20:43:36 +08:00
|
|
|
ignore_conflicts)
|
2010-07-07 20:03:24 +08:00
|
|
|
acc["properties"] = properties_merger(acc["name"],
|
|
|
|
acc.get("properties", []),
|
2010-07-05 22:18:46 +08:00
|
|
|
new.get("properties", []),
|
2010-07-07 20:43:36 +08:00
|
|
|
ignore_conflicts)
|
2009-10-07 23:11:54 +08:00
|
|
|
|
2010-07-07 20:43:36 +08:00
|
|
|
def classes_merger(key, acc, new, ignore_conflicts):
|
2010-07-07 21:42:46 +08:00
|
|
|
acc_dict = dict((v["name"], v) for v in acc)
|
|
|
|
result = list(acc) # shallow copy
|
|
|
|
for w in new:
|
|
|
|
if w["name"] in acc_dict:
|
|
|
|
class_merger(acc_dict[w["name"]], w, ignore_conflicts)
|
2009-10-07 23:11:54 +08:00
|
|
|
else:
|
2010-06-21 18:49:50 +08:00
|
|
|
result.append(w)
|
2010-06-21 10:44:33 +08:00
|
|
|
return result
|
2009-10-07 23:11:54 +08:00
|
|
|
|
2009-10-07 21:55:02 +08:00
|
|
|
mergers = {
|
2009-10-07 23:11:54 +08:00
|
|
|
"extension": (extension_info_merger, []),
|
|
|
|
"domains": (domains_merger, []),
|
|
|
|
"constants": (constants_merger, []),
|
|
|
|
"classes": (classes_merger, []),
|
2009-10-07 21:55:02 +08:00
|
|
|
}
|
|
|
|
|
2010-07-07 20:43:36 +08:00
|
|
|
def merge_load_specs(filenames, ignore_conflicts):
|
2012-10-04 22:23:24 +08:00
|
|
|
handles = [open(filename) for filename in filenames]
|
2009-10-07 21:55:02 +08:00
|
|
|
docs = [json.load(handle) for handle in handles]
|
|
|
|
spec = {}
|
|
|
|
for doc in docs:
|
2015-12-17 21:00:53 +08:00
|
|
|
for (key, value) in doc.items():
|
2009-10-07 21:55:02 +08:00
|
|
|
(merger, default_value) = mergers.get(key, (default_spec_value_merger, None))
|
2010-07-07 20:43:36 +08:00
|
|
|
spec[key] = merger(key, spec.get(key, default_value), value, ignore_conflicts)
|
2009-10-07 21:55:02 +08:00
|
|
|
for handle in handles: handle.close()
|
|
|
|
return spec
|
2015-12-18 23:00:57 +08:00
|
|
|
|
2008-07-03 20:35:10 +08:00
|
|
|
class AmqpSpec:
|
2010-07-05 22:18:46 +08:00
|
|
|
# Slight wart: use a class member rather than change the ctor signature
|
|
|
|
# to avoid breaking everyone else's code.
|
2010-07-07 20:43:36 +08:00
|
|
|
ignore_conflicts = False
|
2010-07-05 22:18:46 +08:00
|
|
|
|
2009-10-07 21:55:02 +08:00
|
|
|
def __init__(self, filenames):
|
2010-07-07 20:43:36 +08:00
|
|
|
self.spec = merge_load_specs(filenames, AmqpSpec.ignore_conflicts)
|
2008-07-03 20:35:10 +08:00
|
|
|
|
|
|
|
self.major = self.spec['major-version']
|
|
|
|
self.minor = self.spec['minor-version']
|
2015-12-17 21:00:53 +08:00
|
|
|
self.revision = 'revision' in self.spec and self.spec['revision'] or 0
|
2008-07-03 20:35:10 +08:00
|
|
|
self.port = self.spec['port']
|
|
|
|
|
|
|
|
self.domains = {}
|
|
|
|
insert_base_types(self.domains)
|
|
|
|
for entry in self.spec['domains']:
|
|
|
|
self.domains[ entry[0] ] = entry[1]
|
|
|
|
|
|
|
|
self.constants = []
|
|
|
|
for d in self.spec['constants']:
|
2015-12-17 21:00:53 +08:00
|
|
|
if 'class' in d:
|
2008-07-03 20:35:10 +08:00
|
|
|
klass = d['class']
|
|
|
|
else:
|
|
|
|
klass = ''
|
|
|
|
self.constants.append((d['name'], d['value'], klass))
|
|
|
|
|
|
|
|
self.classes = []
|
|
|
|
for element in self.spec['classes']:
|
2011-01-07 09:19:12 +08:00
|
|
|
self.classes.append(AmqpClass(self, element))
|
2015-12-18 23:00:57 +08:00
|
|
|
|
2008-07-03 20:35:10 +08:00
|
|
|
def allClasses(self):
|
|
|
|
return self.classes
|
2015-12-18 23:00:57 +08:00
|
|
|
|
2008-07-03 20:35:10 +08:00
|
|
|
def allMethods(self):
|
|
|
|
return [m for c in self.classes for m in c.allMethods()]
|
|
|
|
|
|
|
|
def resolveDomain(self, n):
|
|
|
|
return self.domains[n]
|
|
|
|
|
|
|
|
class AmqpEntity:
|
|
|
|
def __init__(self, element):
|
|
|
|
self.element = element
|
|
|
|
self.name = element['name']
|
2015-12-18 23:00:57 +08:00
|
|
|
|
2008-07-03 20:35:10 +08:00
|
|
|
class AmqpClass(AmqpEntity):
|
|
|
|
def __init__(self, spec, element):
|
|
|
|
AmqpEntity.__init__(self, element)
|
|
|
|
self.spec = spec
|
|
|
|
self.index = int(self.element['id'])
|
|
|
|
|
|
|
|
self.methods = []
|
|
|
|
for method_element in self.element['methods']:
|
|
|
|
self.methods.append(AmqpMethod(self, method_element))
|
|
|
|
|
|
|
|
self.hasContentProperties = False
|
|
|
|
for method in self.methods:
|
|
|
|
if method.hasContent:
|
|
|
|
self.hasContentProperties = True
|
|
|
|
break
|
|
|
|
|
|
|
|
self.fields = []
|
2015-12-17 21:00:53 +08:00
|
|
|
if 'properties' in self.element:
|
2008-07-03 20:35:10 +08:00
|
|
|
index = 0
|
|
|
|
for e in self.element['properties']:
|
|
|
|
self.fields.append(AmqpField(self, e, index))
|
|
|
|
index = index + 1
|
2015-12-18 23:00:57 +08:00
|
|
|
|
2008-07-03 20:35:10 +08:00
|
|
|
def allMethods(self):
|
|
|
|
return self.methods
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return 'AmqpClass("' + self.name + '")'
|
|
|
|
|
|
|
|
class AmqpMethod(AmqpEntity):
|
|
|
|
def __init__(self, klass, element):
|
|
|
|
AmqpEntity.__init__(self, element)
|
|
|
|
self.klass = klass
|
|
|
|
self.index = int(self.element['id'])
|
2015-12-17 21:00:53 +08:00
|
|
|
if 'synchronous' in self.element:
|
2008-07-03 20:35:10 +08:00
|
|
|
self.isSynchronous = self.element['synchronous']
|
|
|
|
else:
|
|
|
|
self.isSynchronous = False
|
2015-12-17 21:00:53 +08:00
|
|
|
if 'content' in self.element:
|
2008-07-03 20:35:10 +08:00
|
|
|
self.hasContent = self.element['content']
|
|
|
|
else:
|
|
|
|
self.hasContent = False
|
|
|
|
self.arguments = []
|
|
|
|
|
|
|
|
index = 0
|
|
|
|
for argument in element['arguments']:
|
|
|
|
self.arguments.append(AmqpField(self, argument, index))
|
|
|
|
index = index + 1
|
2015-12-18 23:00:57 +08:00
|
|
|
|
2008-07-03 20:35:10 +08:00
|
|
|
def __repr__(self):
|
|
|
|
return 'AmqpMethod("' + self.klass.name + "." + self.name + '" ' + repr(self.arguments) + ')'
|
|
|
|
|
|
|
|
class AmqpField(AmqpEntity):
|
|
|
|
def __init__(self, method, element, index):
|
|
|
|
AmqpEntity.__init__(self, element)
|
|
|
|
self.method = method
|
|
|
|
self.index = index
|
|
|
|
|
2015-12-17 21:00:53 +08:00
|
|
|
if 'type' in self.element:
|
2008-07-03 20:35:10 +08:00
|
|
|
self.domain = self.element['type']
|
|
|
|
else:
|
|
|
|
self.domain = self.element['domain']
|
2015-12-18 23:00:57 +08:00
|
|
|
|
2015-12-17 21:00:53 +08:00
|
|
|
if 'default-value' in self.element:
|
2008-07-03 20:45:32 +08:00
|
|
|
self.defaultvalue = self.element['default-value']
|
|
|
|
else:
|
|
|
|
self.defaultvalue = None
|
2008-07-03 20:35:10 +08:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return 'AmqpField("' + self.name + '")'
|
|
|
|
|
2010-06-03 23:11:31 +08:00
|
|
|
def do_main(header_fn, body_fn):
|
|
|
|
do_main_dict({"header": header_fn, "body": body_fn})
|
|
|
|
|
|
|
|
def do_main_dict(funcDict):
|
2008-07-03 20:35:10 +08:00
|
|
|
def usage():
|
|
|
|
print >> sys.stderr , "Usage:"
|
2010-07-07 20:03:24 +08:00
|
|
|
print >> sys.stderr , " %s <function> <path_to_amqp_spec.json>... <path_to_output_file>" % (sys.argv[0])
|
2010-06-03 23:11:31 +08:00
|
|
|
print >> sys.stderr , " where <function> is one of %s" % ", ".join([k for k in funcDict.keys()])
|
|
|
|
|
2009-10-07 21:55:02 +08:00
|
|
|
def execute(fn, amqp_specs, out_file):
|
2009-01-10 02:05:59 +08:00
|
|
|
stdout = sys.stdout
|
|
|
|
f = open(out_file, 'w')
|
2011-01-13 19:23:01 +08:00
|
|
|
success = False
|
2009-01-10 02:05:59 +08:00
|
|
|
try:
|
2011-01-13 19:23:01 +08:00
|
|
|
sys.stdout = f
|
|
|
|
fn(amqp_specs)
|
|
|
|
success = True
|
2009-01-10 02:05:59 +08:00
|
|
|
finally:
|
|
|
|
sys.stdout = stdout
|
|
|
|
f.close()
|
2011-01-13 19:23:01 +08:00
|
|
|
if not success:
|
|
|
|
os.remove(out_file)
|
2009-01-10 02:05:59 +08:00
|
|
|
|
2010-07-05 22:18:46 +08:00
|
|
|
parser = OptionParser()
|
2010-07-07 20:43:36 +08:00
|
|
|
parser.add_option("--ignore-conflicts", action="store_true", dest="ignore_conflicts", default=False)
|
2010-07-05 22:18:46 +08:00
|
|
|
(options, args) = parser.parse_args()
|
|
|
|
|
|
|
|
if len(args) < 3:
|
2008-07-03 20:35:10 +08:00
|
|
|
usage()
|
|
|
|
sys.exit(1)
|
|
|
|
else:
|
2010-07-05 22:18:46 +08:00
|
|
|
function = args[0]
|
|
|
|
sources = args[1:-1]
|
|
|
|
dest = args[-1]
|
2010-07-07 20:43:36 +08:00
|
|
|
AmqpSpec.ignore_conflicts = options.ignore_conflicts
|
2015-12-17 21:00:53 +08:00
|
|
|
if function in funcDict:
|
2010-07-05 22:18:46 +08:00
|
|
|
execute(funcDict[function], sources, dest)
|
2008-07-03 20:35:10 +08:00
|
|
|
else:
|
|
|
|
usage()
|
2008-10-10 23:26:06 +08:00
|
|
|
sys.exit(1)
|