Back to index

rabbitmq-server  2.8.4
amqp_codegen.py
Go to the documentation of this file.
00001 ##  The contents of this file are subject to the Mozilla Public License
00002 ##  Version 1.1 (the "License"); you may not use this file except in
00003 ##  compliance with the License. You may obtain a copy of the License
00004 ##  at http://www.mozilla.org/MPL/
00005 ##
00006 ##  Software distributed under the License is distributed on an "AS IS"
00007 ##  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
00008 ##  the License for the specific language governing rights and
00009 ##  limitations under the License.
00010 ##
00011 ##  The Original Code is RabbitMQ.
00012 ##
00013 ##  The Initial Developer of the Original Code is VMware, Inc.
00014 ##  Copyright (c) 2007-2012 VMware, Inc.  All rights reserved.
00015 ##
00016 
00017 from __future__ import nested_scopes
00018 import re
00019 import sys
00020 import os
00021 from optparse import OptionParser
00022 
00023 try:
00024     try:
00025         import simplejson as json
00026     except ImportError, e:
00027         if sys.hexversion >= 0x20600f0:
00028             import json
00029         else:
00030             raise e
00031 except ImportError:
00032     print >> sys.stderr , " You don't appear to have simplejson.py installed"
00033     print >> sys.stderr , " (an implementation of a JSON reader and writer in Python)."
00034     print >> sys.stderr , " You can install it:"
00035     print >> sys.stderr , "   - by running 'apt-get install python-simplejson' on Debian-based systems,"
00036     print >> sys.stderr , "   - by running 'yum install python-simplejson' on Fedora/Red Hat system,"
00037     print >> sys.stderr , "   - by running 'port install py25-simplejson' on Macports on OS X"
00038     print >> sys.stderr , "     (you may need to say 'make PYTHON=python2.5', as well),"
00039     print >> sys.stderr , "   - from sources from 'http://pypi.python.org/pypi/simplejson'"
00040     print >> sys.stderr , "   - simplejson is a standard json library in the Python core since 2.6"
00041     sys.exit(1)
00042 
00043 def insert_base_types(d):
00044     for t in ['octet', 'shortstr', 'longstr', 'short', 'long',
00045               'longlong', 'bit', 'table', 'timestamp']:
00046         d[t] = t
00047 
00048 class AmqpSpecFileMergeConflict(Exception): pass
00049 
00050 # If ignore_conflicts is true, then we allow acc and new to conflict,
00051 # with whatever's already in acc winning and new being ignored. If
00052 # ignore_conflicts is false, acc and new must not conflict.
00053 
00054 def default_spec_value_merger(key, acc, new, ignore_conflicts):
00055     if acc is None or acc == new or ignore_conflicts:
00056         return new
00057     else:
00058         raise AmqpSpecFileMergeConflict(key, acc, new)
00059 
00060 def extension_info_merger(key, acc, new, ignore_conflicts):
00061     return acc + [new]
00062 
00063 def domains_merger(key, acc, new, ignore_conflicts):
00064     merged = dict((k, v) for [k, v] in acc)
00065     for [k, v] in new:
00066         if merged.has_key(k):
00067             if not ignore_conflicts:
00068                 raise AmqpSpecFileMergeConflict(key, acc, new)
00069         else:
00070             merged[k] = v
00071 
00072     return [[k, v] for (k, v) in merged.iteritems()]
00073 
00074 def merge_dict_lists_by(dict_key, acc, new, ignore_conflicts):
00075     acc_index = set(v[dict_key] for v in acc)
00076     result = list(acc) # shallow copy
00077     for v in new:
00078         if v[dict_key] in acc_index:
00079             if not ignore_conflicts:
00080                 raise AmqpSpecFileMergeConflict(description, acc, new)
00081         else:
00082             result.append(v)
00083     return result
00084 
00085 def constants_merger(key, acc, new, ignore_conflicts):
00086     return merge_dict_lists_by("name", acc, new, ignore_conflicts)
00087 
00088 def methods_merger(classname, acc, new, ignore_conflicts):
00089     return merge_dict_lists_by("name", acc, new, ignore_conflicts)
00090 
00091 def properties_merger(classname, acc, new, ignore_conflicts):
00092     return merge_dict_lists_by("name", acc, new, ignore_conflicts)
00093 
00094 def class_merger(acc, new, ignore_conflicts):
00095     acc["methods"] = methods_merger(acc["name"],
00096                                     acc["methods"],
00097                                     new["methods"],
00098                                     ignore_conflicts)
00099     acc["properties"] = properties_merger(acc["name"],
00100                                           acc.get("properties", []),
00101                                           new.get("properties", []),
00102                                           ignore_conflicts)
00103 
00104 def classes_merger(key, acc, new, ignore_conflicts):
00105     acc_dict = dict((v["name"], v) for v in acc)
00106     result = list(acc) # shallow copy
00107     for w in new:
00108         if w["name"] in acc_dict:
00109             class_merger(acc_dict[w["name"]], w, ignore_conflicts)
00110         else:
00111             result.append(w)
00112     return result
00113 
00114 mergers = {
00115     "extension": (extension_info_merger, []),
00116     "domains": (domains_merger, []),
00117     "constants": (constants_merger, []),
00118     "classes": (classes_merger, []),
00119 }
00120 
00121 def merge_load_specs(filenames, ignore_conflicts):
00122     handles = [file(filename) for filename in filenames]
00123     docs = [json.load(handle) for handle in handles]
00124     spec = {}
00125     for doc in docs:
00126         for (key, value) in doc.iteritems():
00127             (merger, default_value) = mergers.get(key, (default_spec_value_merger, None))
00128             spec[key] = merger(key, spec.get(key, default_value), value, ignore_conflicts)
00129     for handle in handles: handle.close()
00130     return spec
00131         
00132 class AmqpSpec:
00133     # Slight wart: use a class member rather than change the ctor signature
00134     # to avoid breaking everyone else's code.
00135     ignore_conflicts = False
00136 
00137     def __init__(self, filenames):
00138         self.spec = merge_load_specs(filenames, AmqpSpec.ignore_conflicts)
00139 
00140         self.major = self.spec['major-version']
00141         self.minor = self.spec['minor-version']
00142         self.revision = self.spec.has_key('revision') and self.spec['revision'] or 0
00143         self.port =  self.spec['port']
00144 
00145         self.domains = {}
00146         insert_base_types(self.domains)
00147         for entry in self.spec['domains']:
00148             self.domains[ entry[0] ] = entry[1]
00149 
00150         self.constants = []
00151         for d in self.spec['constants']:
00152             if d.has_key('class'):
00153                 klass = d['class']
00154             else:
00155                 klass = ''
00156             self.constants.append((d['name'], d['value'], klass))
00157 
00158         self.classes = []
00159         for element in self.spec['classes']:
00160             self.classes.append(AmqpClass(self, element))
00161         
00162     def allClasses(self):
00163         return self.classes
00164     
00165     def allMethods(self):
00166         return [m for c in self.classes for m in c.allMethods()]
00167 
00168     def resolveDomain(self, n):
00169         return self.domains[n]
00170 
00171 class AmqpEntity:
00172     def __init__(self, element):
00173         self.element = element
00174         self.name = element['name']
00175     
00176 class AmqpClass(AmqpEntity):
00177     def __init__(self, spec, element):
00178         AmqpEntity.__init__(self, element)
00179         self.spec = spec
00180         self.index = int(self.element['id'])
00181 
00182         self.methods = []
00183         for method_element in self.element['methods']:
00184             self.methods.append(AmqpMethod(self, method_element))
00185 
00186         self.hasContentProperties = False
00187         for method in self.methods:
00188             if method.hasContent:
00189                 self.hasContentProperties = True
00190                 break
00191 
00192         self.fields = []
00193         if self.element.has_key('properties'):
00194             index = 0
00195             for e in self.element['properties']:
00196                 self.fields.append(AmqpField(self, e, index))
00197                 index = index + 1
00198             
00199     def allMethods(self):
00200         return self.methods
00201 
00202     def __repr__(self):
00203         return 'AmqpClass("' + self.name + '")'
00204 
00205 class AmqpMethod(AmqpEntity):
00206     def __init__(self, klass, element):
00207         AmqpEntity.__init__(self, element)
00208         self.klass = klass
00209         self.index = int(self.element['id'])
00210         if self.element.has_key('synchronous'):
00211             self.isSynchronous = self.element['synchronous']
00212         else:
00213             self.isSynchronous = False
00214         if self.element.has_key('content'):
00215             self.hasContent = self.element['content']
00216         else:
00217             self.hasContent = False
00218         self.arguments = []
00219 
00220         index = 0
00221         for argument in element['arguments']:
00222             self.arguments.append(AmqpField(self, argument, index))
00223             index = index + 1
00224         
00225     def __repr__(self):
00226         return 'AmqpMethod("' + self.klass.name + "." + self.name + '" ' + repr(self.arguments) + ')'
00227 
00228 class AmqpField(AmqpEntity):
00229     def __init__(self, method, element, index):
00230         AmqpEntity.__init__(self, element)
00231         self.method = method
00232         self.index = index
00233 
00234         if self.element.has_key('type'):
00235             self.domain = self.element['type']
00236         else:
00237             self.domain = self.element['domain']
00238             
00239         if self.element.has_key('default-value'):
00240             self.defaultvalue = self.element['default-value']
00241         else:
00242             self.defaultvalue = None
00243 
00244     def __repr__(self):
00245         return 'AmqpField("' + self.name + '")'
00246 
00247 def do_main(header_fn, body_fn):
00248     do_main_dict({"header": header_fn, "body": body_fn})
00249 
00250 def do_main_dict(funcDict):
00251     def usage():
00252         print >> sys.stderr , "Usage:"
00253         print >> sys.stderr , "  %s <function> <path_to_amqp_spec.json>... <path_to_output_file>" % (sys.argv[0])
00254         print >> sys.stderr , " where <function> is one of %s" % ", ".join([k for k in funcDict.keys()])
00255 
00256     def execute(fn, amqp_specs, out_file):
00257         stdout = sys.stdout
00258         f = open(out_file, 'w')
00259         success = False
00260         try:
00261             sys.stdout = f
00262             fn(amqp_specs)
00263             success = True
00264         finally:
00265             sys.stdout = stdout
00266             f.close()
00267             if not success:
00268                 os.remove(out_file)
00269 
00270     parser = OptionParser()
00271     parser.add_option("--ignore-conflicts", action="store_true", dest="ignore_conflicts", default=False)
00272     (options, args) = parser.parse_args()
00273 
00274     if len(args) < 3:
00275         usage()
00276         sys.exit(1)
00277     else:
00278         function = args[0]
00279         sources = args[1:-1]
00280         dest = args[-1]
00281         AmqpSpec.ignore_conflicts = options.ignore_conflicts
00282         if funcDict.has_key(function):
00283             execute(funcDict[function], sources, dest)
00284         else:
00285             usage()
00286             sys.exit(1)