11from  __future__ import  annotations 
22
33import  inspect 
4+ import  re 
45import  sys 
56from  contextlib  import  contextmanager 
67from  enum  import  EnumMeta , IntEnum , StrEnum , auto 
7- from  functools  import  cached_property 
8+ from  functools  import  cached_property ,  partial ,  wraps 
89from  logging  import  getLevelNamesMapping 
910from  pathlib  import  Path 
1011from  time  import  time 
11- from  typing  import  TYPE_CHECKING , Literal , get_args 
12+ from  typing  import  TYPE_CHECKING , Literal , LiteralString ,  TypeVar ,  get_args 
1213
1314from  . import  logging 
1415from  ._compat  import  deprecated , old_positionals 
1516from  ._singleton  import  SingletonMeta 
1617from  .logging  import  _RootLogger , _set_log_file , _set_log_level 
1718
1819if  TYPE_CHECKING :
19-     from  collections .abc  import  Generator , Iterable 
20+     from  collections .abc  import  Callable ,  Generator , Iterable ,  Mapping 
2021    from  typing  import  Any , ClassVar , Self , TextIO 
2122
2223    from  ._types  import  HVGFlavor 
3031    _VerbosityName  =  Literal ["error" , "warning" , "info" , "hint" , "debug" ]
3132    _LoggingLevelName  =  Literal ["CRITICAL" , "ERROR" , "WARNING" , "INFO" , "HINT" , "DEBUG" ]
3233
34+ T  =  TypeVar ("T" , bound = LiteralString )
35+ 
3336
3437AnnDataFileFormat  =  Literal ["h5ad" , "zarr" ]
3538
3639
40+ _preset_postprocessors : list [Callable [[], None ]] =  []
41+ 
42+ 
43+ def  _postprocess_preset_prop (
44+     prop : cached_property [T ],
45+     param : str ,
46+     get_map : Callable [[], Mapping [Preset , LiteralString ]],
47+ ) ->  None :
48+     map  =  get_map ()
49+ 
50+     map_type  =  inspect .signature (get_map ).return_annotation 
51+     value_type  =  re .fullmatch (r"Mapping\[Preset, (.*)\]" , map_type )[1 ]
52+ 
53+     added_doc  =  "\n " .join (
54+         f":attr:`{ k .name }  `\n     Default: `{ param }  ={ v !r}  `"  for  k , v  in  map .items ()
55+     )
56+ 
57+     prop .__doc__  =  f"{ prop .__doc__ } \n \n { added_doc }  " 
58+     prop .func .__annotations__ ["return" ] =  value_type 
59+ 
60+ 
61+ def  _preset_property (
62+     param : str ,
63+ ) ->  Callable [[Callable [[], Mapping [Preset , T ]]], cached_property [T ]]:
64+     def  decorator (get_map : Callable [[], Mapping [Preset , T ]]) ->  cached_property [T ]:
65+         @wraps (get_map ) 
66+         def  get (self : Preset ) ->  T :
67+             return  get_map ()[self ]
68+ 
69+         prop  =  cached_property (get )
70+         _preset_postprocessors .append (
71+             partial (_postprocess_preset_prop , prop , param , get_map )
72+         )
73+         return  prop 
74+ 
75+     return  decorator 
76+ 
77+ 
3778class  Preset (StrEnum ):
3879    """Presets for :attr:`scanpy.settings.preset`. 
3980
@@ -46,14 +87,17 @@ class Preset(StrEnum):
4687    SeuratV5  =  auto ()
4788    """Try to match Seurat 5.* as closely as possible.""" 
4889
49-     @cached_property  
50-     def  highly_variable_genes (self ) ->  HVGFlavor :
90+     @_preset_property ( "flavor" )  
91+     def  highly_variable_genes () ->  Mapping [ Preset ,  HVGFlavor ] :
5192        """Flavor for :func:`~scanpy.pp.highly_variable_genes`.""" 
52-         match  self :
53-             case  Preset .ScanpyV1 :
54-                 return  "seurat" 
55-             case  Preset .SeuratV5 :
56-                 return  "seurat_v3" 
93+         return  {
94+             Preset .ScanpyV1 : "seurat" ,
95+             Preset .SeuratV5 : "seurat_v3" ,
96+         }
97+ 
98+ 
99+ for  _postprocess  in  _preset_postprocessors :
100+     _postprocess ()
57101
58102
59103_VERBOSITY_TO_LOGLEVEL : dict [int  |  _VerbosityName , _LoggingLevelName ] =  {
0 commit comments