-def read_search_path(conf: configparser.SectionProxy) -> SearchPath:
- if 'alias_of' in conf:
- return AliasSearchPath(**dict(conf.items()))
- if 'channel_url' in conf:
- return ChannelSearchPath(**dict(conf.items()))
- return GitSearchPath(**dict(conf.items()))
+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