-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Request to share selective memories from main component to other components #22
Comments
Subclassing won't work, because components (and bots) have unique, random IDs assigned during initialization. This was made to prevent conflicts between components, but this also kills your use case as a side effect. The hacky way to fix this issue is manually editing the I'll think about an API for this, but I don't think this will be implemented before a refactor of the shared memory I plan to do in the future (converting it to a drivers-powered thing, in order to provide a standardized API and different storage engines). |
Thanks for the info. I believe I've found a solution that will work for me given the backend I plan to use. |
I thought about it a bit, and I think I found a good API for this: class MyComponent(botogram.Component):
component_name = "test"
component_isolated = False That I am, as always, open for feedback :) |
I like this. I'd assume any non-isolated component could still initialize a memory (not just the bot/main component). That way everything's still handled by the responsible component. With this, one could also easily setup something like component dependencies. A BackendComponent can initialize a shared memory holding a connection to the backend. Then any number of other components expecting such a connection in the shared memory can simply depend on the BackendComponent. Maybe a check can be made from Whether this fits in or is non-limiting to your future plans for shared memories, I'm not sure. |
Yeah, you should be able to initialize the memory from any non-isolated components.
Maybe in the future. The problem with this is, there are no way to reference another component:
Another possible API, which allows to use multiple shared shared memories: class MyComponent(botogram.Component):
component_name = "test"
component_memory = "backend" |
I see. Then having multiple named shared memories seems like a simple solution that requires only a little more thought when putting dependent components together. Any components that need to work together simply use the same shared memory space. Should components be able to utilize multiple component_memories = [
'default', # memories initialized from the main component (bot)
'backend' # memories initialized from a BackendComponent
] How would memory initialization then work? It seems component should be able to initialize memories to any space. # Initialize a memory into the "default" space
@bot.init_shared_memory(space="default")
def default_memory(shared)
shared["example"] = 1 # Within a component, initialize a memory to the "backend" space
self.add_shared_memory_initializer("backend", connection_memory) When accessing shared memories, perhaps the @bot.command("example")
def example_command(shared):
num = shared["default"]["example"] # or
num = shared.default["example"] I like the solution of named |
Sorry for the delayed response! Had a busy week. I'm not fully sure about multiple shared memories for every component. The first example breaks every current code, and the second one might break existing methods (it's a dict-like object). I'll think about this later. |
Another (perhaps common) use case I've come across where inter-component memory sharing is required is with OAuth type scenarios. When looking to use various Web APIs which require authorization, access tokens are generally short lived, or can be revoked for a number of reasons. Once you have to renew access tokens, there's no way to provide the new tokens to all components that may need them. By the way, congrats on releasing botogram to the wider world! Nice work. |
FYI, if anyone else is manually setting |
I'll add a new I don't think messing around with other components' memories is a good idea, because there is no dependency management and a component should be able to manage its memory without other components playing around with it. |
Sounds reasonable. With the |
Prefixing every item with your component's name? If you want to avoid conflicts there is the component memory. |
The refactor planned in #54 should also allow you to create custom memories. |
bucket = bot.shared.get_bucket("backend")
bucket.memory["a"] = "b" Would this be enough @patternspandemic? |
Sure. Looks to be a global memory with named access to 'buckets' for organization? Works for me, I think as long as any component can initialize memories to any bucket. |
Uh, yeah, I forgot to explain what buckets are! I'm implementing the new shared state (uh, new name) from scratch (with a lot of nice things), and I'm designing it to be easily customizable (you will be able to easily create drivers, for example backed by a database or redis) and better organized. Buckets are now the groups of shared things (memories or locks): each component/bot combination has its own bucket as before, there will maybe be a global bucket (I'm deciding if it's necessary now) and you will be able to create new buckets as shown in the previous comment. This means buckets you create will have the exact functionality of the ones you get on your hooks, because they will be the same thing after all. With a bit of hackery you will also be able to access buckets of other components (those buckets are named Another thing is, you will be able to create shared objects detached to the main shared.memory[chat.id] = shared.object("dict")
shared.memory[chat.id]["action"] = "I'm finally synchronized!" EDIT: this probably won't be the final API. |
I see how a global bucket wouldn't be necessary if any component could ask for a named bucket from I assume prepared memories are still part of buckets, and that a component prepares memories to its own bucket. I guess this would mean the @prepare_memory
def prepare_some_memories(shared):
# Prep a memory to this component's bucket
shared.memory["count"] = 0
# Request a named bucket, and add a memory to it.
my_bucket = shared.get_bucket("my_bucket")
my_bucket.memory["other"] = "Custom bucket memory!" If one can get a named bucket from the I think I need to see more examples of the detached Good work Pietro |
Yes, I don't want to take (useful) stuff away :)
The
I need to think more about an API for this. The example you posted won't work most of the times, because preparers are called when they're needed (they must not be considered as an
You get a named bucket from the
The "I don't want to reassing" was one of the most important reasons I started this rewrite: it's really ugly to type, and it leds to race conditions really easily (you should put a lock around each of this operation). One of the other nice things is, you can create any synchronized objects botogram supports: currently |
Hi Pietro,
Might it be possible to allow access to certain memories set directly on the bot's main component from all or some other components? Such is the case when some number of components require the same shared resource like a DB connection. I suppose I could try sub-classing a component with a memory of a DB connection, but I'm not sure the base class' shared memory will translate to its sub-classes.
I think a builtin method that doesn't require sub-classing would be more useful and easier to use anyhow. Perhaps if nothing else a simple way to allow a component to access to the main_component's shared memory. Thoughts?
Thank you,
Brad
The text was updated successfully, but these errors were encountered: