1616# License along with C-PAC. If not, see <https://www.gnu.org/licenses/>.
1717"""C-PAC Configuration class and related functions."""
1818
19- from collections .abc import Iterable
19+ from collections .abc import Iterable , KeysView
20+ from importlib .resources import files
2021import os
2122import re
2223from typing import Any , cast , Literal , Optional , overload
2324from warnings import warn
2425
2526from click import BadParameter
26- import pkg_resources as p
2727import yaml
2828
2929from CPAC .pipeline .nipype_pipeline_engine import MapNode , Node
30- from .diff import dct_diff
30+ from .diff import dct_diff , DiffDict
3131
3232CONFIG_KEY_TYPE = str | list [str ]
3333_DICT = dict
@@ -48,6 +48,10 @@ def __init__(self):
4848class NestedKeyMixin :
4949 """Provide methods for getting and setting nested keys."""
5050
51+ def dict (self ) -> dict [Any , Any ]:
52+ """Show contents as a dict."""
53+ return {k : v for k , v in self .__dict__ .items () if not callable (v )}
54+
5155 def __contains__ (self , item : str | list [Any ]) -> bool :
5256 """Check if an item is in the Configuration."""
5357 if isinstance (item , str ):
@@ -59,7 +63,7 @@ def __contains__(self, item: str | list[Any]) -> bool:
5963 return False
6064
6165 def __getitem__ (self , key : Iterable ) -> Any :
62- """Get an item from a Configuration ."""
66+ """Get an item from a nested dictionary ."""
6367 self ._check_keys (key )
6468 if isinstance (key , str ):
6569 return getattr (self , key )
@@ -69,7 +73,7 @@ def __getitem__(self, key: Iterable) -> Any:
6973 return None
7074
7175 def __setitem__ (self , key : Iterable , value : Any ) -> None :
72- """Set an item in a Configuration ."""
76+ """Set an item in a nested dictionary ."""
7377 self ._check_keys (key )
7478 if isinstance (key , str ):
7579 setattr (self , key , value )
@@ -78,8 +82,8 @@ def __setitem__(self, key: Iterable, value: Any) -> None:
7882 else :
7983 self .key_type_error (key )
8084
81- def __sub__ (self : "Configuration " , other : "Configuration" ) :
82- """Return the set difference between two Configurations .
85+ def __sub__ (self : "NestedKeyMixin " , other : "NestedKeyMixin" ) -> DiffDict :
86+ """Return the set difference between two nested dictionaries .
8387
8488 Examples
8589 --------
@@ -133,7 +137,7 @@ def _nonestr_to_None(self, d):
133137 if isinstance (d , list ):
134138 return [self ._nonestr_to_None (i ) for i in d ]
135139 if isinstance (d , set ):
136- return {self ._nonestr_to_None (i ) for i in d }
140+ return {self ._nonestr_to_None (i ) for i in list ( d ) }
137141 if isinstance (d , dict ):
138142 return {i : self ._nonestr_to_None (d [i ]) for i in d }
139143 return d
@@ -150,7 +154,7 @@ def _check_keys(keys: Iterable) -> None:
150154 msg = f"`set_nested` keys must be iterable, got { type (keys )} ."
151155 raise error (msg )
152156
153- def get_nested (self , _d : "Configuration | _DICT" , keys : Iterable ) -> Any :
157+ def get_nested (self , _d : "NestedKeyMixin | _DICT" , keys : Iterable ) -> Any :
154158 """Get a value from a Configuration dictionary given a nested key."""
155159 self ._check_keys (keys )
156160 if _d is None :
@@ -164,16 +168,20 @@ def get_nested(self, _d: "Configuration | _DICT", keys: Iterable) -> Any:
164168 return _d [keys [0 ]]
165169 return _d
166170
171+ def keys (self ) -> KeysView [Any ]:
172+ """Show toplevel keys of a nested dict."""
173+ return self .dict ().keys ()
174+
167175 @overload
168176 def set_nested (
169- self , d : "Configuration " , keys : Iterable , value : Any
170- ) -> "Configuration " : ...
177+ self , d : "NestedKeyMixin " , keys : Iterable , value : Any
178+ ) -> "NestedKeyMixin " : ...
171179 @overload
172180 def set_nested (self , d : _DICT , keys : Iterable , value : Any ) -> _DICT : ...
173181 def set_nested (
174- self , d : "Configuration | _DICT" , keys : Iterable , value : Any
175- ) -> "Configuration | _DICT" :
176- """Set a nested key in a Configuration dictionary."""
182+ self , d : "NestedKeyMixin | _DICT" , keys : Iterable , value : Any
183+ ) -> "NestedKeyMixin | _DICT" :
184+ """Set a nested key in a nested dictionary."""
177185 self ._check_keys (keys )
178186 if isinstance (keys , str ):
179187 d [keys ] = value
@@ -435,6 +443,25 @@ class Configuration(NestedKeyMixin):
435443 'slack_420349_preconfig'
436444 """
437445
446+ amplitude_low_frequency_fluctuation : dict
447+ anatomical_preproc : dict
448+ FROM : str
449+ functional_preproc : dict
450+ longitudinal_template_generation : dict
451+ network_centrality : dict
452+ nuisance_corrections : dict
453+ pipeline_setup : dict
454+ post_processing : dict
455+ PyPEER : dict
456+ regional_homogeneity : dict
457+ registration_workflows : dict
458+ seed_based_correlation_analysis : dict
459+ segmentation : dict
460+ skip_env_check : bool
461+ surface_analysis : dict
462+ timeseries_extraction : dict
463+ voxel_mirrored_homotopic_connectivity : dict
464+
438465 def __init__ (
439466 self , config_map : Optional [dict ] = None , skip_env_check : bool = False
440467 ) -> None :
@@ -490,6 +517,7 @@ def __init__(
490517 regressor ["Name" ] = nipype_friendly_name (regressor ["Name" ])
491518
492519 config_map = schema (config_map )
520+ assert isinstance (config_map , dict )
493521
494522 # remove 'skip env check' now that the config is validated
495523 if "skip env check" in config_map :
@@ -525,15 +553,12 @@ def __repr__(self):
525553 return str (self .dict ())
526554
527555 def __copy__ (self ):
556+ """Copy a pipeline Configuration."""
528557 newone = type (self )({})
529558 newone .__dict__ .update (self .__dict__ )
530559 newone ._update_attr ()
531560 return newone
532561
533- def dict (self ) -> dict [Any , Any ]:
534- """Show contents of a C-PAC configuration as a dict."""
535- return {k : v for k , v in self .__dict__ .items () if not callable (v )}
536-
537562 def get (self , key : Any , default : Any = None , / ) -> Any :
538563 """Provide convenience access from `Configuration` to :meth:`dict.get` .
539564
@@ -549,10 +574,6 @@ def get(self, key: Any, default: Any = None, /) -> Any:
549574 """
550575 return self .dict ().get (key , default )
551576
552- def keys (self ):
553- """Show toplevel keys of a C-PAC configuration dict."""
554- return self .dict ().keys ()
555-
556577 def set_from_ENV (self , conf ): # pylint: disable=invalid-name
557578 """Replace strings like $VAR and ${VAR} with environment variable values.
558579
@@ -625,9 +646,11 @@ def set_without_ENV(self, conf): # pylint: disable=invalid-name
625646 return conf
626647
627648 def sub_pattern (self , pattern , orig_key ):
649+ """Make a defined pattern substitution."""
628650 return orig_key .replace (pattern , self [pattern [2 :- 1 ].split ("." )])
629651
630652 def check_pattern (self , orig_key , tags = None ):
653+ """Make defined pattern substitutions."""
631654 if tags is None :
632655 tags = []
633656 if isinstance (orig_key , dict ):
@@ -681,6 +704,7 @@ def check_path(key):
681704 setattr (self , attr_key , new_key )
682705
683706 def update (self , key , val = ConfigurationDictUpdateConflation ()):
707+ """Update a C-PAC pipeline Configuration."""
684708 if isinstance (key , dict ):
685709 raise ConfigurationDictUpdateConflation
686710 if isinstance (val , Exception ):
@@ -704,7 +728,7 @@ def orientation_node(
704728 return orientation_node (name = name , orientation = orientation , node_type = node_type )
705729
706730
707- def check_pname (p_name : str , pipe_config : Configuration ) -> str :
731+ def check_pname (p_name : Optional [ str ] , pipe_config : Configuration ) -> str :
708732 """Check / set `p_name`, the str representation of a pipeline for use in filetrees.
709733
710734 Parameters
@@ -816,9 +840,8 @@ def preconfig_yaml(preconfig_name="default", load=False):
816840 if load :
817841 with open (preconfig_yaml (preconfig_name ), "r" , encoding = "utf-8" ) as _f :
818842 return yaml .safe_load (_f )
819- return p .resource_filename (
820- "CPAC" ,
821- os .path .join ("resources" , "configs" , f"pipeline_config_{ preconfig_name } .yml" ),
843+ return files ("CPAC" ).joinpath (
844+ f"resources/configs/pipeline_config_{ preconfig_name } .yml"
822845 )
823846
824847
0 commit comments