2424import re
2525import datetime as dt
2626import collections
27+ import numbers
28+ import typing
2729
2830from .parameterized import (
2931 Parameterized , Parameter , String , ParameterizedFunction , ParamOverrides ,
@@ -692,7 +694,6 @@ def _force(self,obj,objtype=None):
692694 return gen
693695
694696
695- import numbers
696697def _is_number (obj ):
697698 if isinstance (obj , numbers .Number ): return True
698699 # The extra check is for classes that behave like numbers, such as those
@@ -752,6 +753,10 @@ def __init__(self, default=b"", regex=None, allow_None=False, **kwargs):
752753 self .allow_None = (default is None or allow_None )
753754 self ._validate (default )
754755
756+ @property
757+ def pytype (self ):
758+ return typing .Union [bytes , None ] if self .allow_None else bytes
759+
755760 def _validate_regex (self , val , regex ):
756761 if (val is None and self .allow_None ):
757762 return
@@ -834,6 +839,10 @@ def __init__(self, default=0.0, bounds=None, softbounds=None,
834839 self .step = step
835840 self ._validate (default )
836841
842+ @property
843+ def pytype (self ):
844+ return typing .Union [numbers .Number , None ] if self .allow_None else numbers .Number
845+
837846 def __get__ (self , obj , objtype ):
838847 """
839848 Same as the superclass's __get__, but if the value was
@@ -963,6 +972,10 @@ class Integer(Number):
963972 def __init__ (self , default = 0 , ** params ):
964973 Number .__init__ (self , default = default , ** params )
965974
975+ @property
976+ def pytype (self ):
977+ return typing .Union [int , None ] if self .allow_None else int
978+
966979 def _validate_value (self , val , allow_None ):
967980 if callable (val ):
968981 return
@@ -1000,6 +1013,10 @@ def __init__(self, default=False, bounds=(0,1), **params):
10001013 self .bounds = bounds
10011014 super (Boolean , self ).__init__ (default = default , ** params )
10021015
1016+ @property
1017+ def pytype (self ):
1018+ return typing .Union [bool , None ] if self .allow_None else bool
1019+
10031020 def _validate_value (self , val , allow_None ):
10041021 if allow_None :
10051022 if not isinstance (val , bool ) and val is not None :
@@ -1034,6 +1051,14 @@ def __init__(self, default=(0,0), length=None, **params):
10341051 self .length = length
10351052 self ._validate (default )
10361053
1054+ @property
1055+ def pytype (self ):
1056+ if self .length :
1057+ pytype = typing .Tuple [(typing .Any ,)* self .length ]
1058+ else :
1059+ ptype = typing .Tuple [typing .Any , ...]
1060+ return typing .Union [pytype , None ] if self .allow_None else pytype
1061+
10371062 def _validate_value (self , val , allow_None ):
10381063 if val is None and allow_None :
10391064 return
@@ -1071,6 +1096,14 @@ def deserialize(cls, value):
10711096class NumericTuple (Tuple ):
10721097 """A numeric tuple Parameter (e.g. (4.5,7.6,3)) with a fixed tuple length."""
10731098
1099+ @property
1100+ def pytype (self ):
1101+ if self .length :
1102+ pytype = typing .Tuple [(numbers .Number ,)* self .length ]
1103+ else :
1104+ ptype = typing .Tuple [numbers .Number , ...]
1105+ return typing .Union [pytype , None ] if self .allow_None else pytype
1106+
10741107 def _validate_value (self , val , allow_None ):
10751108 super (NumericTuple , self )._validate_value (val , allow_None )
10761109 if allow_None and val is None :
@@ -1088,6 +1121,11 @@ class XYCoordinates(NumericTuple):
10881121 def __init__ (self , default = (0.0 , 0.0 ), ** params ):
10891122 super (XYCoordinates ,self ).__init__ (default = default , length = 2 , ** params )
10901123
1124+ @property
1125+ def pytype (self ):
1126+ pytype = typing .Tuple [numbers .Number , numbers .Number ]
1127+ return typing .Union [pytype , None ] if self .allow_None else pytype
1128+
10911129
10921130class Callable (Parameter ):
10931131 """
@@ -1099,6 +1137,11 @@ class Callable(Parameter):
10991137 2.4, so instantiate must be False for those values.
11001138 """
11011139
1140+ @proprty
1141+ def pytype (self ):
1142+ ctype = typing .Callable [..., typing .Any ]
1143+ return typing .Union [ctype , None ] if self .allow_None else ctype
1144+
11021145 def _validate_value (self , val , allow_None ):
11031146 if (allow_None and val is None ) or callable (val ):
11041147 return
@@ -1197,6 +1240,11 @@ class SelectorBase(Parameter):
11971240
11981241 __abstract = True
11991242
1243+ @proprty
1244+ def pytype (self ):
1245+ literal = typing .Literal [tuple (self .get_range ().values ())]
1246+ return typing .Union [literal , None ] if self .allow_None else literal
1247+
12001248 def get_range (self ):
12011249 raise NotImplementedError ("get_range() must be implemented in subclasses." )
12021250
@@ -1433,6 +1481,17 @@ def __init__(self, default=[], class_=None, item_type=None,
14331481 ** params )
14341482 self ._validate (default )
14351483
1484+ @property
1485+ def pytype (self ):
1486+ if isinstance (self .item_type , tuple ):
1487+ item_type = typing .Union [self .item_type ]
1488+ elif self .item_type is not None :
1489+ item_type = self .item_type
1490+ else :
1491+ item_type = typing .Any
1492+ list_type = typing .List [item_type ]
1493+ return typing .Union [list_type , None ] if self .allow_None else list_type
1494+
14361495 def _validate (self , val ):
14371496 """
14381497 Checks that the value is numeric and that it is within the hard
@@ -1487,6 +1546,11 @@ class HookList(List):
14871546 """
14881547 __slots__ = ['class_' , 'bounds' ]
14891548
1549+ @property
1550+ def pytype (self ):
1551+ list_type = typing .List [typing .Callable [[], None ]]
1552+ return typing .Union [list_type , None ] if self .allow_None else list_type
1553+
14901554 def _validate_value (self , val , allow_None ):
14911555 super (HookList , self )._validate_value (val , allow_None )
14921556 if allow_None and val is None :
@@ -1506,16 +1570,27 @@ class Dict(ClassSelector):
15061570 def __init__ (self , default = None , ** params ):
15071571 super (Dict , self ).__init__ (dict , default = default , ** params )
15081572
1573+ @property
1574+ def pytype (self ):
1575+ dict_type = typing .Dict [typing .Hashable , typing .Any ]
1576+ return typing .Union [dict_type , None ] if self .allow_None else dict_type
1577+
1578+
15091579
15101580class Array (ClassSelector ):
15111581 """
15121582 Parameter whose value is a numpy array.
15131583 """
15141584
15151585 def __init__ (self , default = None , ** params ):
1516- from numpy import ndarray
1586+
15171587 super (Array , self ).__init__ (ndarray , allow_None = True , default = default , ** params )
15181588
1589+ @property
1590+ def pytype (self ):
1591+ from numpy import ndarray
1592+ return ndarray
1593+
15191594 @classmethod
15201595 def serialize (cls , value ):
15211596 if value is None :
@@ -1559,6 +1634,11 @@ def __init__(self, default=None, rows=None, columns=None, ordered=None, **params
15591634 super (DataFrame ,self ).__init__ (pdDFrame , default = default , ** params )
15601635 self ._validate (self .default )
15611636
1637+ @property
1638+ def pytype (self ):
1639+ from pandas import DataFrame
1640+ return DataFrame
1641+
15621642 def _length_bounds_check (self , bounds , length , name ):
15631643 message = '{name} length {length} does not match declared bounds of {bounds}'
15641644 if not isinstance (bounds , tuple ):
@@ -1635,6 +1715,11 @@ def __init__(self, default=None, rows=None, allow_None=False, **params):
16351715 ** params )
16361716 self ._validate (self .default )
16371717
1718+ @property
1719+ def pytype (self ):
1720+ from pandas import Series
1721+ return Series
1722+
16381723 def _length_bounds_check (self , bounds , length , name ):
16391724 message = '{name} length {length} does not match declared bounds of {bounds}'
16401725 if not isinstance (bounds , tuple ):
@@ -1778,6 +1863,13 @@ def __init__(self, default=None, search_paths=None, **params):
17781863 self .search_paths = search_paths
17791864 super (Path ,self ).__init__ (default ,** params )
17801865
1866+ @property
1867+ def pytype (self ):
1868+ path_types = (str , pathlib .Path )
1869+ if self .allow_None :
1870+ path_types += (None ,)
1871+ return typing .Union [path_types ]
1872+
17811873 def _resolve (self , path ):
17821874 return resolve_path (path , path_to_file = None , search_paths = self .search_paths )
17831875
@@ -1904,6 +1996,12 @@ def __init__(self, default=None, objects=None, **kwargs):
19041996 super (ListSelector ,self ).__init__ (
19051997 objects = objects , default = default , empty_default = True , ** kwargs )
19061998
1999+ @property
2000+ def pytype (self ):
2001+ literal = typing .Literal [tuple (self .get_range ().values ())]
2002+ ltype = typing .List [literal ]
2003+ return typing .Union [ltype , None ] if self .allow_None else ltype
2004+
19072005 def compute_default (self ):
19082006 if self .default is None and callable (self .compute_default_fn ):
19092007 self .default = self .compute_default_fn ()
@@ -1954,6 +2052,14 @@ class Date(Number):
19542052 def __init__ (self , default = None , ** kwargs ):
19552053 super (Date , self ).__init__ (default = default , ** kwargs )
19562054
2055+ @property
2056+ def pytype (self ):
2057+ if self .allow_None :
2058+ date_types = dt_types + (None ,)
2059+ else :
2060+ date_types = dt_types
2061+ return typing .Union [date_types ]
2062+
19572063 def _validate_value (self , val , allow_None ):
19582064 """
19592065 Checks that the value is numeric and that it is within the hard
@@ -1998,6 +2104,10 @@ class CalendarDate(Number):
19982104 def __init__ (self , default = None , ** kwargs ):
19992105 super (CalendarDate , self ).__init__ (default = default , ** kwargs )
20002106
2107+ @property
2108+ def pytype (self ):
2109+ return typing .Union [dt .datetime , None ] if self .allow_None else dt .datetime
2110+
20012111 def _validate_value (self , val , allow_None ):
20022112 """
20032113 Checks that the value is numeric and that it is within the hard
@@ -2076,6 +2186,10 @@ def __init__(self, default=None, allow_named=True, **kwargs):
20762186 self .allow_named = allow_named
20772187 self ._validate (default )
20782188
2189+ @property
2190+ def pytype (self ):
2191+ return typing .Union [str , None ] if self .allow_None else str
2192+
20792193 def _validate (self , val ):
20802194 self ._validate_value (val , self .allow_None )
20812195 self ._validate_allow_named (val , self .allow_named )
@@ -2151,6 +2265,12 @@ class DateRange(Range):
21512265 Bounds must be specified as datetime or date types (see param.dt_types).
21522266 """
21532267
2268+ @property
2269+ def pytype (self ):
2270+ date_type = typing .Union [dt_types ]
2271+ range_type = typing .Tuple [date_type , date_type ]
2272+ return typing .Union [range_type , None ] if self .allow_None else range_type
2273+
21542274 def _validate_value (self , val , allow_None ):
21552275 # Cannot use super()._validate_value as DateRange inherits from
21562276 # NumericTuple which check that the tuple values are numbers and
@@ -2205,6 +2325,7 @@ def deserialize(cls, value):
22052325 # As JSON has no tuple representation
22062326 return tuple (deserialized )
22072327
2328+
22082329class CalendarDateRange (Range ):
22092330 """
22102331 A date range specified as (start_date, end_date).
@@ -2281,6 +2402,10 @@ def __init__(self,default=False,bounds=(0,1),**params):
22812402 # back to False while triggered callbacks are executing
22822403 super (Event , self ).__init__ (default = default ,** params )
22832404
2405+ @property
2406+ def pytype (self ):
2407+ return bool
2408+
22842409 def _reset_event (self , obj , val ):
22852410 val = False
22862411 if obj is None :
0 commit comments