public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-commits] proj/pkgcore/pkgcore:master commit in: src/pkgcore/config/
@ 2023-01-17 20:50 Arthur Zamarin
  0 siblings, 0 replies; 4+ messages in thread
From: Arthur Zamarin @ 2023-01-17 20:50 UTC (permalink / raw
  To: gentoo-commits

commit:     8384c20cc1b33a31ff1e49e61a4547c109b0d8e4
Author:     Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Sun Jan 15 05:09:42 2023 +0000
Commit:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Tue Jan 17 20:49:46 2023 +0000
URL:        https://gitweb.gentoo.org/proj/pkgcore/pkgcore.git/commit/?id=8384c20c

fix(config): add exception chaining for an internal error.

This code was written before exception chaining, but it's esoteric
enough we very much want the chain to be thrown.

Signed-off-by: Brian Harring <ferringb <AT> gmail.com>
Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org>

 src/pkgcore/config/basics.py | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/pkgcore/config/basics.py b/src/pkgcore/config/basics.py
index 2bc3e1c9b..91e16dabf 100644
--- a/src/pkgcore/config/basics.py
+++ b/src/pkgcore/config/basics.py
@@ -86,13 +86,12 @@ class ConfigType:
         if not getattr(hint_overrides, "authorative", None):
             try:
                 code = getattr(func_obj, "__code__")
-            except AttributeError:
+            except AttributeError as e:
                 if func_obj != object.__init__:
                     raise TypeError(
-                        "func %s has no %r attribute; likely a "
+                        f"func {original_func_obj!r} isn't usable; likely a "
                         "builtin object which can't be introspected without hints"
-                        % (original_func_obj, "__code__")
-                    )
+                    ) from e
             else:
                 if code.co_argcount and code.co_varnames[0] == "self":
                     args = code.co_varnames[1 : code.co_argcount]


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [gentoo-commits] proj/pkgcore/pkgcore:master commit in: src/pkgcore/config/
@ 2023-02-02 20:10 Arthur Zamarin
  0 siblings, 0 replies; 4+ messages in thread
From: Arthur Zamarin @ 2023-02-02 20:10 UTC (permalink / raw
  To: gentoo-commits

commit:     c36abcbef28a151c464ed3176672a5cac4aa2b24
Author:     Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Sun Jan 15 21:28:11 2023 +0000
Commit:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Thu Feb  2 19:59:11 2023 +0000
URL:        https://gitweb.gentoo.org/proj/pkgcore/pkgcore.git/commit/?id=c36abcbe

refactor(config): simplify render_prepends signature.

The flatten argument is purely type specific, and there are no
users of this function beyond one internal site.  Thus simplify
the API (to make typing easier), and to make future refactoring easier.

Signed-off-by: Brian Harring <ferringb <AT> gmail.com>
Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org>

 src/pkgcore/config/central.py | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/pkgcore/config/central.py b/src/pkgcore/config/central.py
index b142963b4..128538364 100644
--- a/src/pkgcore/config/central.py
+++ b/src/pkgcore/config/central.py
@@ -8,6 +8,7 @@ __all__ = (
     "ConfigManager",
 )
 
+import typing
 import weakref
 from collections import defaultdict, deque, namedtuple
 from itertools import chain
@@ -75,7 +76,7 @@ class _ConfigStack(defaultdict):
             return val
         return None
 
-    def render_prepends(self, manager, key, type_name, flatten=True):
+    def render_prepends(self, manager, key: str, type_name: str) -> list[typing.Any]:
         results = []
         # keep in mind that the sequence we get is a top -> bottom walk of the config
         # as such for this operation we have to reverse it when building the content-
@@ -94,7 +95,7 @@ class _ConfigStack(defaultdict):
             if append:
                 results += [append]
 
-        if flatten:
+        if type_name != "str":
             results = chain.from_iterable(results)
         return list(results)
 
@@ -544,9 +545,7 @@ class ConfigManager:
                 typename = typename[5:]
 
             if typename.startswith("refs:") or typename in ("list", "str"):
-                result = config_stack.render_prepends(
-                    self, key, typename, flatten=(typename != "str")
-                )
+                result = config_stack.render_prepends(self, key, typename)
                 if typename == "str":
                     result = " ".join(result)
             else:


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [gentoo-commits] proj/pkgcore/pkgcore:master commit in: src/pkgcore/config/
@ 2023-02-02 20:10 Arthur Zamarin
  0 siblings, 0 replies; 4+ messages in thread
From: Arthur Zamarin @ 2023-02-02 20:10 UTC (permalink / raw
  To: gentoo-commits

commit:     d81d312e7f1d42d420cbff58066788eb03748886
Author:     Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Mon Jan 16 02:59:48 2023 +0000
Commit:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Thu Feb  2 19:59:11 2023 +0000
URL:        https://gitweb.gentoo.org/proj/pkgcore/pkgcore.git/commit/?id=d81d312e

refactor(config): first pass of typing annotations for pkgcore.config.*

In a few spots (optionxform for example), I'm realigning
variable names w/ the class we're overriding.  No changes
visible for consuming code for that.

For the rest, I'm annotating the types, but this is the first step.
Specifically:
1) arg_type needs to be an enum (api change) to constrict the allowed values
2) multiple protocols need to be created to enumerate the expected interfaces
   for what's being passed in (central/config manager for example)
3) this first pass of type annotations is already flushing out code impedence.
   See pkgcore/ferringb#41 for an example.

Signed-off-by: Brian Harring <ferringb <AT> gmail.com>
Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org>

 src/pkgcore/config/__init__.py | 17 ++++----
 src/pkgcore/config/basics.py   | 79 +++++++++++++++++++++++-------------
 src/pkgcore/config/central.py  | 91 ++++++++++++++++++++++++++----------------
 src/pkgcore/config/cparser.py  | 10 +++--
 4 files changed, 123 insertions(+), 74 deletions(-)

diff --git a/src/pkgcore/config/__init__.py b/src/pkgcore/config/__init__.py
index 50ac571b9..83e58a568 100644
--- a/src/pkgcore/config/__init__.py
+++ b/src/pkgcore/config/__init__.py
@@ -7,21 +7,22 @@ __all__ = ("load_config",)
 # actually needed
 
 import os
+import typing
 
 from .. import const
 from . import central, cparser
 
 
 def load_config(
-    user_conf_file=const.USER_CONF_FILE,
-    system_conf_file=const.SYSTEM_CONF_FILE,
-    debug=False,
+    user_conf_file: typing.Optional[str] = const.USER_CONF_FILE,
+    system_conf_file: typing.Optional[str] = const.SYSTEM_CONF_FILE,
+    debug: bool = False,
     prepend_sources=(),
-    skip_config_files=False,
-    profile_override=None,
-    location=None,
+    skip_config_files: bool = False,
+    profile_override: typing.Optional[str] = None,
+    location: typing.Optional[str] = None,
     **kwargs
-):
+) -> central.CompatConfigManager:
     """The main entry point for any code looking to use pkgcore.
 
     Args:
@@ -29,7 +30,7 @@ def load_config(
         system_conf_file (optional[str]): system pkgcore config file path
         profile_override (optional[str]): targeted profile instead of system setting
         location (optional[str]): path to pkgcore config file or portage config directory
-        skip_config_files (optional[str]): don't attempt to load any config files
+        skip_config_files (bool): don't attempt to load any config files
 
     Returns:
         :obj:`pkgcore.config.central.ConfigManager` instance: system config

diff --git a/src/pkgcore/config/basics.py b/src/pkgcore/config/basics.py
index 0ff78c784..7576e7182 100644
--- a/src/pkgcore/config/basics.py
+++ b/src/pkgcore/config/basics.py
@@ -22,6 +22,7 @@ __all__ = (
     "parse_config_file",
 )
 
+import typing
 from functools import partial
 
 from snakeoil import modules
@@ -50,7 +51,13 @@ class ConfigType:
     :ivar allow_unknowns: controls whether unknown settings should error.
     """
 
-    def __init__(self, func_obj):
+    callable: typing.Callable
+    types: dict[str, str]
+    positional: tuple[str]
+    required: tuple[str]
+    allow_unknowns: bool
+
+    def __init__(self, func_obj: typing.Callable) -> None:
         """Create from a callable (function, member function, class).
 
         It uses the defaults to determine type:
@@ -77,6 +84,8 @@ class ConfigType:
         # need without it. Most of the code in its getargs function
         # deals with tuples inside argument definitions, which we do
         # not support anyway.
+        #
+        # TODO: use the inspect module, speed is less of an issue in 2023.
         self.types = {}
 
         varargs, args, defaults, varkw = (), (), (), ()
@@ -165,7 +174,9 @@ class ConfigType:
 class LazySectionRef:
     """Abstract base class for lazy-loaded section references."""
 
-    def __init__(self, central, typename):
+    typename: str
+
+    def __init__(self, central, typename: str) -> None:
         self.central = central
         self.typename = typename.split(":", 1)[1]
         self.cached_config = None
@@ -185,13 +196,15 @@ class LazySectionRef:
                 )
         return self.cached_config
 
-    def instantiate(self):
+    def instantiate(self) -> typing.Any:
         """Convenience method returning the instantiated section."""
         return self.collapse().instantiate()
 
 
 class LazyNamedSectionRef(LazySectionRef):
-    def __init__(self, central, typename, name):
+    name: str
+
+    def __init__(self, central, typename: str, name: str) -> None:
         super().__init__(central, typename)
         self.name = name
 
@@ -200,7 +213,7 @@ class LazyNamedSectionRef(LazySectionRef):
 
 
 class LazyUnnamedSectionRef(LazySectionRef):
-    def __init__(self, central, typename, section):
+    def __init__(self, central, typename: str, section) -> None:
         super().__init__(central, typename)
         self.section = section
 
@@ -215,15 +228,15 @@ class ConfigSection:
     be an Interface.
     """
 
-    def __contains__(self, name):
+    def __contains__(self, name: str) -> bool:
         """Check if a key is in this section."""
         raise NotImplementedError(self.__contains__)
 
-    def keys(self):
+    def keys(self) -> list[str]:
         """Return a list of keys."""
         raise NotImplementedError(self.keys)
 
-    def render_value(self, central, name, arg_type):
+    def render_value(self, central, name: str, arg_type):
         """Return a setting, converted to the requested type."""
         raise NotImplementedError(self, "render_value")
 
@@ -231,7 +244,10 @@ class ConfigSection:
 class DictConfigSection(ConfigSection):
     """Turns a dict and a conversion function into a ConfigSection."""
 
-    def __init__(self, conversion_func, source_dict):
+    func: typing.Callable
+    dict: dict[str, typing.Any]
+
+    def __init__(self, conversion_func, source_dict: dict[str, typing.Any]) -> None:
         """Initialize.
 
         :type conversion_func: callable.
@@ -243,13 +259,13 @@ class DictConfigSection(ConfigSection):
         self.func = conversion_func
         self.dict = source_dict
 
-    def __contains__(self, name):
+    def __contains__(self, name: str) -> bool:
         return name in self.dict
 
-    def keys(self):
+    def keys(self) -> list[str]:
         return list(self.dict.keys())
 
-    def render_value(self, central, name, arg_type):
+    def render_value(self, central, name: str, arg_type: str):
         try:
             return self.func(central, self.dict[name], arg_type)
         except IGNORED_EXCEPTIONS:
@@ -263,7 +279,12 @@ class DictConfigSection(ConfigSection):
 class FakeIncrementalDictConfigSection(ConfigSection):
     """Turns a dict and a conversion function into a ConfigSection."""
 
-    def __init__(self, conversion_func, source_dict):
+    func: typing.Callable
+    dict: dict[str, typing.Any]
+
+    def __init__(
+        self, conversion_func: typing.Callable, source_dict: dict[str, typing.Any]
+    ) -> None:
         """Initialize.
 
         A request for a section of a list type will look for
@@ -281,14 +302,14 @@ class FakeIncrementalDictConfigSection(ConfigSection):
         self.func = conversion_func
         self.dict = source_dict
 
-    def __contains__(self, name):
+    def __contains__(self, name: str) -> bool:
         return (
             name in self.dict
             or name + ".append" in self.dict
             or name + ".prepend" in self.dict
         )
 
-    def keys(self):
+    def keys(self) -> list[str]:
         keys = set()
         for key in self.dict:
             if key.endswith(".append"):
@@ -298,7 +319,7 @@ class FakeIncrementalDictConfigSection(ConfigSection):
             keys.add(key)
         return list(keys)
 
-    def render_value(self, central, name, arg_type):
+    def render_value(self, central, name: str, arg_type: str):
         # Check if we need our special incremental magic.
         if arg_type in ("list", "str", "repr") or arg_type.startswith("refs:"):
             result = []
@@ -418,8 +439,9 @@ class FakeIncrementalDictConfigSection(ConfigSection):
             ) from e
 
 
-def str_to_list(string):
+def str_to_list(string: str) -> list[str]:
     """Split on whitespace honoring quoting for new tokens."""
+    # TODO: replace this with shlex or equivalent parsing.
     l = []
     i = 0
     e = len(string)
@@ -455,15 +477,16 @@ def str_to_list(string):
     return l
 
 
-def str_to_str(string):
+def str_to_str(string: str) -> str:
     """Yank leading/trailing whitespace and quotation, along with newlines."""
+    # TODO: replace these with shlex
     s = string.strip()
     if len(s) > 1 and s[0] in "\"'" and s[0] == s[-1]:
         s = s[1:-1]
     return s.replace("\n", " ").replace("\t", " ")
 
 
-def str_to_bool(string):
+def str_to_bool(string: str) -> bool:
     """Convert a string to a boolean."""
     s = str_to_str(string).lower()
     if s in ("no", "false", "0"):
@@ -473,7 +496,7 @@ def str_to_bool(string):
     raise errors.ConfigurationError(f"{s!r} is not a boolean")
 
 
-def str_to_int(string):
+def str_to_int(string: str) -> int:
     """Convert a string to a integer."""
     string = str_to_str(string)
     try:
@@ -490,7 +513,7 @@ _str_converters = {
 }
 
 
-def convert_string(central, value, arg_type):
+def convert_string(central, value, arg_type: str):
     """Conversion func for a string-based DictConfigSection."""
     if not isinstance(value, str):
         raise ValueError(
@@ -519,7 +542,7 @@ def convert_string(central, value, arg_type):
     return func(value)
 
 
-def convert_asis(central, value, arg_type):
+def convert_asis(central, value, arg_type: str):
     """ "Conversion" func assuming the types are already correct."""
     if arg_type == "callable":
         if not callable(value):
@@ -558,7 +581,7 @@ def convert_asis(central, value, arg_type):
     return value
 
 
-def convert_hybrid(central, value, arg_type):
+def convert_hybrid(central, value, arg_type: str):
     """Automagically switch between :obj:`convert_string` and :obj:`convert_asis`.
 
     :obj:`convert_asis` is used for arg_type str and if value is not a string.
@@ -579,7 +602,7 @@ ConfigSectionFromStringDict = partial(FakeIncrementalDictConfigSection, convert_
 AutoConfigSection = partial(FakeIncrementalDictConfigSection, convert_hybrid)
 
 
-def section_alias(target, typename):
+def section_alias(target, typename: str) -> AutoConfigSection:
     """Build a ConfigSection that instantiates a named reference.
 
     Because of central's caching our instantiated value will be
@@ -594,11 +617,11 @@ def section_alias(target, typename):
 
 
 @configurable(types={"path": "str", "parser": "callable"}, typename="configsection")
-def parse_config_file(path, parser):
+def parse_config_file(path: str, parser):
     try:
         f = open(path, "r")
-    except (IOError, OSError):
-        raise errors.InstantiationError(f"failed opening {path!r}")
+    except (IOError, OSError) as e:
+        raise errors.InstantiationError(f"failed opening {path!r}") from e
     try:
         return parser(f)
     finally:
@@ -614,7 +637,7 @@ class ConfigSource:
 
 
 class GeneratedConfigSource(ConfigSource):
-    def __init__(self, section_data, description):
+    def __init__(self, section_data, description: str) -> None:
         self.description = description
         self.section_data = section_data
 

diff --git a/src/pkgcore/config/central.py b/src/pkgcore/config/central.py
index 128538364..53609221b 100644
--- a/src/pkgcore/config/central.py
+++ b/src/pkgcore/config/central.py
@@ -33,18 +33,20 @@ class _ConfigMapping(mappings.DictMixin):
     any of them are remote!
     """
 
-    def __init__(self, manager, typename):
+    typename: str
+
+    def __init__(self, manager, typename: str) -> None:
         super().__init__()
         self.manager = manager
         self.typename = typename
 
-    def __getitem__(self, key):
+    def __getitem__(self, key: str) -> typing.Any:
         conf = self.manager.collapse_named_section(key, raise_on_missing=False)
         if conf is None or conf.type.name != self.typename:
             raise KeyError(key)
         return conf.instantiate()
 
-    def keys(self):
+    def keys(self) -> typing.Iterator[str]:
         for name in self.manager.sections():
             try:
                 collapsed = self.manager.collapse_named_section(name)
@@ -57,26 +59,31 @@ class _ConfigMapping(mappings.DictMixin):
                 if collapsed.type.name == self.typename:
                     yield name
 
-    def __contains__(self, key):
+    def __contains__(self, key: str) -> bool:
         conf = self.manager.collapse_named_section(key, raise_on_missing=False)
         return conf is not None and conf.type.name == self.typename
 
 
-class _ConfigStack(defaultdict):
-    def __init__(self):
+class _ConfigStack(defaultdict[str, list[typing.Any]]):
+    def __init__(self) -> None:
         super().__init__(list)
 
-    def render_vals(self, manager, key, type_name):
+    def render_vals(
+        self, manager, key: str, type_name: str
+    ) -> typing.Iterator[typing.Any]:
         for data in self.get(key, ()):
             if key in data.section:
                 yield data.section.render_value(manager, key, type_name)
 
-    def render_val(self, manager, key, type_name):
+    def render_val(
+        self, manager, key: str, type_name: str
+    ) -> typing.Optional[typing.Any]:
         for val in self.render_vals(manager, key, type_name):
             return val
         return None
 
     def render_prepends(self, manager, key: str, type_name: str) -> list[typing.Any]:
+
         results = []
         # keep in mind that the sequence we get is a top -> bottom walk of the config
         # as such for this operation we have to reverse it when building the content-
@@ -96,8 +103,8 @@ class _ConfigStack(defaultdict):
                 results += [append]
 
         if type_name != "str":
-            results = chain.from_iterable(results)
-        return list(results)
+            results = list(chain.from_iterable(results))
+        return results
 
 
 class CollapsedConfig:
@@ -114,7 +121,19 @@ class CollapsedConfig:
     :ivar name: our section name or C{None} for an anonymous section.
     """
 
-    def __init__(self, type_obj, config, manager, debug=False, default=False):
+    type_obj: basics.ConfigType
+    config: dict[str, typing.Any]
+    debug: bool
+    default: bool
+
+    def __init__(
+        self,
+        type_obj: basics.ConfigType,
+        config: dict[str, typing.Any],
+        manager,
+        debug: bool = False,
+        default: bool = False,
+    ) -> None:
         """Initialize instance vars."""
         # Check if we got all values required to instantiate.
         missing = set(type_obj.required) - set(config)
@@ -137,7 +156,7 @@ class CollapsedConfig:
             manager = weakref.ref(manager)
         self.manager = manager
 
-    def instantiate(self):
+    def instantiate(self) -> typing.Any:
         if self._instance is None:
             try:
                 self._instance = self._instantiate()
@@ -147,7 +166,7 @@ class CollapsedConfig:
                 raise errors.InstantiationError(self.name) from e
         return self._instance
 
-    def _instantiate(self):
+    def _instantiate(self) -> typing.Any:
         """Call our type's callable, cache and return the result.
 
         Calling instantiate more than once will return the cached value.
@@ -232,13 +251,13 @@ class CollapsedConfig:
 
         return self._instance
 
-    def __getstate__(self):
+    def __getstate__(self) -> dict[str, typing.Any]:
         d = self.__dict__.copy()
         # pull actual value from weakref
         d["manager"] = d["manager"]()
         return d
 
-    def __setstate__(self, state):
+    def __setstate__(self, state: dict[str, typing.Any]) -> None:
         self.__dict__ = state.copy()
         # reset weakref
         self.__dict__["manager"] = weakref.ref(self.__dict__["manager"])
@@ -248,29 +267,29 @@ class _ConfigObjMap:
     def __init__(self, manager):
         self._manager = manager
 
-    def __getattr__(self, attr):
+    def __getattr__(self, attr: str) -> typing.Any:
         return _ConfigMapping(self._manager, attr)
 
-    def __getitem__(self, key):
+    def __getitem__(self, key: str) -> typing.Any:
         val = getattr(self._manager.objects, key, klass.sentinel)
         if val is None:
             raise KeyError(key)
         return val
 
-    def __getstate__(self):
+    def __getstate__(self) -> dict[str, typing.Any]:
         # Explicitly defined to force pickling to work as expected without
         # trying to pull __getstate__ from _ConfigMapping due to __getattr__.
         return self.__dict__.copy()
 
-    def __setstate__(self, state):
+    def __setstate__(self, state: dict[str, typing.Any]) -> None:
         self.__dict__.update(state)
 
 
 class CompatConfigManager:
-    def __init__(self, manager):
+    def __init__(self, manager) -> None:
         self._manager = manager
 
-    def __getattr__(self, attr):
+    def __getattr__(self, attr: str) -> typing.Any:
         if attr == "_manager":
             return object.__getattribute__(self, "_manager")
         obj = getattr(self._manager, attr, klass.sentinel)
@@ -296,7 +315,7 @@ class ConfigManager:
     section with a name starting with "autoload".
     """
 
-    def __init__(self, configs=(), debug=False):
+    def __init__(self, configs=(), debug: bool = False):
         """Initialize.
 
         :type configs: sequence of mappings of string to ConfigSection.
@@ -319,7 +338,7 @@ class ConfigManager:
             return config
         return basics.GeneratedConfigSource(config, "unknown")
 
-    def reload(self):
+    def reload(self) -> None:
         """Reinitialize from the config sources originally passed in.
 
         This throws away all cached instances and re-executes autoloads.
@@ -336,15 +355,15 @@ class ConfigManager:
         for config in self.original_config_sources:
             self.add_config_source(config)
 
-    def update(self, config):
+    def update(self, config) -> None:
         """Reinitialize using an additional supplied config."""
         self.original_config_sources += (config,)
         self.reload()
 
-    def add_config_source(self, config):
-        return self._add_config_source(self._compat_mangle_config(config))
+    def add_config_source(self, config) -> None:
+        self._add_config_source(self._compat_mangle_config(config))
 
-    def _add_config_source(self, config):
+    def _add_config_source(self, config) -> None:
         """Pull extra type and config sections from configs and use them.
 
         Things loaded this way are added after already loaded things
@@ -398,11 +417,11 @@ class ConfigManager:
             if collapsed.type.name == "configsection":
                 self.add_config_source(instance)
 
-    def sections(self):
+    def sections(self) -> typing.Iterator[str]:
         """Return an iterator of all section names."""
         return iter(self.sections_lookup.keys())
 
-    def collapse_named_section(self, name, raise_on_missing=True):
+    def collapse_named_section(self, name: str, raise_on_missing: bool = True):
         """Collapse a config by name, possibly returning a cached instance.
 
         @returns: :obj:`CollapsedConfig`.
@@ -436,7 +455,7 @@ class ConfigManager:
         finally:
             self._refs.remove(name)
 
-    def _get_inherited_sections(self, name, sections):
+    def _get_inherited_sections(self, name: str, sections):
         # List of (name, ConfigSection, index) tuples, most specific first.
         slist = [(name, sections)]
 
@@ -479,13 +498,13 @@ class ConfigManager:
                     slist.append((inherit, target))
         return [_section_data(name, stack[0]) for (name, stack) in slist]
 
-    def _section_is_inherit_only(self, section):
+    def _section_is_inherit_only(self, section) -> bool:
         if "inherit-only" in section:
             if section.render_value(self, "inherit-only", "bool"):
                 return True
         return False
 
-    def collapse_section(self, sections, _name=None):
+    def collapse_section(self, sections, _name: typing.Optional[str] = None):
         """Collapse a ConfigSection to a :obj:`CollapsedConfig`."""
 
         if self._section_is_inherit_only(sections[0]):
@@ -518,7 +537,7 @@ class ConfigManager:
         return collapsed
 
     @klass.jit_attr
-    def types(self):
+    def types(self) -> dict[str, dict[str, typing.Any]]:
         type_map = defaultdict(dict)
         for name, sections in self.sections_lookup.items():
             if self._section_is_inherit_only(sections[0]):
@@ -529,7 +548,7 @@ class ConfigManager:
             (k, mappings.ImmutableDict(v)) for k, v in type_map.items()
         )
 
-    def _render_config_stack(self, type_obj, config_stack):
+    def _render_config_stack(self, type_obj, config_stack) -> dict[str, typing.Any]:
         conf = {}
         for key in config_stack:
             typename = type_obj.types.get(key)
@@ -547,6 +566,8 @@ class ConfigManager:
             if typename.startswith("refs:") or typename in ("list", "str"):
                 result = config_stack.render_prepends(self, key, typename)
                 if typename == "str":
+                    # TODO: figure out why this is needed and likely remove it.
+                    # it's likely just doing ' '.join([item])
                     result = " ".join(result)
             else:
                 result = config_stack.render_val(self, key, typename)
@@ -581,7 +602,7 @@ class ConfigManager:
 
         return mappings.ImmutableDict(conf)
 
-    def get_default(self, type_name):
+    def get_default(self, type_name: str) -> typing.Optional[typing.Any]:
         """Finds the configuration specified default obj of type_name.
 
         Returns C{None} if no defaults.

diff --git a/src/pkgcore/config/cparser.py b/src/pkgcore/config/cparser.py
index 7c49cc141..b578f4ec2 100644
--- a/src/pkgcore/config/cparser.py
+++ b/src/pkgcore/config/cparser.py
@@ -5,6 +5,7 @@ ini based configuration format
 __all__ = ("config_from_file",)
 
 import configparser
+import typing
 
 from snakeoil import mappings
 
@@ -12,11 +13,14 @@ from . import basics, errors
 
 
 class CaseSensitiveConfigParser(configparser.ConfigParser):
-    def optionxform(self, val):
-        return val
+    """Parse to enforce case sensitivity for configparser"""
 
+    def optionxform(self, optionstr: str) -> str:
+        """preserve case sensitivity"""
+        return optionstr
 
-def config_from_file(file_obj):
+
+def config_from_file(file_obj: typing.Iterable[str]) -> mappings.LazyValDict:
     """
     generate a config dict
 


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [gentoo-commits] proj/pkgcore/pkgcore:master commit in: src/pkgcore/config/
@ 2023-02-05 18:16 Arthur Zamarin
  0 siblings, 0 replies; 4+ messages in thread
From: Arthur Zamarin @ 2023-02-05 18:16 UTC (permalink / raw
  To: gentoo-commits

commit:     95e8f3b3d418347eaa36aa3eed40de2a6e436d35
Author:     Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Tue Jan 17 09:07:19 2023 +0000
Commit:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Sun Feb  5 18:08:21 2023 +0000
URL:        https://gitweb.gentoo.org/proj/pkgcore/pkgcore.git/commit/?id=95e8f3b3

fix(config): Add deprecation warnings for very old config shims

The context of this is fa90aff05306fb4935604e64645f2d1d2049233e; the
original 'central' manager exposed objecst in the same attr namespace as
it's own internals.  Whilst no conflicts occured, it was possible, so
the configuration objects were moved to .objects to remove the potential.

A compatibility shim was added (CompatConfigManager), but no warnings
were added at the time due to presumably the module not existing.

Either way, it exists, thus add a warning.  There are multiple users
in pkgcore's code (fixed in followup commit).  Pkgcheck and pkgdev are
clean, but it may be wise to wait a release or two before removing the
compatibility shim.  It's been in place 11 years, not like it's going to
kill us to wait another month...

Signed-off-by: Brian Harring <ferringb <AT> gmail.com>
Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org>

 src/pkgcore/config/central.py | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/pkgcore/config/central.py b/src/pkgcore/config/central.py
index 0cd2f392e..12e0ce13b 100644
--- a/src/pkgcore/config/central.py
+++ b/src/pkgcore/config/central.py
@@ -9,6 +9,7 @@ __all__ = (
 )
 
 import typing
+import warnings
 import weakref
 from collections import defaultdict, deque, namedtuple
 
@@ -255,6 +256,12 @@ class _ConfigObjMap:
 
 
 class CompatConfigManager:
+    """This is a compatibility hack to alias attribute access to the new location
+
+    See commit fa90aff05306fb4935604e64645f2d1d2049233e for when this was introduced.
+
+    This should be removed once consumers are converted in their access."""
+
     def __init__(self, manager) -> None:
         self._manager = manager
 
@@ -264,6 +271,10 @@ class CompatConfigManager:
         obj = getattr(self._manager, attr, klass.sentinel)
         if obj is klass.sentinel:
             obj = getattr(self._manager.objects, attr)
+            warnings.warn(
+                f"Access to central objects must be done via '.objects.{attr}' rather than '.{attr}'",
+                stacklevel=2,
+            )
         return obj
 
     __dir__ = klass.DirProxy("_manager")


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2023-02-05 18:16 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-02-02 20:10 [gentoo-commits] proj/pkgcore/pkgcore:master commit in: src/pkgcore/config/ Arthur Zamarin
  -- strict thread matches above, loose matches on Subject: below --
2023-02-05 18:16 Arthur Zamarin
2023-02-02 20:10 Arthur Zamarin
2023-01-17 20:50 Arthur Zamarin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox