Skip to content

Commit

Permalink
[bugfix](config) set undeclared config field should not raise error (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
cosven authored Dec 1, 2024
1 parent 654a024 commit 8407eb8
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 10 deletions.
33 changes: 27 additions & 6 deletions feeluown/config.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from typing import Optional
import logging
import warnings
from collections import namedtuple
Expand All @@ -15,19 +16,33 @@ class Config:
用户可以在 rc 文件中配置各个选项的值
"""

def __init__(self):
def __init__(self, name: str = 'config', parent: Optional['Config'] = None):
object.__setattr__(self, '_name', name)
object.__setattr__(self, '_parent', parent)
object.__setattr__(self, '_fields', {})
object.__setattr__(self, '_undeclared_fields', {})

def __getattr__(self, name):
# tips: 这里不能用 getattr 来获取值, 否则会死循环
if name == '_fields':
return object.__getattribute__(self, '_fields')
if name in ('_fields', '_parent', '_name', '_undeclared_fields'):
return object.__getattribute__(self, name)
if name in self._fields:
try:
object.__getattribute__(self, name)
return object.__getattribute__(self, name)
except AttributeError:
return self._fields[name].default
return object.__getattribute__(self, name)
elif name in self._undeclared_fields:
return self._undeclared_fields[name]

# Requirement:
# User may define config like
# app.plugin.X = Y
# When 'plugin' is not installed, such config should not raise an error.
# To achieve this, return a subconfig when accessing an undeclared key.
logger.warning(f'Undeclared subconfig: {self.fullname}.{name}')
tmpconfig = Config(name=name, parent=self)
self._undeclared_fields[name] = tmpconfig
return tmpconfig

def __setattr__(self, name, value):
if name in self._fields:
Expand All @@ -38,7 +53,13 @@ def __setattr__(self, name, value):
# TODO: 校验值类型
object.__setattr__(self, name, value)
else:
logger.warning('Assign to an undeclared config key.')
logger.warning(f'Assign to an undeclared config key: {self.fullname}.{name}')

@property
def fullname(self) -> str:
if self._parent is None:
return self._name
return f'{self._parent.fullname}.{self._name}'

def deffield(self, name, type_=None, default=None, desc='', warn=None):
"""Define a configuration field
Expand Down
2 changes: 1 addition & 1 deletion feeluown/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def init_config(self, config: Config):
.. versionadded: 3.7.15
"""
myconfig = Config()
myconfig = Config(name=self.name, parent=config)

names = [self.name]
# Currently, plugin name looks like fuo_xxx and xxx is the real name.
Expand Down
10 changes: 7 additions & 3 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ def test_config_set_known_field(config):


def test_config_set_unknown_field(config):
"""Set unknown field should cause no effects"""
"""
Set unknown field should cause no effects.
Access unknown field should return a Config object.
"""
config.hey = 0
with pytest.raises(AttributeError):
config.hey
assert isinstance(config.hey, Config)
config.plugin.X = 0 # should not raise error
assert isinstance(config.plugin.X, Config)


def test_config_set_subconfig(config):
Expand Down

0 comments on commit 8407eb8

Please sign in to comment.