Repository: mdsol/rwslib

Issue:
RWSPostErrorResponse not returned in send_request
Hi RWSLib team,

by default an RWSErrorResponse is returned when a request is sent as a PostDataRequest. In case of an error it could be useful to get the attribute ErrorOriginLocation from the class RWSPostErrorResponse.

A patch could be easily applied with a code like that:
    if isinstance(request_object, rws_requests.PostDataRequest):
        error = rwsobjects.RWSPostErrorResponse(r.text)
        errDesc = error.error_client_response_message
    else:
        error = RWSErrorResponse(r.text)
        errDesc = error.errordescription

   ....

  raise RWSException(errDesc, error)


In same time a change could also be done on class RWSPostErrorResponse to harmonize the attribute name reasoncode, to change attribute name reason_code to reasoncode.

Thanks in advance for your support.

Kind regards,
Joel.


Discussion:

Patch:
diff --git a/.gitignore b/.gitignore
index 69709ee..7ca0da4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,6 @@ htmlcov
 
 # VirtualEnvs!
 venv
+
+# Pyenv
+.python-version
diff --git a/.travis.yml b/.travis.yml
index fdd0825..6ccf6fd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,9 @@
 language: python
 python:
-- 3.5
 - 3.6
 - 3.7
 - 3.8
+- 3.9
 install: python setup.py install
 script: python setup.py test
 deploy:
diff --git a/__init__.py b/__init__.py
deleted file mode 100644
index d3bd69e..0000000
--- a/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# -*- coding: utf-8 -*-
-
-__author__ = 'glow'
diff --git a/requirements.txt b/requirements.txt
index 313c8d1..6e08ccb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,5 @@
 lxml
 requests
 six
-enum34; python_version < '3.4'
 click
 faker
diff --git a/rwslib/__init__.py b/rwslib/__init__.py
index 1b22c30..f51717e 100644
--- a/rwslib/__init__.py
+++ b/rwslib/__init__.py
@@ -2,15 +2,16 @@
 
 __title__ = "rwslib"
 __author__ = "Ian Sparks (isparks@trialgrid.com)"
-__version__ = "1.2.6"
+__maintainer__ = "Geoff Low (glow@mdsol.com)"
+__version__ = "1.2.7"
 __license__ = "MIT"
-__copyright__ = "Copyright 2017 Medidata Solutions Inc"
+__copyright__ = "Copyright 2021 Medidata Solutions Inc"
 
 
 import requests
 
 from .rws_requests import RWSRequest, make_url
-from .rwsobjects import RWSException, RWSError, RWSErrorResponse
+from .rwsobjects import RWSException, RWSError, RWSErrorResponse, RWSPostErrorResponse
 
 import time
 
@@ -127,7 +128,7 @@ class RWSConnection(object):
         if r.status_code in [400, 404]:
             # Is it a RWS response?
             if r.text.startswith("<Response"):
-                error = RWSErrorResponse(r.text)
+                error = RWSErrorResponse(r.text) if request_object.method == "GET" else RWSPostErrorResponse(r.text)
                 raise RWSException(error.errordescription, error)
             elif "<html" in r.text:
                 raise RWSException("IIS Error", r.text)
@@ -155,7 +156,7 @@ class RWSConnection(object):
             if r.headers.get("content-type").startswith("text/xml"):
                 # XML response
                 if r.text.startswith("<Response"):
-                    error = RWSErrorResponse(r.text)
+                    error = RWSErrorResponse(r.text) if request_object.method == "GET" else RWSPostErrorResponse(r.text)
                 elif "ODM" in r.text:
                     error = RWSError(r.text)
             else:
@@ -168,7 +169,7 @@ class RWSConnection(object):
             if "<" in r.text:
                 # XML like
                 if r.text.strip().startswith("<Response"):
-                    error = RWSErrorResponse(r.text)
+                    error = RWSErrorResponse(r.text) if request_object.method == "GET" else RWSPostErrorResponse(r.text)
                 elif "ODM" in r.text:
                     error = RWSError(r.text)
                 else:
diff --git a/rwslib/builders/admindata.py b/rwslib/builders/admindata.py
index ae105d2..09d46a1 100644
--- a/rwslib/builders/admindata.py
+++ b/rwslib/builders/admindata.py
@@ -286,7 +286,7 @@ class SimpleChildElement(ODMElement):
         :param builder: 
         :return: 
         """
-        builder.start(self.__class__.__name__)
+        builder.start(self.__class__.__name__, {})
         builder.data(self.text)
         builder.end(self.__class__.__name__)
 
@@ -372,7 +372,7 @@ class Country(ODMElement):
         :param builder: 
         :return: 
         """
-        builder.start(self.__class__.__name__)
+        builder.start(self.__class__.__name__, {})
         builder.data(self.country_code)
         builder.end(self.__class__.__name__)
 
diff --git a/rwslib/builders/clinicaldata.py b/rwslib/builders/clinicaldata.py
index 8109f09..42601ab 100644
--- a/rwslib/builders/clinicaldata.py
+++ b/rwslib/builders/clinicaldata.py
@@ -732,7 +732,7 @@ class Annotations(ODMElement):
         """
         Build XML by appending to builder
         """
-        builder.start("Annotations")
+        builder.start("Annotations", {})
 
         # populate the flags
         for annotation in self.annotations:
diff --git a/rwslib/rwsobjects.py b/rwslib/rwsobjects.py
index f94bff0..84ecf86 100644
--- a/rwslib/rwsobjects.py
+++ b/rwslib/rwsobjects.py
@@ -240,7 +240,7 @@ class RWSPostResponse(RWSResponse):
         self.draft_imported = r_get("DraftImported")
 
 
-class RWSPostErrorResponse(RWSResponse):
+class RWSPostErrorResponse(RWSErrorResponse):
     """
     Responses to Clinical data post messages have additional Attributes to normal RWS Response messages:
 
@@ -262,7 +262,7 @@ class RWSPostErrorResponse(RWSResponse):
         """
         :param str xml: Error response
         """
-        RWSResponse.__init__(self, xml)
+        RWSErrorResponse.__init__(self, xml)
 
         # Get additional properties
         r_get = self.root.get
diff --git a/setup.py b/setup.py
index 7a92251..bc58302 100644
--- a/setup.py
+++ b/setup.py
@@ -24,6 +24,9 @@ packages = [
 
 rwsinit = open("rwslib/__init__.py").read()
 author = re.search(r"__author__ = \"([^\"]+)\"", rwsinit).group(1)
+author_email = re.search('\(([^\)]+)', author).group(1)
+maintainer = re.search(r"__maintainer__ = \"([^\"]+)\"", rwsinit).group(1)
+maintainer_email = re.search('\(([^\)]+)', maintainer).group(1)
 version = re.search(r"__version__ = \"([^\"]+)\"", rwsinit).group(1)
 
 with open("README.md", "r") as fh:
@@ -36,7 +39,9 @@ setup(
     long_description=long_description,
     long_description_content_type="text/markdown",
     author=author,
-    author_email="isparks@trialgrid.com",
+    author_email=author_email,
+    maintainer=maintainer,
+    maintainer_email=maintainer_email,
     packages=packages,
     package_dir={"rwslib": "rwslib"},
     include_package_data=True,
@@ -45,7 +50,6 @@ setup(
                       'six',
                       'click',
                       'faker',
-                      "enum34; python_version < '3.4'",
                       ],
     tests_require=['mock', 'httpretty'],
     license="MIT License",
@@ -58,10 +62,10 @@ setup(
         'Natural Language :: English',
         'License :: OSI Approved :: MIT License',
         'Programming Language :: Python',
-        'Programming Language :: Python :: 3.5',
         'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.7',
         'Programming Language :: Python :: 3.8',
+        'Programming Language :: Python :: 3.9',
     ],
     entry_points="""
     [console_scripts]
diff --git a/tox.ini b/tox.ini
index 98b4695..56f196d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -4,7 +4,7 @@
 # and then run "tox" from this directory.
 
 [tox]
-envlist = clean, py35, py36, py37, py38, anaconda3, stats
+envlist = clean, py36, py37, py38, py39, anaconda3, stats
 recreate = true
 
 [testenv]


Test patch:
diff --git a/rwslib/tests/test_rwslib.py b/rwslib/tests/test_rwslib.py
index 876f9b5..b291bfc 100644
--- a/rwslib/tests/test_rwslib.py
+++ b/rwslib/tests/test_rwslib.py
@@ -7,6 +7,7 @@ from mock import mock
 
 import rwslib
 from rwslib.rws_requests import StudyDatasetRequest
+from rwslib.rwsobjects import RWSPostErrorResponse
 
 
 # TODO: per the Repository, httpretty is not supporting Python3 - do we need to replace?
@@ -180,6 +181,11 @@ class TestErrorResponse(unittest.TestCase):
         with self.assertRaises(rwslib.RWSException) as exc:
             v = rave.send_request(rwslib.rws_requests.PostDataRequest("<ODM/>"))
         self.assertEqual("Subject already exists.", str(exc.exception))
+        rws_error = exc.exception.rws_error
+        self.assertEqual("/ODM/ClinicalData[1]/SubjectData[1]", str(rws_error.error_origin_location))
+        self.assertEqual("RWS00024", str(rws_error.reasoncode))
+        self.assertTrue(isinstance(rws_error, (RWSPostErrorResponse, )))
+        self.assertFalse(rws_error.istransactionsuccessful)
 
     @httpretty.activate
     def test_400_error_iis_error(self):
diff --git a/rwslib/tests/test_rwsobjects.py b/rwslib/tests/test_rwsobjects.py
index de4cef8..ab7c20d 100644
--- a/rwslib/tests/test_rwsobjects.py
+++ b/rwslib/tests/test_rwsobjects.py
@@ -356,6 +356,8 @@ class TestRWSPostErrorResponse(unittest.TestCase):
         self.assertEqual(
             "/ODM/ClinicalData[1]/SubjectData[1]", parsed.error_origin_location
         )
+        self.assertEqual("RWS00024", parsed.reason_code)
+        self.assertEqual("RWS00024", parsed.reasoncode)
 
 
 class TestRWSStudies(unittest.TestCase):