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 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')) -> None:
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
- if self.debug:
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
- try:
29
- print(log, end='')
30
- except (OSError, IOError):
31
- pass
72
+ # Console output
73
+ if self.logging_options.console:
74
+ try:
75
+ print(log, end='')
76
+ except (OSError, IOError):
77
+ pass
32
78
 
33
- if self.debug:
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")))
@@ -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
- self.logger = Logger(self.config.debug)
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.1
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=EnbPrICXG5kJ6lQGSQO4ZuBqikngmZVbH2bcCxR3qWk,14204
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=KEQr8G0eFZxlI3LJIaaNWOKD8o3NhKsZD06swXFn3FI,1313
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=69uiGwgD9Pau1D5hMjGxFIMRKIKF9G_peQGc1DOLEI8,2111
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=4MpiCGypoW0yso2BFjSvE3uTLCTbOinfwoIiYTdLIo4,9477
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.1.dist-info/METADATA,sha256=vZW1syFDf3ekHfwfHNEP1HG4LKOH9_do81VCE5hTGgg,1982
43
- qase_python_commons-4.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
- qase_python_commons-4.0.1.dist-info/top_level.txt,sha256=Mn5aFk7H7Uia4s1NRDsvebu8vCrFy9nOuRIBfkIY5kQ,5
45
- qase_python_commons-4.0.1.dist-info/RECORD,,
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,,