Skip to content

feat(core): pass bound domain to provider initialize and enforce domain-scoped binding#616

Open
jonathannorris wants to merge 8 commits into
mainfrom
feat/domain-scoped-provider
Open

feat(core): pass bound domain to provider initialize and enforce domain-scoped binding#616
jonathannorris wants to merge 8 commits into
mainfrom
feat/domain-scoped-provider

Conversation

@jonathannorris

@jonathannorris jonathannorris commented Jul 2, 2026

Copy link
Copy Markdown
Member

Summary

  • Add optional domain parameter to provider initialize and pass the bound domain from the provider registry (spec 1.1.2.2, 2.4.1)
  • Add domain_scoped provider declaration and reject binding a domain-scoped instance to more than one domain (spec 2.4.3, 1.1.8.1)
  • Add registry and API tests, including legacy single-argument initialize backward compatibility

Motivation

Unblocks OFREP static-context providers that need the bound domain at init time to scope persisted cache keys per open-feature/spec#393 and protocol ADR 0009.

Notes

  • Non-breaking: domain is an optional second parameter to initialize
  • Non-domain-scoped providers retain existing behavior (single instance can back multiple domains; initialize runs once)
  • Legacy providers that only implement initialize(context) remain compatible; the registry inspects the initialize signature and omits domain when it is not accepted

Related Issues

Closes #615

Relates to: open-feature/spec#393

Test plan

  • pytest (180 tests)

Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jonathannorris, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 54 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d275c42c-0765-402e-9dd5-f17edc821f86

📥 Commits

Reviewing files that changed from the base of the PR and between ea8d686 and ee39f56.

📒 Files selected for processing (3)
  • openfeature/provider/_registry.py
  • tests/provider/test_registry.py
  • tests/test_api.py
📝 Walkthrough

Walkthrough

Adds an optional domain parameter to provider initialize methods and a domain_scoped property on AbstractProvider. ProviderRegistry inspects provider initialize signatures to conditionally pass domain, enforces that domain-scoped providers bind to at most one domain, and updates initialization/error-handling flow accordingly. Includes new test helper and tests.

Changes

Domain-aware provider initialization

Layer / File(s) Summary
Provider interface: domain parameter and domain_scoped property
openfeature/provider/__init__.py
FeatureProvider/AbstractProvider initialize gains an optional domain: str | None = None parameter; AbstractProvider adds a domain_scoped property defaulting to False.
Registry helpers: signature inspection
openfeature/provider/_registry.py
New helpers use inspect to determine if initialize accepts domain (with Mock handling) and call it accordingly.
Registry binding and initialization wiring
openfeature/provider/_registry.py
set_provider/set_default_provider reject invalid domain-scoped rebinding; _provider_bindings, _reject_domain_scoped_rebind, _initialize_provider, and _run_initialize/_call_initialize propagate domain through initialization, including the async init thread.
Legacy provider test helper
tests/legacy_init_provider.py
Adds LegacyInitProvider test class tracking initialize calls and the last evaluation context via a single-argument override.
Registry unit tests for domain scoping
tests/provider/test_registry.py
Tests cover domain propagation, domain-scoped binding restrictions, legacy-signature handling, error propagation without retry, _is_domain_scoped, and helper edge cases.
API-level tests for domain propagation
tests/test_api.py
Tests verify domain passthrough to initialize, legacy provider behavior, and rejection of rebinding a domain-scoped provider to a second domain.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
    participant Client as API/Client
    participant Registry as ProviderRegistry
    participant Provider as FeatureProvider

    Client->>Registry: set_provider(domain, provider, wait_for_init=True)
    Registry->>Registry: _reject_domain_scoped_rebind(provider, domain)
    alt provider.domain_scoped and already bound to different domain
        Registry-->>Client: raise GeneralError
    else binding allowed
        Registry->>Registry: _initialize_accepts_domain(provider.initialize)
        alt initialize accepts domain
            Registry->>Provider: initialize(evaluation_context, domain=domain)
        else legacy signature
            Registry->>Provider: initialize(evaluation_context)
        end
        Provider-->>Registry: status READY / error
        Registry-->>Client: provider bound and initialized
    end
Loading

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the core change: passing the bound domain to provider initialization and enforcing domain-scoped binding.
Description check ✅ Passed The description matches the changeset and explains the domain parameter, domain-scoped enforcement, and compatibility/testing work.
Linked Issues check ✅ Passed The implementation aligns with #615 by adding optional domain initialization, domain-scoped binding rules, and backward-compatible tests.
Out of Scope Changes check ✅ Passed No clear out-of-scope changes are present; the added helpers and tests support the domain-initialization and scoping requirements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.55%. Comparing base (79bb01b) to head (ee39f56).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #616      +/-   ##
==========================================
+ Coverage   98.34%   98.55%   +0.20%     
==========================================
  Files          45       46       +1     
  Lines        2483     2698     +215     
==========================================
+ Hits         2442     2659     +217     
+ Misses         41       39       -2     
Flag Coverage Δ
unittests 98.55% <100.00%> (+0.20%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jonathannorris jonathannorris changed the title feat: supply bound domain to provider initialize feat(core): pass bound domain to provider initialize and enforce domain-scoped binding Jul 3, 2026
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
@jonathannorris jonathannorris requested a review from Copilot July 3, 2026 16:10
@jonathannorris jonathannorris marked this pull request as ready for review July 3, 2026 16:10
@jonathannorris jonathannorris requested review from a team as code owners July 3, 2026 16:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the provider registry initialization flow to pass the bound domain into provider initialize(...) (while preserving backward compatibility for legacy single-argument initialize(context) implementations) and introduces an opt-in domain_scoped provider declaration that prevents the same provider instance from being bound to multiple domains.

Changes:

  • Extend FeatureProvider.initialize to accept an optional domain argument and pass the bound domain from ProviderRegistry during initialization.
  • Add domain_scoped support and enforce single-domain binding for domain-scoped provider instances.
  • Add/adjust API + registry tests, including a legacy provider fixture to validate backward compatibility.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/test_api.py Updates API-level expectations for initialize(..., domain=...) and adds domain-scoped/legacy coverage.
tests/provider/test_registry.py Adds registry-focused tests for domain passing, domain-scoped binding enforcement, and signature-compat behavior.
tests/legacy_init_provider.py Introduces a legacy provider fixture implementing initialize(context) only.
openfeature/provider/_registry.py Implements domain-aware initialization dispatch and domain-scoped rebind enforcement.
openfeature/provider/init.py Updates the provider protocol/base class to include domain in initialize and adds a domain_scoped declaration hook.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread openfeature/provider/_registry.py Outdated
Comment on lines +20 to +26
if "domain_scoped" in getattr(provider, "__dict__", {}):
return bool(provider.__dict__["domain_scoped"])
class_value = getattr(type(provider), "domain_scoped", False)
if isinstance(class_value, bool):
return class_value
return False

Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Jonathan Norris <jonathan.norris@dynatrace.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
openfeature/provider/_registry.py (1)

1-4: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Keep unittest.mock out of the registry runtime path. _initialize_accepts_domain still imports and branches on Mock during provider registration. If this is only for test doubles, move the compatibility shim into test helpers and rely on the callable signature in production code.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openfeature/provider/_registry.py` around lines 1 - 4, The registry runtime
path still depends on unittest.mock through the _initialize_accepts_domain logic
in _registry.py, which should be removed from production registration flow.
Update the provider registration path to rely on the callable signature
inspection in _initialize_accepts_domain without importing or branching on Mock,
and move any test-double compatibility handling into test-only helpers or
fixtures so the registry stays free of unittest.mock at runtime.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@openfeature/provider/_registry.py`:
- Around line 1-4: The registry runtime path still depends on unittest.mock
through the _initialize_accepts_domain logic in _registry.py, which should be
removed from production registration flow. Update the provider registration path
to rely on the callable signature inspection in _initialize_accepts_domain
without importing or branching on Mock, and move any test-double compatibility
handling into test-only helpers or fixtures so the registry stays free of
unittest.mock at runtime.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: af35abb4-85c5-4a13-a422-6f4002c375b5

📥 Commits

Reviewing files that changed from the base of the PR and between 79bb01b and ea8d686.

📒 Files selected for processing (5)
  • openfeature/provider/__init__.py
  • openfeature/provider/_registry.py
  • tests/legacy_init_provider.py
  • tests/provider/test_registry.py
  • tests/test_api.py

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.

Supply the bound domain to provider initialization

2 participants