0

How do I pass a dictionary in to a python 2.7 function called via a Flask POST request that are not derived from the URL (or inbound via the POST request) but generated used elsewhere in my script, e.g.

@app.route('/url_route', methods=['POST'])
def my_function(foo, bar):
    baz = foo + bar # both are 'internal' variables, e.g. not via POST

    (do other stuff with inbound data from POST request)

    return baz, other stuff

When I try and trigger my_function via the POST request I get an error:

my_function() takes exactly 2 arguments (0 given)

I know could avoid this by declaring them both global, e.g.

def my_function(foo, bar):
    global foo
    global bar

    baz = foo + bar

Bu do I have any other alternatives?

pniessen
  • 13
  • 5
  • Where are these variables defined? – Suever Mar 20 '16 at 19:07
  • 3
    This smells of poor design. Can you zoom out a little and tell us what these variables are and why they live outside of this? – Suever Mar 20 '16 at 19:17
  • Variables are a pair of dictionaries initially defined in the global namespace just after `if name = __main__`. This script supports a HTML front end through which the user uploads a data file, upon which several intermediary analytic steps are performed by several function calls outside my_function, and then a set of results are returned back via a POST request. The dictionaries (here foo and bar) store the inputs, outputs from the intermediate steps, and the final results that are uploaded. – pniessen Mar 20 '16 at 19:55
  • I would probably use redis or some other key/value store to store the results rather than relying on variables. The issue with your current implementation is if two people upload data at the same time, the data gets overwritten. – Suever Mar 20 '16 at 19:59
  • Sorry, I was being imprecise - these are not variables, but key/value pairs, and each user authenticates on front end and has their own unique space in the dictionaries, e.g. `dict[unique_session_id_1][rest_of_user_1_data_here], dict[unique_sesion_id_2][rest_of_user_2_data_here]`, etc – pniessen Mar 20 '16 at 20:03
  • So if you want to go about it that way globals are pretty much the only way. I would still recommend some other key/value store so that you can spin up multiple workers that can have concurrent access to the same underlying data. – Suever Mar 20 '16 at 20:09
  • Thanks - at some point I will migrate to a proper persistent data structure (mongod etc) but was hoping for a simpler short-term option :-) – pniessen Mar 20 '16 at 20:26
  • Update: though my dict-based approach worked in test, when I moved to a multithreaded production environment I ultimately moved my globals to a database (mongodb), as globals are not available across threads. Took a small amount of work but was worth it! – pniessen Apr 21 '16 at 17:56

1 Answers1

2

If they are constants, IMO globals are ok.

If you have to run some code to generate the values, I would define methods (and possibly put them in another module if they are to be reused) that I can call from my_function. Actually, even for constants you could abstract them with a simple function.

def foo():
    # compute and return value

def bar():
    # compute and return value 

def my_function():    
    baz = foo() + bar()

Also, the application context in flask might be of some use for you if you want to cache resources like database connections for example

Edit after reading the comments on the original question

You should probably really use a key-value store, maybe consider flask-session as well: https://pythonhosted.org/Flask-Session/ Otherwise you'll get not only multithreaded problems (can be fixed with using a lock), but also multiprocess problems as soon as you run two worker processes -- one request might save data in 1 process, but the next request can land on a different one.

If you really want to do this only for prototyping, I'd suggest you at least abstract the access to the user data instead of exposing the raw dictionary. One simple ways is something like this:

class FooStore(object):

    FOO = {}

    @classmethod
    def get(cls, user_id):
        return FOO.get(user_id)

    @classmethod
    def set(cls, user_id, value):
        return cls.FOO[user_id] = value

# Access/set the values
FooStore.set(1, {'data': 'some user data'})
FooStore.get(1)

Later you will be able to easily change the implementations of set and get. For example serialize and store to Redis and fetch and deserialize from Redis, respectively. It will also allow for easier testing.

emil.p.stanchev
  • 672
  • 5
  • 13