qase-python-commons 3.1.3__py3-none-any.whl → 4.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of qase-python-commons might be problematic. Click here for more details.
- qase/__init__.py +3 -0
- qase/commons/client/api_v1_client.py +169 -143
- qase/commons/client/api_v2_client.py +77 -23
- qase/commons/client/base_api_client.py +12 -1
- qase/commons/config.py +159 -20
- qase/commons/logger.py +82 -13
- qase/commons/models/__init__.py +0 -2
- qase/commons/models/attachment.py +11 -8
- qase/commons/models/basemodel.py +12 -3
- qase/commons/models/config/framework.py +61 -0
- qase/commons/models/config/qaseconfig.py +34 -0
- qase/commons/models/config/run.py +19 -0
- qase/commons/models/config/testops.py +45 -3
- qase/commons/models/external_link.py +41 -0
- qase/commons/models/relation.py +16 -6
- qase/commons/models/result.py +16 -31
- qase/commons/models/run.py +17 -2
- qase/commons/models/runtime.py +15 -1
- qase/commons/models/step.py +43 -11
- qase/commons/profilers/__init__.py +4 -3
- qase/commons/profilers/db.py +965 -5
- qase/commons/profilers/network.py +5 -1
- qase/commons/reporters/core.py +50 -9
- qase/commons/reporters/report.py +11 -6
- qase/commons/reporters/testops.py +56 -22
- qase/commons/status_mapping/__init__.py +12 -0
- qase/commons/status_mapping/status_mapping.py +237 -0
- qase/commons/util/__init__.py +9 -0
- qase/commons/util/host_data.py +140 -0
- qase/commons/utils.py +95 -0
- {qase_python_commons-3.1.3.dist-info → qase_python_commons-4.1.3.dist-info}/METADATA +16 -11
- qase_python_commons-4.1.3.dist-info/RECORD +45 -0
- {qase_python_commons-3.1.3.dist-info → qase_python_commons-4.1.3.dist-info}/WHEEL +1 -1
- qase/commons/models/suite.py +0 -13
- qase_python_commons-3.1.3.dist-info/RECORD +0 -40
- {qase_python_commons-3.1.3.dist-info → qase_python_commons-4.1.3.dist-info}/top_level.txt +0 -0
|
@@ -4,6 +4,40 @@ from .plan import PlanConfig
|
|
|
4
4
|
from .run import RunConfig
|
|
5
5
|
from ..basemodel import BaseModel
|
|
6
6
|
from ... import QaseUtils
|
|
7
|
+
from typing import List
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ConfigurationValue(BaseModel):
|
|
11
|
+
name: str = None
|
|
12
|
+
value: str = None
|
|
13
|
+
|
|
14
|
+
def __init__(self, name: str = None, value: str = None):
|
|
15
|
+
self.name = name
|
|
16
|
+
self.value = value
|
|
17
|
+
|
|
18
|
+
def set_name(self, name: str):
|
|
19
|
+
self.name = name
|
|
20
|
+
|
|
21
|
+
def set_value(self, value: str):
|
|
22
|
+
self.value = value
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ConfigurationsConfig(BaseModel):
|
|
26
|
+
values: List[ConfigurationValue] = None
|
|
27
|
+
create_if_not_exists: bool = None
|
|
28
|
+
|
|
29
|
+
def __init__(self):
|
|
30
|
+
self.values = []
|
|
31
|
+
self.create_if_not_exists = False
|
|
32
|
+
|
|
33
|
+
def set_values(self, values: List[ConfigurationValue]):
|
|
34
|
+
self.values = values
|
|
35
|
+
|
|
36
|
+
def set_create_if_not_exists(self, create_if_not_exists):
|
|
37
|
+
self.create_if_not_exists = QaseUtils.parse_bool(create_if_not_exists)
|
|
38
|
+
|
|
39
|
+
def add_value(self, name: str, value: str):
|
|
40
|
+
self.values.append(ConfigurationValue(name=name, value=value))
|
|
7
41
|
|
|
8
42
|
|
|
9
43
|
class TestopsConfig(BaseModel):
|
|
@@ -13,14 +47,19 @@ class TestopsConfig(BaseModel):
|
|
|
13
47
|
run: RunConfig = None
|
|
14
48
|
plan: PlanConfig = None
|
|
15
49
|
batch: BatchConfig = None
|
|
50
|
+
configurations: ConfigurationsConfig = None
|
|
51
|
+
status_filter: List[str] = None
|
|
52
|
+
show_public_report_link: bool = None
|
|
16
53
|
|
|
17
54
|
def __init__(self):
|
|
18
55
|
self.api = ApiConfig()
|
|
19
56
|
self.run = RunConfig()
|
|
20
57
|
self.batch = BatchConfig()
|
|
21
58
|
self.plan = PlanConfig()
|
|
59
|
+
self.configurations = ConfigurationsConfig()
|
|
22
60
|
self.defect = False
|
|
23
|
-
self.
|
|
61
|
+
self.status_filter = []
|
|
62
|
+
self.show_public_report_link = False
|
|
24
63
|
|
|
25
64
|
def set_project(self, project: str):
|
|
26
65
|
self.project = project
|
|
@@ -28,5 +67,8 @@ class TestopsConfig(BaseModel):
|
|
|
28
67
|
def set_defect(self, defect):
|
|
29
68
|
self.defect = QaseUtils.parse_bool(defect)
|
|
30
69
|
|
|
31
|
-
def
|
|
32
|
-
self.
|
|
70
|
+
def set_status_filter(self, status_filter: List[str]):
|
|
71
|
+
self.status_filter = status_filter
|
|
72
|
+
|
|
73
|
+
def set_show_public_report_link(self, show_public_report_link):
|
|
74
|
+
self.show_public_report_link = QaseUtils.parse_bool(show_public_report_link)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from .basemodel import BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ExternalLinkType(Enum):
|
|
7
|
+
"""External link types supported by Qase TestOps"""
|
|
8
|
+
JIRA_CLOUD = 'jiraCloud'
|
|
9
|
+
JIRA_SERVER = 'jiraServer'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ExternalLinkConfig(BaseModel):
|
|
13
|
+
"""Configuration for external link"""
|
|
14
|
+
type: ExternalLinkType = None
|
|
15
|
+
link: str = None
|
|
16
|
+
|
|
17
|
+
def __init__(self, type: ExternalLinkType = None, link: str = None):
|
|
18
|
+
self.type = type
|
|
19
|
+
self.link = link
|
|
20
|
+
|
|
21
|
+
def set_type(self, type: str):
|
|
22
|
+
"""Set external link type from string"""
|
|
23
|
+
if type == 'jiraCloud':
|
|
24
|
+
self.type = ExternalLinkType.JIRA_CLOUD
|
|
25
|
+
elif type == 'jiraServer':
|
|
26
|
+
self.type = ExternalLinkType.JIRA_SERVER
|
|
27
|
+
else:
|
|
28
|
+
raise ValueError(f"Invalid external link type: {type}. Supported types: jiraCloud, jiraServer")
|
|
29
|
+
|
|
30
|
+
def set_link(self, link: str):
|
|
31
|
+
"""Set external link URL or identifier"""
|
|
32
|
+
self.link = link
|
|
33
|
+
|
|
34
|
+
def to_api_type(self) -> str:
|
|
35
|
+
"""Convert to API enum value"""
|
|
36
|
+
if self.type == ExternalLinkType.JIRA_CLOUD:
|
|
37
|
+
return 'jira-cloud'
|
|
38
|
+
elif self.type == ExternalLinkType.JIRA_SERVER:
|
|
39
|
+
return 'jira-server'
|
|
40
|
+
else:
|
|
41
|
+
raise ValueError(f"Invalid external link type: {self.type}")
|
qase/commons/models/relation.py
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
from .basemodel import BaseModel
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
class
|
|
5
|
-
def __init__(self,
|
|
6
|
-
self.
|
|
4
|
+
class SuiteData(BaseModel):
|
|
5
|
+
def __init__(self, title: str) -> None:
|
|
6
|
+
self.public_id = None
|
|
7
7
|
self.title = title
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
class RelationSuite(BaseModel):
|
|
11
|
+
def __init__(self) -> None:
|
|
12
|
+
self.data = []
|
|
13
|
+
|
|
14
|
+
def add_data(self, data: SuiteData) -> None:
|
|
15
|
+
self.data.append(data)
|
|
16
|
+
|
|
17
|
+
|
|
10
18
|
class Relation(BaseModel):
|
|
11
|
-
def __init__(self
|
|
12
|
-
self.
|
|
13
|
-
|
|
19
|
+
def __init__(self):
|
|
20
|
+
self.suite = RelationSuite()
|
|
21
|
+
|
|
22
|
+
def add_suite(self, suite: SuiteData) -> None:
|
|
23
|
+
self.suite.add_data(suite)
|
qase/commons/models/result.py
CHANGED
|
@@ -4,7 +4,6 @@ import uuid
|
|
|
4
4
|
from typing import Type, Optional, Union, Dict, List
|
|
5
5
|
from .basemodel import BaseModel
|
|
6
6
|
from .step import Step
|
|
7
|
-
from .suite import Suite
|
|
8
7
|
from .attachment import Attachment
|
|
9
8
|
from .relation import Relation
|
|
10
9
|
from .. import QaseUtils
|
|
@@ -26,7 +25,7 @@ class Execution(BaseModel):
|
|
|
26
25
|
stacktrace: Optional[str] = None,
|
|
27
26
|
thread: Optional[str] = QaseUtils.get_thread_name()
|
|
28
27
|
):
|
|
29
|
-
self.start_time =
|
|
28
|
+
self.start_time = QaseUtils.get_real_time()
|
|
30
29
|
self.status = status
|
|
31
30
|
self.end_time = end_time
|
|
32
31
|
self.duration = duration
|
|
@@ -34,16 +33,22 @@ class Execution(BaseModel):
|
|
|
34
33
|
self.thread = thread
|
|
35
34
|
|
|
36
35
|
def set_status(self, status: Optional[str]):
|
|
37
|
-
if status
|
|
36
|
+
if status is None:
|
|
38
37
|
self.status = status
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
# Convert to lowercase for validation
|
|
41
|
+
status_lower = status.lower()
|
|
42
|
+
if status_lower in ['passed', 'failed', 'skipped', 'untested', 'invalid', 'disabled', 'blocked']:
|
|
43
|
+
self.status = status_lower
|
|
39
44
|
else:
|
|
40
|
-
raise ValueError('Step status must be one of: passed, failed, skipped, untested')
|
|
45
|
+
raise ValueError('Step status must be one of: passed, failed, skipped, untested, invalid, disabled, blocked')
|
|
41
46
|
|
|
42
47
|
def get_status(self):
|
|
43
48
|
return self.status
|
|
44
49
|
|
|
45
50
|
def complete(self):
|
|
46
|
-
self.end_time =
|
|
51
|
+
self.end_time = QaseUtils.get_real_time()
|
|
47
52
|
self.duration = (int)((self.end_time - self.start_time) * 1000)
|
|
48
53
|
|
|
49
54
|
|
|
@@ -70,20 +75,16 @@ class Result(BaseModel):
|
|
|
70
75
|
self.id: str = str(uuid.uuid4())
|
|
71
76
|
self.title: str = title
|
|
72
77
|
self.signature: str = signature
|
|
73
|
-
self.
|
|
74
|
-
self.testops_id: Optional[int] = None
|
|
78
|
+
self.testops_ids: Optional[List[int]] = None
|
|
75
79
|
self.execution: Type[Execution] = Execution()
|
|
76
80
|
self.fields: Dict[Type[Field]] = {}
|
|
77
81
|
self.attachments: List[Attachment] = []
|
|
78
82
|
self.steps: List[Type[Step]] = []
|
|
79
83
|
self.params: Optional[dict] = {}
|
|
80
84
|
self.param_groups: Optional[List[List[str]]] = []
|
|
81
|
-
self.
|
|
82
|
-
self.relations: List[Type[Relation]] = []
|
|
85
|
+
self.relations: Type[Relation] = None
|
|
83
86
|
self.muted: bool = False
|
|
84
87
|
self.message: Optional[str] = None
|
|
85
|
-
self.suite: Optional[Type[Suite]] = None
|
|
86
|
-
QaseUtils.get_host_data()
|
|
87
88
|
|
|
88
89
|
def add_message(self, message: str) -> None:
|
|
89
90
|
self.message = message
|
|
@@ -97,20 +98,14 @@ class Result(BaseModel):
|
|
|
97
98
|
def add_attachment(self, attachment: Attachment) -> None:
|
|
98
99
|
self.attachments.append(attachment)
|
|
99
100
|
|
|
100
|
-
def add_relation(self, relation: Type[Relation]) -> None:
|
|
101
|
-
self.relations.append(relation)
|
|
102
|
-
|
|
103
101
|
def add_param(self, key: str, value: str) -> None:
|
|
104
102
|
self.params[key] = value
|
|
105
103
|
|
|
106
104
|
def add_param_groups(self, values: List[str]) -> None:
|
|
107
105
|
self.param_groups.append(values)
|
|
108
106
|
|
|
109
|
-
def
|
|
110
|
-
self.relations
|
|
111
|
-
|
|
112
|
-
def add_suite(self, suite: Type[Suite]) -> None:
|
|
113
|
-
self.suite = suite
|
|
107
|
+
def set_relation(self, relation: Relation) -> None:
|
|
108
|
+
self.relations = relation
|
|
114
109
|
|
|
115
110
|
def get_status(self) -> Optional[str]:
|
|
116
111
|
return self.execution.status
|
|
@@ -126,18 +121,8 @@ class Result(BaseModel):
|
|
|
126
121
|
return self.fields[name]
|
|
127
122
|
return None
|
|
128
123
|
|
|
129
|
-
def
|
|
130
|
-
|
|
131
|
-
# Hack for old API
|
|
132
|
-
return 0
|
|
133
|
-
return self.testops_id
|
|
124
|
+
def get_testops_ids(self) -> Optional[List[int]]:
|
|
125
|
+
return self.testops_ids
|
|
134
126
|
|
|
135
127
|
def get_duration(self) -> int:
|
|
136
128
|
return self.execution.duration
|
|
137
|
-
|
|
138
|
-
def get_suite_title(self) -> Optional[str]:
|
|
139
|
-
if self.suite:
|
|
140
|
-
return self.suite.title
|
|
141
|
-
|
|
142
|
-
def set_run_id(self, run_id: str) -> None:
|
|
143
|
-
self.run_id = run_id
|
qase/commons/models/run.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import json
|
|
2
|
-
|
|
3
1
|
from typing import Optional, List
|
|
4
2
|
|
|
5
3
|
from .basemodel import BaseModel
|
|
@@ -26,6 +24,7 @@ class RunStats(BaseModel):
|
|
|
26
24
|
self.failed = 0
|
|
27
25
|
self.skipped = 0
|
|
28
26
|
self.broken = 0
|
|
27
|
+
self.invalid = 0
|
|
29
28
|
self.muted = 0
|
|
30
29
|
self.total = 0
|
|
31
30
|
|
|
@@ -39,6 +38,8 @@ class RunStats(BaseModel):
|
|
|
39
38
|
self.skipped += 1
|
|
40
39
|
elif status == "broken":
|
|
41
40
|
self.broken += 1
|
|
41
|
+
elif status == "invalid":
|
|
42
|
+
self.invalid += 1
|
|
42
43
|
self.total += 1
|
|
43
44
|
if result.get('muted', False):
|
|
44
45
|
self.muted += 1
|
|
@@ -71,6 +72,7 @@ class Run(BaseModel):
|
|
|
71
72
|
"duration": result["execution"]["duration"],
|
|
72
73
|
"thread": result["execution"]["thread"]
|
|
73
74
|
}
|
|
75
|
+
self._extract_path_from_relations(result)
|
|
74
76
|
self.results.append(compact_result)
|
|
75
77
|
self.execution.track(result)
|
|
76
78
|
self.stats.track(result)
|
|
@@ -79,3 +81,16 @@ class Run(BaseModel):
|
|
|
79
81
|
|
|
80
82
|
def add_host_data(self, host_data: dict):
|
|
81
83
|
self.host_data = host_data
|
|
84
|
+
|
|
85
|
+
def _extract_path_from_relations(self, relations_dict):
|
|
86
|
+
|
|
87
|
+
titles = []
|
|
88
|
+
if "relations" in relations_dict and "suite" in relations_dict["relations"]:
|
|
89
|
+
if "data" in relations_dict["relations"]["suite"]:
|
|
90
|
+
data_list = relations_dict["relations"]["suite"]["data"]
|
|
91
|
+
titles = [item["title"] for item in data_list if "title" in item]
|
|
92
|
+
|
|
93
|
+
path = "/".join(titles)
|
|
94
|
+
|
|
95
|
+
if path and path not in self.suites:
|
|
96
|
+
self.suites.append(path)
|
qase/commons/models/runtime.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from .
|
|
1
|
+
from . import Result
|
|
2
|
+
from .step import Step
|
|
2
3
|
from .attachment import Attachment
|
|
3
4
|
|
|
4
5
|
|
|
@@ -41,3 +42,16 @@ class Runtime:
|
|
|
41
42
|
self.result.add_attachment(attachment)
|
|
42
43
|
except Exception as e:
|
|
43
44
|
raise QaseRuntimeException(e)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def add_param(self, key: str, value: str):
|
|
48
|
+
"""
|
|
49
|
+
Add a parameter to the current result.
|
|
50
|
+
"""
|
|
51
|
+
if self.result is not None:
|
|
52
|
+
self.result.params[key] = value
|
|
53
|
+
|
|
54
|
+
def clear(self):
|
|
55
|
+
self.result = None
|
|
56
|
+
self.steps = {}
|
|
57
|
+
self.step_id = None
|
qase/commons/models/step.py
CHANGED
|
@@ -5,6 +5,7 @@ from enum import Enum
|
|
|
5
5
|
from typing import Optional, Union, Dict, List, Type
|
|
6
6
|
from .attachment import Attachment
|
|
7
7
|
from .basemodel import BaseModel
|
|
8
|
+
from .. import QaseUtils
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class StepType(Enum):
|
|
@@ -20,6 +21,7 @@ class StepTextData(BaseModel):
|
|
|
20
21
|
def __init__(self, action: str, expected_result: Optional[str] = None):
|
|
21
22
|
self.action = action
|
|
22
23
|
self.expected_result = expected_result
|
|
24
|
+
self.input_data = None
|
|
23
25
|
|
|
24
26
|
|
|
25
27
|
class StepAssertData(BaseModel):
|
|
@@ -42,10 +44,20 @@ class StepRequestData(BaseModel):
|
|
|
42
44
|
self.response_body = None
|
|
43
45
|
self.status_code = None
|
|
44
46
|
if isinstance(request_body, bytes):
|
|
45
|
-
|
|
47
|
+
try:
|
|
48
|
+
request_body = request_body.decode('utf-8')
|
|
49
|
+
except UnicodeDecodeError:
|
|
50
|
+
# For binary data (like file uploads), keep as base64 encoded string
|
|
51
|
+
import base64
|
|
52
|
+
request_body = base64.b64encode(request_body).decode('ascii')
|
|
46
53
|
self.request_body = request_body
|
|
47
54
|
if isinstance(request_headers, bytes):
|
|
48
|
-
|
|
55
|
+
try:
|
|
56
|
+
request_headers = request_headers.decode('utf-8')
|
|
57
|
+
except UnicodeDecodeError:
|
|
58
|
+
# For binary headers, keep as base64 encoded string
|
|
59
|
+
import base64
|
|
60
|
+
request_headers = base64.b64encode(request_headers).decode('ascii')
|
|
49
61
|
self.request_headers = request_headers
|
|
50
62
|
self.request_method = request_method
|
|
51
63
|
self.request_url = request_url
|
|
@@ -55,16 +67,33 @@ class StepRequestData(BaseModel):
|
|
|
55
67
|
self.status_code = status_code
|
|
56
68
|
|
|
57
69
|
if isinstance(response_body, bytes):
|
|
58
|
-
|
|
70
|
+
try:
|
|
71
|
+
response_body = response_body.decode('utf-8')
|
|
72
|
+
except UnicodeDecodeError:
|
|
73
|
+
# For binary data (like file downloads), keep as base64 encoded string
|
|
74
|
+
import base64
|
|
75
|
+
response_body = base64.b64encode(response_body).decode('ascii')
|
|
59
76
|
self.response_body = response_body
|
|
60
77
|
if isinstance(response_headers, bytes):
|
|
61
|
-
|
|
78
|
+
try:
|
|
79
|
+
response_headers = response_headers.decode('utf-8')
|
|
80
|
+
except UnicodeDecodeError:
|
|
81
|
+
# For binary headers, keep as base64 encoded string
|
|
82
|
+
import base64
|
|
83
|
+
response_headers = base64.b64encode(response_headers).decode('ascii')
|
|
62
84
|
self.response_headers = response_headers
|
|
63
85
|
|
|
64
86
|
|
|
65
87
|
class StepDbQueryData(BaseModel):
|
|
66
|
-
def __init__(self, query: str, expected_result: str
|
|
88
|
+
def __init__(self, query: str, expected_result: str = None,
|
|
89
|
+
database_type: str = None, execution_time: float = None,
|
|
90
|
+
rows_affected: int = None, connection_info: str = None):
|
|
67
91
|
self.query = query
|
|
92
|
+
self.expected_result = expected_result
|
|
93
|
+
self.database_type = database_type
|
|
94
|
+
self.execution_time = execution_time
|
|
95
|
+
self.rows_affected = rows_affected
|
|
96
|
+
self.connection_info = connection_info
|
|
68
97
|
|
|
69
98
|
|
|
70
99
|
class StepSleepData(BaseModel):
|
|
@@ -74,21 +103,25 @@ class StepSleepData(BaseModel):
|
|
|
74
103
|
|
|
75
104
|
class StepExecution(BaseModel):
|
|
76
105
|
def __init__(self, status: Optional[str] = 'untested', end_time: int = 0, duration: int = 0):
|
|
77
|
-
self.start_time =
|
|
106
|
+
self.start_time = QaseUtils.get_real_time()
|
|
78
107
|
self.status = status
|
|
79
108
|
self.end_time = end_time
|
|
80
109
|
self.duration = duration
|
|
110
|
+
self.attachments = []
|
|
81
111
|
|
|
82
112
|
def set_status(self, status: Optional[str]):
|
|
83
|
-
if status in ['passed', 'failed', 'skipped', 'blocked', 'untested']:
|
|
113
|
+
if status in ['passed', 'failed', 'skipped', 'blocked', 'untested', 'invalid']:
|
|
84
114
|
self.status = status
|
|
85
115
|
else:
|
|
86
|
-
raise ValueError('Step status must be one of: passed, failed, skipped, blocked, untested')
|
|
116
|
+
raise ValueError('Step status must be one of: passed, failed, skipped, blocked, untested, invalid')
|
|
87
117
|
|
|
88
118
|
def complete(self):
|
|
89
|
-
self.end_time =
|
|
119
|
+
self.end_time = QaseUtils.get_real_time()
|
|
90
120
|
self.duration = int((self.end_time - self.start_time) * 1000)
|
|
91
121
|
|
|
122
|
+
def add_attachment(self, attachment: Attachment):
|
|
123
|
+
self.attachments.append(attachment)
|
|
124
|
+
|
|
92
125
|
|
|
93
126
|
class Step(BaseModel):
|
|
94
127
|
def __init__(self,
|
|
@@ -107,7 +140,6 @@ class Step(BaseModel):
|
|
|
107
140
|
self.data = data
|
|
108
141
|
self.parent_id = parent_id
|
|
109
142
|
self.execution = StepExecution()
|
|
110
|
-
self.attachments = []
|
|
111
143
|
self.steps = []
|
|
112
144
|
|
|
113
145
|
def set_parent_id(self, parent_id: Optional[str]):
|
|
@@ -132,4 +164,4 @@ class Step(BaseModel):
|
|
|
132
164
|
self.steps = steps
|
|
133
165
|
|
|
134
166
|
def add_attachment(self, attachment: Attachment):
|
|
135
|
-
self.
|
|
167
|
+
self.execution.add_attachment(attachment)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from .network import NetworkProfiler, NetworkProfilerSingleton
|
|
2
2
|
from .sleep import SleepProfiler
|
|
3
|
-
from .db import
|
|
3
|
+
from .db import DatabaseProfiler, DatabaseProfilerSingleton
|
|
4
4
|
|
|
5
5
|
__all__ = [
|
|
6
6
|
NetworkProfiler,
|
|
7
7
|
NetworkProfilerSingleton,
|
|
8
8
|
SleepProfiler,
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
DatabaseProfiler,
|
|
10
|
+
DatabaseProfilerSingleton
|
|
11
|
+
]
|