0

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?

  • 1
    Didn't you [ask the same thing](https://stackoverflow.com/questions/40680362/str-object-has-no-attribute-submitfeedresult) 6 months ago? – Wondercricket May 25 '17 at 18:10
  • @Wondercricket I did, I thought I had solved it but I did not. Should I be editing that post instead? If so I will close this one out – Kevin Pasquarella May 25 '17 at 18:44
  • @Wondercricket let me know if that is the correct method for re-opening an issue on stackoverflow – Kevin Pasquarella May 25 '17 at 19:29
  • @Wondercricket according to this meta post, I should link back to the original question for context, but keep this question open: https://meta.stackoverflow.com/questions/266767/what-is-the-the-best-way-to-ask-follow-up-questions – Kevin Pasquarella May 25 '17 at 19:42
  • 1
    I wasn't 100% sure on the proper proceeds on this situation, but great job on locating that meta post. – Wondercricket May 25 '17 at 19:45
  • @Wondercricket not a problem, glad it cleared that up for both of us. Thanks for sticking around for my thinking "out-loud" – Kevin Pasquarella May 25 '17 at 19:47
  • 1
    Why not just stick a type check in there and then log debugging info? Like, what is in that string? What inputs cause it to be returned? etc. Additionally, have you looked at the source for that method? Under what conditions would it return a string? – pvg May 26 '17 at 17:00
  • @pvg in the type check, `submit_feed_response` is type `str` (meaning the error is with that, not `conn` as i thought). `conn` comes back with type ``, as it should. how can i check what is in the string itself? looking through the source code, i do not see something that would cause it to return a string – Kevin Pasquarella May 26 '17 at 19:27
  • @pvg the value of `submit_feed_response` is the xml response of the previous feed (not the actual response of the result of the feed) – Kevin Pasquarella May 26 '17 at 19:36
  • the type of `submit_feed_response` (checking when the application works) should be `` – Kevin Pasquarella May 26 '17 at 19:48
  • What i mean is, instead of catching the exception, etc, just check if `submit_feed_response` is `str` instead of instance of class whatever, before you dereference anything in it. At that point you know shit has gone down. Log as much as you can and review the log. Where is the source for this library? – pvg May 26 '17 at 19:50
  • @pvg the source is here: https://github.com/boto/boto/blob/develop/boto/mws/connection.py. makes sense. what i've done is logged the types and values and compared between when the application runs successfully and when it fails. i can see _where_ `submit_feed_response` becomes a string, but i'm still not fully understanding the _how_, or how i can get around it (i.e. deleting the data from memory, or whatever is the most pythonic way to do so) – Kevin Pasquarella May 26 '17 at 19:54
  • Well, the purpose of the check and logging is not so you find out where, the trace already tells you that. It's so that you can record enough of the application state to tell you what is going on when the error occurs. For instance, what's in the string? In any event, looking briefly at this code it seems it could easily return a string. If the response happens to not be the correct content type, you get the body back as a string. – pvg May 26 '17 at 20:11
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/145236/discussion-between-kevin-pasquarella-and-pvg). – Kevin Pasquarella May 26 '17 at 20:23

1 Answers1

0

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.