Skip to content

feat: add wireup.instance() helper for registering existing instances#105

Merged
maldoinc merged 11 commits into
maldoinc:masterfrom
DarshanCode2005:feat-injectable-list
Mar 8, 2026
Merged

feat: add wireup.instance() helper for registering existing instances#105
maldoinc merged 11 commits into
maldoinc:masterfrom
DarshanCode2005:feat-injectable-list

Conversation

@DarshanCode2005

@DarshanCode2005 DarshanCode2005 commented Jan 24, 2026

Copy link
Copy Markdown
Contributor

Closes #102

Summary

This PR introduces a new helper, wireup.instance(...), which allows direct registration of pre-existing objects (e.g. database connections, settings, legacy singletons) with the Wireup container—without requiring a factory function.

container = create_sync_container(
    injectables=[
        wireup.instance(db_conn, as_type=DatabaseConnection),
        wireup.instance(replica, as_type=DatabaseConnection, qualifier="read"),
    ]
)

What’s included

  • New public helper: wireup.instance(obj, as_type, qualifier=None)
  • Singleton semantics (instance is never recreated)
  • Support for qualifiers
  • Full container integration
  • Unit, integration, and regression tests
  • README documentation

Behavior & guarantees

  • as_type is required and validated
  • The container always returns the same instance
  • Qualified resolution works as expected
  • Duplicate (type, qualifier) registrations fail
  • Behavior is equivalent to the existing singleton-factory workaround

Tests added

  • Unit tests for provider behavior and metadata
  • Integration tests for resolution and injection
  • Regression tests ensuring parity with legacy factory patterns

All tests are passing.


Related discussion

Implements the feature discussed in #94 (comment).

@maldoinc maldoinc left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👋 Hi there! Thanks for the PR! This is a great addition and fills a gap in the API nicely. The implementation looks solid and the tests cover the key scenarios well.

I pulled this locally and ran the tests. Everything functions as expected! I just have a couple of small items to address to get the CI green and the code fully compliant with the project's standards.

1. Formatting

It looks like the new files need to be formatted to match the project style. The CI failed on the formatting check. You can run:

make format
# or
uv run ruff format .

This will fix the make lint failure.

2. Typing

There is a mypy error in wireup/_instance.py because we're attaching an attribute to a function, which strict typing forbids by default for Callable.

wireup/_instance.py:35: error: "Callable[[], T]" has no attribute "__wireup_registration__"  [attr-defined]

You can fix this by adding a type ignore, similar to how it's done in wireup/_annotations.py:

    _instance_provider.__wireup_registration__ = InjectableDeclaration( # type: ignore[attr-defined]
        obj=_instance_provider,
        ...
    )

3. Verification

I verified that this works even with Generic types (like List[int]), which is awesome.

# verified locally
container = create_sync_container(injectables=[
    instance([1, 2], as_type=List[int])
])

Great work! looking forward to merging this.

@maldoinc maldoinc left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is clearly vibecoded I decided to automate the review which you can see is just as terrible as the implementation.

When I create these issues on GitHub it's not because I can't write slop fast enough but rather to encourage new contributors with good first tickets and for them to learn and get some more practical experience. I'm not against using this per se but at least do take some time to go through the repo and get an understanding.

Comment thread test/unit/test_instance.py Outdated
from wireup import instance
from wireup._annotations import InjectableDeclaration

class TestInstanceProvider(unittest.TestCase):

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Project uses pytest.

Comment thread wireup/_instance.py Outdated
# The container requires a return type annotation to determine what is being provided.
_instance_provider.__annotations__["return"] = as_type

_instance_provider.__wireup_registration__ = InjectableDeclaration(

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to actually do this here, you can just add @injectable in the factory you create and the decorator will create the registration.

Comment thread wireup/_instance.py Outdated

def instance(
obj: T,
as_type: type[Any] | None = None,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is required then it shouldn't be made optional with a default value.

Comment thread wireup/_instance.py


def instance(
obj: T,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anything other than obj should be keyword only to make the api more readable in this case and make the registration more explicit.

@DarshanCode2005

Copy link
Copy Markdown
Contributor Author

Thanks for the context. That makes sense, and I appreciate the goal of keeping issues beginner-friendly. I’m still actively reading through the docs and taking time to understand the repo structure and design decisions. That said, I do have a reasonably high-level understanding of the project wire-up and the dependency injection flow, which is what I relied on here. I was careful not to push anything that could introduce bad or unsafe code. The AI edits were only used for small refinements, not as a substitute for understanding, and I intentionally held off on making any changes I wasn’t confident about. I’ll keep digging deeper into the codebase and overall context before proposing further changes. Thanks for the feedback. @maldoinc

@maldoinc

Copy link
Copy Markdown
Owner

@DarshanCode2005 let me know if you still are interested in taking this to the finish line

@DarshanCode2005

Copy link
Copy Markdown
Contributor Author

Sure, I’m interested in completing this. I was a bit tied up with some other work, but I’ll try to finish it by today. Thanks!

@maldoinc maldoinc left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this. This is almost there, there's just a tiny issue.

Due to the created factory returning a TypeVar, wireup cannot do it's usual type checks on whether as_type matches obj and as_type.

See this example to repro

class A: pass
class B: pass

wireup.create_sync_container(injectables=[instance(A(), as_type=B)])

You can fix it, by updating both __signature__ and __annotations__["return"] after creating it. Then the existing registry validation machinery will work unchanged. Let's also add the above example as a test asserting it should raise type mismatch error.

Comment thread test/unit/test_instance.py Outdated
@@ -0,0 +1,70 @@
from typing import Annotated

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from typing_extensions

@maldoinc

maldoinc commented Mar 8, 2026

Copy link
Copy Markdown
Owner

@DarshanCode2005 you can run make lint locally to run the checks

@maldoinc maldoinc merged commit 0a9dd37 into maldoinc:master Mar 8, 2026
10 checks passed
@maldoinc

maldoinc commented Mar 8, 2026

Copy link
Copy Markdown
Owner

Thank you @DarshanCode2005

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.

Feature: Support registering existing instances in injectables list

2 participants