Skip to content

Commit

Permalink
Fix interpolation to structured config from within typed list
Browse files Browse the repository at this point in the history
This PR closes #1005, fixing a bug where a validation error was raised
when an interpolation points to a structured config from within a typed
list.

```python
from dataclasses import dataclass, field
from omegaconf import MISSING, OmegaConf

@DataClass
class User:
    name: str = MISSING

@DataClass
class Config:
    user: User = User("John")
    users: list[User] = field(default_factory=lambda: ["${user}"])

cfg = OmegaConf.structured(Config)
OmegaConf.resolve(cfg)
print(cfg)
```
BEFORE
------
```text
$ python repro.py
$ python repro.py
Traceback (most recent call last):
  ...
omegaconf.errors.ValidationError: Invalid type assigned: str is not a subclass of User. value: ${user}
    full_key: users[0]
    reference_type=List[User]
    object_type=list
```
AFTER
-----
```text
$ python repro.py
{'user': {'name': 'John'}, 'users': [{'name': 'John'}]}
```
  • Loading branch information
Jasha10 committed Nov 7, 2023
1 parent ceec6f4 commit 0612c1c
Show file tree
Hide file tree
Showing 5 changed files with 12 additions and 0 deletions.
2 changes: 2 additions & 0 deletions omegaconf/listconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ def _validate_set(self, key: Any, value: Any) -> None:
vk = get_value_kind(value)
if vk == ValueKind.MANDATORY_MISSING:
return
if vk == ValueKind.INTERPOLATION:
return
else:
is_optional, target_type = _resolve_optional(self._metadata.element_type)
value_type = OmegaConf.get_type(value)
Expand Down
2 changes: 2 additions & 0 deletions tests/structured_conf/data/attr_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ class OptionalUser:
class InterpolationToUser:
user: User = User("Bond", 7)
admin: User = II("user")
admin_list: List[User] = [II("user")]
admin_dict: Dict[str, User] = {"bond": II("user")}


@attr.s(auto_attribs=True)
Expand Down
2 changes: 2 additions & 0 deletions tests/structured_conf/data/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ class OptionalUser:
class InterpolationToUser:
user: User = field(default_factory=lambda: User("Bond", 7))
admin: User = II("user")
admin_list: List[User] = field(default_factory=lambda: [II("user")])
admin_dict: Dict[str, User] = field(default_factory=lambda: {"bond": II("user")})


@dataclass
Expand Down
2 changes: 2 additions & 0 deletions tests/structured_conf/data/dataclasses_pre_311.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ class OptionalUser:
class InterpolationToUser:
user: User = User("Bond", 7)
admin: User = II("user")
admin_list: List[User] = field(default_factory=lambda: [II("user")])
admin_dict: Dict[str, User] = field(default_factory=lambda: {"bond": II("user")})


@dataclass
Expand Down
4 changes: 4 additions & 0 deletions tests/structured_conf/test_structured_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,10 @@ def test_interpolation_to_structured(self, module: Any, resolve: bool) -> None:
OmegaConf.resolve(cfg)
assert OmegaConf.get_type(cfg.admin) is module.User
assert cfg.admin == {"name": "Bond", "age": 7}
assert OmegaConf.get_type(cfg.admin_list[0]) is module.User
assert cfg.admin_list == [{"name": "Bond", "age": 7}]
assert OmegaConf.get_type(cfg.admin_dict["bond"]) is module.User
assert cfg.admin_dict == {"bond": {"name": "Bond", "age": 7}}

class TestMissing:
def test_missing1(self, module: Any) -> None:
Expand Down

0 comments on commit 0612c1c

Please sign in to comment.