Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/pandas/compat/numpy/function.py: 47%

184 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1""" 

2For compatibility with numpy libraries, pandas functions or methods have to 

3accept '*args' and '**kwargs' parameters to accommodate numpy arguments that 

4are not actually used or respected in the pandas implementation. 

5 

6To ensure that users do not abuse these parameters, validation is performed in 

7'validators.py' to make sure that any extra parameters passed correspond ONLY 

8to those in the numpy signature. Part of that validation includes whether or 

9not the user attempted to pass in non-default values for these extraneous 

10parameters. As we want to discourage users from relying on these parameters 

11when calling the pandas implementation, we want them only to pass in the 

12default values for these parameters. 

13 

14This module provides a set of commonly used default arguments for functions and 

15methods that are spread throughout the codebase. This module will make it 

16easier to adjust to future upstream changes in the analogous numpy signatures. 

17""" 

18from __future__ import annotations 

19 

20from typing import ( 

21 Any, 

22 TypeVar, 

23 overload, 

24) 

25 

26from numpy import ndarray 

27 

28from pandas._libs.lib import ( 

29 is_bool, 

30 is_integer, 

31) 

32from pandas._typing import Axis 

33from pandas.errors import UnsupportedFunctionCall 

34from pandas.util._validators import ( 

35 validate_args, 

36 validate_args_and_kwargs, 

37 validate_kwargs, 

38) 

39 

40AxisNoneT = TypeVar("AxisNoneT", Axis, None) 

41 

42 

43class CompatValidator: 

44 def __init__( 

45 self, 

46 defaults, 

47 fname=None, 

48 method: str | None = None, 

49 max_fname_arg_count=None, 

50 ) -> None: 

51 self.fname = fname 

52 self.method = method 

53 self.defaults = defaults 

54 self.max_fname_arg_count = max_fname_arg_count 

55 

56 def __call__( 

57 self, 

58 args, 

59 kwargs, 

60 fname=None, 

61 max_fname_arg_count=None, 

62 method: str | None = None, 

63 ) -> None: 

64 if args or kwargs: 

65 fname = self.fname if fname is None else fname 

66 max_fname_arg_count = ( 

67 self.max_fname_arg_count 

68 if max_fname_arg_count is None 

69 else max_fname_arg_count 

70 ) 

71 method = self.method if method is None else method 

72 

73 if method == "args": 

74 validate_args(fname, args, max_fname_arg_count, self.defaults) 

75 elif method == "kwargs": 

76 validate_kwargs(fname, kwargs, self.defaults) 

77 elif method == "both": 

78 validate_args_and_kwargs( 

79 fname, args, kwargs, max_fname_arg_count, self.defaults 

80 ) 

81 else: 

82 raise ValueError(f"invalid validation method '{method}'") 

83 

84 

85ARGMINMAX_DEFAULTS = {"out": None} 

86validate_argmin = CompatValidator( 

87 ARGMINMAX_DEFAULTS, fname="argmin", method="both", max_fname_arg_count=1 

88) 

89validate_argmax = CompatValidator( 

90 ARGMINMAX_DEFAULTS, fname="argmax", method="both", max_fname_arg_count=1 

91) 

92 

93 

94def process_skipna(skipna: bool | ndarray | None, args) -> tuple[bool, Any]: 

95 if isinstance(skipna, ndarray) or skipna is None: 

96 args = (skipna,) + args 

97 skipna = True 

98 

99 return skipna, args 

100 

101 

102def validate_argmin_with_skipna(skipna: bool | ndarray | None, args, kwargs) -> bool: 

103 """ 

104 If 'Series.argmin' is called via the 'numpy' library, the third parameter 

105 in its signature is 'out', which takes either an ndarray or 'None', so 

106 check if the 'skipna' parameter is either an instance of ndarray or is 

107 None, since 'skipna' itself should be a boolean 

108 """ 

109 skipna, args = process_skipna(skipna, args) 

110 validate_argmin(args, kwargs) 

111 return skipna 

112 

113 

114def validate_argmax_with_skipna(skipna: bool | ndarray | None, args, kwargs) -> bool: 

115 """ 

116 If 'Series.argmax' is called via the 'numpy' library, the third parameter 

117 in its signature is 'out', which takes either an ndarray or 'None', so 

118 check if the 'skipna' parameter is either an instance of ndarray or is 

119 None, since 'skipna' itself should be a boolean 

120 """ 

121 skipna, args = process_skipna(skipna, args) 

122 validate_argmax(args, kwargs) 

123 return skipna 

124 

125 

126ARGSORT_DEFAULTS: dict[str, int | str | None] = {} 

127ARGSORT_DEFAULTS["axis"] = -1 

128ARGSORT_DEFAULTS["kind"] = "quicksort" 

129ARGSORT_DEFAULTS["order"] = None 

130ARGSORT_DEFAULTS["kind"] = None 

131 

132 

133validate_argsort = CompatValidator( 

134 ARGSORT_DEFAULTS, fname="argsort", max_fname_arg_count=0, method="both" 

135) 

136 

137# two different signatures of argsort, this second validation for when the 

138# `kind` param is supported 

139ARGSORT_DEFAULTS_KIND: dict[str, int | None] = {} 

140ARGSORT_DEFAULTS_KIND["axis"] = -1 

141ARGSORT_DEFAULTS_KIND["order"] = None 

142validate_argsort_kind = CompatValidator( 

143 ARGSORT_DEFAULTS_KIND, fname="argsort", max_fname_arg_count=0, method="both" 

144) 

145 

146 

147def validate_argsort_with_ascending(ascending: bool | int | None, args, kwargs) -> bool: 

148 """ 

149 If 'Categorical.argsort' is called via the 'numpy' library, the first 

150 parameter in its signature is 'axis', which takes either an integer or 

151 'None', so check if the 'ascending' parameter has either integer type or is 

152 None, since 'ascending' itself should be a boolean 

153 """ 

154 if is_integer(ascending) or ascending is None: 

155 args = (ascending,) + args 

156 ascending = True 

157 

158 validate_argsort_kind(args, kwargs, max_fname_arg_count=3) 

159 # error: Incompatible return value type (got "int", expected "bool") 

160 return ascending # type: ignore[return-value] 

161 

162 

163CLIP_DEFAULTS: dict[str, Any] = {"out": None} 

164validate_clip = CompatValidator( 

165 CLIP_DEFAULTS, fname="clip", method="both", max_fname_arg_count=3 

166) 

167 

168 

169@overload 

170def validate_clip_with_axis(axis: ndarray, args, kwargs) -> None: 

171 ... 

172 

173 

174@overload 

175def validate_clip_with_axis(axis: AxisNoneT, args, kwargs) -> AxisNoneT: 

176 ... 

177 

178 

179def validate_clip_with_axis( 

180 axis: ndarray | AxisNoneT, args, kwargs 

181) -> AxisNoneT | None: 

182 """ 

183 If 'NDFrame.clip' is called via the numpy library, the third parameter in 

184 its signature is 'out', which can takes an ndarray, so check if the 'axis' 

185 parameter is an instance of ndarray, since 'axis' itself should either be 

186 an integer or None 

187 """ 

188 if isinstance(axis, ndarray): 

189 args = (axis,) + args 

190 # error: Incompatible types in assignment (expression has type "None", 

191 # variable has type "Union[ndarray[Any, Any], str, int]") 

192 axis = None # type: ignore[assignment] 

193 

194 validate_clip(args, kwargs) 

195 # error: Incompatible return value type (got "Union[ndarray[Any, Any], 

196 # str, int]", expected "Union[str, int, None]") 

197 return axis # type: ignore[return-value] 

198 

199 

200CUM_FUNC_DEFAULTS: dict[str, Any] = {} 

201CUM_FUNC_DEFAULTS["dtype"] = None 

202CUM_FUNC_DEFAULTS["out"] = None 

203validate_cum_func = CompatValidator( 

204 CUM_FUNC_DEFAULTS, method="both", max_fname_arg_count=1 

205) 

206validate_cumsum = CompatValidator( 

207 CUM_FUNC_DEFAULTS, fname="cumsum", method="both", max_fname_arg_count=1 

208) 

209 

210 

211def validate_cum_func_with_skipna(skipna, args, kwargs, name) -> bool: 

212 """ 

213 If this function is called via the 'numpy' library, the third parameter in 

214 its signature is 'dtype', which takes either a 'numpy' dtype or 'None', so 

215 check if the 'skipna' parameter is a boolean or not 

216 """ 

217 if not is_bool(skipna): 

218 args = (skipna,) + args 

219 skipna = True 

220 

221 validate_cum_func(args, kwargs, fname=name) 

222 return skipna 

223 

224 

225ALLANY_DEFAULTS: dict[str, bool | None] = {} 

226ALLANY_DEFAULTS["dtype"] = None 

227ALLANY_DEFAULTS["out"] = None 

228ALLANY_DEFAULTS["keepdims"] = False 

229ALLANY_DEFAULTS["axis"] = None 

230validate_all = CompatValidator( 

231 ALLANY_DEFAULTS, fname="all", method="both", max_fname_arg_count=1 

232) 

233validate_any = CompatValidator( 

234 ALLANY_DEFAULTS, fname="any", method="both", max_fname_arg_count=1 

235) 

236 

237LOGICAL_FUNC_DEFAULTS = {"out": None, "keepdims": False} 

238validate_logical_func = CompatValidator(LOGICAL_FUNC_DEFAULTS, method="kwargs") 

239 

240MINMAX_DEFAULTS = {"axis": None, "out": None, "keepdims": False} 

241validate_min = CompatValidator( 

242 MINMAX_DEFAULTS, fname="min", method="both", max_fname_arg_count=1 

243) 

244validate_max = CompatValidator( 

245 MINMAX_DEFAULTS, fname="max", method="both", max_fname_arg_count=1 

246) 

247 

248RESHAPE_DEFAULTS: dict[str, str] = {"order": "C"} 

249validate_reshape = CompatValidator( 

250 RESHAPE_DEFAULTS, fname="reshape", method="both", max_fname_arg_count=1 

251) 

252 

253REPEAT_DEFAULTS: dict[str, Any] = {"axis": None} 

254validate_repeat = CompatValidator( 

255 REPEAT_DEFAULTS, fname="repeat", method="both", max_fname_arg_count=1 

256) 

257 

258ROUND_DEFAULTS: dict[str, Any] = {"out": None} 

259validate_round = CompatValidator( 

260 ROUND_DEFAULTS, fname="round", method="both", max_fname_arg_count=1 

261) 

262 

263SORT_DEFAULTS: dict[str, int | str | None] = {} 

264SORT_DEFAULTS["axis"] = -1 

265SORT_DEFAULTS["kind"] = "quicksort" 

266SORT_DEFAULTS["order"] = None 

267validate_sort = CompatValidator(SORT_DEFAULTS, fname="sort", method="kwargs") 

268 

269STAT_FUNC_DEFAULTS: dict[str, Any | None] = {} 

270STAT_FUNC_DEFAULTS["dtype"] = None 

271STAT_FUNC_DEFAULTS["out"] = None 

272 

273SUM_DEFAULTS = STAT_FUNC_DEFAULTS.copy() 

274SUM_DEFAULTS["axis"] = None 

275SUM_DEFAULTS["keepdims"] = False 

276SUM_DEFAULTS["initial"] = None 

277 

278PROD_DEFAULTS = STAT_FUNC_DEFAULTS.copy() 

279PROD_DEFAULTS["axis"] = None 

280PROD_DEFAULTS["keepdims"] = False 

281PROD_DEFAULTS["initial"] = None 

282 

283MEDIAN_DEFAULTS = STAT_FUNC_DEFAULTS.copy() 

284MEDIAN_DEFAULTS["overwrite_input"] = False 

285MEDIAN_DEFAULTS["keepdims"] = False 

286 

287STAT_FUNC_DEFAULTS["keepdims"] = False 

288 

289validate_stat_func = CompatValidator(STAT_FUNC_DEFAULTS, method="kwargs") 

290validate_sum = CompatValidator( 

291 SUM_DEFAULTS, fname="sum", method="both", max_fname_arg_count=1 

292) 

293validate_prod = CompatValidator( 

294 PROD_DEFAULTS, fname="prod", method="both", max_fname_arg_count=1 

295) 

296validate_mean = CompatValidator( 

297 STAT_FUNC_DEFAULTS, fname="mean", method="both", max_fname_arg_count=1 

298) 

299validate_median = CompatValidator( 

300 MEDIAN_DEFAULTS, fname="median", method="both", max_fname_arg_count=1 

301) 

302 

303STAT_DDOF_FUNC_DEFAULTS: dict[str, bool | None] = {} 

304STAT_DDOF_FUNC_DEFAULTS["dtype"] = None 

305STAT_DDOF_FUNC_DEFAULTS["out"] = None 

306STAT_DDOF_FUNC_DEFAULTS["keepdims"] = False 

307validate_stat_ddof_func = CompatValidator(STAT_DDOF_FUNC_DEFAULTS, method="kwargs") 

308 

309TAKE_DEFAULTS: dict[str, str | None] = {} 

310TAKE_DEFAULTS["out"] = None 

311TAKE_DEFAULTS["mode"] = "raise" 

312validate_take = CompatValidator(TAKE_DEFAULTS, fname="take", method="kwargs") 

313 

314 

315def validate_take_with_convert(convert: ndarray | bool | None, args, kwargs) -> bool: 

316 """ 

317 If this function is called via the 'numpy' library, the third parameter in 

318 its signature is 'axis', which takes either an ndarray or 'None', so check 

319 if the 'convert' parameter is either an instance of ndarray or is None 

320 """ 

321 if isinstance(convert, ndarray) or convert is None: 

322 args = (convert,) + args 

323 convert = True 

324 

325 validate_take(args, kwargs, max_fname_arg_count=3, method="both") 

326 return convert 

327 

328 

329TRANSPOSE_DEFAULTS = {"axes": None} 

330validate_transpose = CompatValidator( 

331 TRANSPOSE_DEFAULTS, fname="transpose", method="both", max_fname_arg_count=0 

332) 

333 

334 

335def validate_window_func(name, args, kwargs) -> None: 

336 numpy_args = ("axis", "dtype", "out") 

337 msg = ( 

338 f"numpy operations are not valid with window objects. " 

339 f"Use .{name}() directly instead " 

340 ) 

341 

342 if len(args) > 0: 

343 raise UnsupportedFunctionCall(msg) 

344 

345 for arg in numpy_args: 

346 if arg in kwargs: 

347 raise UnsupportedFunctionCall(msg) 

348 

349 

350def validate_rolling_func(name, args, kwargs) -> None: 

351 numpy_args = ("axis", "dtype", "out") 

352 msg = ( 

353 f"numpy operations are not valid with window objects. " 

354 f"Use .rolling(...).{name}() instead " 

355 ) 

356 

357 if len(args) > 0: 

358 raise UnsupportedFunctionCall(msg) 

359 

360 for arg in numpy_args: 

361 if arg in kwargs: 

362 raise UnsupportedFunctionCall(msg) 

363 

364 

365def validate_expanding_func(name, args, kwargs) -> None: 

366 numpy_args = ("axis", "dtype", "out") 

367 msg = ( 

368 f"numpy operations are not valid with window objects. " 

369 f"Use .expanding(...).{name}() instead " 

370 ) 

371 

372 if len(args) > 0: 

373 raise UnsupportedFunctionCall(msg) 

374 

375 for arg in numpy_args: 

376 if arg in kwargs: 

377 raise UnsupportedFunctionCall(msg) 

378 

379 

380def validate_groupby_func(name, args, kwargs, allowed=None) -> None: 

381 """ 

382 'args' and 'kwargs' should be empty, except for allowed kwargs because all 

383 of their necessary parameters are explicitly listed in the function 

384 signature 

385 """ 

386 if allowed is None: 

387 allowed = [] 

388 

389 kwargs = set(kwargs) - set(allowed) 

390 

391 if len(args) + len(kwargs) > 0: 

392 raise UnsupportedFunctionCall( 

393 "numpy operations are not valid with groupby. " 

394 f"Use .groupby(...).{name}() instead" 

395 ) 

396 

397 

398RESAMPLER_NUMPY_OPS = ("min", "max", "sum", "prod", "mean", "std", "var") 

399 

400 

401def validate_resampler_func(method: str, args, kwargs) -> None: 

402 """ 

403 'args' and 'kwargs' should be empty because all of their necessary 

404 parameters are explicitly listed in the function signature 

405 """ 

406 if len(args) + len(kwargs) > 0: 

407 if method in RESAMPLER_NUMPY_OPS: 

408 raise UnsupportedFunctionCall( 

409 "numpy operations are not valid with resample. " 

410 f"Use .resample(...).{method}() instead" 

411 ) 

412 else: 

413 raise TypeError("too many arguments passed in") 

414 

415 

416def validate_minmax_axis(axis: int | None, ndim: int = 1) -> None: 

417 """ 

418 Ensure that the axis argument passed to min, max, argmin, or argmax is zero 

419 or None, as otherwise it will be incorrectly ignored. 

420 

421 Parameters 

422 ---------- 

423 axis : int or None 

424 ndim : int, default 1 

425 

426 Raises 

427 ------ 

428 ValueError 

429 """ 

430 if axis is None: 

431 return 

432 if axis >= ndim or (axis < 0 and ndim + axis < 0): 

433 raise ValueError(f"`axis` must be fewer than the number of dimensions ({ndim})")