Skip to content

Commit 4bf31d5

Browse files
EwoutHmaartenbreddels
authored andcommitted
Fix Python 3.14 compatibility: RuntimeError in BaseSettings iteration
Python 3.14 enforces stricter rules around dictionary mutation during iteration. The BaseSettings class in minisettings.py was modifying cls.__dict__ via setattr() while iterating over it, causing a RuntimeError. Fixed by creating a snapshot of dictionary items before iteration in both __init_subclass__ and __init__ methods using list(cls.__dict__.items()). Fixes the import error: RuntimeError: dictionary changed size during iteration
1 parent 7cedc74 commit 4bf31d5

File tree

1 file changed

+22
-26
lines changed

1 file changed

+22
-26
lines changed

solara/minisettings.py

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -101,37 +101,33 @@ def __init__(self, **kwargs) -> None:
101101
cls = type(self)
102102
self._values = {**kwargs}
103103
keys = {k.upper() for k in os.environ.keys()}
104-
for key, field in cls.__dict__.items():
105-
if key in kwargs:
104+
105+
for key, field in list(cls.__dict__.items()):
106+
if key in kwargs or not isinstance(field, _Field):
106107
continue
107-
if isinstance(field, _Field):
108-
value = field.default
109-
if field.default_factory:
110-
value = field.default_factory()
111-
112-
if field.env:
113-
env_key = field.env.upper()
114-
if env_key in keys:
115-
# do a case-insensitive lookup
116-
for env_var_cased in os.environ.keys():
117-
if env_key.upper() == env_var_cased.upper():
118-
value = convert(field.annotation, os.environ[env_var_cased])
119-
self._values[key] = value
108+
value = field.default
109+
if field.default_factory:
110+
value = field.default_factory()
111+
if field.env:
112+
env_key = field.env.upper()
113+
if env_key in keys:
114+
for env_var_cased in os.environ.keys():
115+
if env_key.upper() == env_var_cased.upper():
116+
value = convert(field.annotation, os.environ[env_var_cased])
117+
self._values[key] = value
120118

121119
def __init_subclass__(cls) -> None:
122120
cls.__fields__ = {}
123-
for key, field in cls.__dict__.items():
124-
if key.startswith("_"):
125-
continue
126-
if key == "Config":
121+
items = list(cls.__dict__.items())
122+
123+
for key, value in items:
124+
if key.startswith("_") or key == "Config" or inspect.isfunction(value):
127125
continue
128-
if not isinstance(field, _Field):
129-
if inspect.isfunction(field):
130-
continue
131-
field = Field(field)
132-
setattr(cls, key, field)
133-
field.__set_name__(cls, key)
134-
cls.__fields__[key] = field
126+
if not isinstance(value, _Field):
127+
value = Field(value)
128+
setattr(cls, key, value)
129+
value.__set_name__(cls, key)
130+
cls.__fields__[key] = value
135131

136132
def dict(self, by_alias=True):
137133
values = self._values.copy()

0 commit comments

Comments
 (0)