Skip to content

How to configure so that only parameters marked with Inject[SomeType] are injected? #83

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

Open
jonathanmach opened this issue Jul 12, 2023 · 6 comments

Comments

@jonathanmach
Copy link

jonathanmach commented Jul 12, 2023

Hi, I have been diving deep into the lib and can't stress enough how much of a great project this is!

I'm using flask_injector and Flask-Pydantic, however, they seem to be conflicting with each other.
(EDIT: I don't think this is Pydantic-specific)

ie:

@webhooks_api.route("/my_webhook", methods=["POST"])
@pydantic_validate()
def my_webhook(body: WebhookContract):
    ...

In the code above, flask_injector seems to be taking priority and raising:

E  injector.UnsatisfiedRequirement: core.domains.rest.webhooks has an unsatisfied requirement on WebhookContract

Any tips on how to configure things so that only parameters marked with Inject[SomeType] are injected?
See: https://injector.readthedocs.io/en/latest/api.html#injector.Inject

@jonathanmach jonathanmach changed the title How to configure things so that only parameters marked with Inject[SomeType] are injected? How to configure so that only parameters marked with Inject[SomeType] are injected? Jul 13, 2023
@jstasiak
Copy link
Collaborator

Hey, that's a good question – that's not really supported at the moment and I think we need to make it configurable

@jonathanmach
Copy link
Author

Hey, that's a good question – that's not really supported at the moment and I think we need to make it configurable

Hey @jstasiak, I hope all is well.
I'm planning to pick this up and contribute with a PR for it. Do you have any suggestions or advices here that you think might help me implement this new functionality?
Thanks in advance!

@jstasiak
Copy link
Collaborator

Hey @jonathanmach, I imagine it'll be roughly like this:

In this piece of code

if hasattr(fun, '__call__') and not isinstance(fun, type):


    if hasattr(fun, '__call__') and not isinstance(fun, type):
        try:
            type_hints = get_type_hints(fun)
        except (AttributeError, TypeError):
            # Some callables aren't introspectable with get_type_hints,
            # let's assume they don't have anything to inject. The exception
            # types handled here are what I encountered so far.
            # It used to be AttributeError, then https://github.com/python/typing/pull/314
            # changed it to TypeError.
            wrap_it = False
        except NameError:
            wrap_it = True
        else:
            type_hints.pop('return', None)
            wrap_it = type_hints != {}
        if wrap_it:
            return wrap_fun(inject(fun), injector)
  • Replace get_type_hints() with injector.get_bindings() to detect if we actually have anything to inject
  • Replace inject(fun) with fun to stop assuming we want to inject everything by default

@raul-macedo-freire
Copy link

raul-macedo-freire commented Apr 10, 2024

Hey there, any updates on this issue ? I came across the same problem and would be happy to help, if @jonathanmach hasn't been able to get to work on it yet.
I've tried @jstasiak suggestion, however it would require inject decorator to be included for endpoint methods, was that an expected behaviour ?

@jstasiak
Copy link
Collaborator

Hey @raul-macedo-freire, all the updates we have on this are above. Can you clarify what do you mean by "it would require inject decorator to be included for endpoint methods"? An example would be great.

@raul-macedo-freire
Copy link

Hey there, I'm embarrassed by how long I took to come back in here hehe. But I've tried to reproduce what I was initially trying to achieve and noticed that I was actually trying to keep both behaviors (Flask-Pydantic and Flask-Injector). I tried to find a way to make Flask-Injector wrap the route functions with binded classes and keep Flask-Pydantic validated models in the wraped functions. To explain what I meant on my first comment, @jstasiak, with the proposed solution I could not achieve what I initially intended, since the binded functions would no longer work, requiring the @inject decorator to explicitly inject.

Without any changes in Flask-Injector, I tried the following


from flask import Flask, request
from flask_pydantic import validate
from pydantic import BaseModel, ConfigDict, Field
from injector import inject
from flask_injector import FlaskInjector

app = Flask(__name__)

class Person(BaseModel):
    name: str
    age: int 

class SaveModel:
    @inject
    def __init__(self, name: str):
        self.name = name

    def save(self, data: Person):
        # Simulate saving to a database
        print(f"Saving {data.name} to the database.")
        return data

def configure(binder):
    binder.bind(
        SaveModel,
        to=SaveModel('David')
    )


@app.route("/",methods=["POST"])
@validate(body=Person)
def save_stuff(save_model: SaveModel):
    # Use the injected SaveModel instance
    save_model.save(request.body_params)
    return "Ok"

FlaskInjector(app=app, modules=[configure])


if __name__=="__main__":
    app.run(debug=True)

If I were to implement the proposed changes, save_stuff would have to be decorated with @inject in order to wrap the route with SaveModel, however even with this solution, pydantic will keep throwing exceptions since it will try to validate SaveModel agains some TypeAdapter.

I believe @jonathanmach has achieved more progress under the proposed solution.

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

No branches or pull requests

3 participants