Kevin Pasquarella Kevin Pasquarella - 1 year ago 66
Python Question

How to prevent an object's class from changing

I'm having trouble in a flask application with an object. The first time the function is run, it runs perfectly fine. The second time it is run, however, I get an error that 'str' object has no attribute SubmitFeedResult. I'm able to get around this by restarting the application and running the function again (which then runs fine). The object is being changed to a string after the function is run, and I am wondering if there is a way to prevent the object's class from changing to string (I am leaning towards no because it does this due to the source code in the library that I am using), or what a good way around this issue would be. For the application to run successfully for other users, I cannot have an object with class

str


I asked this question a few months ago, solving it briefly but now the issue has come back. This is the link to the original post for some context: 'str' Object has no attribute 'SubmitFeedResult'

Here is the main function:

@app.route('/submission/', methods=['GET','POST'])
def feed_submission():
if request.method == 'POST':
file = request.files['file']
# Only XML files allowed
if not allowed_filetype(file.filename):
output = '<h2 style="color:red">Filetype must be XML! Please upload an XML file.</h2>'
return output
raise ValueError('Filetype Must Be XML.')
# Read file and encode it for transfer to Amazon
file_name = request.form['file_name']
f = file.read()
u = f.decode("utf-8-sig")
c = u.encode("utf-8")
feed_content = c
submit_feed_response = conn.submit_feed(
FeedType=feed_operation(file_name),
PurgeAndReplace=False,
MarketplaceIdList=[MARKETPLACE_ID],
content_type='text/xml',
FeedContent=feed_content)
feed_info = submit_feed_response.SubmitFeedResult.FeedSubmissionInfo.FeedSubmissionId
return redirect(url_for('feed_result', feed_id=feed_info))
else:
return render_template('submit_feed.html',access_key=MWS_ACCESS_KEY,secret_key=MWS_SECRET_KEY,
merchant_id=MERCHANT_ID,marketplace_id=MARKETPLACE_ID)


After this function runs, it redirects to this function to return the response of the request:

@app.route('/feed-result/<int:feed_id>')
def feed_result(feed_id):
response = MWSConnection._parse_response = lambda s,x,y,z: z
submitted_feed = conn.get_feed_submission_result(FeedSubmissionId=feed_id)
result = Response(submitted_feed, mimetype='text/xml')
return(result)


I logged the information of the types and values of
submit_feed_response
before the issue happens and after to see what is causing the error:

typeof1 = type(submit_feed_response)
val1 = submit_feed_response
typeof_conn1 = type(conn)
app.logger.warning("Type of submit_feed_response (original): " + str(typeof1))
app.logger.warning("Value of submit_feed_response: " + str(val1))


In the logs, when the app runs as it should, I see:

Type of submit_feed_response (original): <class 'boto.mws.response.SubmitFeedResponse'>
Value of submit_feed_response: SubmitFeedResponse{u'xmlns': u'http://mws.amazonaws.com/doc/2009-01-01/'}(SubmitFeedResult: SubmitFeedResult{}(FeedSubmissionInfo: FeedSubmissionInfo{}...


After it fails, I see (as expected) the type is string:

Type of submit_feed_response: <type 'str'>
Value of submit_feed_response: <?xml version="1.0"?>


......

Here is the traceback:

Traceback (most recent call last):
File "C:\Python27\lib\site-packages\flask\app.py", line 2000, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Python27\lib\site-packages\flask\app.py", line 1991, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "C:\Python27\lib\site-packages\flask\app.py", line 1567, in handle_exception
reraise(exc_type, exc_value, tb)
File "C:\Python27\lib\site-packages\flask\app.py", line 1988, in wsgi_app
response = self.full_dispatch_request()
File "C:\Python27\lib\site-packages\flask\app.py", line 1641, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Python27\lib\site-packages\flask\app.py", line 1544, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "C:\Python27\lib\site-packages\flask\app.py", line 1639, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Python27\lib\site-packages\flask\app.py", line 1625, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:\Users\CTS 41\Documents\Amazon-app\application.py", line 98, in feed_submission
feed_info = submit_feed_response.SubmitFeedResult.FeedSubmissionInfo.FeedSubmissionId
AttributeError: 'str' object has no attribute 'SubmitFeedResult'


It seems that the library is causing the object to return a string once it is run. Is there a good, pythonic way around this? i.e. am I able to somehow change the class of the object to ensure it stays that, or delete it from memory completely? I've tried a try except block, and that comes back with the same error.

EDIT

I have narrowed the issue down to:

response = MWSConnection._parse_response = lambda s,x,y,z: z
in the function
feed_result
. It is there to return the XML response from boto (the call does not naturally come back in XML format, and I am not sure of another way to return it in XML, refer to How can I return XML from boto calls?).

I decided to insert

MWSConnection._parse_response = None
above the line

feed_info = feed.SubmitFeedResult.FeedSubmissionInfo.FeedSubmissionId
, however it is still coming back with the same error. Is there a way I can clear this function from memory after it is run once? I need this function to properly serve the response, but perhaps there is a better way?

Answer Source

I was able to solve this. The issue, as stated in the question, was stemming from the line response = MWSConnection._parse_response = lambda s,x,y,z: z in feed_result. This line is necessary to parse the response into XML and to serve it, which was causing the response for the submission of the feed to be parsed into an XML string after this function was called. To solve this, I checked to see if feed was not a string, and retrieved the ID as I originally did:

if type(feed) is not str:
            feed_info = feed.SubmitFeedResult.FeedSubmissionInfo.FeedSubmissionId

If it is a string, it is parsed as an XML string using ElementTree to retrieve the ID:

else:
            tree = et.fromstring(feed)
            xmlns = {'response': '{http://mws.amazonaws.com/doc/2009-01-01/}'}
            info = tree.find('.//{response}FeedSubmissionId'.format(**xmlns))
            feed_info = info.text

Which now works as it should without having to restart the application.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download