From 5b2a7d8ca9c6e5969c91ec3f2aa65c3b000291ce Mon Sep 17 00:00:00 2001 From: "reportportal.io" Date: Fri, 10 Apr 2026 09:41:51 +0000 Subject: [PATCH 1/4] Changelog update --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d2cd63..38efd9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] + +## [5.7.3] ### Added - Microseconds precision for timestamps, by @HardNorth From 0310eb2f81ed96457b5f185c170a0d39aa437d0e Mon Sep 17 00:00:00 2001 From: "reportportal.io" Date: Fri, 10 Apr 2026 09:41:52 +0000 Subject: [PATCH 2/4] Version update --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 54c0f72..a53b151 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import find_packages, setup -__version__ = "5.7.3" +__version__ = "5.7.4" TYPE_STUBS = ["*.pyi"] From 85d8ce6d23f601f94cf763284c4fff2f2ae0b95a Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Fri, 10 Apr 2026 12:54:28 +0300 Subject: [PATCH 3/4] Fix microsecond logging for nested steps --- CHANGELOG.md | 2 ++ reportportal_client/logs/__init__.py | 5 +++-- reportportal_client/steps/__init__.py | 19 +++++++++++++------ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38efd9e..a3230d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] +### Fixed +- Microsecond logging for nested steps, by @HardNorth ## [5.7.3] ### Added diff --git a/reportportal_client/logs/__init__.py b/reportportal_client/logs/__init__.py index abc029c..e0ca67c 100644 --- a/reportportal_client/logs/__init__.py +++ b/reportportal_client/logs/__init__.py @@ -16,12 +16,13 @@ import logging import sys import threading +from datetime import datetime, timezone from typing import TYPE_CHECKING, Any, Optional, Union from urllib.parse import urlparse # noinspection PyProtectedMember from reportportal_client._internal.local import current, set_current -from reportportal_client.helpers import TYPICAL_MULTIPART_FOOTER_LENGTH, timestamp +from reportportal_client.helpers import TYPICAL_MULTIPART_FOOTER_LENGTH if TYPE_CHECKING: from reportportal_client.client import RP @@ -195,7 +196,7 @@ def emit(self, record: logging.LogRecord) -> None: set_current(rp_client) if rp_client: rp_client.log( - timestamp(), + datetime.now(tz=timezone.utc), msg, level=log_level, attachment=record.__dict__.get("attachment", None), diff --git a/reportportal_client/steps/__init__.py b/reportportal_client/steps/__init__.py index 2f9a046..0e73bab 100644 --- a/reportportal_client/steps/__init__.py +++ b/reportportal_client/steps/__init__.py @@ -42,6 +42,7 @@ def test_my_nested_step(): pass """ +from datetime import datetime, timezone from functools import wraps from typing import Any, Callable, Optional, TypeVar, Union @@ -52,7 +53,7 @@ def test_my_nested_step(): # noinspection PyProtectedMember from reportportal_client._internal.local import current -from reportportal_client.helpers import get_function_params, timestamp +from reportportal_client.helpers import get_function_params NESTED_STEP_ITEMS = ( "step", @@ -88,7 +89,11 @@ def __init__(self, rp_client: "rp.RP"): self.client = rp_client def start_nested_step( - self, name: str, start_time: str, parameters: Optional[dict[str, Any]] = None, **_: dict[str, Any] + self, + name: str, + start_time: Union[str, datetime], + parameters: Optional[dict[str, Any]] = None, + **_: dict[str, Any], ) -> Union[Optional[str], Task[Optional[str]]]: """Start Nested Step on ReportPortal. @@ -106,7 +111,7 @@ def start_nested_step( def finish_nested_step( self, item_id: Union[str, Task[Optional[str]]], - end_time: str, + end_time: Union[str, datetime], status: Optional[str] = None, **_: dict[str, Any], ) -> Union[Optional[str], Task[Optional[str]]]: @@ -151,11 +156,13 @@ def __enter__(self) -> None: rp_client = self.client or current() if not rp_client: return - self.__item_id = rp_client.step_reporter.start_nested_step(self.name, timestamp(), parameters=self.params) + self.__item_id = rp_client.step_reporter.start_nested_step( + self.name, datetime.now(tz=timezone.utc), parameters=self.params + ) if self.params: param_list = [str(key) + ": " + str(value) for key, value in sorted(self.params.items())] param_str = "Parameters: " + "; ".join(param_list) - rp_client.log(timestamp(), param_str, level="INFO", item_id=self.__item_id) + rp_client.log(datetime.now(tz=timezone.utc), param_str, level="INFO", item_id=self.__item_id) def __exit__(self, exc_type: type[BaseException], exc_val, exc_tb) -> None: """Exit the runtime context related to this object.""" @@ -169,7 +176,7 @@ def __exit__(self, exc_type: type[BaseException], exc_val, exc_tb) -> None: step_status = self.status if any((exc_type, exc_val, exc_tb)): step_status = "FAILED" - rp_client.step_reporter.finish_nested_step(self.__item_id, timestamp(), step_status) + rp_client.step_reporter.finish_nested_step(self.__item_id, datetime.now(tz=timezone.utc), step_status) def __call__(self, *args, **kwargs): """Wrap and call a function reference. From b6fcaad90235f774e9a80b99f7b8d9d54ae7f1fd Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Fri, 10 Apr 2026 13:07:06 +0300 Subject: [PATCH 4/4] Fix tests --- tests/logs/test_rp_log_handler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/logs/test_rp_log_handler.py b/tests/logs/test_rp_log_handler.py index 9ab7fd1..b01ee37 100644 --- a/tests/logs/test_rp_log_handler.py +++ b/tests/logs/test_rp_log_handler.py @@ -10,9 +10,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License -import re -# noinspection PyUnresolvedReferences +from datetime import datetime from unittest import mock # noinspection PyPackageRequirements @@ -68,7 +67,8 @@ def test_emit_simple(mocked_handle): call_args = mock_client.log.call_args[0] call_kwargs = mock_client.log.call_args[1] - assert re.match("^[0-9]+$", call_args[0]) + log_time = call_args[0] + assert isinstance(log_time, datetime) assert test_message == call_args[1] assert call_kwargs["level"] == "INFO" assert not call_kwargs["attachment"]