Skip to content

Demo Multiple Integrations#117

Closed
ianjosephwilson wants to merge 24 commits into
t-strings:mainfrom
ianjosephwilson:ian/integrations
Closed

Demo Multiple Integrations#117
ianjosephwilson wants to merge 24 commits into
t-strings:mainfrom
ianjosephwilson:ian/integrations

Conversation

@ianjosephwilson
Copy link
Copy Markdown
Contributor

This has 4 examples in the processor_integration_test.py module:

  1. a component processor that uses a ContextVar to dynamically resolve component callables AND provide extra component kwargs
  2. a component processor that uses an app_state, the default dict[str, object], to provide extra component kwargs
  3. a component processor that uses a "well defined" app_state, defined with a dataclass, to provide extra component kwargs
  4. an interpolation with a callback format_spec that uses a ContextVar to dynamically inject an attribute value into a component

This doesn't have any implementation of "layering" the app state or context vars where a component in the middle of the tree could change the value. My old example used ContextVars which I think is more sane probably. There is a place for that to get added but I think we'd do it later which we already discussed.

Adding a app_state to html() was really putting things over the edge so I tried to simpify html() as well.

@ianjosephwilson ianjosephwilson changed the title Ian/integrations Demo Multiple Integrations Apr 25, 2026
@davepeck
Copy link
Copy Markdown
Contributor

davepeck commented Apr 25, 2026

My first instinct was that we should only ship an extension point here when we have real world use cases from devs.

a component processor

That said, IComponentProcessor seems like a good protocol to tease out, and provided_attrs is useful. It's a small enough delta that maybe we should take it today?

My old example used ContextVars which I think is more sane probably.

I think my instinct is the same.

Specifically, it doesn't look like the TemplateProcessor ever reads app_state? We thread app_state every-which-where so it can make it to the one place that matters: IComponentProcessor.process(...).

Do we have a concrete example where typed app_state is clearly preferable to a ContextVar?

I tried to simpify html() as well.

Feels like that could be a separate PR?

@pauleveritt
Copy link
Copy Markdown
Contributor

I did some work today to integrate my tdom-svcs thing with this branch. It helped a lot, the size of my "fork" went down completely.

With a little bit more work, my "fork" would be even smaller. Here's a gist writeup with:

  • The change I could really use in processor.py
  • Based on that, what my custom processor would look like
  • Some explanation of what the hell I'm doing

@ianjosephwilson
Copy link
Copy Markdown
Contributor Author

ianjosephwilson commented Apr 26, 2026

I did some work today to integrate my tdom-svcs thing with this branch. It helped a lot, the size of my "fork" went down completely.

Yeah it seems like we're getting closer.

With a little bit more work, my "fork" would be even smaller. Here's a gist writeup with:

  • The change I could really use in processor.py

@pauleveritt

Is there a reason you can't use a ContextVar instead of app_state here?

Also why is it not possible to just resolve the component_callable and resolve the extra kwargs without have to pass of the entire invocation to the DI system? I guess one issue is that maybe if a local value is provided you wouldn't want to dig out the DI value to just throw it away. Seems like a limitation of prop_component_kwargs. I'll think about that one that is probably something we might want to avoid. The idea would be that you could still use that function but you'd replace the entire ComponentProcessor if you needed to (and not subclass).

We try to "capture" the component_object to return and its not clear if that is possible in your gist. It is the only way for the TemplateProcessor to interact with the Component without just getting Templates. The TemplateProcessor isn't doing that now but that is an extension point. Specially the 2nd entry in the 2-tuple that is returned from ComponentProcessor.process().

  • Based on that, what my custom processor would look like
  • Some explanation of what the hell I'm doing

@ianjosephwilson
Copy link
Copy Markdown
Contributor Author

@pauleveritt I wonder if its possible to just replicate the call to _prep_component_kwargs in your component processor when needed, but use these new flags, raise_on_missing=False, and then continue to hopscotch. I don't know how to handle the "capturing" part. This is helpful info though.

@pauleveritt
Copy link
Copy Markdown
Contributor

Ok, I refactored based on both of your comments and today's changes. I don't really need anything upstream from you now, so that's good news.

I'm investigating ways to eliminate the module mutable globals on my side. I know you have a lot on your plate, so let me know if you want me to share with you.

@ianjosephwilson
Copy link
Copy Markdown
Contributor Author

ianjosephwilson commented Apr 28, 2026

My first instinct was that we should only ship an extension point here when we have real world use cases from devs.

a component processor

That said, IComponentProcessor seems like a good protocol to tease out, and provided_attrs is useful. It's a small enough delta that maybe we should take it today?

@davepeck
I started cleaning it up in #118 but I'm not sure if we need to pre-include the extended signature for the component processor, Template | tuple[Template, object], as demo'ed in #119

My old example used ContextVars which I think is more sane probably.

I think my instinct is the same.

Specifically, it doesn't look like the TemplateProcessor ever reads app_state? We thread app_state every-which-where so it can make it to the one place that matters: IComponentProcessor.process(...).

I think my concern is that we can't add it later and if ContextVar isn't wins-all-around we might regret it. You need it everytime you process component but it seems that the performance is ?pretty good? They seem magically but there is something about the setup that is a little confusing, like "patting your head and rubbing your stomach". It would have been nice if there was an easier way to just "inject" the app state without having to do so much "extension-inating". It seems like too much to add the DI into tdom but then .. it is kind of weird without ANY DI.

Maybe I'll try some things out. I think most people would expect something like this or at least this is just so brain-dead easy its fun. Maybe we can wrap a ContextVar to do something like this "behind the scenes". At least for people that don't want to use a framework or dependency injection system or custom context variable right away.

def MyComp(my_state: dict[str, object]) -> Template:
    ...
my_result = html(my_template, app_state={"my_state": {"my_theme": "red"}})

Do we have a concrete example where typed app_state is clearly preferable to a ContextVar?

I tried to simpify html() as well.

Feels like that could be a separate PR?

Yes, I brought assume_ctx back in for now.

@ianjosephwilson
Copy link
Copy Markdown
Contributor Author

Ok, I refactored based on both of your comments and today's changes. I don't really need anything upstream from you now, so that's good news.

I'm investigating ways to eliminate the module mutable globals on my side. I know you have a lot on your plate, so let me know if you want me to share with you.

@pauleveritt

I don't know which mutable globals you're talking about in this area. Is that in your original gist?

Do you know if you're able to use a ContextVar in place of a sanctioned app_state as we might keep that out for now?

Something like this:

SvcsContainerCtx = ContextVar[svcs.Container|None]('SvcsContainerCtx', default=None)

# setup container and setup template processor with custom component processor to use container via
# container_object_or_none = SvcsContainerCtx.get()

# html() call would look something like this
def your_html(template: Template) -> str:
    with SvcsContainerCtx.set(svcs_container_object):
        return template_processor.process(template, assume_ctx=ProcessContext())

@ianjosephwilson
Copy link
Copy Markdown
Contributor Author

Closing this for now as we merged #118.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants