diff options
author | Jacek Konieczny <jajcus@jajcus.net> | 2004-12-28 18:55:01 +0300 |
---|---|---|
committer | Jacek Konieczny <jajcus@jajcus.net> | 2004-12-28 18:55:01 +0300 |
commit | 2e06d391ba010a9a9124338cbfa20c50a2ae2412 (patch) | |
tree | 64fe22d67d2a758a4aa47b23db5a4a99cd761c4b /auxtools | |
parent | 717065c8d492efaa31d7c647804dc3cad1a50576 (diff) |
- UML model building infrastructure
Diffstat (limited to 'auxtools')
-rwxr-xr-x | auxtools/code2xmi.py | 325 | ||||
-rwxr-xr-x | auxtools/xmimerge.py | 122 |
2 files changed, 447 insertions, 0 deletions
diff --git a/auxtools/code2xmi.py b/auxtools/code2xmi.py new file mode 100755 index 0000000..9d60ddb --- /dev/null +++ b/auxtools/code2xmi.py @@ -0,0 +1,325 @@ +#!/usr/bin/python + + +import os,sys + +def main(module_list): + options = {'target':None, 'modules':list(module_list), 'verbosity':1, + 'prj_name':'', 'action':'html', 'tests':{'basic':1}, + 'show_imports':0, 'frames':1, 'private':None, + 'list_classes_separately': 0, 'debug':0, + 'docformat':None, 'top':None, 'inheritance': None, + 'ignore_param_mismatch': 0, 'alphabetical': 1} + + + modules=_import(options['modules'],1) + + # Record the order of the modules in options. + from epydoc.uid import make_uid + muids = [] + for m in modules: + try: + muids.append(make_uid(m)) + except: + raise + if sys.stderr.softspace: print >>sys.stderr + print >>sys.stderr, 'Failed to create a UID for %s' % m + + # Build their documentation + docmap = _make_docmap(modules, options) + f=Formatter(docmap) + print f.format(module_list) + +def escape(s): + return str(s).replace('&','&').replace('<','<').replace('>','>').replace('"','"').replace("'",''') + +class _Progress: + """ + + The progress meter that is used by C{cli} to report its progress. + It prints the status to C{stderrr}. Depending on the verbosity, + setting it will produce different outputs. + + To update the progress meter, call C{report} with the name of the + object that is about to be processed. + """ + def __init__(self, action, verbosity, total_items, html_file=0): + """ + Create a new progress meter. + + @param action: A string indicating what action is performed on + each objcet. Examples are C{"writing"} and C{"building + docs for"}. + @param verbosity: The verbosity level. This controls what the + progress meter output looks like. + @param total_items: The total number of items that will be + processed with this progress meter. This is used to let + the user know how much progress epydoc has made. + @param html_file: Whether to assume that arguments are html + file names, and munge them appropriately. + """ + self._action = action + self._verbosity = verbosity + self._total_items = total_items + self._item_num = 1 + self._html_file = 0 + + def report(self, argument): + """ + Update the progress meter. + @param argument: The object that is about to be processed. + """ + if self._verbosity <= 0: return + + if self._verbosity==1: + if self._item_num == 1 and self._total_items <= 70: + sys.stderr.write(' [') + if (self._item_num % 60) == 1 and self._total_items > 70: + sys.stderr.write(' [%3d%%] ' % + (100.0*self._item_num/self._total_items)) + sys.stderr.write('.') + sys.stderr.softspace = 1 + if (self._item_num % 60) == 0 and self._total_items > 70: + print >>sys.stderr + if self._item_num == self._total_items: + if self._total_items <= 70: sys.stderr.write(']') + print >>sys.stderr + elif self._verbosity>1: + TRACE_FORMAT = ((' [%%%dd/%d]' % (len(`self._total_items`), + self._total_items))+ + ' %s %%s' % self._action) + + if self._html_file: + (dir, file) = os.path.split(argument) + (root, d) = os.path.split(dir) + if d in ('public', 'private'): + argument = os.path.join(d, file) + else: + fname = argument + + print >>sys.stderr, TRACE_FORMAT % (self._item_num, argument) + self._item_num += 1 + + +def _import(module_names, verbosity): + """ + @return: A list of the modules contained in the given files. + Duplicates are removed. Order is preserved. + @rtype: C{list} of C{module} + @param module_names: The list of module filenames. + @type module_names: C{list} of C{string} + @param verbosity: Verbosity level for tracing output. + @type verbosity: C{int} + """ + from epydoc.imports import import_module, find_modules + + # First, expand packages. + for name in module_names[:]: + if os.path.isdir(name): + # In-place replacement. + index = module_names.index(name) + new_modules = find_modules(name) + if new_modules: + module_names[index:index+1] = new_modules + elif verbosity >= 0: + if sys.stderr.softspace: print >>sys.stderr + print >>sys.stderr, 'Error: %r is not a pacakge' % name + + if verbosity > 0: + print >>sys.stderr, 'Importing %s modules.' % len(module_names) + modules = [] + progress = _Progress('Importing', verbosity, len(module_names)) + + for name in module_names: + progress.report(name) + # Import the module, and add it to the list. + try: + module = import_module(name) + if module not in modules: modules.append(module) + elif verbosity > 2: + if sys.stderr.softspace: print >>sys.stderr + print >>sys.stderr, ' (duplicate)' + except ImportError, e: + if verbosity >= 0: + if sys.stderr.softspace: print >>sys.stderr + print >>sys.stderr, e + + if len(modules) == 0: + print >>sys.stderr, '\nError: no modules successfully loaded!' + sys.exit(1) + return modules + +def _make_docmap(modules, options): + """ + Construct the documentation map for the given modules. + + @param modules: The modules that should be documented. + @type modules: C{list} of C{Module} + @param options: Options from the command-line arguments. + @type options: C{dict} + """ + from epydoc.objdoc import DocMap, report_param_mismatches + + verbosity = options['verbosity'] + document_bases = 1 + document_autogen_vars = 1 + inheritance_groups = (options['inheritance'] == 'grouped') + inherit_groups = (options['inheritance'] != 'grouped') + d = DocMap(verbosity, document_bases, document_autogen_vars, + inheritance_groups, inherit_groups) + if options['verbosity'] > 0: + print >>sys.stderr, ('Building API documentation for %d modules.' + % len(modules)) + progress = _Progress('Building docs for', verbosity, len(modules)) + + for module in modules: + progress.report(module.__name__) + # Add the module. Catch any exceptions that get generated. + try: d.add(module) + except Exception, e: + if options['debug']: raise + else: _internal_error(e) + except: + if options['debug']: raise + else: _internal_error() + + if not options['ignore_param_mismatch']: + if not report_param_mismatches(d): + estr = ' (To supress these warnings, ' + estr += 'use --ignore-param-mismatch)' + print >>sys.stderr, estr + + return d + + +HEADER="""<?xml version="1.0" encoding="utf-8" ?> +<XMI xmi.version="1.1" xmlns:UML="org.omg/standards/UML"> + <XMI.header> + <XMI.metamodel name="UML" version="1.3" href="UML.xml"/> + <XMI.model name="PyXMPP" href="pyxmpp.xml"/> + </XMI.header> + <XMI.content> + <UML:Model> + <UML:Stereotype visibility="public" xmi.id="/Stereotype:classmethod" name="classmethod" /> + <UML:Stereotype visibility="public" xmi.id="/Stereotype:staticmethod" name="staticmethod" /> +""" +FOOTER=""" + </UML:Model> + </XMI.content> +</XMI>""" + +class Formatter: + def __init__(self,docmap): + self.docmap=docmap + + def format(self,modules=None): + self.generalizations=[] + ret=HEADER + if modules: + from epydoc.uid import findUID + for m in modules: + uid=findUID(m) + if uid.is_module(): + print >>sys.stderr,"Formatting: %s\n" % (uid,) + ret+=self.format_module(uid,True) + else: + print >>sys.stderr,"Skipping: %s (not a module)\n" % (uid,) + else: + decorated = [(u.name().lower(), u) for u in self.docmap.keys() if u.is_module()] + decorated.sort() + uids = [d[-1] for d in decorated] + for uid in uids: + if uid.is_module(): + ret+=self.format_module(uid,False) + ret+="\n".join(self.generalizations)+"\n" + ret+=FOOTER + return ret + + def format_module(self,uid,recursive): + if recursive: + name=uid.shortname() + else: + name=uid.name() + ret=" <UML:Package xmi.id='%s' name='%s'>\n" % (escape(uid),escape(name)) + doc=self.docmap[uid] + classes=doc.classes() + for cls in classes: + ret+=self.format_class(cls) + if recursive and uid.is_package(): + modules=doc.modules() + for mod in modules: + ret+=self.format_module(mod.target(),True) + ret+=" </UML:Package>\n" + return ret + + def format_class(self,link): + name=link.name() + uid=link.target() + doc=self.docmap[uid] + descr=doc.descr() + ret=(" <UML:Class xmi.id='%s' name='%s' comment='%s'>\n" + % (escape(uid),escape(name),escape(descr.to_plaintext(None)))) + for meth in doc.allmethods(): + ret+=self.format_method(meth,doc) + for att in doc.ivariables(): + ret+=self.format_attribute(att,doc) + ret+=" </UML:Class>\n" + for base in doc.bases(): + buid=base.target() + g=(" <UML:Generalization xmi.id='%s(%s)'" + " child='%s' parent='%s' visibility='public'/>" + % (escape(uid),escape(buid),escape(uid),escape(buid))) + self.generalizations.append(g) + return ret + + def format_attribute(self,var,container): + uid=var.uid() + name=var.name() + descr=var.descr() + cuid = container.uid() + inherited = (cuid.is_class() and uid.cls() != cuid) + if inherited: + return "" + if descr: + descr=" comment='%s'" % (escape(descr.to_plaintext(None)),) + else: + descr="" + if uid.is_public(): + vis=" visibility='public'" + else: + vis=" visibility='private'" + return (" <UML:Attribute xmi.id='%s' name='%s' %s%s />\n" % + (escape(uid),escape(name),vis,descr)) + + def format_method(self,link,container): + uid=link.target() + name=link.name() + doc=self.docmap[uid] + descr=doc.descr() + cuid = container.uid() + inherited = (cuid.is_class() and uid.cls() != cuid) + if inherited: + return "" + if name.startswith("__") and not descr: + # ignore special/private methods without description + return "" + if doc is not None and uid.is_any_method() and not doc.has_docstring(): + doc = self.docmap.documented_ancestor(uid) or doc + if descr: + descr=" comment='%s'" % (escape(descr.to_plaintext(None)),) + else: + descr="" + if uid.is_public(): + vis=" visibility='public'" + else: + vis=" visibility='private'" + if uid.is_classmethod(): + st=" stereotype='/Stereotype:classmethod'" + elif uid.is_staticmethod(): + st=" stereotype='/Stereotype:staticmethod'" + else: + st="" + return (" <UML:Operation xmi.id='%s' name='%s' %s%s%s />\n" % + (escape(uid),escape(name),vis,descr,st)) + +main(sys.argv[1:]) diff --git a/auxtools/xmimerge.py b/auxtools/xmimerge.py new file mode 100755 index 0000000..bfa6997 --- /dev/null +++ b/auxtools/xmimerge.py @@ -0,0 +1,122 @@ +#!/usr/bin/python + +import libxml2 +import sys + + +class Merger: + def __init__(self,model_file,auto_file): + self.model_doc=libxml2.parseFile(sys.argv[1]) + self.auto_doc=libxml2.parseFile(sys.argv[2]) + self.xmi_id_map={} + self.auto_xmi_id_map={} + self.old_elements={} + def merge(self): + self.output_doc=libxml2.newDoc("1.0") + new_root=self.output_doc.newChild(None,"XMI",None) + uml_ns=new_root.newNs("org.omg/standards/UML","UML") + root=self.model_doc.getRootElement() + n=root.children + while n: + if n.type!="element": + n=n.next + continue + if n.name=="XMI.content": + new_content=new_root.addChild(n.docCopyNode(self.output_doc,False)) + p=n.get_properties() + while p: + new_content.setProp(p.name,p.getContent()) + p=p.next + self.merge_xmi_content(new_content,n) + else: + new_root.addChild(n.docCopyNode(self.output_doc,True)) + n=n.next + return self.output_doc + + def merge_xmi_content(self,target,old_content): + self.old_elements={} + n=old_content.children + while n: + if n.type!="element": + n=n.next + continue + try: + if n.ns().getContent()=="org.omg/standards/UML" and n.name=="Model": + self.parse_old_model(n) + n=n.next + continue + except libxml2.treeError: + pass + target.addChild(n.docCopyNode(self.output_doc,True)) + n=n.next + auto_content=self.auto_doc.xpathEval("//XMI/XMI.content/*") + for n in auto_content: + if n.ns().getContent()=="org.omg/standards/UML" and n.name=="Model": + new_model=target.addChild(n.docCopyNode(self.output_doc,False)) + p=n.get_properties() + while p: + new_model.setProp(p.name,p.getContent()) + p=p.next + self.merge_uml_model(new_model,n) + else: + target.addChild(n.docCopyNode(self.output_doc,True)) + + def merge_uml_model(self,target,subtree,path=""): + n=subtree.children + while n: + if n.type!="element": + n=n.next + continue + + new_node=target.addChild(n.docCopyNode(self.output_doc,False)) + p=n.get_properties() + while p: + old_val=p.getContent() + if p.name in ("stereotype","parent","child"): + val=self.xmi_id_map.get(old_val) + if not val: + val=self.auto_xmi_id_map.get(old_val) + if not val: + val=old_val + else: + val=old_val + new_node.setProp(p.name,val) + p=p.next + + npath="%s/%s:%s" % (path,n.name,n.prop("name")) + auto_xmi_id=n.prop("xmi.id") + xmi_id=self.xmi_id_map.get(npath) + if xmi_id: + if auto_xmi_id: + self.auto_xmi_id_map[auto_xmi_id]=xmi_id + new_node.setProp("xmi.id",xmi_id) + self.merge_uml_model(new_node,n,npath) + n=n.next + for oe in self.old_elements.get(path,[]): + target.addChild(oe) + + def parse_old_model(self,subtree,path=""): + n=subtree.children + while n: + if n.type!="element": + n=n.next + continue + if (n.ns().getContent()!="org.omg/standards/UML" or + n.name not in ("Stereotype","Class","Operation","Attribute","Package")): + oe=n.docCopyNode(self.output_doc,True) + if not path in self.old_elements: + self.old_elements[path]=[oe] + else: + self.old_elements[path].append(oe) + n=n.next + continue + npath="%s/%s:%s" % (path,n.name,n.prop("name")) + xmi_id=n.prop("xmi.id") + if xmi_id: + self.xmi_id_map[npath]=xmi_id + self.parse_old_model(n,npath) + n=n.next + +m=Merger(sys.argv[1],sys.argv[2]) +out=m.merge() +print out.serialize(format=True) |