00001
00002
00003
00004
00005 '''rtshell
00006
00007 Copyright (C) 2009-2014
00008 Yosuke Matsusaka and Geoffrey Biggs
00009 Intelligent Systems Research Institute,
00010 National Institute of Advanced Industrial Science and Technology (AIST),
00011 Japan
00012 All rights reserved.
00013 Licensed under the Eclipse Public License -v 1.0 (EPL)
00014 http://www.opensource.org/licenses/eclipse-1.0.txt
00015
00016 Implementation of the command to display component documentation.
00017
00018 '''
00019
00020
00021 import docutils.core
00022 import optparse
00023 import os
00024 import os.path
00025 import rtctree.tree
00026 import rtctree.path
00027 import sys
00028 import traceback
00029
00030 import path
00031 import rts_exceptions
00032 import rtshell
00033
00034
00035 def escape(s):
00036 return s.replace('"', "'")
00037
00038
00039 def section(s, level=0):
00040 result = []
00041 if level == 0:
00042 result.append(s)
00043 result.append('=' * len(s))
00044 elif level == 1:
00045 result.append(s)
00046 result.append('-' * len(s))
00047 return result
00048
00049
00050 def get_ports_docs(comp):
00051 result = []
00052 result.append('.. csv-table:: Ports')
00053 result.append(' :header: "Name", "Type", "DataType", "Description"')
00054 result.append(' :widths: 8, 8, 8, 26')
00055 result.append(' ')
00056 for p in comp.ports:
00057 if p.porttype == 'DataInPort' or p.porttype == 'DataOutPort':
00058 datatype = p.properties['dataport.data_type']
00059 else:
00060 datatype = ''
00061 if 'description' in p.properties:
00062 description = p.properties['description']
00063 else:
00064 description = ''
00065 result.append(' "{0}", "{1}", "{2}", "{3}"'.format(p.name,
00066 p.porttype, datatype, escape(description)))
00067 return result
00068
00069
00070 def get_config_docs(comp):
00071 result = []
00072 result.append('.. csv-table:: Configuration parameters')
00073 result.append(' :header: "Name", "Description"')
00074 result.append(' :widths: 12, 38')
00075 result.append(' ')
00076
00077 desc_set = None
00078 if '__description__' in comp.conf_sets:
00079 desc_set = comp.conf_sets['__description__']
00080 for n in comp.conf_sets['default'].data:
00081 if desc_set and desc_set.has_param(n):
00082 description = desc_set.data[n]
00083 else:
00084 description = ''
00085 result.append(' "{0}", "{1}"'.format(n, escape(description)))
00086 return result
00087
00088
00089 from rtstodot import port_name as dot_port_name
00090 from rtstodot import escape as dot_escape
00091
00092 def make_comp_graph(comp):
00093 result = []
00094 result.append('.. digraph:: comp')
00095 result.append('')
00096 result.append(' rankdir=LR;')
00097 result.append(' {0} [shape=Mrecord, label="{1}"];'.format(dot_escape(comp.type_name), comp.type_name))
00098 for p in comp.ports:
00099 if p.porttype == 'DataInPort':
00100 pname = dot_port_name(p.name)
00101 result.append(' {0} [shape=plaintext, label="{1}"];'.format(dot_escape(pname), pname))
00102 result.append(' {0} -> {1};'.format(dot_escape(pname), dot_escape(comp.type_name)))
00103 elif p.porttype == 'DataOutPort':
00104 pname = dot_port_name(p.name)
00105 result.append(' {0} [shape=plaintext, label="{1}"];'.format(dot_escape(pname), pname))
00106 result.append(' {0} -> {1};'.format(dot_escape(comp.type_name), dot_escape(pname)))
00107 return result
00108
00109
00110 def get_section_title(sec):
00111 if sec == 'intro':
00112 return 'Introduction'
00113 elif sec == 'reqs':
00114 return 'Pre-requisites'
00115 elif sec == 'install':
00116 return 'Installation'
00117 elif sec == 'usage':
00118 return 'Usage'
00119 elif sec == 'misc':
00120 return 'Miscellaneous'
00121 elif sec == 'changelog':
00122 return 'Changelog'
00123 else:
00124 return sec
00125
00126
00127 def do_section(result, comp, doc_set, sec, options):
00128 if sec == 'ports' and comp.ports:
00129 result += section('Ports', 1)
00130 result += get_ports_docs(comp)
00131 result.append('')
00132 if options.graph == True:
00133 result += make_comp_graph(comp)
00134 result.append('')
00135 elif (sec == 'config' and
00136 'default' in comp.conf_sets and comp.conf_sets['default'].data):
00137 result += section('Configuration parameters', 1)
00138 result += get_config_docs(comp)
00139 result.append('')
00140 elif doc_set and doc_set.has_param(sec):
00141 title = get_section_title(sec)
00142 body = doc_set.data[sec]
00143 result += section(title, 1)
00144 result.append(doc_set.data[sec])
00145 result.append('')
00146
00147
00148 def get_comp_docs(comp, tree, options):
00149 result = []
00150 result += section(comp.type_name, 0)
00151 result.append(comp.description)
00152 result.append('')
00153
00154 result.append(':Vendor: {0}'.format(comp.vendor))
00155 result.append(':Version: {0}'.format(comp.version))
00156 result.append(':Category: {0}'.format(comp.category))
00157
00158 doc_set = None
00159 order = ['intro', 'reqs', 'install', 'usage', 'ports', 'config', 'misc',
00160 'changelog']
00161 sections = ['ports', 'config']
00162 if '__doc__' in comp.conf_sets:
00163 doc_set = comp.conf_sets['__doc__']
00164 if doc_set.has_param('__order__') and doc_set.data['__order__']:
00165 order = doc_set.data['__order__'].split(',')
00166 sections += [k for k in doc_set.data.keys() if not k.startswith('__')]
00167
00168 if doc_set:
00169 if doc_set.has_param('__license__'):
00170 result.append(':License: {0}'.format(
00171 comp.conf_sets['__doc__'].data['__license__']))
00172 if doc_set.has_param('__contact__'):
00173 result.append(':Contact: {0}'.format(
00174 comp.conf_sets['__doc__'].data['__contact__']))
00175 if doc_set.has_param('__url__'):
00176 result.append(':URL: {0}'.format(
00177 comp.conf_sets['__doc__'].data['__url__']))
00178 result.append('')
00179
00180
00181 for s in order:
00182 if s not in sections:
00183 if doc_set:
00184 print >>sys.stderr, ('{0}: Unknown section in order: '
00185 '{1}'.format(os.path.basename(sys.argv[0]), s))
00186 continue
00187 do_section(result, comp, doc_set, s, options)
00188
00189
00190 for s in [s for s in sections if s not in order]:
00191 do_section(result, comp, doc_set, s, options)
00192
00193 return result
00194
00195
00196 def get_docs(cmd_path, full_path, options, tree=None):
00197 path, port = rtctree.path.parse_path(full_path)
00198 if not path[-1]:
00199
00200 raise rts_exceptions.NotAComponentError(cmd_path)
00201 if port:
00202 raise rts_exceptions.NotAComponentError(cmd_path)
00203
00204 if not tree:
00205 tree = rtctree.tree.RTCTree(paths=path)
00206
00207 if not tree.has_path(path):
00208 raise rts_exceptions.NoSuchObjectError(cmd_path)
00209 object = tree.get_node(path)
00210
00211 if object.is_component:
00212 return get_comp_docs(object, tree, options)
00213 elif object.is_zombie:
00214 raise rts_exceptions.ZombieObjectError(cmd_path)
00215 else:
00216 raise rts_exceptions.NotAComponentError(cmd_path)
00217
00218
00219 def main(argv=None, tree=None):
00220 usage = '''Usage: %prog [options] <path>
00221 Display component documentation.'''
00222 version = rtshell.RTSH_VERSION
00223 parser = optparse.OptionParser(usage=usage, version=version)
00224 parser.add_option('-f', '--format', dest='format', type='choice',
00225 choices=('rst', 'html', 'latex'), default='html',
00226 help='Output format (one of "rst", "html" or "latex"). '
00227 '[Default: %default]')
00228 parser.add_option('-g', '--graph', dest='graph', action='store_true',
00229 default=False,
00230 help='Draw component graph. [Default: %default]')
00231 parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
00232 default=False,
00233 help='Output verbose information. [Default: %default]')
00234
00235 if argv:
00236 sys.argv = [sys.argv[0]] + argv
00237 try:
00238 options, args = parser.parse_args()
00239 except optparse.OptionError, e:
00240 print >>sys.stderr, 'OptionError:', e
00241 return 1
00242
00243 if not args:
00244
00245 print >>sys.stderr, '{0}: No component specified.'.format(
00246 os.path.basename(sys.argv[0]))
00247 return 1
00248 elif len(args) == 1:
00249 cmd_path = args[0]
00250 else:
00251 print >>sys.stderr, usage
00252 return 1
00253 full_path = path.cmd_path_to_full_path(cmd_path)
00254
00255 try:
00256 docs = '\n'.join(get_docs(cmd_path, full_path, options, tree=tree))
00257 if options.format == 'rst':
00258 print docs
00259 else:
00260 print docutils.core.publish_string(docs, writer_name=options.format)
00261 except Exception, e:
00262 if options.verbose:
00263 traceback.print_exc()
00264 print >>sys.stderr, '{0}: {1}'.format(os.path.basename(sys.argv[0]), e)
00265 return 1
00266 return 0
00267
00268
00269
00270