11# distutils: language = c++
22# cython: language_level = 3
3+ import builtins
34import datetime
45import json
56from libcpp cimport bool
67from libcpp.string cimport string
78from libcpp.vector cimport vector
89from libcpp.optional cimport optional
910
11+ from openpilot.common.swaglog import cloudlog
12+
1013cdef extern from " common/params.h" :
1114 cpdef enum ParamKeyFlag:
1215 PERSISTENT
@@ -42,6 +45,25 @@ cdef extern from "common/params.h":
4245 void clearAll(ParamKeyFlag)
4346 vector[string] allKeys()
4447
48+ PYTHON_2_CPP = {
49+ (str , STRING): lambda v : v,
50+ (builtins.bool, BOOL): lambda v : " 1" if v else " 0" ,
51+ (int , INT): str ,
52+ (float , FLOAT): str ,
53+ (datetime.datetime, TIME): lambda v : v.isoformat(),
54+ (dict , JSON): json.dumps,
55+ (list , JSON): json.dumps,
56+ (bytes, BYTES): lambda v : v,
57+ }
58+ CPP_2_PYTHON = {
59+ STRING: lambda v : v.decode(" utf-8" ),
60+ BOOL: lambda v : v == b" 1" ,
61+ INT: int ,
62+ FLOAT: float ,
63+ TIME: lambda v : datetime.datetime.fromisoformat(v.decode(" utf-8" )),
64+ JSON: json.loads,
65+ BYTES: lambda v : v,
66+ }
4567
4668def ensure_bytes (v ):
4769 return v.encode() if isinstance (v, str ) else v
@@ -74,45 +96,38 @@ cdef class Params:
7496 raise UnknownKeyName(key)
7597 return key
7698
77- def cast (self , t , value , default ):
99+ def python2cpp (self , proposed_type , expected_type , value , key ):
100+ cast = PYTHON_2_CPP.get((proposed_type, expected_type))
101+ if cast:
102+ return cast(value)
103+ raise TypeError (f" Type mismatch while writing param {key}: {proposed_type=} {expected_type=} {value=}" )
104+
105+ def cpp2python (self , t , value , default , key ):
78106 if value is None :
79107 return None
80108 try :
81- if t == STRING:
82- return value.decode(" utf-8" )
83- elif t == BOOL:
84- return value == b" 1"
85- elif t == INT:
86- return int (value)
87- elif t == FLOAT:
88- return float (value)
89- elif t == TIME:
90- return datetime.datetime.fromisoformat(value.decode(" utf-8" ))
91- elif t == JSON:
92- return json.loads(value)
93- elif t == BYTES:
94- return value
95- else :
96- raise TypeError ()
97- except (TypeError , ValueError ):
98- return self .cast(t, default, None )
109+ return CPP_2_PYTHON[t](value)
110+ except (KeyError , TypeError , ValueError ):
111+ cloudlog.warning(f" Failed to cast param {key} with {value=} from type {t=}" )
112+ return self .cpp2python(t, default, None , key)
99113
100114 def get (self , key , bool block = False , bool return_default = False ):
101115 cdef string k = self .check_key(key)
102116 cdef ParamKeyType t = self .p.getKeyType(k)
117+ cdef optional[string] default = self .p.getKeyDefaultValue(k)
103118 cdef string val
104119 with nogil:
105120 val = self .p.get(k, block)
106121
107- default_val = self .get_default_value(k ) if return_default else None
122+ default_val = (default.value() if default.has_value() else None ) if return_default else None
108123 if val == b" " :
109124 if block:
110125 # If we got no value while running in blocked mode
111126 # it means we got an interrupt while waiting
112127 raise KeyboardInterrupt
113128 else :
114- return self .cast (t, default_val, None )
115- return self .cast (t, val, default_val)
129+ return self .cpp2python (t, default_val, None , key )
130+ return self .cpp2python (t, val, default_val, key )
116131
117132 def get_bool (self , key , bool block = False ):
118133 cdef string k = self .check_key(key)
@@ -121,6 +136,11 @@ cdef class Params:
121136 r = self .p.getBool(k, block)
122137 return r
123138
139+ def _put_cast (self , key , dat ):
140+ cdef string k = self .check_key(key)
141+ cdef ParamKeyType t = self .p.getKeyType(k)
142+ return ensure_bytes(self .python2cpp(type (dat), t, dat, key))
143+
124144 def put (self , key , dat ):
125145 """
126146 Warning: This function blocks until the param is written to disk!
@@ -129,7 +149,7 @@ cdef class Params:
129149 in general try to avoid writing params as much as possible.
130150 """
131151 cdef string k = self .check_key(key)
132- cdef string dat_bytes = ensure_bytes( dat)
152+ cdef string dat_bytes = self ._put_cast(key, dat)
133153 with nogil:
134154 self .p.put(k, dat_bytes)
135155
@@ -140,7 +160,7 @@ cdef class Params:
140160
141161 def put_nonblocking (self , key , dat ):
142162 cdef string k = self .check_key(key)
143- cdef string dat_bytes = ensure_bytes( dat)
163+ cdef string dat_bytes = self ._put_cast(key, dat)
144164 with nogil:
145165 self .p.putNonBlocking(k, dat_bytes)
146166
@@ -165,5 +185,7 @@ cdef class Params:
165185 return self .p.allKeys()
166186
167187 def get_default_value (self , key ):
168- cdef optional[string] default = self .p.getKeyDefaultValue(self .check_key(key))
169- return default.value() if default.has_value() else None
188+ cdef string k = self .check_key(key)
189+ cdef ParamKeyType t = self .p.getKeyType(k)
190+ cdef optional[string] default = self .p.getKeyDefaultValue(k)
191+ return self .cpp2python(t, default.value(), None , key) if default.has_value() else None
0 commit comments