From 690ae7941aa36496346908ed674d3329e619875d Mon Sep 17 00:00:00 2001 From: Hugo-C Date: Sat, 7 Jun 2025 23:42:47 +0200 Subject: [PATCH 1/2] Properly truncate Response's repr --- winrm/__init__.py | 20 ++++++++++++++------ winrm/tests/test_session.py | 14 +++++++++++++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/winrm/__init__.py b/winrm/__init__.py index ec31a32..74bcd72 100644 --- a/winrm/__init__.py +++ b/winrm/__init__.py @@ -6,6 +6,7 @@ import warnings import xml.etree.ElementTree as ET from base64 import b64encode +from dataclasses import dataclass from winrm.protocol import Protocol @@ -21,15 +22,22 @@ FEATURE_PROXY_SUPPORT = True -class Response(object): +@dataclass +class Response: """Response from a remote command execution""" - def __init__(self, args: tuple[bytes, bytes, int]) -> None: - self.std_out, self.std_err, self.status_code = args + std_out: bytes + std_err: bytes + status_code: int = 0 def __repr__(self) -> str: - # TODO put tree dots at the end if out/err was truncated - return ''.format(self.status_code, self.std_out[:20], self.std_err[:20]) + def shorten_repr(obj: bytes) -> str: + obj_repr = repr(obj[:20]) + if len(obj) > 20: + obj_repr = f"{obj_repr[:-1]}...{obj_repr[-1]}" # bytes repr usually ends with a single quote + return obj_repr + + return f'' class Session(object): @@ -43,7 +51,7 @@ def run_cmd(self, command: str, args: collections.abc.Iterable[str | bytes] = () # TODO optimize perf. Do not call open/close shell every time shell_id = self.protocol.open_shell() command_id = self.protocol.run_command(shell_id, command, args) - rs = Response(self.protocol.get_command_output(shell_id, command_id)) + rs = Response(*self.protocol.get_command_output(shell_id, command_id)) self.protocol.cleanup_command(shell_id, command_id) self.protocol.close_shell(shell_id) return rs diff --git a/winrm/tests/test_session.py b/winrm/tests/test_session.py index 4990932..ce16ec2 100644 --- a/winrm/tests/test_session.py +++ b/winrm/tests/test_session.py @@ -1,6 +1,6 @@ import pytest -from winrm import Session +from winrm import Response, Session def test_run_cmd(protocol_fake): @@ -89,3 +89,15 @@ def test_decode_clixml_invalid_xml(): actual = s._clean_error_msg(msg) assert actual == msg + + +def test_response_repr_short(): + r = Response(std_out=b"short std out", std_err=b"short std err") + + assert repr(r) == "" + + +def test_response_repr_long(): + r = Response(std_out=b"some very long std out that take more than 20 chars", std_err=b"some very long std err that take more than 20 chars") + + assert repr(r) == "" From a85ec37605abd8c0e1648bd411c24e1fa14fbee0 Mon Sep 17 00:00:00 2001 From: Hugo-C Date: Sat, 7 Jun 2025 23:55:12 +0200 Subject: [PATCH 2/2] Use reprlib --- winrm/__init__.py | 9 ++------- winrm/tests/test_session.py | 7 +++++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/winrm/__init__.py b/winrm/__init__.py index 74bcd72..4f227b9 100644 --- a/winrm/__init__.py +++ b/winrm/__init__.py @@ -2,6 +2,7 @@ import collections.abc import re +import reprlib import typing as t import warnings import xml.etree.ElementTree as ET @@ -31,13 +32,7 @@ class Response: status_code: int = 0 def __repr__(self) -> str: - def shorten_repr(obj: bytes) -> str: - obj_repr = repr(obj[:20]) - if len(obj) > 20: - obj_repr = f"{obj_repr[:-1]}...{obj_repr[-1]}" # bytes repr usually ends with a single quote - return obj_repr - - return f'' + return f'' class Session(object): diff --git a/winrm/tests/test_session.py b/winrm/tests/test_session.py index ce16ec2..ccf2cda 100644 --- a/winrm/tests/test_session.py +++ b/winrm/tests/test_session.py @@ -98,6 +98,9 @@ def test_response_repr_short(): def test_response_repr_long(): - r = Response(std_out=b"some very long std out that take more than 20 chars", std_err=b"some very long std err that take more than 20 chars") + r = Response( + std_out=b"some very long std out that take more than 20 chars", + std_err=b"some very long std err that take more than 20 chars", + ) - assert repr(r) == "" + assert repr(r) == ""