def new_var(var, appname): """Outputs a new definition when VarManager makes a new variable.""" app = module.apps[appname] has_category = syslx.patterns( app.attrs) & {'human', 'cron', 'db', 'external', 'ui'} assert len(has_category) <= 1 (order, agent) = { 'human': (0, 'actor'), 'ui': (1, 'boundary'), 'cron': (2, 'control'), 'db': (4, 'database'), 'external': (5, 'control'), }.get(''.join(has_category), (3, 'control')) isoctrl = [] for ctrl in app.attrs.keys(): if 'iso_ctrl' in ctrl: isoctrl.append(re.sub(r'iso_ctrl_(.*)_txt', r'\1', ctrl)) label = params.appfmt(appname=appname, controls=', '.join(i for i in sorted(isoctrl)), **diagutil.attr_fmt_vars(app.attrs)).replace( u'\n', ur'\n') var_names.append( ((order, int(var[1:])), u'{} "{}" as {}'.format(agent, label, var)))
def new_var(var, appname): """Outputs a new definition when VarManager makes a new variable.""" app = module.apps[appname] has_category = syslx.patterns(app.attrs) & { 'human', 'cron', 'db', 'external', 'ui'} assert len(has_category) <= 1 (order, agent) = { 'human': (0, 'actor'), 'ui': (1, 'boundary'), 'cron': (2, 'control'), 'db': (4, 'database'), 'external': (5, 'control'), }.get( ''.join(has_category), (3, 'control')) isoctrl = [] for ctrl in app.attrs.keys(): if 'iso_ctrl' in ctrl: isoctrl.append(re.sub(r'iso_ctrl_(.*)_txt', r'\1', ctrl)) label = params.appfmt( appname=appname, controls=', '.join(i for i in sorted(isoctrl)), **diagutil.attr_fmt_vars(app.attrs) ).replace(u'\n', ur'\n') var_names.append( ((order, int(var[1:])), u'{} "{}" as {}'.format(agent, label, var)))
def new_var(var, name): """Outputs a new definition when VarManager makes a new variable.""" app = module.apps[name] write('[{}] as {}{}', appfmt(appname=name_map.get(name, name), **diagutil.attr_fmt_vars(app.attrs)).replace('\n', r'\n'), var, ' <<highlight>>' if name in highlights else '')
def dataviews(module, args): """Generate a set of data views.""" out = [] parts = re.match(ur'^(.*?)(?:\s*<-\s*(.*?))?$', args.project) (appname, epname) = parts.groups() app = module.apps.get(appname) if not app: raise Exception('Invalid project "{}"'.format(args.project)) if epname is not None: endpts = [app.endpoints.get(epname)] else: endpts = app.endpoints.itervalues() out_fmt = diagutil.parse_fmt(args.output) for endpt in endpts: types = [] for stmt in endpt.stmt: appname = stmt.action.action if not module.apps.get(appname): continue for (name, typespec) in module.apps.get(appname).types.iteritems(): if typespec.WhichOneof( 'type') == 'relation' or typespec.WhichOneof( 'type') == 'tuple': types.append((appname, name, typespec)) args.output = out_fmt(appname=appname, epname=endpt.name, eplongname=endpt.long_name, **diagutil.attr_fmt_vars(app.attrs, endpt.attrs)) if args.filter and not re.match(args.filter, args.output): continue out.append(_generate_view(module, appname, types)) diagutil.output_plantuml(args, out[-1]) return out
def dataviews(module, args): """Generate a set of data views.""" out = [] parts = re.match(ur'^(.*?)(?:\s*<-\s*(.*?))?$', args.project) (appname, epname) = parts.groups() app = module.apps.get(appname) if not app: raise Exception('Invalid project "{}"'.format(args.project)) if epname is not None: endpts = [app.endpoints.get(epname)] else: endpts = app.endpoints.itervalues() out_fmt = diagutil.parse_fmt(args.output) for endpt in endpts: types = [] for stmt in endpt.stmt: appname = stmt.action.action if not module.apps.get(appname): continue for (name, typespec) in module.apps.get(appname).types.iteritems(): if typespec.WhichOneof('type') == 'relation' or typespec.WhichOneof('type') == 'tuple': types.append((appname, name, typespec)) args.output = out_fmt( appname=appname, epname=endpt.name, eplongname=endpt.long_name, **diagutil.attr_fmt_vars(app.attrs, endpt.attrs)) if args.filter and not re.match(args.filter, args.output): continue out.append(_generate_view(module, appname, types)) diagutil.output_plantuml(args, out[-1]) return out
def new_var(var, name): """Outputs a new definition when VarManager makes a new variable.""" # TODO dodgy, should be using context (look at syslseqs) attrs = {} state_name = '' if istoplevel: template = 'state "{}" as X{}{} {{' attrs = module.apps[name].attrs state_name = name else: template = ' state "{}" as {}{}' (app_name, _, ep_name) = name.partition(' : ') attrs = module.apps[app_name].endpoints[ep_name].attrs state_name = ep_name write(template, appfmt(appname=state_name, **diagutil.attr_fmt_vars(attrs)).replace('\n', r'\n'), var, ' <<highlight>>' if name in highlights else '')
def cmd(args): """Handle subcommand.""" (module, _, _) = syslloader.load( args.modules, args.validations, args.root) def output_sd(args, params): """Generate and output a sequence diagram.""" out = sequence_diag(module, params) diagutil.output_plantuml(args, out) epfilters = os.getenv('SYSL_SD_FILTERS', '*').split(',') # TODO: Find a cleaner way to trigger multi-output. if '%(epname)' in args.output: out_fmt = diagutil.parse_fmt(args.output) for appname in args.app: app = module.apps[appname] bbs = [[e.s for e in bbox.a.elt] for bbox in syslx.View(app.attrs)['blackboxes'].a.elt] seqtitle = diagutil.parse_fmt(syslx.View( app.attrs)['seqtitle'].s or args.seqtitle) epfmt = diagutil.parse_fmt( syslx.View(app.attrs)['epfmt'].s or args.endpoint_format) appfmt = diagutil.parse_fmt( syslx.View(app.attrs)['appfmt'].s or args.app_format) for (name, endpt) in sorted( app.endpoints.iteritems(), key=lambda kv: kv[1].name): if not any(fnmatch.fnmatch(name, filt) for filt in epfilters): continue attrs = {u'@' + name: value.s for (name, value) in endpt.attrs.iteritems()} args.output = out_fmt( appname=appname, epname=name, eplongname=endpt.long_name, **attrs) if args.filter and not re.match(args.filter, args.output): continue bbs2 = [[e.s for e in bbox.a.elt] for bbox in syslx.View(endpt.attrs)['blackboxes'].a.elt] varrefs = diagutil.attr_fmt_vars( app.attrs, endpt.attrs, epname=endpt.name, eplongname=endpt.long_name) out = sequence_diag(module, SequenceDiagParams( endpoints=[' :: '.join(s.call.target.part) + ' <- ' + s.call.endpoint for s in endpt.stmt if s.WhichOneof('stmt') == 'call'], epfmt=epfmt, appfmt=appfmt, activations=args.activations, title=seqtitle(**varrefs).replace('\n', r'\n'), blackboxes=bbs + bbs2)) diagutil.output_plantuml(args, out) else: if not args.endpoint: raise Exception( 'sysl sd requires either one specific endpoint, ' + 'e.g. --endpoint "ATM <- GetBalance", or an output ' + 'pattern with %(epname), e.g. -o "out/%(epname).svg".') out = sequence_diag(module, SequenceDiagParams( # -s builds list of lists (idkw). endpoints=args.endpoint, epfmt=diagutil.parse_fmt(args.endpoint_format), appfmt=diagutil.parse_fmt(args.app_format), activations=args.activations, title=args.title, blackboxes=args.blackbox)) diagutil.output_plantuml(args, out)
def visit_endpoint( from_app, appname, epname, uptos, sender_patterns, sender_endpt_patterns=None, stmt=None, deactivate=None): """Recursively visit an endpoint.""" if from_app: sender = var_name(syslx.fmt_app_name(from_app.name)) else: sender = '[' agent = var_name(appname) app = module.apps.get(appname) endpt = app.endpoints.get(epname) assert endpt def visit_stmts(stmts, deactivate, last_parent_stmt): """Recursively visit a stmt list.""" def block(last_stmt, block_stmts, fmt, *args): """Output a compound block.""" write(fmt, *args) with write.indent(): return visit_stmts(block_stmts, deactivate, last_stmt) def block_with_end(last_stmt, block_stmts, fmt, *args): """Output a compound block, including the 'end' clause.""" payload = block(last_stmt, block_stmts, fmt, *args) write('end') return payload payload = None for (i, stmt) in enumerate(stmts): last_stmt = last_parent_stmt and i == len(stmts) - 1 if stmt.HasField('call'): with write.indent(): payload = visit_endpoint( app, syslx.fmt_app_name(stmt.call.target), stmt.call.endpoint, uptos, app_patterns, syslx.patterns(stmt.attrs) | syslx.patterns( endpt.attrs), stmt, last_stmt and deactivate) elif stmt.HasField('action'): #write('{0} -> {0} : {1}', agent, r'\n'.join(textwrap.wrap(stmt.action.action, 40))) write('{0} -> {0} : {1}', agent, stmt.action.action) elif stmt.HasField('cond'): payload = block_with_end(last_stmt, stmt.cond.stmt, 'opt {}', stmt.cond.test) elif stmt.HasField('loop'): payload = block_with_end(last_stmt, stmt.loop.stmt, 'loop {} {}', stmt.loop.Mode.Name( stmt.loop.mode), stmt.loop.criterion) elif stmt.HasField('loop_n'): payload = block_with_end(last_stmt, stmt.loop_n.stmt, 'loop {} times', stmt.loop_n.count) elif stmt.HasField('foreach'): payload = block_with_end(last_stmt, stmt.foreach.stmt, 'loop for each {}', stmt.foreach.collection) elif stmt.HasField('group'): payload = block_with_end(last_stmt, stmt.group.stmt, 'group {}', stmt.group.title) elif stmt.HasField('alt'): prefix = 'alt' for (j, choice) in enumerate(stmt.alt.choice): last_alt_stmt = last_stmt and j == len( stmt.alt.choice) - 1 payload = block(last_alt_stmt, choice.stmt, '{} {}', prefix, choice.cond) prefix = 'else' write('end') elif stmt.HasField('ret'): rargs = [] for param in syslalgo.yield_ret_params(stmt.ret.payload): if param != '...' and '.' in param: (an, pn) = rex.split(r'\.', param) rarg = format_args(an, pn) if rarg: rargs.append(rarg) else: rargs.append(param) write('{}<--{} : {}', sender, agent, ' | '.join(p for p in rargs)) else: raise Exception('No statement!') return payload app_patterns = syslx.patterns(app.attrs) target_patterns = syslx.patterns(endpt.attrs) patterns = target_patterns human = 'human' in app_patterns human_sender = 'human' in sender_patterns cron = 'cron' in sender_patterns needs_int = not (human or human_sender or cron) and sender != agent label = re.sub(ur'^.*? -> ', u' ⬄ ', unicode(epname)) cron = 'cron' in app_patterns if stmt: assert stmt.HasField('call') ptrns = '' if bool(sender_endpt_patterns) or bool(patterns): ptrns = u', '.join(sorted(sender_endpt_patterns)) + \ u' → ' + u', '.join(sorted(patterns)) isoctrl = [] for ctrl in endpt.attrs.keys(): if 'iso_ctrl' in ctrl: isoctrl.append(re.sub(r'iso_ctrl_(.*)_txt', r'\1', ctrl)) epargs = [] def format_args(an, pn): arg = '.'.join((an, pn)) if arg: conf = module.apps[an].types[pn].attrs['iso_conf'].s[:1].upper( ) integ = module.apps[an].types[pn].attrs['iso_integ'].s[:1].upper( ) isocolor = 'red' if 'R' in conf else 'green' arg = '<color blue>' + arg + '</color>' + ' <<color ' + isocolor + '>' + \ (conf if conf else '?') + ', ' + \ (integ if integ else '?') + '</color>>' return arg for p in endpt.param: an = ' :: '.join(p.type.type_ref.ref.appname.part) pn = '.'.join(p.type.type_ref.ref.path) eparg = format_args(an, pn) if eparg: epargs.append(eparg) label = params.epfmt( epname=label, human='human' if human else '', human_sender='human sender' if human_sender else '', needs_int='needs_int' if needs_int else '', args=' | '.join(e for e in epargs), #args=', '.join(p.name for p in stmt.call.arg), patterns=ptrns, controls=', '.join(i for i in sorted(isoctrl)), **diagutil.attr_fmt_vars(stmt.attrs) ).replace('\n', r'\n') if not ((human and sender == '[') or cron): ep_patterns = syslx.patterns(endpt.attrs) icon = '<&timer> ' if 'cron' in ep_patterns else '' write('{}->{} : {}{}', sender, agent, icon, label) if log_integration and stmt: log_integration(app=from_app, stmt=stmt, patterns=patterns) rargs = [] for param in syslalgo.yield_ret_params( syslalgo.return_payload(endpt.stmt)): if param != '...' and '.' in param: (an, pn) = rex.split(r'\.', param) rarg = format_args(an, pn) if rarg: rargs.append(rarg) else: rargs.append(param) payload = ' | '.join(p for p in rargs) calling_self = from_app and syslx.fmt_app_name( from_app.name) == appname if not calling_self and not payload and deactivate: deactivate() if len(endpt.stmt): hit_blackbox = False for (upto, comment) in itertools.chain( uptos.iteritems(), already_visited.keys()): # Compare the common prefix of the current endpt and upto. upto_parts = upto.split(' <- ') if [appname, epname][:len(upto_parts)] == upto_parts: hit_blackbox = True if payload: write.activate(agent) if comment is not None: write('note over {}: {}', agent, comment or 'see below') else: if comment is not None: write('note {}: {}', 'left' if sender > agent else 'right', comment or 'see below') if payload: write('{}<--{} : {}', sender, agent, payload) write.deactivate(agent) break if not hit_blackbox: with write.activated(agent, human or cron) as deactivate: # 'see above') visiting = (appname + ' <- ' + epname, None) already_visited[visiting] += 1 try: return visit_stmts(endpt.stmt, deactivate, True) finally: already_visited[visiting] -= 1 if not already_visited[visiting]: del already_visited[visiting]
def cmd(args): """Handle subcommand.""" (module, _, _) = syslloader.load(args.modules, args.validations, args.root) def output_sd(args, params): """Generate and output a sequence diagram.""" out = sequence_diag(module, params) diagutil.output_plantuml(args, out) epfilters = os.getenv('SYSL_SD_FILTERS', '*').split(',') # TODO: Find a cleaner way to trigger multi-output. if '%(epname)' in args.output: out_fmt = diagutil.parse_fmt(args.output) for appname in args.app: app = module.apps[appname] bbs = [[e.s for e in bbox.a.elt] for bbox in syslx.View(app.attrs)['blackboxes'].a.elt] seqtitle = diagutil.parse_fmt( syslx.View(app.attrs)['seqtitle'].s or args.seqtitle) epfmt = diagutil.parse_fmt( syslx.View(app.attrs)['epfmt'].s or args.endpoint_format) appfmt = diagutil.parse_fmt( syslx.View(app.attrs)['appfmt'].s or args.app_format) for (name, endpt) in sorted(app.endpoints.iteritems(), key=lambda kv: kv[1].name): if not any( fnmatch.fnmatch(name, filt) for filt in epfilters): continue attrs = { u'@' + name: value.s for (name, value) in endpt.attrs.iteritems() } args.output = out_fmt(appname=appname, epname=name, eplongname=endpt.long_name, **attrs) if args.filter and not re.match(args.filter, args.output): continue bbs2 = [[ e.s for e in bbox.a.elt ] for bbox in syslx.View(endpt.attrs)['blackboxes'].a.elt] varrefs = diagutil.attr_fmt_vars( app.attrs, endpt.attrs, epname=endpt.name, eplongname=endpt.long_name) out = sequence_diag( module, SequenceDiagParams(endpoints=[ ' :: '.join(s.call.target.part) + ' <- ' + s.call.endpoint for s in endpt.stmt if s.WhichOneof('stmt') == 'call' ], epfmt=epfmt, appfmt=appfmt, activations=args.activations, title=seqtitle(**varrefs).replace( '\n', r'\n'), blackboxes=bbs + bbs2)) diagutil.output_plantuml(args, out) else: if not args.endpoint: raise Exception( 'sysl sd requires either one specific endpoint, ' + 'e.g. --endpoint "ATM <- GetBalance", or an output ' + 'pattern with %(epname), e.g. -o "out/%(epname).svg".') out = sequence_diag( module, SequenceDiagParams( # -s builds list of lists (idkw). endpoints=args.endpoint, epfmt=diagutil.parse_fmt(args.endpoint_format), appfmt=diagutil.parse_fmt(args.app_format), activations=args.activations, title=args.title, blackboxes=args.blackbox)) diagutil.output_plantuml(args, out)
def visit_endpoint(from_app, appname, epname, uptos, sender_patterns, sender_endpt_patterns=None, stmt=None, deactivate=None): """Recursively visit an endpoint.""" if from_app: sender = var_name(syslx.fmt_app_name(from_app.name)) else: sender = '[' agent = var_name(appname) app = module.apps.get(appname) endpt = app.endpoints.get(epname) assert endpt def visit_stmts(stmts, deactivate, last_parent_stmt): """Recursively visit a stmt list.""" def block(last_stmt, block_stmts, fmt, *args): """Output a compound block.""" write(fmt, *args) with write.indent(): return visit_stmts(block_stmts, deactivate, last_stmt) def block_with_end(last_stmt, block_stmts, fmt, *args): """Output a compound block, including the 'end' clause.""" payload = block(last_stmt, block_stmts, fmt, *args) write('end') return payload payload = None for (i, stmt) in enumerate(stmts): last_stmt = last_parent_stmt and i == len(stmts) - 1 if stmt.HasField('call'): with write.indent(): payload = visit_endpoint( app, syslx.fmt_app_name(stmt.call.target), stmt.call.endpoint, uptos, app_patterns, syslx.patterns(stmt.attrs) | syslx.patterns(endpt.attrs), stmt, last_stmt and deactivate) elif stmt.HasField('action'): #write('{0} -> {0} : {1}', agent, r'\n'.join(textwrap.wrap(stmt.action.action, 40))) write('{0} -> {0} : {1}', agent, stmt.action.action) elif stmt.HasField('cond'): payload = block_with_end(last_stmt, stmt.cond.stmt, 'opt {}', stmt.cond.test) elif stmt.HasField('loop'): payload = block_with_end( last_stmt, stmt.loop.stmt, 'loop {} {}', stmt.loop.Mode.Name(stmt.loop.mode), stmt.loop.criterion) elif stmt.HasField('loop_n'): payload = block_with_end(last_stmt, stmt.loop_n.stmt, 'loop {} times', stmt.loop_n.count) elif stmt.HasField('foreach'): payload = block_with_end(last_stmt, stmt.foreach.stmt, 'loop for each {}', stmt.foreach.collection) elif stmt.HasField('group'): payload = block_with_end(last_stmt, stmt.group.stmt, 'group {}', stmt.group.title) elif stmt.HasField('alt'): prefix = 'alt' for (j, choice) in enumerate(stmt.alt.choice): last_alt_stmt = last_stmt and j == len( stmt.alt.choice) - 1 payload = block(last_alt_stmt, choice.stmt, '{} {}', prefix, choice.cond) prefix = 'else' write('end') elif stmt.HasField('ret'): rargs = [] for param in syslalgo.yield_ret_params(stmt.ret.payload): if param != '...' and '.' in param: (an, pn) = rex.split(r'\.', param) rarg = format_args(an, pn) if rarg: rargs.append(rarg) else: rargs.append(param) write('{}<--{} : {}', sender, agent, ' | '.join(p for p in rargs)) else: raise Exception('No statement!') return payload app_patterns = syslx.patterns(app.attrs) target_patterns = syslx.patterns(endpt.attrs) patterns = target_patterns human = 'human' in app_patterns human_sender = 'human' in sender_patterns cron = 'cron' in sender_patterns needs_int = not (human or human_sender or cron) and sender != agent label = re.sub(ur'^.*? -> ', u' ⬄ ', unicode(epname)) cron = 'cron' in app_patterns if stmt: assert stmt.HasField('call') ptrns = '' if bool(sender_endpt_patterns) or bool(patterns): ptrns = u', '.join(sorted(sender_endpt_patterns)) + \ u' → ' + u', '.join(sorted(patterns)) isoctrl = [] for ctrl in endpt.attrs.keys(): if 'iso_ctrl' in ctrl: isoctrl.append(re.sub(r'iso_ctrl_(.*)_txt', r'\1', ctrl)) epargs = [] def format_args(an, pn): arg = '.'.join((an, pn)) if arg: conf = module.apps[an].types[pn].attrs[ 'iso_conf'].s[:1].upper() integ = module.apps[an].types[pn].attrs[ 'iso_integ'].s[:1].upper() isocolor = 'red' if 'R' in conf else 'green' arg = '<color blue>' + arg + '</color>' + ' <<color ' + isocolor + '>' + \ (conf if conf else '?') + ', ' + \ (integ if integ else '?') + '</color>>' return arg for p in endpt.param: an = ' :: '.join(p.type.type_ref.ref.appname.part) pn = '.'.join(p.type.type_ref.ref.path) eparg = format_args(an, pn) if eparg: epargs.append(eparg) label = params.epfmt( epname=label, human='human' if human else '', human_sender='human sender' if human_sender else '', needs_int='needs_int' if needs_int else '', args=' | '.join(e for e in epargs), #args=', '.join(p.name for p in stmt.call.arg), patterns=ptrns, controls=', '.join(i for i in sorted(isoctrl)), **diagutil.attr_fmt_vars(stmt.attrs)).replace('\n', r'\n') if not ((human and sender == '[') or cron): ep_patterns = syslx.patterns(endpt.attrs) icon = '<&timer> ' if 'cron' in ep_patterns else '' write('{}->{} : {}{}', sender, agent, icon, label) if log_integration and stmt: log_integration(app=from_app, stmt=stmt, patterns=patterns) rargs = [] for param in syslalgo.yield_ret_params( syslalgo.return_payload(endpt.stmt)): if param != '...' and '.' in param: (an, pn) = rex.split(r'\.', param) rarg = format_args(an, pn) if rarg: rargs.append(rarg) else: rargs.append(param) payload = ' | '.join(p for p in rargs) calling_self = from_app and syslx.fmt_app_name( from_app.name) == appname if not calling_self and not payload and deactivate: deactivate() if len(endpt.stmt): hit_blackbox = False for (upto, comment) in itertools.chain(uptos.iteritems(), already_visited.keys()): # Compare the common prefix of the current endpt and upto. upto_parts = upto.split(' <- ') if [appname, epname][:len(upto_parts)] == upto_parts: hit_blackbox = True if payload: write.activate(agent) if comment is not None: write('note over {}: {}', agent, comment or 'see below') else: if comment is not None: write('note {}: {}', 'left' if sender > agent else 'right', comment or 'see below') if payload: write('{}<--{} : {}', sender, agent, payload) write.deactivate(agent) break if not hit_blackbox: with write.activated(agent, human or cron) as deactivate: # 'see above') visiting = (appname + ' <- ' + epname, None) already_visited[visiting] += 1 try: return visit_stmts(endpt.stmt, deactivate, True) finally: already_visited[visiting] -= 1 if not already_visited[visiting]: del already_visited[visiting]
def integration_views(module, deps, args): """Generate an integration view.""" def find_matching_apps(integrations): """Yield all apps that match the integrations.""" exclude = set(args.exclude) app_re = re.compile( r'^(?:{})(?: *::|$)'.format('|'.join(integrations))) for ((app1, _), (app2, _)) in deps: if not ({app1, app2} & exclude): for app in [app1, app2]: if app_re.match(app) and 'human' not in syslx.patterns( module.apps[app].attrs): yield app def find_apps(matching_apps): """Yield all apps that are relevant to this view.""" exclude = set(args.exclude) for ((app1, _), (app2, _)) in deps: if not ({app1, app2} & exclude) and ({app1, app2} & matching_apps): for app in [app1, app2]: if ('human' not in syslx.patterns(module.apps[app].attrs)): yield app out_fmt = diagutil.parse_fmt(args.output) # The "project" app that specifies the required view of the integrations app = module.apps[args.project] out = [] # Interate over each endpoint within the selected project for endpt in app.endpoints.itervalues(): # build the set of excluded items exclude_attr = endpt.attrs.get('exclude') exclude = set( e.s for e in exclude_attr.a.elt) if exclude_attr else set() passthrough_apps_attr = endpt.attrs.get('passthrough') passthrough_apps = set( e.s for e in passthrough_apps_attr.a.elt) if passthrough_apps_attr else set() integrations = [] # endpt.stmt's "action" will conatain the "apps" whose integration is to be drawn # each one of these will be placed into the "integrations" list for s in endpt.stmt: assert s.WhichOneof('stmt') == 'action', str(s) integrations.append(s.action.action) # include the requested "app" and all the apps upon which the requested "app" # depends in the app set matching_apps = set(find_matching_apps(integrations)) apps = set(find_apps(matching_apps)) - exclude - passthrough_apps def find_integrations(): """Return all integrations between relevant apps.""" integration_set = {((app1, ep1), (app2, ep2)) for ((app1, ep1), (app2, ep2)) in deps if ({app1, app2} <= apps and not ({app1, app2} & exclude)) and not {ep1, ep2} & {'.. * <- *', '*'}} if passthrough_apps: def passthrough_walk(dep, integration_set): """ visit passthrough + 1: dep tuple, integration_set tuples """ # add to integration set if (not ({dep[1][0]} & exclude) and not ( {dep[1][1]} & {'.. * <- *', '*'})): integration_set.add(dep) # find the next outbond dep if dep[1][0] in passthrough_apps: # call visit_endpoints for all calls for stmt in yield_call_statements( module.apps[dep[1][0]].endpoints[dep[1][1]].stmt): app_2_name = ' :: '.join( part for part in stmt.call.target.part) ep_2_name = stmt.call.endpoint passthrough_walk( ((dep[1]), (app_2_name, ep_2_name)), integration_set) return # collect outbound dependencies outbound_deps = {((app1, ep1), (app2, ep2)) for ((app1, ep1), (app2, ep2)) in deps if (({app1, app2} <= apps) or ({app1} <= apps and {app2} <= passthrough_apps)) and not ({app1, app2} & exclude) and not ({ep1, ep2} & {'.. * <- *', '*'})} # collect outbound pubsub dependencies # inbound_deps # add passthroughs for dep in outbound_deps: passthrough_walk(dep, integration_set) return integration_set args.output = out_fmt( appname=args.project, epname=endpt.name, eplongname=endpt.long_name, **diagutil.attr_fmt_vars(app.attrs, endpt.attrs)) if args.filter and not re.match(args.filter, args.output): continue # print args.project, matching_apps out.append(_generate_view(module, args, find_integrations(), matching_apps, app, apps, endpt)) diagutil.output_plantuml(args, out[-1]) return out
def generate_state_view(): def make_varmgr(istoplevel=False): """Return a variable manager instance.""" appfmt = syslx.View(module.apps.get( args.project).attrs)['appfmt'].s appfmt = diagutil.parse_fmt(appfmt) def new_var(var, name): """Outputs a new definition when VarManager makes a new variable.""" # TODO dodgy, should be using context (look at syslseqs) attrs = {} state_name = '' if istoplevel: template = 'state "{}" as X{}{} {{' attrs = module.apps[name].attrs state_name = name else: template = ' state "{}" as {}{}' (app_name, _, ep_name) = name.partition(' : ') attrs = module.apps[app_name].endpoints[ep_name].attrs state_name = ep_name write(template, appfmt(appname=state_name, **diagutil.attr_fmt_vars(attrs)).replace('\n', r'\n'), var, ' <<highlight>>' if name in highlights else '') return diagutil.VarManager(new_var) with write.uml(): if diagram_title: write('title ' + diagram_title) write('left to right direction') write('scale max 16384 height') write('hide empty description') write('skinparam state {{') write(' BackgroundColor FloralWhite') write(' BorderColor Black') write(' ArrowColor Crimson') if highlight_color: write(' BackgroundColor<<highlight>> ' + highlight_color) if arrow_color: write(' ArrowColor ' + arrow_color) if indirect_arrow_color and indirect_arrow_color != 'none': write(' ArrowColor<<indirect>> ' + indirect_arrow_color) write(' ArrowColor<<internal>> ' + indirect_arrow_color) write('}}') var_name = make_varmgr() tl_var_name = make_varmgr(True) clusters = {} # group end points and build the declarations for (app_a, ep_a), (app_b, ep_b) in integrations: if restrict_by and restrict_by not in module.apps[app_a].attrs.keys( ) + module.apps[app_b].attrs.keys(): continue if restrict_by and restrict_by not in module.apps[app_a].endpoints[ep_a].attrs.keys( ) + module.apps[app_b].endpoints[ep_b].attrs.keys(): continue if app_a not in clusters: clusters[app_a] = set() if app_b not in clusters: clusters[app_b] = set() client_ep = ep_b # create clients in the calling app clusters[app_a].add(ep_a) if app_a != app_b and not module.apps[app_a].endpoints[ep_a].is_pubsub: clusters[app_a].add(client_ep + " client") clusters[app_b].add(ep_b) for cluster in clusters: tl_var_name(cluster) for member in clusters[cluster]: var_name(cluster + ' : ' + member) write('}}') processed = [] for ((app_a, ep_a), (app_b, ep_b)) in integrations: if restrict_by and restrict_by not in module.apps[app_a].attrs.keys( ) + module.apps[app_b].attrs.keys(): continue if restrict_by and restrict_by not in module.apps[app_a].endpoints[ep_a].attrs.keys( ) + module.apps[app_b].endpoints[ep_b].attrs.keys(): continue direct = {app_a, app_b} & highlights (match_app, match_ep) = (app_b, ep_b) # build the label label = '' needs_int = app_a != match_app pub_sub_src_ptrns = syslx.patterns( module.apps[app_a].endpoints[ep_a].attrs) tgt_ptrns = syslx.patterns(module.apps[match_app].endpoints[match_ep].attrs) or \ syslx.patterns(module.apps[match_app].attrs) for stmt in yield_call_statements( module.apps[app_a].endpoints[ep_a].stmt): # if restrict_by not in stmt.attrs.keys(): # continue app_b_name = ' :: '.join( part for part in stmt.call.target.part) if match_app == app_b_name and match_ep == stmt.call.endpoint: fmt_vars = diagutil.attr_fmt_vars(stmt.attrs) src_ptrns = syslx.patterns( stmt.attrs) or pub_sub_src_ptrns ptrns = u', '.join(sorted(src_ptrns)) + u' → ' + u', '.join(sorted(tgt_ptrns)) \ if (bool(src_ptrns) or bool(tgt_ptrns)) else '' label = diagutil.parse_fmt(app.attrs["epfmt"].s)( needs_int=needs_int, patterns=ptrns, **fmt_vars) # if not label and middle: # fmt_vars = diagutil.attr_fmt_vars(module.apps[app_b].endpoints[ep_b].attrs) # label = diagutil.parse_fmt(app.attrs["epfmt"].s)(needs_int=needs_int, patterns=u'→' + middle + u'→', **fmt_vars) flow = ".".join([app_a, ep_b, app_b, ep_b]) is_pubsub = module.apps[app_a].endpoints[ep_a].is_pubsub ep_b_client = ep_b + " client" if app_a != app_b: if is_pubsub: write('{} -{}> {}{}', var_name(app_a + ' : ' + ep_a), '[#blue]', var_name(app_b + ' : ' + ep_b), ' : ' + label if label else '') else: write('{} -{}> {}', var_name(app_a + ' : ' + ep_a), '[#' + indirect_arrow_color + ']-' if indirect_arrow_color else '[#silver]-', var_name(app_a + ' : ' + ep_b_client)) if flow not in processed: write('{} -{}> {}{}', var_name(app_a + ' : ' + ep_b_client), '[#black]', var_name(app_b + ' : ' + ep_b), ' : ' + label if label else '') processed.append(flow) else: write('{} -{}> {}{}', var_name(app_a + ' : ' + ep_a), '[#' + indirect_arrow_color + ']-' if indirect_arrow_color else '[#silver]-', var_name(app_b + ' : ' + ep_b), ' : ' + label if label else '')