Copy-constructing a derived config from a base instance requires explicitly re-stating pinned field defaults
- Dominant language
- Python
- Stars
- 1
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Description
**Summary**
When a derived config re-declares a base field as `Pinned[T]`, copy-constructing the derived config from a base instance raises a `ValueError` unless the user explicitly passes `field=Pinned` (or `field=Pin(value)`) at the call site. The derived class's `pdefaults` entry alone is not sufficient to suppress the error.
**Motivation**
The derived class has already declared the field as pinned and set its default via `pdefaults`. Requiring the user to re-state the pinned default at every call site is redundant and easy to forget, producing a confusing error with no indication of what went wrong.
**Proposed behavior**
When copy-constructing a derived config from a base instance, fields declared `Pinned[T]` in the derived class should not require the user to re-state the pinned default at the call site. The derived class's `pdefaults` entry should be sufficient.
This applies when the pinned field is managed by a `@pproperty`:
```python
from __future__ import annotations
from pconfigs import pconfig, pdefaults, Pinned, pproperty
@pconfig
class BaseConfig:
mode: str
pdefaults += BaseConfig(
mode='train',
)
@pconfig
class DerivedConfig(BaseConfig):
mode: Pinned[str]
@pproperty
def mode(self) -> str:
return 'eval'
pdefaults += DerivedConfig(
mode=Pinned,
)
print(DerivedConfig(BaseConfig()).mode) # raises ValueError ✗ — proposed: 'eval'
```
And when the pinned field uses a plain `Pin(value)` default:
```python
@pconfig
class DerivedConfig(BaseConfig):
mode: Pinned[str]
pdefaults += DerivedConfig(
mode=Pin('eval'),
)
print(DerivedConfig(BaseConfig()).mode) # raises ValueError ✗ — proposed: 'eval'
```
**Current workaround**
Pass the pinned default explicitly at the call site.
For the `@pproperty` case:
```python
config = DerivedConfig(
BaseConfig(),
mode=Pinned,
)
# ✓ returns 'eval'
```
For the `Pin(value)` case:
```python
config = DerivedConfig(
BaseConfig(),
mode=Pin(pdefaults(DerivedConfig).mode),
)
# ✓ returns 'eval'
```
Contributor guide
Research direction
Start by tracing copy construction from BaseConfig to DerivedConfig through the pconfig, Pinned, pdefaults, pproperty, and Pin entry points shown in the examples. Reproduce both the @pproperty and Pin(value) cases, then verify that DerivedConfig(BaseConfig()).mode uses the derived pdefaults value without requiring an explicit pinned argument.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100