Skip to content
Open

Flow #64

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion metrics/flow/flow/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .flow import Flow
from .understand import Entity, Metrics
from .change import Oids, Change
17 changes: 17 additions & 0 deletions metrics/flow/flow/models/change.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import dataclasses

@dataclasses.dataclass(frozen=True)
class Oids:
__slots__ = ['before', 'after']

before: str
after: str


@dataclasses.dataclass(frozen=True)
class Change:
__slots__ = ['path', 'type', 'oids']

path: str
type: int
oids: Oids
4 changes: 2 additions & 2 deletions metrics/flow/flow/models/flow.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import dataclasses

from .understand import Entity
from .change import Change


@dataclasses.dataclass(frozen=True)
class Flow:
__slots__ = ['entity', 'ninput', 'noutput', 'npath']

entity: Entity
entity: Change
ninput: int
noutput: int
npath: int
21 changes: 0 additions & 21 deletions metrics/flow/flow/models/understand.py

This file was deleted.

2 changes: 1 addition & 1 deletion metrics/flow/flow/schemas/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .flow import FlowSchema
from .understand import EntitySchema, MetricsSchema
from .change import ChangeSchema
20 changes: 20 additions & 0 deletions metrics/flow/flow/schemas/change.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from marshmallow import Schema, fields, post_load
from ..models import Oids, Change

class OidsSchema(Schema):
before = fields.String()
after = fields.String()

@post_load
def make_oids(self, data, **kwargs):
return Oids(**data)


class ChangeSchema(Schema):
path = fields.String()
type = fields.Integer()
oids = fields.Nested(OidsSchema)

@post_load
def make_change(self, data, **kwargs):
return Change(**data)
4 changes: 2 additions & 2 deletions metrics/flow/flow/schemas/flow.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from marshmallow import Schema, fields, post_load

from .understand import EntitySchema
from .change import ChangeSchema
from ..models import Flow


class FlowSchema(Schema):
entity = fields.Nested(EntitySchema)
change = fields.Nested(ChangeSchema)
ninput = fields.Integer()
noutput = fields.Integer()
npath = fields.Integer()
Expand Down
189 changes: 170 additions & 19 deletions metrics/flow/flow/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,184 @@
from nameko.rpc import rpc, RpcProxy

from .models import Flow
from .schemas import FlowSchema, MetricsSchema
from .schemas import FlowSchema, ChangeSchema

import re

logger = logging.getLogger(__name__)
METRICS = ['CountInput', 'CountOutput', 'CountPath']
SRC_NS = 'http://www.srcML.org/srcML/src'


def _count_fan_in(global_variable_reads):
return len(global_variable_reads)

def _count_fan_out(variable_writes):
fan_out = 0

for key in list(variable_writes):
if len(variable_writes[key]['expressions']) > 0:
fan_out += 1

members_modded = list(
set(
[m for m in variable_writes[key]['members_modified'] if m.rstrip() != '']))

indicies_modded = list(
set(
[i for i in variable_writes[key]['indices_modified'] if i.rstrip() != '']))

fan_out += len(members_modded) + len(indicies_modded)

return fan_out

def _count_npath_from_acyc_paths(acyc_paths, depth = 0):
npath = 0
pos = 0

while pos < len(acyc_paths):
path = acyc_paths[pos]

if isinstance(path, dict):
next_path = acyc_paths[pos + 1] if pos + 1 < len(acyc_paths) else {}
next_path_type = next_path["type"] if "type" in next_path.keys() else ""

previous_path = acyc_paths[pos - 1] if pos - 1 > 0 else {}
previous_path_type = previous_path["type"] if "type" in previous_path.keys() else ""

p_children = path["children"] if "children" in path.keys() else []
p_type = path["type"] if "type" in path.keys() else ""

npath_child = _count_npath_from_acyc_paths(acyc_paths = p_children, depth = depth + 1)

if re.fullmatch(rf"{{{SRC_NS}}}if", p_type):# == "if_stmt":
p_if_type = path["if_type"] if "if_type" in path.keys() else ""
next_if_type = next_path["if_type"] if "if_type" in next_path.keys() else ""

if p_if_type != 'elseif':
if (next_if_type == 'elseif' or
re.fullmatch(rf'{{{SRC_NS}}}else', next_path_type)):
npath += 1 + npath_child
elif re.fullmatch(rf'{{{SRC_NS}}}if|switch|loop', previous_path_type):
if npath_child == 0:
npath = npath + 2 if npath == 0 else npath * 2
else:
npath = (
npath + (2 * npath_child)
if npath == 0
else npath * 2 * npath_child)
else:
npath = npath + 2 * npath_child if npath_child > 0 else npath + 2
else:
npath += 1 + npath_child

elif re.fullmatch(rf"{{{SRC_NS}}}(for|while|do)", p_type):
if re.fullmatch(
r'^if|elseif|else|loop|switch$',
previous_path_type):
npath = npath * (1 + npath_child) if npath_child > 0 else npath * 2
else:
npath = npath + 1 + npath_child if npath_child > 0 else npath + 2

def _transform(metrics):
flows = list()
itemgetter = operator.itemgetter(*METRICS)
for item in metrics:
entity = item.entity
(ninput, noutput, npath) = itemgetter(item.metrics)
if ninput is not None or noutput is not None or npath is not None:
flows.append(Flow(
entity=entity, ninput=ninput, noutput=noutput, npath=npath
))
return flows
elif re.fullmatch(rf"{{{SRC_NS}}}else", p_type):
npath += 1 + npath_child
elif re.fullmatch(rf"{{{SRC_NS}}}switch", p_type):
npath += 1 + npath_child
elif re.fullmatch(rf"{{{SRC_NS}}}case", p_type):
npath += 1 + npath_child
elif re.fullmatch(rf"{{{SRC_NS}}}default", p_type):
npath += npath_child
pos += 1

if npath == 0 and depth == 0:
npath = 1

class ComplexityService:
return npath

class FlowService:
name = 'flow'

config = Config()
understand_rpc = RpcProxy('understand')
parser_rpc = RpcProxy('parser')
repo_rpc = RpcProxy('repository')

@rpc
def metrics_from_contents(self, file_name, contents):
functions = self.parser_rpc.get_functions_with_properties(file_name, contents)

ninput = 0
noutput = 0
npath = 0

if functions is not None:
for function in functions:
func_ninput = 0
func_noutput = 0
func_npath = 0

func_npath = _count_npath_from_acyc_paths(
function["acyclical_paths_tree"],
depth = 0
)

func_ninput = (
func_ninput +
_count_fan_in(function["global_variable_reads"]) +
len(function["functions_called_by"])
)

func_noutput += _count_fan_out(
function["global_variable_writes"]
)

logger.debug(function["file_name"])
logger.debug(function["signature"])
logger.debug(" fanin: " + str(func_ninput))
logger.debug("fanout: " + str(func_noutput))
logger.debug(" npath: " + str(func_npath))
logger.debug('-'*30)

func_noutput += 1 if function["has_return"] else 0

ninput += func_ninput
noutput += func_noutput
npath += func_npath

return {
'ninput': ninput,
'noutput': noutput,
'npath': npath
}

@rpc
def collect(self, project, **options):
logger.debug(project)
metrics = self.understand_rpc.get_metrics(project, METRICS)
metrics = MetricsSchema(many=True).load(metrics)
return FlowSchema(many=True).dump(_transform(metrics))
def collect(self, project, sha, **options):
flow_metrics = []
changes = self.repo_rpc.get_changes(project = project, sha = sha)

logger.debug("Displaying contents")
for change in changes:
file_name = change["path"].split('/')[-1]
oids = change["oids"]
oid_after = oids["after"]
contents = self.repo_rpc.get_content(project, oid_after)

metrics = self.metrics_from_contents(
file_name = file_name,
contents = contents
)

change_obj = ChangeSchema(many = False).dump({
'path': change["path"],
'type': change["type"],
'oids': oids
})

flow_metrics.append({
'change': change_obj,
**metrics
})

if len(flow_metrics) > 0:
return FlowSchema(many=True).dump(flow_metrics)

return None
5 changes: 4 additions & 1 deletion metrics/functionchurn/functionchurn/schemas/parser.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from marshmallow import Schema, fields, post_load
from marshmallow import Schema, fields, post_load, EXCLUDE

from ..models import Comment, Function, Position, Span

Expand Down Expand Up @@ -34,6 +34,9 @@ class FunctionSchema(Schema):
signature = fields.String()
span = fields.Nested(SpanSchema)

class Meta:
unknown = EXCLUDE

@post_load
def make_function(self, data, **kwargs):
return Function(**data)
5 changes: 4 additions & 1 deletion metrics/loc/loc/schemas/parser.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from marshmallow import Schema, fields, post_load
from marshmallow import Schema, fields, post_load, EXCLUDE

from ..models import Comment, Function, Position, Span

Expand Down Expand Up @@ -34,6 +34,9 @@ class FunctionSchema(Schema):
signature = fields.String()
span = fields.Nested(SpanSchema)

class Meta:
unknown = EXCLUDE

@post_load
def make_function(self, data, **kwargs):
return Function(**data)
Loading