Metric Plugins ============== Understand can display tables and charts built from metric values. Additional metrics can be provided to Understand by writing a metric plugin. A sample plugin is below. The parameter `metric` passed to `test_*` and `value` is an :class:`Metric ` object. A metrics plugin can provide multiple metrics. Use the :meth:`id ` method to find the requested metric id. The :meth:`database ` is also available. Important: If the plugin needs to know what other metrics are available for an entity, architecture, or database, it must use the `metric` parameter's :meth:`list ` method. It is not safe to call any other methods that list metrics from inside a metric plugin such as :meth:`ent.metrics `, :meth:`arch.metrics `, :meth:`db.metrics `, or :meth:`Metric.list `. Those metric list functions may call back into the metrics plugin leading to infinite recursion. It is safe to retrieve metric values using the normal metric methods ( :meth:`ent.metric `, :meth:`arch.metric `, or :meth:`db.metric `). The sample below maps old 6.3 metric ids to the new metric ids. :: # A sample metrics plugin. # # This plugin maps ids from Understand 6.3 to the new 6.4 ids. import understand # For this plugin, define the map from old id to new id metDict = { "AltCountLineBlank" :"CountLineBlankWithInactive", "AltCountLineCode" :"CountLineCodeWithInactive", "AltCountLineComment" :"CountLineCommentWithInactive", "CountLineBlank_Html" :"CountLineBlankHtml", "CountLineBlank_Javascript" :"CountLineBlankJavascript", "CountLineBlank_Php" :"CountLineBlankPhp", "CountLineCode_Javascript" :"CountLineCodeJavascript", "CountLineCode_Php" :"CountLineCodePhp", "CountLineComment_Html" :"CountLineCommentHtml", "CountLineComment_Javascript" :"CountLineCommentJavascript", "CountLineComment_Php" :"CountLineCommentPhp", "CountLine_Html" :"CountLineHtml", "CountLine_Javascript" :"CountLineJavascript", "CountLine_Php" :"CountLinePhp", "AltAvgLineBlank" :"AvgCountLineBlankWithInactive", "AltAvgLineCode" :"AvgCountLineCodeWithInactive", "AltAvgLineComment" :"AvgCountLineCommentWithInactive", "AvgLine" :"AvgCountLine", "AvgLineBlank" :"AvgCountLineBlank", "AvgLineCode" :"AvgCountLineCode", "AvgLineComment" :"AvgCountLineComment", "CountDeclProgunit" : "CountDeclProgUnit", "CountStmtDecl_Javascript" :"CountStmtDeclJavascript", "CountStmtDecl_Php" :"CountStmtDeclPhp", "CountStmtExe_Javascript" :"CountStmtExeJavascript", "CountStmtExe_Php" :"CountStmtExePhp", } def ids(): """ Required, a list of metric ids that this script provides For example, CountLineCode is a metric id. """ return list(metDict.keys()) def name(id): """ Required, the name of the metric given by id. For example, CountLineCode -> "Source Lines of Code" """ # This sample does not provide new names for these metrics. return id; def description(id): """ Required, the description of the metric given by id For example, CountLineCode -> "Number of lines containing source code" """ # It is safe to call understand.Metric.description from a plugin, as long # as the id passed to understand.Metric.description is not an id provided # by this plugin. return understand.Metric.description(metDict.get(id)) def is_integer(id): """ Optional, return True if the metric value is an integer. If this function it not implemented, it is assumed false, meaning the value should be represented as a double/float. """ # All the renamed metrics were integer metrics return True # One of the following three test functions should return True. def test_entity(metric, ent): """ Optional, return True if metric can be calculated for the given entity. """ # Important: It is NOT safe to call ent.metrics() here to check for the # existence of the new metric id. That method will include plugin metrics # and lead to infinite recursion. Instead, use the metric object to retrieve # only built-in metrics. return metDict.get(metric.id()) in metric.list(ent) def test_architecture(metric, arch): """ Optional, return True if metric can be calculated for the given architecture. """ # Important: It is NOT safe to call arch.metrics() here to check for the # existence of the new metric id. That method will include plugin metrics # and lead to infinite recursion. Instead, use the metric object to retrieve # only built-in metrics. return metDict.get(metric.id()) in metric.list(arch) def test_global(metric, db): """ Optional, return True if metric can be calculated for the given database. """ # Important: It is NOT safe to call db.metrics() here to check for the # existence of the new metric id. That method will include plugin metrics # and lead to infinite recursion. Instead, use the metric object to retrieve # only built-in metrics. return metDict.get(metric.id()) in metric.list(db) def test_available(metric,entkindstr): """ Optional, return True if the metric is potentially available. This is used when there isn't a specific target for the metric, like lists of metrics available for export, or for a treemap. Use metric.db() to retrieve the database. If the metric is language specific, the code might look like this: return "Ada" in metric.db().language() entkindstr may be empty. If it is empty, return True as long as the metric is available for an entity, architecture, or the project as a whole. If entkindstr is not empty, return True only if the metric is available for entities matching the provided kind string. Kind checks are performed like this: my_kinds = set(understand.Kind.list_entity(myMetricKindString) test_kinds = set(understand.Kind.list_entity(entkindstr) return len(my_kinds.intersection(test_kinds)) > 0 """ # Important: It is NOT safe to call understand.Metric.list() here to check for # the existence of the new metric id. That method will include plugin metrics # and lead to infinite recursion. Instead, use the metric object to retrieve # only built-in metrics. return metDict.get(metric.id()) in metric.list(entkindstr) def value(metric, target): """ Required, return the metric value for the target. The target may be an entity, architecture, or database depending on which test functions returned True. """ id = metDict.get(metric.id()) # It is safe to get the value of any metric defined outside this plugin # using ent.metric(), db.metric(), and arch.metric(). Because the target # will have the metric method no matter what kind it is, this line works: return float(target.metric([id])[id]) # But, if different targets need to be treated differently, use isinstance # to determine the target: # if isinstance(target, understand.Db): # pass # elif isinstance(target, understand.Arch): # pass # else: # must be entity # pass