Support Automatic Discovery Of `Domain.root_path`
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
- Explicit
root_path
if provided _guess_caller_path()
env.DOMAIN_ROOT_PATH
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:
- Explicit
root_path
if provided _guess_caller_path()
(a helper function that resolves the calling file)env.DOMAIN_ROOT_PATH
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.