qase-python-commons 4.0.1__py3-none-any.whl → 4.1.0__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/commons/config.py +22 -3
- qase/commons/logger.py +63 -7
- qase/commons/models/config/qaseconfig.py +24 -1
- qase/commons/reporters/core.py +2 -1
- {qase_python_commons-4.0.1.dist-info → qase_python_commons-4.1.0.dist-info}/METADATA +1 -1
- {qase_python_commons-4.0.1.dist-info → qase_python_commons-4.1.0.dist-info}/RECORD +8 -8
- {qase_python_commons-4.0.1.dist-info → qase_python_commons-4.1.0.dist-info}/WHEEL +0 -0
- {qase_python_commons-4.0.1.dist-info → qase_python_commons-4.1.0.dist-info}/top_level.txt +0 -0
qase/commons/config.py
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import json
|
|
3
|
-
from .logger import Logger
|
|
3
|
+
from .logger import Logger, LoggingOptions
|
|
4
4
|
from .models.config.qaseconfig import QaseConfig, Mode
|
|
5
|
+
from .utils import QaseUtils
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class ConfigManager:
|
|
8
9
|
|
|
9
10
|
def __init__(self, config_file='./qase.config.json'):
|
|
10
|
-
self.logger = Logger()
|
|
11
11
|
self.__config_file = config_file
|
|
12
12
|
self.config = QaseConfig()
|
|
13
13
|
|
|
14
|
+
# Initialize temporary logger for error handling during config loading
|
|
15
|
+
self.logger = Logger(debug=False)
|
|
16
|
+
|
|
14
17
|
self.__load_file_config()
|
|
15
18
|
self.__load_env_config()
|
|
19
|
+
|
|
20
|
+
# Re-initialize logger with proper logging options after config is loaded
|
|
21
|
+
logging_options = LoggingOptions(
|
|
22
|
+
console=self.config.logging.console if self.config.logging.console is not None else True,
|
|
23
|
+
file=self.config.logging.file if self.config.logging.file is not None else self.config.debug
|
|
24
|
+
)
|
|
25
|
+
self.logger = Logger(debug=self.config.debug, logging_options=logging_options)
|
|
16
26
|
|
|
17
27
|
def validate_config(self):
|
|
18
28
|
errors: list[str] = []
|
|
@@ -201,6 +211,9 @@ class ConfigManager:
|
|
|
201
211
|
xfail_status.get("xpass")
|
|
202
212
|
)
|
|
203
213
|
|
|
214
|
+
if config.get("logging"):
|
|
215
|
+
self.config.set_logging(config.get("logging"))
|
|
216
|
+
|
|
204
217
|
except Exception as e:
|
|
205
218
|
self.logger.log("Failed to load config from file", "error")
|
|
206
219
|
|
|
@@ -326,5 +339,11 @@ class ConfigManager:
|
|
|
326
339
|
if key == 'QASE_PYTEST_XFAIL_STATUS_XPASS':
|
|
327
340
|
self.config.framework.pytest.xfail_status.set_xpass(value)
|
|
328
341
|
|
|
342
|
+
if key == 'QASE_LOGGING_CONSOLE':
|
|
343
|
+
self.config.logging.set_console(QaseUtils.parse_bool(value))
|
|
344
|
+
|
|
345
|
+
if key == 'QASE_LOGGING_FILE':
|
|
346
|
+
self.config.logging.set_file(QaseUtils.parse_bool(value))
|
|
347
|
+
|
|
329
348
|
except Exception as e:
|
|
330
|
-
self.logger.log("Failed to load config from env vars {e}", "error")
|
|
349
|
+
self.logger.log(f"Failed to load config from env vars {e}", "error")
|
qase/commons/logger.py
CHANGED
|
@@ -1,14 +1,39 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import datetime
|
|
3
3
|
import threading
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class LoggingOptions:
|
|
8
|
+
def __init__(self, console: bool = True, file: bool = False):
|
|
9
|
+
self.console = console
|
|
10
|
+
self.file = file
|
|
4
11
|
|
|
5
12
|
|
|
6
13
|
class Logger:
|
|
7
14
|
_log_file = None
|
|
8
15
|
|
|
9
|
-
def __init__(self, debug: bool = False, prefix: str = '', dir: str = os.path.join('.', 'logs')
|
|
16
|
+
def __init__(self, debug: bool = False, prefix: str = '', dir: str = os.path.join('.', 'logs'),
|
|
17
|
+
logging_options: Optional[LoggingOptions] = None) -> None:
|
|
10
18
|
self.debug = debug
|
|
11
|
-
|
|
19
|
+
self.prefix = prefix
|
|
20
|
+
self.dir = dir
|
|
21
|
+
|
|
22
|
+
# Initialize logging options
|
|
23
|
+
if logging_options is None:
|
|
24
|
+
# Default behavior: console always enabled, file enabled only in debug mode
|
|
25
|
+
self.logging_options = LoggingOptions(
|
|
26
|
+
console=True,
|
|
27
|
+
file=debug
|
|
28
|
+
)
|
|
29
|
+
else:
|
|
30
|
+
self.logging_options = logging_options
|
|
31
|
+
|
|
32
|
+
# Override with environment variables if set
|
|
33
|
+
self._load_env_logging_options()
|
|
34
|
+
|
|
35
|
+
# Setup file logging if enabled
|
|
36
|
+
if self.logging_options.file:
|
|
12
37
|
if Logger._log_file is None:
|
|
13
38
|
timestamp = self._get_timestamp()
|
|
14
39
|
filename = f'{prefix}_{timestamp}.log'
|
|
@@ -21,16 +46,38 @@ class Logger:
|
|
|
21
46
|
|
|
22
47
|
self.lock = threading.Lock()
|
|
23
48
|
|
|
49
|
+
def _load_env_logging_options(self):
|
|
50
|
+
"""Load logging options from environment variables"""
|
|
51
|
+
# QASE_LOGGING_CONSOLE
|
|
52
|
+
console_env = os.environ.get('QASE_LOGGING_CONSOLE')
|
|
53
|
+
if console_env is not None:
|
|
54
|
+
self.logging_options.console = console_env.lower() in ('true', '1', 'yes', 'on')
|
|
55
|
+
|
|
56
|
+
# QASE_LOGGING_FILE
|
|
57
|
+
file_env = os.environ.get('QASE_LOGGING_FILE')
|
|
58
|
+
if file_env is not None:
|
|
59
|
+
self.logging_options.file = file_env.lower() in ('true', '1', 'yes', 'on')
|
|
60
|
+
|
|
61
|
+
# Legacy QASE_DEBUG support
|
|
62
|
+
debug_env = os.environ.get('QASE_DEBUG')
|
|
63
|
+
if debug_env is not None and debug_env.lower() in ('true', '1', 'yes', 'on'):
|
|
64
|
+
# When debug is enabled via env, enable file logging if not explicitly disabled
|
|
65
|
+
if not hasattr(self.logging_options, 'file') or self.logging_options.file is None:
|
|
66
|
+
self.logging_options.file = True
|
|
67
|
+
|
|
24
68
|
def log(self, message: str, level: str = 'info'):
|
|
25
69
|
time_str = self._get_timestamp("%H:%M:%S")
|
|
26
70
|
log = f"[Qase][{time_str}][{level}] {message}\n"
|
|
27
71
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
72
|
+
# Console output
|
|
73
|
+
if self.logging_options.console:
|
|
74
|
+
try:
|
|
75
|
+
print(log, end='')
|
|
76
|
+
except (OSError, IOError):
|
|
77
|
+
pass
|
|
32
78
|
|
|
33
|
-
|
|
79
|
+
# File output
|
|
80
|
+
if self.logging_options.file:
|
|
34
81
|
with self.lock:
|
|
35
82
|
with open(Logger._log_file, 'a', encoding='utf-8') as f:
|
|
36
83
|
f.write(log)
|
|
@@ -39,6 +86,15 @@ class Logger:
|
|
|
39
86
|
if self.debug:
|
|
40
87
|
self.log(message, 'debug')
|
|
41
88
|
|
|
89
|
+
def log_error(self, message: str):
|
|
90
|
+
self.log(message, 'error')
|
|
91
|
+
|
|
92
|
+
def log_warning(self, message: str):
|
|
93
|
+
self.log(message, 'warning')
|
|
94
|
+
|
|
95
|
+
def log_info(self, message: str):
|
|
96
|
+
self.log(message, 'info')
|
|
97
|
+
|
|
42
98
|
@staticmethod
|
|
43
99
|
def _get_timestamp(fmt: str = "%Y%m%d"):
|
|
44
100
|
now = datetime.datetime.now()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
-
from typing import List, Dict
|
|
2
|
+
from typing import List, Dict, Optional
|
|
3
3
|
|
|
4
4
|
from .framework import Framework
|
|
5
5
|
from .report import ReportConfig
|
|
@@ -14,6 +14,21 @@ class Mode(Enum):
|
|
|
14
14
|
off = "off"
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
class LoggingConfig(BaseModel):
|
|
18
|
+
console: Optional[bool] = None
|
|
19
|
+
file: Optional[bool] = None
|
|
20
|
+
|
|
21
|
+
def __init__(self):
|
|
22
|
+
self.console = None
|
|
23
|
+
self.file = None
|
|
24
|
+
|
|
25
|
+
def set_console(self, console: bool):
|
|
26
|
+
self.console = console
|
|
27
|
+
|
|
28
|
+
def set_file(self, file: bool):
|
|
29
|
+
self.file = file
|
|
30
|
+
|
|
31
|
+
|
|
17
32
|
class ExecutionPlan(BaseModel):
|
|
18
33
|
path: str = None
|
|
19
34
|
|
|
@@ -37,6 +52,7 @@ class QaseConfig(BaseModel):
|
|
|
37
52
|
framework: Framework = None
|
|
38
53
|
exclude_params: list = None
|
|
39
54
|
status_mapping: Dict[str, str] = None
|
|
55
|
+
logging: LoggingConfig = None
|
|
40
56
|
|
|
41
57
|
def __init__(self):
|
|
42
58
|
self.mode = Mode.off
|
|
@@ -49,6 +65,7 @@ class QaseConfig(BaseModel):
|
|
|
49
65
|
self.profilers = []
|
|
50
66
|
self.exclude_params = []
|
|
51
67
|
self.status_mapping = {}
|
|
68
|
+
self.logging = LoggingConfig()
|
|
52
69
|
|
|
53
70
|
def set_mode(self, mode: str):
|
|
54
71
|
if any(mode == e.value for e in Mode.__members__.values()):
|
|
@@ -75,3 +92,9 @@ class QaseConfig(BaseModel):
|
|
|
75
92
|
|
|
76
93
|
def set_status_mapping(self, status_mapping: Dict[str, str]):
|
|
77
94
|
self.status_mapping = status_mapping
|
|
95
|
+
|
|
96
|
+
def set_logging(self, logging_config: dict):
|
|
97
|
+
if logging_config.get("console") is not None:
|
|
98
|
+
self.logging.set_console(QaseUtils.parse_bool(logging_config.get("console")))
|
|
99
|
+
if logging_config.get("file") is not None:
|
|
100
|
+
self.logging.set_file(QaseUtils.parse_bool(logging_config.get("file")))
|
qase/commons/reporters/core.py
CHANGED
|
@@ -24,7 +24,8 @@ class QaseCoreReporter:
|
|
|
24
24
|
reporter_name: Union[str, None] = None):
|
|
25
25
|
config.validate_config()
|
|
26
26
|
self.config = config.config
|
|
27
|
-
|
|
27
|
+
# Use the logger from ConfigManager instead of creating a new one
|
|
28
|
+
self.logger = config.logger
|
|
28
29
|
self._execution_plan = None
|
|
29
30
|
self.profilers = []
|
|
30
31
|
self.overhead = 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qase-python-commons
|
|
3
|
-
Version: 4.0
|
|
3
|
+
Version: 4.1.0
|
|
4
4
|
Summary: A library for Qase TestOps and Qase Report
|
|
5
5
|
Author-email: Qase Team <support@qase.io>
|
|
6
6
|
Project-URL: Homepage, https://github.com/qase-tms/qase-python/tree/main/qase-python-commons
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
qase/__init__.py,sha256=FLefSJnT6MTsTfZSDjMbMImI6674fAoJ5ZT3PNWGcRo,37
|
|
2
2
|
qase/commons/__init__.py,sha256=3HI65PJES4Q6YvtkSuRPh6tZboTETJo8wbdHlNYaePU,323
|
|
3
|
-
qase/commons/config.py,sha256=
|
|
3
|
+
qase/commons/config.py,sha256=ivunk-6IBfsXyRf-_Y_Je_K8boD4gDupv8v2Yf3nm2M,15162
|
|
4
4
|
qase/commons/loader.py,sha256=-MMY4HgSI6q1xq3NaJoq_w4liM73qdFKjYLVCT1E7Pc,1064
|
|
5
|
-
qase/commons/logger.py,sha256=
|
|
5
|
+
qase/commons/logger.py,sha256=V_QTSDWNnUgd0j58rydYYN1AYOu3wa9T2fWjpOyxyuA,3430
|
|
6
6
|
qase/commons/utils.py,sha256=utPRoYyThLs2tgD1lmjkwJ9KZuE7ZjliUiZkJJWFca0,3330
|
|
7
7
|
qase/commons/client/api_v1_client.py,sha256=_dpvldRonALKubInPU03hYhyGJqxeq3DiIuyjDeOngg,11543
|
|
8
8
|
qase/commons/client/api_v2_client.py,sha256=GsIrXJcBw6GtzvJjbjMYa0tvUIxEEe4pALRN2LBMKPM,9043
|
|
@@ -22,7 +22,7 @@ qase/commons/models/config/batch.py,sha256=X0H8SVOCCD2pV6LSMqjI-tIjRcLifnrM5Mare
|
|
|
22
22
|
qase/commons/models/config/connection.py,sha256=wK2fGjc0G0rMVVhPnjw_t_M1YWZwANlhwl-awmI7XSo,516
|
|
23
23
|
qase/commons/models/config/framework.py,sha256=sSWKQp18zxiS_79_XDH2hkHLpYEQnyH3bRquaT8XYYY,1803
|
|
24
24
|
qase/commons/models/config/plan.py,sha256=JbAY7qfGXYreXOLa32OLxw6z0paeCCf87-2b1m8xkks,137
|
|
25
|
-
qase/commons/models/config/qaseconfig.py,sha256=
|
|
25
|
+
qase/commons/models/config/qaseconfig.py,sha256=oWc03PKV8-jb3JNgs_tqQP-22wsKZW25eSaAacYl4no,2838
|
|
26
26
|
qase/commons/models/config/report.py,sha256=g3Z2B3Tewaasjc1TMj2mkXxz0Zc1C39sHeTXH0MRM2Y,497
|
|
27
27
|
qase/commons/models/config/run.py,sha256=UGE5PA_EyiFpfIev-TOob1BocxT1vdUdWW3ELMD58zQ,1239
|
|
28
28
|
qase/commons/models/config/testops.py,sha256=rJ9wW-VMt-5XaoPdRUKeM9rlV0eTiRINEhEulFVczv0,1893
|
|
@@ -31,7 +31,7 @@ qase/commons/profilers/db.py,sha256=Am1tvvLgJq4_A8JsuSeBGf47BD2lnSX-5KiMjSgr-Ko,
|
|
|
31
31
|
qase/commons/profilers/network.py,sha256=zKNBnTQG4BMg8dn8O--tQzQLpu-qs5ADhHEnqIas0gM,4950
|
|
32
32
|
qase/commons/profilers/sleep.py,sha256=HT6h0R-2XHZAoBYRxS2T_KC8RrnEoVjP7MXusaE4Nec,1624
|
|
33
33
|
qase/commons/reporters/__init__.py,sha256=J0aNLzb_MPPT_zF8BtX_w9nj_U7Ad06RGpyWK5Pxq1o,169
|
|
34
|
-
qase/commons/reporters/core.py,sha256=
|
|
34
|
+
qase/commons/reporters/core.py,sha256=FJx1kxXkUz9vwkSAXapkNS3vF97Igvlq7Yyjp-4BpeI,9539
|
|
35
35
|
qase/commons/reporters/report.py,sha256=ZLwtVn5gjwgJFtfbpLUO-vW3M3skEq3AhKJwtmM0nUw,4810
|
|
36
36
|
qase/commons/reporters/testops.py,sha256=uph_8upIt5bu4ncqyK7ehXR9NqtpZ34wGUBFLVJKOpE,7481
|
|
37
37
|
qase/commons/status_mapping/__init__.py,sha256=NPsWKDC7rGSCYfVunnGnHxL0_HmKWH7RBaMjWTH_Mtk,322
|
|
@@ -39,7 +39,7 @@ qase/commons/status_mapping/status_mapping.py,sha256=kohSLNK6_qbMSP-M8gxHTTmOECg
|
|
|
39
39
|
qase/commons/util/__init__.py,sha256=0sRRfrMOIPCHpk9tXM94Pj10qrk18B61qEcbLpRjw_I,74
|
|
40
40
|
qase/commons/util/host_data.py,sha256=n8o5PDs8kELCZZ5GR7Jug6LsgZHWJudU7iRmZHRdrlw,5264
|
|
41
41
|
qase/commons/validators/base.py,sha256=wwSn-4YiuXtfGMGnSKgo9Vm5hAKevVmmfd2Ro6Q7MYQ,173
|
|
42
|
-
qase_python_commons-4.0.
|
|
43
|
-
qase_python_commons-4.0.
|
|
44
|
-
qase_python_commons-4.0.
|
|
45
|
-
qase_python_commons-4.0.
|
|
42
|
+
qase_python_commons-4.1.0.dist-info/METADATA,sha256=JcshiFrQAEhDrqLCi3QflCIJcPMfDF4qLsEqLt-ansE,1982
|
|
43
|
+
qase_python_commons-4.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
44
|
+
qase_python_commons-4.1.0.dist-info/top_level.txt,sha256=Mn5aFk7H7Uia4s1NRDsvebu8vCrFy9nOuRIBfkIY5kQ,5
|
|
45
|
+
qase_python_commons-4.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|