+K = TypeVar('K')
+V = TypeVar('V')
+
+
+def partition_dict(pred: Callable[[K, V], bool],
+ d: Dict[K, V]) -> Tuple[Dict[K, V], Dict[K, V]]:
+ selected: Dict[K, V] = {}
+ remaining: Dict[K, V] = {}
+ for k, v in d.items():
+ if pred(k, v):
+ selected[k] = v
+ else:
+ remaining[k] = v
+ return selected, remaining
+
+
+def filter_dict(d: Dict[K, V], fields: Set[K]
+ ) -> Tuple[Dict[K, V], Dict[K, V]]:
+ return partition_dict(lambda k, v: k in fields, d)
+
+
+def read_config_section(
+ conf: configparser.SectionProxy) -> Tuple[SearchPath, Optional[Pin]]:
+ mapping: Mapping[str, Tuple[Type[SearchPath], Type[Pin]]] = {
+ 'alias': (AliasSearchPath, AliasPin),
+ 'channel': (ChannelSearchPath, ChannelPin),
+ 'git': (GitSearchPath, GitPin),
+ 'symlink': (SymlinkSearchPath, SymlinkPin),
+ }
+ SP, P = mapping[conf['type']]
+ _, all_fields = filter_dict(dict(conf.items()), set(['type']))
+ pin_fields, remaining_fields = filter_dict(all_fields, set(P._fields))
+ # Error suppression works around https://github.com/python/mypy/issues/9007
+ pin_present = pin_fields or P._fields == ()
+ pin = P(**pin_fields) if pin_present else None # type: ignore
+ return SP(**remaining_fields), pin
+
+
+def read_pinned_config_section(
+ section: str, conf: configparser.SectionProxy) -> Tuple[SearchPath, Pin]:
+ sp, pin = read_config_section(conf)
+ if pin is None:
+ raise RuntimeError(
+ f'Cannot update unpinned channel "{section}" (Run "pin" before "update")')
+ return sp, pin