Configuring dependency server code from main app server code

What I’m trying to do:
I’m learning how to make good use of home-grown Dependencies. This Dependency grew from a desire to consolidate nearly-identical code from multiple Apps. Only a handful of App-specific parameters exist, but they do need to be passed to the Dependency.

The trick is, the Client code is calling the Dependency directly. We can’t trust the Client to pass these parameters; it might have been hacked. And the Dependency can’t directly call the App’s server-side code, to get them. (I suppose it could do its own anvil.server.call to get them, but that would be slow.)

But both App and Dependency share anvil.server.session, so that looked promising. All we need is a way for the App code to insert values, before the anvil.server.callable function starts running.

What I’ve tried and what’s not working:
Setting a value in anvil.server.session immediately on startup of the App’s server-side code.

Code Sample:

"""Server Module init_server.
Sets App-specific values (context) for our Dependencies to read.
"""
import anvil.server

anvil.server.session['app_name'] = 'mgmt'

In principle, since this code executes unconditionally, before the first anvil.server.callable starts, it should work in all cases:

  • regular server instances, persistent instances, or background tasks
  • server instances started on a different physical computer, than the session originally used, due to load-balancing.
    • My reasoning: The previously-persisted contents of anvil.server.session might not make it from the old computer to the new. (This is a really hard scenario to test!) But even in that case, the code above would still ensure proper app-specific values.

When the Client does its first anvil.server.call (i.e., into the Dependency), the App’s server module above is executed, as planned, before invoking the anvil.server.callable. But the above code produces the following error messages:

AttributeError: 'NoneType' object has no attribute '__setitem__'
at /downlink/anvil/_threaded_server.py:97
called from init_server, line 6

So the session member exists, but it isn’t yet a dict. It will become one later, with values from the current session (if accessible on this server), so any values I might put in here would (probably) get overwritten.

Bad timing, on my part.

Plausible workarounds
One way around this is for the Client to never call the Dependency directly. Instead, the App should intercept the necessary calls, so that they can pass the right (App-specific) context along to the Dependency.

I might yet do this, as it is simple, and accommodates a wide variety of conditions. For example, if any context (deliberately) varies, from one version, branch, or runtime Environment, to the next (e.g., an App Version number), the Dependency still receives the correct details.

This is much more difficult if the Dependency is called directly, from the Client.

Lesson learned
The best thing, in the long term, is to limit the amount of context that the Dependency needs.

What if you just used app secrets? Assuming they are constant values that you need passed. When your dependency code runs it’ll use the secrets of the same name from the main app.

Also as a rule of thumb, I avoid creating callables in a dependency app. What I do instead is write server functions that I import into my main app and create callables around that. I can then add context or other stuff as needed.

1 Like

That’s looking like a good design/implementation rule. My next refactoring step should accomplish that.

There’s a simpler answer to this – dependency code is loaded in the same Python interpreter as regular server code, so you can just import a dependency module from a server module and configure it. Eg in one of your app’s server modules:

import MyDepPackage.DepServerModule

MyDepPackage.DepServerModule.set_config(...)

Anvil ensures that all Server Modules (including all dependencies) are loaded before invoking a server calls, so if you do that in the main body of the module (ie at startup time) the config will be there before the server call arrives!

(Note: I’ve edited the topic title to reflect more closely what you were trying to do here. The answer to the session question is that there’s no reasonable answer to ‘what session are we in?’ when you’re setting up your code for the first time!)

2 Likes

I thought about that. But what if this isn’t the first time I’m calling a server function, and there’s data to be loaded into anvil.server.session from the previous server call? Any value I set here should be overwritten when that previous data is loaded.

anvil.server.session isn’t the place to be storing this kind of information – that’s for storing session-specific data, rather than global configuration data.

If I were implementing that set_config() function, I would have it store the config in global variables in the dependency’s Server Modules. (We discourage use of global variables for storing information between one server call and another, because Anvil tears down and starts up the server-side Python interpreter all the time, but that’s not what we’re doing in this case: Each time the server environment starts, your main app will call set_config() with the same data, and that will do what we want!)

2 Likes

Agreed. I only went to anvil.server.session because I didn’t think of your solution! I am just getting started with creating my own Dependencies, so not all of the options are clear to me yet. But I’ll get there!

Thanks for the technique. I’ll definitely put it to use!

Edit: Tried it. Works beautifully.

Note: import MyDepPackage is not enough. You have to use
import MyDepPackage.DepServerModule

Yeah, I really need to get on board with them too I am totally in the dark