question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Question: Singleton initialization

See original GitHub issue

I’m curious if this is allowed re: best practice (it works, which is great for me, but I’d like to know how foolish it is)

So I have a case where I have some drivers for services. One of my drivers needs another service:

class Foundation(containers.DeclarativeContainer):
    http_service = providers.Singleton(
        HttpService, timeout=6.05
    ) # type: Callable[[], HttpService]


class Drivers(containers.DeclarativeContainer):
    api_driver = providers.Singleton(
        ProductionAPIDriver,
        api_access_key='',
        cache_duration_seconds=600,
        http_service=Foundation.http_service
    ) # type: Callable[[], ProductionAPIDriver]

    data_driver = providers.Singleton(
        # --- !!! > RIGHT HERE < !!! ---
        # This Driver needs to be initialized with a service
        # The service in question needs to be initialized with
        # the driver defined above
        lambda: DataDriver(Services.api_service())
        # --- ^^^ > RIGHT HERE < ^^^ ---
    ) # type: Callable[[], APIDataDriver]

class Services:
    api_service = providers.Singleton(
        APIService,
        driver=Drivers.api_driver
    ) # type: Callable[[], APIService]

    data_service = providers.Singleton(
        DataService,
        driver=Drivers.data_driver
    ) # type: Callable[[], DataService]

So when I need Services.data_service that needs to go getDrivers.data_driver. Drivers.data_driver needs to be initialized with a Services.api_service. Because Drivers.data_driver needs to itself be initialized with a service, I can’t reference the Services at that moment in time, as it’s declared below Drivers. To work around that, I have wrapped the Singleton initialization in a lambda. This allows it to run successfully since the Services name binding inside the lambda is effectively deferred until runtime.

So this is a bit of a hopscotch around but it does work. Now aside from making the flow more difficult to understand from a maintenance perspective, all the bouncing around, are there any other downsides to this approach?

The docs for the Singleton provider state:

Singleton provider creates new instance of specified class (emphasis mine) on first call and returns same instance on every next call.

Of specific note is of specified class. In my case above, the specificed class is a callable in the form of a lambda. Is it safe to assume the docs really just mean of specified CALLABLE?

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
rmk135commented, Nov 9, 2018

and this happens because of this

when you create DeclarativeContainer(name=provider), it creates a DynamicContainer, copy all providers from declarative container to a newly created dynamic one

Correct me if I’m wrong

Yeah, you’re right.

In your example, you would need to use Dependency provider for AppContainer.app & ServiceContainer.service like you do now for value, or you DependenciesContainer provider - http://python-dependency-injector.ets-labs.org/examples/use_cases_miniapp.html.

0reactions
user-name-namecommented, Nov 9, 2018

So, as I understood this code is valid

from dependency_injector.providers import (
    Callable,
    Factory,
    Dependency,
    Singleton,
)
from dependency_injector.containers import DeclarativeContainer

class App: pass

class Service: pass

def handler(app: App, service: Service, value: int):
    return app


class AppContainer(DeclarativeContainer):
    app = Singleton(App)


class ServiceContainer(DeclarativeContainer):
    service = Factory(Service)


class UseCaseContainer(DeclarativeContainer):
    value = Dependency()

    handler = Callable(
        handler,
        app=AppContainer.app,
        service=ServiceContainer.service,
        value=value,
    )


factory = UseCaseContainer(value=2)
app = factory.handler()

assert app != AppContainer.app()
assert AppContainer.app() == AppContainer.app()

and this happens because of this

when you create DeclarativeContainer(name=provider), it creates a DynamicContainer, copy all providers from declarative container to a newly created dynamic one

Correct me if I’m wrong

Read more comments on GitHub >

github_iconTop Results From Across the Web

Java Singleton Design Pattern Best Practices with Examples
1. Eager initialization ... If your singleton class is not using a lot of resources, this is the approach to use. But in...
Read more >
10 Singleton Pattern Interview Questions in Java - Answered
10 Interview question on Singleton Pattern in Java ... Since synchronization is only needed during initialization on singleton instance, ...
Read more >
Java Singleton Design Pattern Practices with Examples
Eager initialization: This is the simplest method of creating a singleton class. In this, object of class is created when it is loaded...
Read more >
Singleton initialization configuration - php
Singleton is not an applicable pattern for classes that can have only one instance. It's for classes that may have only one instance...
Read more >
Singleton Design Pattern
Problem. Application needs one, and only one, instance of an object. Additionally, lazy initialization and global access are necessary.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found