diff --git a/winrm/__init__.py b/winrm/__init__.py
index c3ee260..414e9f4 100644
--- a/winrm/__init__.py
+++ b/winrm/__init__.py
@@ -16,6 +16,53 @@
FEATURE_PROXY_SUPPORT = True
+def clean_error_msg(msg):
+ """converts a Powershell CLIXML message to a more human readable string
+ """
+ # TODO prepare unit test, beautify code
+ # if the msg does not start with this, return it as is
+ if msg.startswith(b"#< CLIXML\r\n"):
+ # for proper xml, we need to remove the CLIXML part
+ # (the first line)
+ msg_xml = msg[11:]
+ try:
+ # remove the namespaces from the xml for easier processing
+ msg_xml = _strip_namespace(msg_xml)
+ root = ET.fromstring(msg_xml)
+ # the S node is the error message, find all S nodes
+ nodes = root.findall("./S")
+ new_msg = ""
+ for s in nodes:
+ # append error msg string to result, also
+ # the hex chars represent CRLF so we replace with newline
+ new_msg += s.text.replace("_x000D__x000A_", "\n")
+ except Exception as e:
+ # if any of the above fails, the msg was not true xml
+ # print a warning and return the original string
+ warnings.warn(
+ "There was a problem converting the Powershell error "
+ "message: %s" % (e))
+ else:
+ # if new_msg was populated, that's our error message
+ # otherwise the original error message will be used
+ if len(new_msg):
+ # remove leading and trailing whitespace while we are here
+ return new_msg.strip().encode('utf-8')
+
+ # either failed to decode CLIXML or there was nothing to decode
+ # just return the original message
+ return msg
+
+
+def _strip_namespace(xml):
+ """strips any namespaces from an xml string"""
+ p = re.compile(b"xmlns=*[\"\"][^\"\"]*[\"\"]")
+ allmatches = p.finditer(xml)
+ for match in allmatches:
+ xml = xml.replace(match.group(), b"")
+ return xml
+
+
class Response(object):
"""Response from a remote command execution"""
def __init__(self, args):
@@ -54,54 +101,9 @@ def run_ps(self, script):
if len(rs.std_err):
# if there was an error message, clean it it up and make it human
# readable
- rs.std_err = self._clean_error_msg(rs.std_err)
+ rs.std_err = clean_error_msg(rs.std_err)
return rs
- def _clean_error_msg(self, msg):
- """converts a Powershell CLIXML message to a more human readable string
- """
- # TODO prepare unit test, beautify code
- # if the msg does not start with this, return it as is
- if msg.startswith(b"#< CLIXML\r\n"):
- # for proper xml, we need to remove the CLIXML part
- # (the first line)
- msg_xml = msg[11:]
- try:
- # remove the namespaces from the xml for easier processing
- msg_xml = self._strip_namespace(msg_xml)
- root = ET.fromstring(msg_xml)
- # the S node is the error message, find all S nodes
- nodes = root.findall("./S")
- new_msg = ""
- for s in nodes:
- # append error msg string to result, also
- # the hex chars represent CRLF so we replace with newline
- new_msg += s.text.replace("_x000D__x000A_", "\n")
- except Exception as e:
- # if any of the above fails, the msg was not true xml
- # print a warning and return the original string
- warnings.warn(
- "There was a problem converting the Powershell error "
- "message: %s" % (e))
- else:
- # if new_msg was populated, that's our error message
- # otherwise the original error message will be used
- if len(new_msg):
- # remove leading and trailing whitespace while we are here
- return new_msg.strip().encode('utf-8')
-
- # either failed to decode CLIXML or there was nothing to decode
- # just return the original message
- return msg
-
- def _strip_namespace(self, xml):
- """strips any namespaces from an xml string"""
- p = re.compile(b"xmlns=*[\"\"][^\"\"]*[\"\"]")
- allmatches = p.finditer(xml)
- for match in allmatches:
- xml = xml.replace(match.group(), b"")
- return xml
-
@staticmethod
def _build_url(target, transport):
match = re.match(
diff --git a/winrm/tests/test_session.py b/winrm/tests/test_session.py
index 875d493..95ad009 100644
--- a/winrm/tests/test_session.py
+++ b/winrm/tests/test_session.py
@@ -1,6 +1,7 @@
import pytest
from winrm import Session
+from winrm import clean_error_msg
def test_run_cmd(protocol_fake):
@@ -62,7 +63,7 @@ def test_decode_clixml_error():
s = Session('windows-host.example.com', auth=('john.smith', 'secret'))
msg = b'#< CLIXML\r\nSystem.Management.Automation.PSCustomObjectSystem.Object1Preparing modules for first use.0-1-1Completed-1 1Preparing modules for first use.0-1-1Completed-1 fake : The term \'fake\' is not recognized as the name of a cmdlet, function, script file, or operable program. Check _x000D__x000A_the spelling of the name, or if a path was included, verify that the path is correct and try again._x000D__x000A_At line:1 char:1_x000D__x000A_+ fake cmdlet_x000D__x000A_+ ~~~~_x000D__x000A_ + CategoryInfo : ObjectNotFound: (fake:String) [], CommandNotFoundException_x000D__x000A_ + FullyQualifiedErrorId : CommandNotFoundException_x000D__x000A_ _x000D__x000A_'
expected = b"fake : The term 'fake' is not recognized as the name of a cmdlet, function, script file, or operable program. Check \nthe spelling of the name, or if a path was included, verify that the path is correct and try again.\nAt line:1 char:1\n+ fake cmdlet\n+ ~~~~\n + CategoryInfo : ObjectNotFound: (fake:String) [], CommandNotFoundException\n + FullyQualifiedErrorId : CommandNotFoundException"
- actual = s._clean_error_msg(msg)
+ actual = clean_error_msg(msg)
assert actual == expected
@@ -70,7 +71,7 @@ def test_decode_clixml_no_clixml():
s = Session('windows-host.example.com', auth=('john.smith', 'secret'))
msg = b"stderr line"
expected = b"stderr line"
- actual = s._clean_error_msg(msg)
+ actual = clean_error_msg(msg)
assert actual == expected
@@ -78,7 +79,7 @@ def test_decode_clixml_no_errors():
s = Session('windows-host.example.com', auth=('john.smith', 'secret'))
msg = b'#< CLIXML\r\nSystem.Management.Automation.PSCustomObjectSystem.Object1Preparing modules for first use.0-1-1Completed-1 1Preparing modules for first use.0-1-1Completed-1 '
expected = msg
- actual = s._clean_error_msg(msg)
+ actual = clean_error_msg(msg)
assert actual == expected
@@ -87,6 +88,6 @@ def test_decode_clixml_invalid_xml():
msg = b'#< CLIXML\r\ndasf'
with pytest.warns(UserWarning, match="There was a problem converting the Powershell error message"):
- actual = s._clean_error_msg(msg)
+ actual = clean_error_msg(msg)
assert actual == msg