SuspensionError - Store application settings

Hello all.

I’m storing settings to Loki logging aggregation, and Microsoft Graph to a setting file in the server, which is working fine by opening the file in servermodule :

def get_setting(key):
    with open("settings.json", "r") as readfile:
        settings = json.load(readfile)
        value = settings[key]
        if not value:
            value = r"http://localhost:4444" if key == "foxpro_connection" else ""
            settings[key] = value
            with open("settings.json", "w") as writefile:
                json.dump(settings, writefile)

    return value

However, the file is inaccessible from the client.

So I tried using Static Data Files, but getting error from the client : SuspensionError: Cannot call a function that blocks or suspends here.
Anybody has suggestion on where to put a global setting file like that? I was thinking about App Secret, but it is not easy to change the value as it is changing it in text file settings.json.

Code Sample:
servermodule.py

from anvil.files import data_files

@anvil.server.callable
def return_text_from_file():
    # Read the contents of a file
    with open(data_files['settings.json']) as f:
        text = f.read()
    return text

client_code\Global.py

from .LokiModule import LokiLogger

log = LokiLogger()

client_code\LokiModule.py

def get_setting(group):
  # Using data_files call from server module - causing SuspensionError on anvil.server.call
  file_text = anvil.server.call("return_text_from_file")
  setting = dict(file_text)


class LokiLogger:
    # Get settings
    loki_setting = get_setting("loki")

Clone link:
(Anvil | Login)

That’s the body of the class
It’s one of the hard edges of skulpt

from time import sleep

class Foo:
   sleep(.5)
   pass

If you move the things that fetch from the server into the module scope rather than the class body it should work as expected

Thank you for the quick reply. Sorry my post wasn’t that clear. I edited it to show the module name. So I have the anvil.server.call in the LokiModule.py get_setting function. But still getting the SuspensionError.

So if I understand it correctly, I cannot have the global variable log, in the Global.py, because it is calling a blocking code (anvil.files.data_files). So I have to move the log variable to the modules. Did I hit the nail on the head?

Peter

Anything inside a class body can’t suspend

class LokiLogger:
  # Get settings
  setting = Setting()  
  loki_setting = setting.get_setting("loki") # I am a call that suspends

  loki_url = loki_setting["loki_url"]
  loki_user = loki_setting["loki_user"]
  loki_pass = loki_setting["loki_pass"]

You need to hoist this into the module scope

setting = Setting()  
loki_setting = setting.get_setting("loki") # i'm in the module scope I can susupend

class LokiLogger:
    loki_url = loki_setting["loki_url"]
    loki_user = loki_setting["loki_user"]
    loki_pass = loki_setting["loki_pass"]

Thank you. That’s good information to know. So Skulpt dictates that class cannot code that blocks or suspends. Appreciates your quick respond!

I didn’t know this.

A form is a class, so it’s this valid for all classes but forms?

Yeap it’s also true for forms. But note we’re only talking about the class setup here. Any code inside methods can suspend just fine.

If more folks had hit this issue it’s likely something we would have prioritised (you would know if you hit it because your Form/class would never get created and you’d have hit the above error at load time). But it’s not a pattern that comes up too often.

Most of the time the top level class body definitions tend to be simple and then more complicated fetches happen in the __init__.

If the above class was a singleton where the setup happened in the __init__ then we wouldn’t be having this conversation. That’s the typical pattern for an Anvil form at least.

1 Like

Ok, in class body, not just in class, I missed that.

I don’t think this is worth investing any time, other than showing an error message that clearly describes the limitation. For example replacing “here” with “in the class body” in the error message “SuspensionError: Cannot call a function that blocks or suspends here” would be good enough.