fix iniparse
lint / docker (push) Successful in 9m14s

fix code passing ruff linter
pre-commit ruff
pre-commit ruff format
This commit is contained in:
2024-11-29 23:45:40 +01:00
parent aa8a68aa80
commit 737f9bea38
27 changed files with 2375 additions and 2016 deletions
+70 -53
View File
@@ -1,4 +1,10 @@
class ConfigNamespace(object):
from typing import Dict, Iterable, List, TextIO, Union, TYPE_CHECKING
if TYPE_CHECKING:
from .ini import INIConfig, INISection
class ConfigNamespace:
"""Abstract class representing the interface of Config objects.
A ConfigNamespace is a collection of names mapped to values, where
@@ -12,27 +18,27 @@ class ConfigNamespace(object):
Subclasses must implement the methods for container-like access,
and this class will automatically provide dotted access.
"""
# Methods that must be implemented by subclasses
def _getitem(self, key):
def _getitem(self, key: str) -> object:
return NotImplementedError(key)
def __setitem__(self, key, value):
def __setitem__(self, key: str, value: object):
raise NotImplementedError(key, value)
def __delitem__(self, key):
def __delitem__(self, key: str) -> None:
raise NotImplementedError(key)
def __iter__(self):
def __iter__(self) -> Iterable[str]:
# FIXME Raise instead return
return NotImplementedError()
def _new_namespace(self, name):
def _new_namespace(self, name: str) -> "ConfigNamespace":
raise NotImplementedError(name)
def __contains__(self, key):
def __contains__(self, key: str) -> bool:
try:
self._getitem(key)
except KeyError:
@@ -44,35 +50,35 @@ class ConfigNamespace(object):
#
# To distinguish between accesses of class members and namespace
# keys, we first call object.__getattribute__(). If that succeeds,
# the name is assumed to be a class member. Otherwise it is
# the name is assumed to be a class member. Otherwise, it is
# treated as a namespace key.
#
# Therefore, member variables should be defined in the class,
# not just in the __init__() function. See BasicNamespace for
# an example.
def __getitem__(self, key):
def __getitem__(self, key: str) -> Union[object, "Undefined"]:
try:
return self._getitem(key)
except KeyError:
return Undefined(key, self)
def __getattr__(self, name):
def __getattr__(self, name: str) -> Union[object, "Undefined"]:
try:
return self._getitem(name)
except KeyError:
if name.startswith('__') and name.endswith('__'):
if name.startswith("__") and name.endswith("__"):
raise AttributeError
return Undefined(name, self)
def __setattr__(self, name, value):
def __setattr__(self, name: str, value: object) -> None:
try:
object.__getattribute__(self, name)
object.__setattr__(self, name, value)
except AttributeError:
self.__setitem__(name, value)
def __delattr__(self, name):
def __delattr__(self, name: str) -> None:
try:
object.__getattribute__(self, name)
object.__delattr__(self, name)
@@ -82,12 +88,12 @@ class ConfigNamespace(object):
# During unpickling, Python checks if the class has a __setstate__
# method. But, the data dicts have not been initialised yet, which
# leads to _getitem and hence __getattr__ raising an exception. So
# we explicitly impement default __setstate__ behavior.
def __setstate__(self, state):
# we explicitly implement default __setstate__ behavior.
def __setstate__(self, state: dict) -> None:
self.__dict__.update(state)
class Undefined(object):
class Undefined:
"""Helper class used to hold undefined names until assignment.
This class helps create any undefined subsections when an
@@ -95,21 +101,24 @@ class Undefined(object):
statement is "cfg.a.b.c = 42", but "cfg.a.b" does not exist yet.
"""
def __init__(self, name, namespace):
object.__setattr__(self, 'name', name)
object.__setattr__(self, 'namespace', namespace)
def __init__(self, name: str, namespace: ConfigNamespace):
# FIXME These assignments into `object` feel very strange.
# What's the reason for it?
object.__setattr__(self, "name", name)
object.__setattr__(self, "namespace", namespace)
def __setattr__(self, name, value):
def __setattr__(self, name: str, value: object) -> None:
obj = self.namespace._new_namespace(self.name)
obj[name] = value
def __setitem__(self, name, value):
def __setitem__(self, name, value) -> None:
obj = self.namespace._new_namespace(self.name)
obj[name] = value
# ---- Basic implementation of a ConfigNamespace
class BasicConfig(ConfigNamespace):
"""Represents a hierarchical collection of named values.
@@ -161,7 +170,7 @@ class BasicConfig(ConfigNamespace):
Finally, values can be read from a file as follows:
>>> from six import StringIO
>>> from io import StringIO
>>> sio = StringIO('''
... # comment
... ui.height = 100
@@ -181,66 +190,73 @@ class BasicConfig(ConfigNamespace):
"""
# this makes sure that __setattr__ knows this is not a namespace key
_data = None
_data: Dict[str, str] = None
def __init__(self):
self._data = {}
def _getitem(self, key):
def _getitem(self, key: str) -> str:
return self._data[key]
def __setitem__(self, key, value):
def __setitem__(self, key: str, value: object) -> None:
# FIXME We can add any object as 'value', but when an integer is read
# from a file, it will be a string. Should we explicitly convert
# this 'value' to string, to ensure consistency?
# It will stay the original type until it is written to a file.
self._data[key] = value
def __delitem__(self, key):
def __delitem__(self, key: str) -> None:
del self._data[key]
def __iter__(self):
def __iter__(self) -> Iterable[str]:
return iter(self._data)
def __str__(self, prefix=''):
lines = []
keys = list(self._data.keys())
def __str__(self, prefix: str = "") -> str:
lines: List[str] = []
keys: List[str] = list(self._data.keys())
keys.sort()
for name in keys:
value = self._data[name]
value: object = self._data[name]
if isinstance(value, ConfigNamespace):
lines.append(value.__str__(prefix='%s%s.' % (prefix,name)))
lines.append(value.__str__(prefix="%s%s." % (prefix, name)))
else:
if value is None:
lines.append('%s%s' % (prefix, name))
lines.append("%s%s" % (prefix, name))
else:
lines.append('%s%s = %s' % (prefix, name, value))
return '\n'.join(lines)
lines.append("%s%s = %s" % (prefix, name, value))
return "\n".join(lines)
def _new_namespace(self, name):
def _new_namespace(self, name: str) -> "BasicConfig":
obj = BasicConfig()
self._data[name] = obj
return obj
def _readfp(self, fp):
def _readfp(self, fp: TextIO) -> None:
while True:
line = fp.readline()
line: str = fp.readline()
if not line:
break
line = line.strip()
if not line: continue
if line[0] == '#': continue
data = line.split('=', 1)
if not line:
continue
if line[0] == "#":
continue
data: List[str] = line.split("=", 1)
if len(data) == 1:
name = line
value = None
else:
name = data[0].strip()
value = data[1].strip()
name_components = name.split('.')
ns = self
name_components = name.split(".")
ns: ConfigNamespace = self
for n in name_components[:-1]:
if n in ns:
ns = ns[n]
if not isinstance(ns, ConfigNamespace):
raise TypeError('value-namespace conflict', n)
maybe_ns: object = ns[n]
if not isinstance(maybe_ns, ConfigNamespace):
raise TypeError("value-namespace conflict", n)
ns = maybe_ns
else:
ns = ns._new_namespace(n)
ns[name_components[-1]] = value
@@ -248,7 +264,8 @@ class BasicConfig(ConfigNamespace):
# ---- Utility functions
def update_config(target, source):
def update_config(target: ConfigNamespace, source: ConfigNamespace):
"""Imports values from source into target.
Recursively walks the <source> ConfigNamespace and inserts values
@@ -276,15 +293,15 @@ def update_config(target, source):
display_clock = True
display_qlength = True
width = 150
"""
for name in sorted(source):
value = source[name]
value: object = source[name]
if isinstance(value, ConfigNamespace):
if name in target:
myns = target[name]
if not isinstance(myns, ConfigNamespace):
raise TypeError('value-namespace conflict')
maybe_myns: object = target[name]
if not isinstance(maybe_myns, ConfigNamespace):
raise TypeError("value-namespace conflict")
myns = maybe_myns
else:
myns = target._new_namespace(name)
update_config(myns, value)