Unable to access trained model from one python function into another and downloading the entire model

My task is to build a webapp which takes user input i.e. PATH OF THE DIRECTORY CONTAINING IMAGES TO TRAIN THE MODEL.

I’ve two functions namely “task2” and “test”.
TASK2 function takes the user input path and then trains the model.
TEST function takes a single image upload from user to test the trained model.

I’m getting error in TEST function which is “NameError: name ‘model’ is not defined”. The name ‘model’ is actually used in TASK2 function. how can i access it here?

https://anvil.works/build#clone:EQECZML276BK6KRD=OCZH3GTIZ5XWHZ6IK2V3USW6

Apart from this, I want the user to download his own trained model that he provided the images for. How can i achieve this task? any suggestion please? i’m unable to find a way to do this.

I’m getting deja vu, right down to the function names.

This is likely a scoping issue, and a lifetime issue.
Scoping

def task2():
  model = []  # name is created inside task2 when task2() runs; destroyed when task2() finishes

def test():
  print(model)

def work_on_a_model():
  task2()  # invoke task2
  test()   # invoke test -- fails.  The name "model" does not exist inside test

This is how Python (and most other programming languages) works: names created inside a function are visible only in that context (that run of that function).

This prevents a huge number of problems. If all names were visible everywhere – and I’ve used old languages that do this – then before you write one line of code, you would need to know every name used anywhere else in the program, simply to avoid accidentally altering the wrong variable.

So, this actually protects you from a huge amount of unwanted work.

Lifetime
When task2's private variable model “dies”, it takes its value with it.

Solution (based on my code above):

def task2():
  model = []  # name is created inside task2 when task2() runs; destroyed when task2() finishes
  return model  # passes model's value back to its caller

def test(model):  # receives value into local variable "model"
  print(model)    # does something with model

def work_on_a_model():
  m = task2()  # invoke task2, preserving the result as local variable m
  test(m)      # invoke test, passing m's value/
  1. This version of task2() preserves the value of its model by returning it to its caller (work_on_a_model).
  2. work_on_a_model preserves the value by assigning it to work_on_a_model's variable m.
  3. work_on_a_model passes that value to test
  4. test receives that value. It is assigned to test's local variable model.

There are other ways to do this, depending on the relationship between the functions (which function calls which). In all cases, choosing the right steps will revolve around Python’s scope and lifetime concepts.

Downloads
That’s a separate issue, that probably deserves its own Forum post.

2 Likes

Re downloads: you’ll want to start here: https://anvil.works/docs/media#uploading-and-downloading-files . Scroll all the way up, to get the underlying ideas.

To be downloaded, your model will have to be transformed into a file-like object. At its heart, a file is a sequence of bytes. If your code creates a Media Object that contains that exact sequence, then it can offer to send that sequence to the user, as a file, via download.

  1. Decide what should that sequence look like. (If the file will be fed to other tools, then those tools will likely decide for you.)
  2. Write code that computes (produces) that sequence, i.e., as a Python object of type bytes.
  3. Using that object, create an Anvil Media Object.
  4. Let the user download the contents of that Media Object.
1 Like

Brilliant explanation! I got your point and tried to do the same. Now everything should work fine but I’m getting some error and unable to fix it. I changed my design and code a little bit.

BEFORE:

AFTER (when i realized that i’m also returning “score” and “size” along with the “uploaded file”):

We can’t see all of line 16. The error may be due to the part of the code we can’t see.

You can paste code into your forum entries. Just put a line containing ``` (just that, nothing else) immediately before your pasted code, and immediately after.

Note that those are backwards tic marks (key is to the left of the 1 key).

  def button_1_click(self, **event_args):
    """This method is called when the button is clicked"""
    
    result, score, size = anvil.server.call('work', self.input.text, self.file_loader_1.file)
    
    
    self.image_1.source = self.file_loader_1.file
    self.result_lbl.text = "%s (%0.2f)" % (result, score)
    self.size.text = size

I’m also adding my jupyter notebook code below (since i’m unable to use tensorflow in free version):

Here’s how I read the effects:

  1. work is calling test
  2. test is returning a tuple of 3 values to work
  3. work is not preserving any of those values
  4. work appears to be returning nothing to its caller. By Python standards, this means it actually will return a single value, Python’s None.
  5. the caller (your browser’s code) is trying to unpack this value (call it “v”), as if you had written
v = anvil.server.call('work', self.input.text, self.file_loader_1.file)
result = v[0]
score = v[1]
size = v[2]

Unfortunately, v is None. You can’t subscript or iterate over None.

3 and 4 are at the root of this problem. Just as work had to preserve task2's result, so that it could be passed to test, work also needs to keep test's result “alive” long enough to return it to work's caller. The short way to do that is to insert return before the call to test:

  return test(file, m)

The long way is

  result, score, size = test(file, m)
  return result, score, size

which gives you a way to inspect and/or do more with the values, before you return them.

1 Like

Again, great explanation! Thank you soo much!! It worked perfectly. I didn’t even realize the small mistake.

Hello! The solution you provided works pretty well but I actually want the function “task2()” to run only once and return the variable “model”. I want to use this “model” variable several times by uploading a single image every time in the “test()” function, to test the uploaded image (provided in “test()” function) according to the trained model (in “task2()” function).

I’m still unable to achieve this :pensive:

Okay, you’ll need to create a place to stash it, one that lives longer than the variables in your function calls. You should decide just how long that value should live. (If the user is interrupted, or has to quit for the day – or go on vacation – should it still be there when they get back?) That will help narrow down your options.

If it lives long enough, then you might have more than one model in existence at the same time. You might then need a way to distinguish one from another. Some kind of id, or name.

Also, since an Anvil app can be used by many people at the same time, you’ll probably want to keep one user’s model(s) distinct from everyone else’s.

So, give these questions some thought.

For now, i just want to use the variable “model” until i close the anvil app myself. For now, i’m not creating this app for multiple users. That is, I just want it in a running condition to use it once myself perfectly.

Sure. In that case, you can define model as a global variable.

Example: https://www.geeksforgeeks.org/global-keyword-in-python/

Something like this:

m = None

@anvil.server.callable
def work(path, file):
    global m  # in this function, m will refer to the above m
    if m is None:
        m = task2(path)
    # code that uses m...

This limits you to one user, and one model at a time. That model will be the one and only model in this code, at any one time. It will live while the notebook is running. As long as those limits work for you, this should be fine.

1 Like

OMG! It worked!! can’t believe it was that simple. THANK YOU SOO MUCH!!

You’re very welcome.

Python is a very rich, but practical language. Even “experts” generally don’t get around to exploring every nook and cranny. For those who have the time to explore, there are vast rewards.

And new add-on packages are being developed (and improved) every day. Very much a gift that keeps on giving.

2 Likes