Skip to content

result.py

from pymwp import Result

FuncResult

Bases: Timeable

Capture details of analysis result for one program (function in C).

Source code in pymwp/result.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
class FuncResult(Timeable):
    """Capture details of analysis result for one program (function in C)."""

    def __init__(
            self, name: str, infinite: bool = False,
            variables: Optional[List[str]] = None,
            relation: Optional[Relation] = None,
            choices: Optional[Choices] = None,
            bound: Optional[Bound] = None):
        """
        Create a function result.

        Attributes:
            name: function name
            infinite: True if result is infinite.
            variables: list of variables.
            relation: corresponding [`Relation`](relation.md)
            choices: choice object [`Choice`](choice.md)
            bound: bound object [`Bound`](bound.md)
        """
        super().__init__()
        self.name = name
        self.vars = variables or []
        self.infinite = infinite
        self.relation = relation
        self.choices = choices
        self.bound = bound

    @property
    def n_vars(self) -> int:
        """Number of variables."""
        return len(self.vars)

    @property
    def n_bounds(self) -> int:
        """Number of bounds."""
        return self.choices.n_bounds if self.choices else 0

    def to_dict(self) -> dict:
        """Serialize function result."""
        return {
            "name": self.name,
            "infinity": self.infinite,
            "variables": self.vars,
            "start_time": self.start_time,
            "end_time": self.end_time,
            "relation": self.relation.to_dict() if self.relation else None,
            **({"choices": self.choices.valid if self.choices else None,
                "bound": self.bound.to_dict() if self.bound else None}
               if not self.infinite else
               {"infty_vars": self.relation.infty_vars()}
               if self.relation else {})}

    @staticmethod
    def from_dict(**kwargs):
        """Deserialize function result."""
        st, et = 'start_time', 'end_time'
        func = FuncResult(kwargs['name'], kwargs['infinity'])
        func.start_time = int(kwargs[st]) if st in kwargs else 0
        func.end_time = int(kwargs[et]) if et in kwargs else 0
        if 'variables' in kwargs:
            func.vars = kwargs['variables']
        if kwargs['relation']:
            matrix = kwargs['relation']['matrix']
            if func.vars:
                func.relation = Relation(func.vars, decode(matrix))
        if 'choices' in kwargs:
            func.choices = Choices(kwargs['choices'])
        if 'bound' in kwargs and func.choices and func.relation:
            func.bound = Bound(kwargs['bound'])
        return func

n_bounds: int property

Number of bounds.

n_vars: int property

Number of variables.

__init__(name, infinite=False, variables=None, relation=None, choices=None, bound=None)

Create a function result.

Attributes:

Name Type Description
name

function name

infinite

True if result is infinite.

variables

list of variables.

relation

corresponding Relation

choices

choice object Choice

bound

bound object Bound

Source code in pymwp/result.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def __init__(
        self, name: str, infinite: bool = False,
        variables: Optional[List[str]] = None,
        relation: Optional[Relation] = None,
        choices: Optional[Choices] = None,
        bound: Optional[Bound] = None):
    """
    Create a function result.

    Attributes:
        name: function name
        infinite: True if result is infinite.
        variables: list of variables.
        relation: corresponding [`Relation`](relation.md)
        choices: choice object [`Choice`](choice.md)
        bound: bound object [`Bound`](bound.md)
    """
    super().__init__()
    self.name = name
    self.vars = variables or []
    self.infinite = infinite
    self.relation = relation
    self.choices = choices
    self.bound = bound

from_dict(**kwargs) staticmethod

Deserialize function result.

Source code in pymwp/result.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
@staticmethod
def from_dict(**kwargs):
    """Deserialize function result."""
    st, et = 'start_time', 'end_time'
    func = FuncResult(kwargs['name'], kwargs['infinity'])
    func.start_time = int(kwargs[st]) if st in kwargs else 0
    func.end_time = int(kwargs[et]) if et in kwargs else 0
    if 'variables' in kwargs:
        func.vars = kwargs['variables']
    if kwargs['relation']:
        matrix = kwargs['relation']['matrix']
        if func.vars:
            func.relation = Relation(func.vars, decode(matrix))
    if 'choices' in kwargs:
        func.choices = Choices(kwargs['choices'])
    if 'bound' in kwargs and func.choices and func.relation:
        func.bound = Bound(kwargs['bound'])
    return func

to_dict()

Serialize function result.

Source code in pymwp/result.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def to_dict(self) -> dict:
    """Serialize function result."""
    return {
        "name": self.name,
        "infinity": self.infinite,
        "variables": self.vars,
        "start_time": self.start_time,
        "end_time": self.end_time,
        "relation": self.relation.to_dict() if self.relation else None,
        **({"choices": self.choices.valid if self.choices else None,
            "bound": self.bound.to_dict() if self.bound else None}
           if not self.infinite else
           {"infty_vars": self.relation.infty_vars()}
           if self.relation else {})}

Program

Bases: object

Details about analyzed C file.

Source code in pymwp/result.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
class Program(object):
    """Details about analyzed C file."""

    def __init__(self, n_lines: int = -1, program_path: str = None):
        """Create program object.

        Attributes:
            n_lines (int): number of lines.
            program_path (str): path to program file.
        """
        self.n_lines: int = n_lines
        self.program_path: Optional[str] = program_path

    def to_dict(self) -> Dict[str, Union[int, str]]:
        """Convert Program object to a dictionary."""
        return {
            'n_lines': self.n_lines,
            'program_path': self.program_path}

    @property
    def name(self) -> Optional[str]:
        """Get name of program, without path and extension.

        Returns:
            Program name or `None` if not set.
        """
        return Path(self.program_path).stem if self.program_path else None

    @staticmethod
    def from_dict(**kwargs) -> Program:
        """Initialize Program object from kwargs.

        Returns:
            Program: initialized program object.

        Raises:
            KeyError: if all Program attributes are not included as kwargs.
        """
        return Program(kwargs['n_lines'], kwargs['program_path'])

name: Optional[str] property

Get name of program, without path and extension.

Returns:

Type Description
Optional[str]

Program name or None if not set.

__init__(n_lines=-1, program_path=None)

Create program object.

Attributes:

Name Type Description
n_lines int

number of lines.

program_path str

path to program file.

Source code in pymwp/result.py
50
51
52
53
54
55
56
57
58
def __init__(self, n_lines: int = -1, program_path: str = None):
    """Create program object.

    Attributes:
        n_lines (int): number of lines.
        program_path (str): path to program file.
    """
    self.n_lines: int = n_lines
    self.program_path: Optional[str] = program_path

from_dict(**kwargs) staticmethod

Initialize Program object from kwargs.

Returns:

Name Type Description
Program Program

initialized program object.

Raises:

Type Description
KeyError

if all Program attributes are not included as kwargs.

Source code in pymwp/result.py
75
76
77
78
79
80
81
82
83
84
85
@staticmethod
def from_dict(**kwargs) -> Program:
    """Initialize Program object from kwargs.

    Returns:
        Program: initialized program object.

    Raises:
        KeyError: if all Program attributes are not included as kwargs.
    """
    return Program(kwargs['n_lines'], kwargs['program_path'])

to_dict()

Convert Program object to a dictionary.

Source code in pymwp/result.py
60
61
62
63
64
def to_dict(self) -> Dict[str, Union[int, str]]:
    """Convert Program object to a dictionary."""
    return {
        'n_lines': self.n_lines,
        'program_path': self.program_path}

Result

Bases: Timeable

Captures analysis result and details about the process.

This result contains

  • program: information about analyzed C File: type Program
  • relations: dictionary of function results: type FuncResult
  • analysis time: measured from before any function has been analyzed, until all functions have been analyzed. It excludes time to write result to file.
Source code in pymwp/result.py
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
class Result(Timeable):
    """Captures analysis result and details about the process.

    This result contains

    - program: information about analyzed C File:
        type [`Program`](result.md#pymwp.result.Program)
    - relations: dictionary of function results:
        type [`FuncResult`](result.md#pymwp.result.FuncResult)
    - analysis time: measured from before any function
        has been analyzed, until all functions have been analyzed.
        It excludes time to write result to file.
    """

    def __init__(self):
        super().__init__()
        self.program: Program = Program()
        self.relations: Dict[str, FuncResult] = {}

    @property
    def n_functions(self) -> int:
        """Number of functions in analyzed program."""
        return len(self.relations.keys())

    def add_relation(self, func_result: FuncResult) -> None:
        """Appends function analysis outcome to result.

        Attributes:
            func_result: function analysis to append to Result.
        """
        self.relations[func_result.name] = func_result
        if not func_result.infinite:
            if func_result.bound:
                logger.info(f'Bound: {Bound.show_poly(func_result.bound)}')
                logger.info(f'Bounds: {func_result.n_bounds}')
            else:
                logger.info('Some bound exists')
        if func_result.infinite:
            logger.info(f'{func_result.name} is infinite')
            if func_result.relation:
                logger.info('Possibly problematic flows:')
                logger.info(func_result.relation.infty_pairs())

    def get_func(
            self, name: Optional[str] = None
    ) -> Union[FuncResult, Dict[str, FuncResult]]:
        """Returns analysis result for function(s).

        * If `name` argument is provided and key exists,
          returns function result for exact value match.
        * If program contained exactly 1 function,
          returns result for that function.
        * Otherwise, returns a dictionary of results for each
          analyzed function, as in: `<function_name, analysis_result>`

        Arguments:
            name: name of function

        Returns:
            Function analysis result, or dictionary of results.
        """
        if name and name in self.relations:
            return self.relations[name]
        if self.n_functions == 1:
            key = next(iter(self.relations))
            return self.relations[key]
        return self.relations

    def log_result(self) -> None:
        """Display here all interesting stats."""
        if self.n_functions == 0:
            logger.warning("Input C file contained no analyzable functions!")

        dur_sc = round(self.time_diff / 1e9, 1)
        dur_ms = int(self.time_diff / 1e6)
        logger.info(f'Total time: {dur_sc} s ({dur_ms} ms)')

    def serialize(self) -> dict:
        """JSON serialize a result object.

        Returns:
            dict: dictionary representation.
        """
        return {
            'start_time': self.start_time,
            'end_time': self.end_time,
            'program': self.program.to_dict(),
            'relations': [v.to_dict() for v in self.relations.values()]}

    @staticmethod
    def deserialize(**kwargs) -> Result:
        """Reverse of serialize.

        Returns:
            Result: Initialized Result object.
        """
        st, et, r = 'start_time', 'end_time', Result()
        r.start_time = int(kwargs[st]) if st in kwargs else 0
        r.end_time = int(kwargs[et]) if et in kwargs else 0
        if 'program' in kwargs:
            r.program = Program.from_dict(**kwargs['program'])
        if 'relations' in kwargs:
            for value in kwargs['relations']:
                r.add_relation(FuncResult.from_dict(**value))
        return r

n_functions: int property

Number of functions in analyzed program.

add_relation(func_result)

Appends function analysis outcome to result.

Attributes:

Name Type Description
func_result

function analysis to append to Result.

Source code in pymwp/result.py
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
def add_relation(self, func_result: FuncResult) -> None:
    """Appends function analysis outcome to result.

    Attributes:
        func_result: function analysis to append to Result.
    """
    self.relations[func_result.name] = func_result
    if not func_result.infinite:
        if func_result.bound:
            logger.info(f'Bound: {Bound.show_poly(func_result.bound)}')
            logger.info(f'Bounds: {func_result.n_bounds}')
        else:
            logger.info('Some bound exists')
    if func_result.infinite:
        logger.info(f'{func_result.name} is infinite')
        if func_result.relation:
            logger.info('Possibly problematic flows:')
            logger.info(func_result.relation.infty_pairs())

deserialize(**kwargs) staticmethod

Reverse of serialize.

Returns:

Name Type Description
Result Result

Initialized Result object.

Source code in pymwp/result.py
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
@staticmethod
def deserialize(**kwargs) -> Result:
    """Reverse of serialize.

    Returns:
        Result: Initialized Result object.
    """
    st, et, r = 'start_time', 'end_time', Result()
    r.start_time = int(kwargs[st]) if st in kwargs else 0
    r.end_time = int(kwargs[et]) if et in kwargs else 0
    if 'program' in kwargs:
        r.program = Program.from_dict(**kwargs['program'])
    if 'relations' in kwargs:
        for value in kwargs['relations']:
            r.add_relation(FuncResult.from_dict(**value))
    return r

get_func(name=None)

Returns analysis result for function(s).

  • If name argument is provided and key exists, returns function result for exact value match.
  • If program contained exactly 1 function, returns result for that function.
  • Otherwise, returns a dictionary of results for each analyzed function, as in: <function_name, analysis_result>

Parameters:

Name Type Description Default
name Optional[str]

name of function

None

Returns:

Type Description
Union[FuncResult, Dict[str, FuncResult]]

Function analysis result, or dictionary of results.

Source code in pymwp/result.py
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
def get_func(
        self, name: Optional[str] = None
) -> Union[FuncResult, Dict[str, FuncResult]]:
    """Returns analysis result for function(s).

    * If `name` argument is provided and key exists,
      returns function result for exact value match.
    * If program contained exactly 1 function,
      returns result for that function.
    * Otherwise, returns a dictionary of results for each
      analyzed function, as in: `<function_name, analysis_result>`

    Arguments:
        name: name of function

    Returns:
        Function analysis result, or dictionary of results.
    """
    if name and name in self.relations:
        return self.relations[name]
    if self.n_functions == 1:
        key = next(iter(self.relations))
        return self.relations[key]
    return self.relations

log_result()

Display here all interesting stats.

Source code in pymwp/result.py
229
230
231
232
233
234
235
236
def log_result(self) -> None:
    """Display here all interesting stats."""
    if self.n_functions == 0:
        logger.warning("Input C file contained no analyzable functions!")

    dur_sc = round(self.time_diff / 1e9, 1)
    dur_ms = int(self.time_diff / 1e6)
    logger.info(f'Total time: {dur_sc} s ({dur_ms} ms)')

serialize()

JSON serialize a result object.

Returns:

Name Type Description
dict dict

dictionary representation.

Source code in pymwp/result.py
238
239
240
241
242
243
244
245
246
247
248
def serialize(self) -> dict:
    """JSON serialize a result object.

    Returns:
        dict: dictionary representation.
    """
    return {
        'start_time': self.start_time,
        'end_time': self.end_time,
        'program': self.program.to_dict(),
        'relations': [v.to_dict() for v in self.relations.values()]}

Timeable

Represents an entity whose runtime can be measured.

Source code in pymwp/result.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Timeable:
    """Represents an entity whose runtime can be measured."""

    def __init__(self):
        self.start_time = 0
        self.end_time = 0

    @property
    def time_diff(self) -> int:
        """Time delta between analysis start and end time."""
        return self.end_time - self.start_time

    @property
    def dur_s(self) -> float:
        """Duration in seconds."""
        return round(self.time_diff / 1e9, 1)

    @property
    def dur_ms(self) -> int:
        """Duration in milliseconds."""
        return int(self.time_diff / 1e6)

    def on_start(self) -> Timeable:
        """Called at start of timeable entity."""
        self.start_time = time.time_ns()
        return self

    def on_end(self) -> Timeable:
        """Called at end of timeable entity."""
        self.end_time = time.time_ns()
        return self

dur_ms: int property

Duration in milliseconds.

dur_s: float property

Duration in seconds.

time_diff: int property

Time delta between analysis start and end time.

on_end()

Called at end of timeable entity.

Source code in pymwp/result.py
41
42
43
44
def on_end(self) -> Timeable:
    """Called at end of timeable entity."""
    self.end_time = time.time_ns()
    return self

on_start()

Called at start of timeable entity.

Source code in pymwp/result.py
36
37
38
39
def on_start(self) -> Timeable:
    """Called at start of timeable entity."""
    self.start_time = time.time_ns()
    return self