Code Monkey home page Code Monkey logo

wireup's People

Contributors

dependabot[bot] avatar maldoinc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

jod35

wireup's Issues

Unit Testing Flask Route Handlers

Hiya! Greatly enjoying this lib; we are exploring DI in one of our services and I've got a PR to add wireup and just use the singleton; the only issue the team has run into is we're not able to inject a mock into the flask route handler. Normally, like with services, we just directly use the target object and instantiate it in the unit test with mocks, however this won't work for flask endpoints. Consider the following code

@app.route("/fake/route/<route_param_one>")
@container.autowire
def import_setup(
    route_param_one: str, greeter: GreeterService
) -> Response:
    name = request.args.get("name")
    greeter.greet(name, route_param_one)

    return make_response("OK")

And this unit test with flask's test app mechanism

class TestImportSetup(TestCase):
    def setUp(self) -> None:
        self.app = app.test_client()

    def test_missing_vendor(self) -> None:
        result = self.app.get("/fake/route/fakedata?name=Cody")

        # How to provide a fake GreeterService if app entry point has already wired it up?
        self.assertEqual(200, result.status_code)
        self.assertEqual("Ok", result.text)

I've tried setting up multiple containers to have an empty test one and a normal prod one but it has proven... quite difficult to use.

Could we perhaps have a context manager style solution?

class TestImportSetup(TestCase):
    def setUp(self) -> None:
        self.app = app.test_client()
        self.mock_greeter = Mock()

    def test_missing_vendor(self) -> None:
        with container.override_inject(GreeterService, self.mock_greeter):
            result = self.app.get("/fake/route/fakedata?name=Cody")

            self.mock_greeter.assert_called_once_with("Cody", "fakedata")
            self.assertEqual(200, result.status_code)
            self.assertEqual("Ok", result.text)

Add support for async factories

Will have to detect if the factory is async or not and then use an async proxy

if (proxy := getattr(self, "_AsyncContainerProxy__proxy_object")) is None:
    async with getattr(self, "_AsyncContainerProxy__lock"):
        if (proxy := getattr(self, "_AsyncContainerProxy__proxy_object")) is None:
            proxy = await getattr(self, "_AsyncContainerProxy__supplier")()
            super().__setattr__("_AsyncContainerProxy__proxy_object", proxy)

Pass known singeltons/parameters when autowiring

The @container.autowire decorator will simply return a new function that will bind container objects to the function on call. Since this will happen on every call, even though cached it still has a small performance penalty.

On registration, if the container has been warmed up we should bind any known singleton services and parameters as the container is considered final.

FastAPI integration raises deprecation warning

A deprecation warning is being raised when calling the FastApi integration.

PATH/lib/python3.12/site-packages/wireup/integration/fastapi_integration.py:34: UserWarning: Using warmup_container is deprecated. Use 'initialize_container' instead
  warmup_container(dependency_container, service_modules or [])

Add support for protocols

Allow injection of types based on protocols. Services can declare themselves what protocol they support and the container can then perform autowiring based on that.

class SupportsFoo(Protocol):
    def foo(self) -> str:
        pass


@container.register(supports=SupportsFoo)
class FooBar:
    def foo(self):
        return "bar"

Requesting SupportsFoo will have FooBar injected.

Error injecting impl from interface when one of the qualifiers is None

When there are multiple implementations for an interface and one of them has None as a qualifier, then that one gets injected when asking for the interface without any additional info.

Automatically injecting impl from interface is supported only when there is one impl. In this case user should use a qualifier. Although not technically a bug because qualifier None does exist.

def test_two_qualifiers_are_injected(self):
    @self.container.autowire
    # This should error
    def inner(sub1: FooBase, sub2: FooBase = wire(qualifier="sub2")):
        self.assertEqual(sub1.foo, "bar")
        self.assertEqual(sub2.foo, "baz")

    self.container.abstract(FooBase)
    self.container.register(FooBar)
    self.container.register(FooBaz, qualifier="sub2")
    inner()

Add locking for multithread applications

Something like

if (proxy := getattr(self, "_ContainerProxy__proxy_object")) is None:
    with getattr(self, "_ContainerProxy__lock"):
        if (proxy := getattr(self, "_ContainerProxy__proxy_object")) is None:
            proxy = getattr(self, "_ContainerProxy__supplier")()
            super().__setattr__("_ContainerProxy__proxy_object", proxy)

Looks like __create_instance will also need updated with locking.

Add support for scoped lifetimes

When using in a web app it can be beneficial to have request-scoped objects to make sure they are initialized only once per request.

Unable to register a service using a qualifier in a factory function

The following registration fails due to service FooBar being registered twice since qualifier is ignored for factories. Factories should also be able to specify qualifiers.

@container.register(qualifier="1")
def foo_factory() -> FooBar:
    return FooBar()

@container.register(qualifier="2")
def foo_factory2() -> FooBar:
    return FooBar()

Allow providing arguments to autowired methods

If an arg has already been provided, the container can skip it. This should enable calling a function with an argument provided by the caller that the container would have otherwise passed.

A use case might be calling flask functions with new dependencies during tests when testing the endpoints as functions rather than e2e.

Should probably emit a log.info for this.

# Flask example

@app.get("/")
@container.autowire
def home(greeter: GreeterService):
    ...


# After: This should be possible
home(greeter=DummyGreeter())

Add support for generator factories

I want to do something like the following:

@service
def create_transaction() -> Iterator[Transaction]:
    transaction = Transaction()
    try:
        yield transaction
    finally:
        transaction.commit()

and also an async version:

@service
async def create_transaction() -> AsyncIterator[Transaction]:
    transaction = Transaction()
    try:
        yield transaction
    finally:
        await transaction.commit()

Usage in fastapi is broken

Both fastapi and wireup currently supply a value for the parameter leading to a runtime error when the function is called

Introduce functional deps

They can ask for dependencies as usual and return a function that will perform the task.

@container.functional
def greeter_factory(translator_service: TranslatorService, other_service ...) -> Callable...
    def greeter(name: str, locale: str = 'en_GB') -> str:
        return ...

    return greeter

@container.autowire
def target(greeter: Annotated[Callable..., Wire(fn=greeter_factory))
    ...

Add abstract injection without importing concrete impl

Currently from my experience, when we are using Annotated[AbstractClass, Wire(qualifier=Any)] - we still need to import redundant Concrete Impl to scope, where @autowire is performed. If such import is not performed, user will experience
wireup.errors.UnknownQualifiedServiceRequestedError

It would be nice for this library to have such feature, or at least, have an docs for this cause, to not import redundant concrete classes, but have a workaround.

Inconsistent usage of Wire() in fastapi and other frameworks

When using fastapi, annotating services with Wire() while required -- is the equivalent of having no annotations when used in other frameworks.

This needs to change so that the behavior is consistent and that since the type is annotated, might as well get the full benefits of the annotation such as throwing when requesting things that don't exist.

Add an easy way to override dependencies

Sometimes when the target method is already autowired it can be a bit difficult to substitute a service for a different implementation.

Add a way for users to override dependencies, preferrably through a ctx manager if they wish to do so.

See #7

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.