Support Automatic Discovery Of `Domain.root_path`

by ADMIN 50 views

Summary

Goal: Remove the need for users to pass __file__ when instantiating a Domain, while still allowing an explicit override. Environments to support:

  • Standard Python execution (scripts, console entry-points)
  • Jupyter / IPython notebooks
  • REPL / interactive shell
  • Frozen / zipapp / PyInstaller builds

Problem Statement

In the current API:

from protean import Domain
domain = Domain(__file__)

Passing __file__ is often redundant — the Domain can infer where it’s being called from. Forgetting it leads to obscure downstream errors. Examples and docs become harder to read and teach.

Desired Behavior

domain = Domain()                      # Auto-detects root path
domain = Domain(root_path="path/to/root")        # Explicit override still allowed
  • Works across various execution contexts.
  • Falls back gracefully when no real file is available (e.g., notebook).

Proposed Implementation

1. Update Domain API

class Domain:
    def __init__(
        self,
        root_path: str = None,  # Optional
        name: str = "",
        config: Optional[Dict] = None,
        identity_function: Optional[Callable] = None,
    ):

2. Add helper to resolve calling file. Sample code:

from pathlib import Path
import sys

def _guess_caller_path() -> Path:
    frame = sys._getframe(2)
    filename = frame.f_code.co_filename

    if filename in {"<stdin>", "<ipython-input>", "<string>"}:
        return Path.cwd()

    return Path(filename).resolve()

3. Resolution priority

  1. Explicit root_path if provided
  2. _guess_caller_path()
  3. env.DOMAIN_ROOT_PATH
  4. Path.cwd() as last resort

Edge Cases

Case Handling
Incorrect manual path Raise InvalidDomainPathError as usual
Factories or wrappers Optionally walk up stack if needed
Interactive shell Fallback to Path.cwd()
Jupyter notebooks Treat <ipython-input> as interactive
Frozen apps Use executable path if sys.frozen is set

Acceptance Criteria:

  • [ ] Domain() works without arguments in standard scripts
  • [ ] Jupyter support with fallback to Path.cwd()
  • [ ] Explicit paths still work
  • [ ] Unit tests for multiple environments

References

  • Python’s sys._getframe, inspect.stack
  • Django’s AppConfig discovery
  • FastAPI config root inference
  • PEP 578

Open for Discussion and Feedback

Before implementation, we would like to gather feedback and discuss the proposed changes. Please share your thoughts on the following:

  • Do you think this change will improve the user experience?
  • Are there any potential edge cases that we may have missed?
  • Do you have any suggestions for the resolution priority?

Your input will help us create a better solution the Domain class.

Frequently Asked Questions

We've received several questions about the proposed change to support automatic discovery of Domain.root_path. Here are some answers to help clarify the discussion:

Q: Why do we need to support automatic discovery of Domain.root_path?

A: Passing __file__ is often redundant, and forgetting it leads to obscure downstream errors. By supporting automatic discovery, we can simplify the API and make it easier for users to get started.

Q: How will the automatic discovery work?

A: The automatic discovery will use the following resolution priority:

  1. Explicit root_path if provided
  2. _guess_caller_path() (a helper function that resolves the calling file)
  3. env.DOMAIN_ROOT_PATH
  4. Path.cwd() as last resort

Q: What about edge cases? How will we handle them?

A: We've identified several edge cases, including:

  • Incorrect manual path: Raise InvalidDomainPathError as usual
  • Factories or wrappers: Optionally walk up the stack if needed
  • Interactive shell: Fallback to Path.cwd()
  • Jupyter notebooks: Treat <ipython-input> as interactive
  • Frozen apps: Use executable path if sys.frozen is set

Q: How will this change affect existing code?

A: The change will not break existing code. However, users who are currently passing __file__ will no longer need to do so. If they want to override the automatic discovery, they can still do so by passing an explicit root_path.

Q: What about unit tests? How will we ensure that the change works correctly?

A: We will write unit tests to cover multiple environments, including standard Python execution, Jupyter notebooks, and frozen apps.

Q: Can you provide more information about the _guess_caller_path() function?

A: The _guess_caller_path() function uses sys._getframe(2) to resolve the calling file. If the filename is <stdin>, <ipython-input>, or <string>, it returns Path.cwd(). Otherwise, it returns the resolved path of the calling file.

Q: How will this change affect the Domain class?

A: The change will simplify the Domain class by removing the need for users to pass __file__. It will also make the class more robust by handling edge cases and providing a fallback for cases where the automatic discovery fails.

Q: What about the acceptance criteria? How will we ensure that the change meets the requirements?

A: We will use the following acceptance criteria to ensure that the change meets the requirements:

  • Domain() works without arguments in standard scripts
  • Jupyter support with fallback to Path.cwd()
  • Explicit paths still work
  • Unit tests for multiple environments

Join the Discussion

We're excited to hear your thoughts on this proposed change. Please share your feedback and questions in the comments below. Your input will help us create a better solution for the Domain class.