Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/faker/providers/python/__init__.py: 23%

208 statements  

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

1import math 

2import string 

3import sys 

4import warnings 

5 

6from decimal import Decimal 

7from enum import Enum 

8from typing import Any, Dict, Iterable, Iterator, List, Optional, Set, Tuple, Type, TypeVar, Union, cast, no_type_check 

9 

10from ...exceptions import BaseFakerException 

11from .. import BaseProvider, ElementsType 

12 

13TypesNames = List[str] 

14TypesSpec = Union[List[Type], Tuple[Type, ...]] 

15TEnum = TypeVar("TEnum", bound=Enum) 

16 

17 

18class EmptyEnumException(BaseFakerException): 

19 pass 

20 

21 

22class Provider(BaseProvider): 

23 default_value_types: ElementsType[str] = ( 

24 "str", 

25 "str", 

26 "str", 

27 "str", 

28 "float", 

29 "int", 

30 "int", 

31 "decimal", 

32 "date_time", 

33 "uri", 

34 "email", 

35 ) 

36 

37 def _check_signature(self, value_types: Optional[TypesSpec], allowed_types: Optional[TypesSpec]) -> TypesSpec: 

38 if value_types is not None and not isinstance(value_types, (list, tuple)): 

39 value_types = (value_types,) 

40 warnings.warn( 

41 "Passing `value_types` as positional arguments is going to be " 

42 "deprecated. Pass them as a list or tuple instead.", 

43 PendingDeprecationWarning, 

44 ) 

45 if allowed_types is not None and not isinstance(allowed_types, (list, tuple)): 

46 allowed_types = (allowed_types,) 

47 warnings.warn( 

48 "Passing `allowed_types` as positional arguments is going to be " 

49 "deprecated. Pass them as a list or tuple instead.", 

50 PendingDeprecationWarning, 

51 ) 

52 if value_types is None: 

53 value_types = () 

54 if allowed_types is None: 

55 allowed_types = () 

56 return tuple(value_types) + tuple(allowed_types) 

57 

58 def pybool(self) -> bool: 

59 return self.random_int(0, 1) == 1 

60 

61 def pystr( 

62 self, 

63 min_chars: Optional[int] = None, 

64 max_chars: int = 20, 

65 prefix: str = "", 

66 suffix: str = "", 

67 ) -> str: 

68 """ 

69 Generates a random string of upper and lowercase letters. 

70 :return: Random of random length between min and max characters. 

71 """ 

72 if min_chars is None: 

73 chars = "".join(self.random_letters(length=max_chars)) 

74 else: 

75 assert max_chars >= min_chars, "Maximum length must be greater than or equal to minimum length" 

76 chars = "".join( 

77 self.random_letters( 

78 length=self.generator.random.randint(min_chars, max_chars), 

79 ), 

80 ) 

81 

82 return prefix + chars + suffix 

83 

84 def pystr_format( 

85 self, 

86 string_format: str = "?#-###{{random_int}}{{random_letter}}", 

87 letters: str = string.ascii_letters, 

88 ) -> str: 

89 return self.bothify(self.generator.parse(string_format), letters=letters) 

90 

91 def pyfloat( 

92 self, 

93 left_digits=None, 

94 right_digits=None, 

95 positive=False, 

96 min_value=None, 

97 max_value=None, 

98 ): 

99 if left_digits is not None and left_digits < 0: 

100 raise ValueError("A float number cannot have less than 0 digits in its " "integer part") 

101 if right_digits is not None and right_digits < 0: 

102 raise ValueError("A float number cannot have less than 0 digits in its " "fractional part") 

103 if left_digits == 0 and right_digits == 0: 

104 raise ValueError("A float number cannot have less than 0 digits in total") 

105 if None not in (min_value, max_value) and min_value > max_value: 

106 raise ValueError("Min value cannot be greater than max value") 

107 if None not in (min_value, max_value) and min_value == max_value: 

108 raise ValueError("Min and max value cannot be the same") 

109 if positive and min_value is not None and min_value <= 0: 

110 raise ValueError("Cannot combine positive=True with negative or zero min_value") 

111 if left_digits is not None and max_value and math.ceil(math.log10(abs(max_value))) > left_digits: 

112 raise ValueError("Max value must fit within left digits") 

113 if left_digits is not None and min_value and math.ceil(math.log10(abs(min_value))) > left_digits: 

114 raise ValueError("Min value must fit within left digits") 

115 

116 # Make sure at least either left or right is set 

117 if left_digits is None and right_digits is None: 

118 needed_left_digits = max(1, math.ceil(math.log10(max(abs(max_value or 1), abs(min_value or 1))))) 

119 right_digits = self.random_int(1, sys.float_info.dig - needed_left_digits) 

120 

121 # If only one side is set, choose #digits for other side 

122 if (left_digits is None) ^ (right_digits is None): 

123 if left_digits is None: 

124 left_digits = max(1, sys.float_info.dig - right_digits) 

125 else: 

126 right_digits = max(1, sys.float_info.dig - left_digits) 

127 

128 # Make sure we don't ask for too many digits! 

129 if left_digits + right_digits > sys.float_info.dig: 

130 raise ValueError( 

131 f"Asking for too many digits ({left_digits} + {right_digits} == {left_digits + right_digits} > " 

132 f"{sys.float_info.dig})", 

133 ) 

134 

135 sign = "" 

136 if (min_value is not None) or (max_value is not None): 

137 # Make sure left_digits still respected 

138 if left_digits is not None: 

139 if max_value is None: 

140 max_value = 10**left_digits # minus smallest representable, adjusted later 

141 if min_value is None: 

142 min_value = -(10**left_digits) # plus smallest representable, adjusted later 

143 

144 if max_value is not None and max_value < 0: 

145 max_value += 1 # as the random_int will be generated up to max_value - 1 

146 if min_value is not None and min_value < 0: 

147 min_value += 1 # as we then append digits after the left_number 

148 left_number = self._safe_random_int( 

149 min_value, 

150 max_value, 

151 positive, 

152 ) 

153 else: 

154 sign = "+" if positive else self.random_element(("+", "-")) 

155 left_number = self.random_number(left_digits) 

156 

157 result = float(f"{sign}{left_number}.{self.random_number(right_digits)}") 

158 if positive and result == 0: 

159 if right_digits: 

160 result = float("0." + "0" * (right_digits - 1) + "1") 

161 else: 

162 result += sys.float_info.epsilon 

163 

164 if right_digits: 

165 result = min(result, 10**left_digits - float(f'0.{"0" * (right_digits - 1)}1')) 

166 result = max(result, -(10**left_digits + float(f'0.{"0" * (right_digits - 1)}1'))) 

167 else: 

168 result = min(result, 10**left_digits - 1) 

169 result = max(result, -(10**left_digits + 1)) 

170 

171 # It's possible for the result to end up > than max_value 

172 # This is a quick hack to ensure result is always smaller. 

173 if max_value is not None: 

174 if result > max_value: 

175 result = result - (result - max_value) 

176 return result 

177 

178 def _safe_random_int(self, min_value: float, max_value: float, positive: bool) -> int: 

179 orig_min_value = min_value 

180 orig_max_value = max_value 

181 

182 if min_value is None: 

183 min_value = max_value - self.random_int() 

184 if max_value is None: 

185 max_value = min_value + self.random_int() 

186 if positive: 

187 min_value = max(min_value, 0) 

188 

189 if min_value == max_value: 

190 return self._safe_random_int(orig_min_value, orig_max_value, positive) 

191 else: 

192 min_value = int(min_value) 

193 max_value = int(max_value - 1) 

194 if max_value < min_value: 

195 max_value += 1 

196 return self.random_int(min_value, max_value) 

197 

198 def pyint(self, min_value: int = 0, max_value: int = 9999, step: int = 1) -> int: 

199 return self.generator.random_int(min_value, max_value, step=step) 

200 

201 def pydecimal( 

202 self, 

203 left_digits=None, 

204 right_digits=None, 

205 positive=False, 

206 min_value=None, 

207 max_value=None, 

208 ): 

209 if left_digits is not None and left_digits < 0: 209 ↛ 210line 209 didn't jump to line 210, because the condition on line 209 was never true

210 raise ValueError("A decimal number cannot have less than 0 digits in its " "integer part") 

211 if right_digits is not None and right_digits < 0: 211 ↛ 212line 211 didn't jump to line 212, because the condition on line 211 was never true

212 raise ValueError("A decimal number cannot have less than 0 digits in its " "fractional part") 

213 if (left_digits is not None and left_digits == 0) and (right_digits is not None and right_digits == 0): 213 ↛ 214line 213 didn't jump to line 214, because the condition on line 213 was never true

214 raise ValueError("A decimal number cannot have 0 digits in total") 

215 if None not in (min_value, max_value) and min_value > max_value: 215 ↛ 216line 215 didn't jump to line 216, because the condition on line 215 was never true

216 raise ValueError("Min value cannot be greater than max value") 

217 if None not in (min_value, max_value) and min_value == max_value: 217 ↛ 218line 217 didn't jump to line 218, because the condition on line 217 was never true

218 raise ValueError("Min and max value cannot be the same") 

219 if positive and min_value is not None and min_value <= 0: 219 ↛ 220line 219 didn't jump to line 220, because the condition on line 219 was never true

220 raise ValueError("Cannot combine positive=True with negative or zero min_value") 

221 if left_digits is not None and max_value and math.ceil(math.log10(abs(max_value))) > left_digits: 221 ↛ 222line 221 didn't jump to line 222, because the condition on line 221 was never true

222 raise ValueError("Max value must fit within left digits") 

223 if left_digits is not None and min_value and math.ceil(math.log10(abs(min_value))) > left_digits: 223 ↛ 224line 223 didn't jump to line 224, because the condition on line 223 was never true

224 raise ValueError("Min value must fit within left digits") 

225 

226 # if either left or right digits are not specified we randomly choose a length 

227 max_random_digits = 100 

228 # Because if min_value is bigger than 10**100 

229 max_digits_from_value = max( 

230 math.ceil(math.log10(abs(min_value or 1))), 

231 math.ceil(math.log10(abs(max_value or 1))), 

232 ) 

233 max_left_random_digits = max(max_random_digits, max_digits_from_value + 10) 

234 

235 if min_value is not None and min_value >= 0: 235 ↛ 237line 235 didn't jump to line 237, because the condition on line 235 was never false

236 sign = "+" 

237 elif max_value is not None and max_value <= 0: 

238 sign = "-" 

239 else: 

240 sign = "+" if positive else self.random_element(("+", "-")) 

241 

242 if sign == "+": 242 ↛ 251line 242 didn't jump to line 251, because the condition on line 242 was never false

243 if max_value is not None: 243 ↛ 246line 243 didn't jump to line 246, because the condition on line 243 was never false

244 left_number = str(self.random_int(max(min_value or 0, 0), max_value)) 

245 else: 

246 min_left_digits = math.ceil(math.log10(max(min_value or 1, 1))) 

247 if left_digits is None: 

248 left_digits = self.random_int(min_left_digits, max_left_random_digits) 

249 left_number = "".join([str(self.random_digit()) for i in range(0, left_digits)]) or "0" 

250 else: 

251 if min_value is not None: 

252 left_number = str(self.random_int(max(max_value or 0, 0), abs(min_value))) 

253 else: 

254 min_left_digits = math.ceil(math.log10(abs(min(max_value or 1, 1)))) 

255 if left_digits is None: 

256 left_digits = self.random_int(min_left_digits, max_left_random_digits) 

257 left_number = "".join([str(self.random_digit()) for i in range(0, left_digits)]) or "0" 

258 

259 if right_digits is None: 259 ↛ 262line 259 didn't jump to line 262, because the condition on line 259 was never false

260 right_digits = self.random_int(0, max_random_digits) 

261 

262 right_number = "".join([str(self.random_digit()) for i in range(0, right_digits)]) 

263 

264 result = Decimal(f"{sign}{left_number}.{right_number}") 

265 

266 # Because the random result might have the same number of decimals as max_value the random number 

267 # might be above max_value or below min_value 

268 if max_value is not None and result > max_value: 268 ↛ 269line 268 didn't jump to line 269, because the condition on line 268 was never true

269 result = Decimal(max_value) 

270 if min_value is not None and result < min_value: 270 ↛ 271line 270 didn't jump to line 271, because the condition on line 270 was never true

271 result = Decimal(min_value) 

272 

273 return result 

274 

275 def pytuple( 

276 self, 

277 nb_elements: int = 10, 

278 variable_nb_elements: bool = True, 

279 value_types: Optional[TypesSpec] = None, 

280 allowed_types: Optional[TypesSpec] = None, 

281 ) -> Tuple[Any, ...]: 

282 return tuple( 

283 self._pyiterable( 

284 nb_elements=nb_elements, 

285 variable_nb_elements=variable_nb_elements, 

286 value_types=value_types, 

287 allowed_types=allowed_types, 

288 ) 

289 ) 

290 

291 def pyset( 

292 self, 

293 nb_elements: int = 10, 

294 variable_nb_elements: bool = True, 

295 value_types: Optional[TypesSpec] = None, 

296 allowed_types: Optional[TypesSpec] = None, 

297 ) -> Set[Any]: 

298 return set( 

299 self._pyiterable( 

300 nb_elements=nb_elements, 

301 variable_nb_elements=variable_nb_elements, 

302 value_types=value_types, 

303 allowed_types=allowed_types, 

304 ) 

305 ) 

306 

307 def pylist( 

308 self, 

309 nb_elements: int = 10, 

310 variable_nb_elements: bool = True, 

311 value_types: Optional[TypesSpec] = None, 

312 allowed_types: Optional[TypesSpec] = None, 

313 ) -> List[Any]: 

314 return list( 

315 self._pyiterable( 

316 nb_elements=nb_elements, 

317 variable_nb_elements=variable_nb_elements, 

318 value_types=value_types, 

319 allowed_types=allowed_types, 

320 ) 

321 ) 

322 

323 @no_type_check 

324 def pyiterable( 

325 self, 

326 nb_elements: int = 10, 

327 variable_nb_elements: bool = True, 

328 value_types: Optional[TypesSpec] = None, 

329 allowed_types: Optional[TypesSpec] = None, 

330 ) -> Iterable[Any]: 

331 value_types: TypesSpec = self._check_signature(value_types, allowed_types) 

332 return self.random_element([self.pylist, self.pytuple, self.pyset])( 

333 nb_elements=nb_elements, 

334 variable_nb_elements=variable_nb_elements, 

335 value_types=value_types, 

336 allowed_types=allowed_types, 

337 ) 

338 

339 def _random_type(self, type_list: List[str]) -> str: 

340 value_type: str = self.random_element(type_list) 

341 

342 method_name = f"py{value_type}" 

343 if hasattr(self, method_name): 

344 value_type = method_name 

345 

346 return self.generator.format(value_type) 

347 

348 def _pyiterable( 

349 self, 

350 nb_elements: int = 10, 

351 variable_nb_elements: bool = True, 

352 value_types: Optional[TypesSpec] = None, 

353 allowed_types: Optional[TypesSpec] = None, 

354 ) -> Iterator: 

355 

356 value_types: TypesSpec = self._check_signature(value_types, allowed_types) 

357 

358 value_types: TypesNames = [ 

359 t if isinstance(t, str) else getattr(t, "__name__", type(t).__name__).lower() 

360 for t in value_types 

361 # avoid recursion 

362 if t not in ["iterable", "list", "tuple", "dict", "set"] 

363 ] 

364 if not value_types: 

365 value_types = self.default_value_types # type: ignore 

366 

367 if variable_nb_elements: 

368 nb_elements = self.randomize_nb_elements(nb_elements, min=1) 

369 

370 for _ in range(nb_elements): 

371 yield self._random_type(value_types) 

372 

373 def pydict( 

374 self, 

375 nb_elements: int = 10, 

376 variable_nb_elements: bool = True, 

377 value_types: Optional[TypesSpec] = None, 

378 allowed_types: Optional[TypesSpec] = None, 

379 ) -> Dict[Any, Any]: 

380 """ 

381 Returns a dictionary. 

382 

383 :nb_elements: number of elements for dictionary 

384 :variable_nb_elements: is use variable number of elements for dictionary 

385 :value_types: type of dictionary values 

386 """ 

387 if variable_nb_elements: 

388 nb_elements = self.randomize_nb_elements(nb_elements, min=1) 

389 

390 return dict( 

391 zip( 

392 self.generator.words(nb_elements, unique=True), 

393 self._pyiterable( 

394 nb_elements=nb_elements, 

395 variable_nb_elements=False, 

396 value_types=value_types, 

397 allowed_types=allowed_types, 

398 ), 

399 ) 

400 ) 

401 

402 def pystruct( 

403 self, 

404 count: int = 10, 

405 value_types: Optional[TypesSpec] = None, 

406 allowed_types: Optional[TypesSpec] = None, 

407 ) -> Tuple[List, Dict, Dict]: 

408 value_types: TypesSpec = self._check_signature(value_types, allowed_types) 

409 

410 value_types: TypesNames = [ 

411 t if isinstance(t, str) else getattr(t, "__name__", type(t).__name__).lower() 

412 for t in value_types 

413 # avoid recursion 

414 if t != "struct" 

415 ] 

416 if not value_types: 

417 value_types = self.default_value_types # type: ignore 

418 

419 types = [] 

420 d = {} 

421 nd = {} 

422 for i in range(count): 

423 d[self.generator.word()] = self._random_type(value_types) 

424 types.append(self._random_type(value_types)) 

425 nd[self.generator.word()] = { 

426 i: self._random_type(value_types), 

427 i 

428 + 1: [ 

429 self._random_type(value_types), 

430 self._random_type(value_types), 

431 self._random_type(value_types), 

432 ], 

433 i 

434 + 2: { 

435 i: self._random_type(value_types), 

436 i + 1: self._random_type(value_types), 

437 i 

438 + 2: [ 

439 self._random_type(value_types), 

440 self._random_type(value_types), 

441 ], 

442 }, 

443 } 

444 return types, d, nd 

445 

446 def enum(self, enum_cls: Type[TEnum]) -> TEnum: 

447 """ 

448 Returns a random enum of the provided input `Enum` type. 

449 

450 :param enum_cls: The `Enum` type to produce the value for. 

451 :returns: A randomly selected enum value. 

452 """ 

453 

454 if enum_cls is None: 

455 raise ValueError("'enum_cls' cannot be None") 

456 

457 if not issubclass(enum_cls, Enum): 

458 raise TypeError("'enum_cls' must be an Enum type") 

459 

460 members: List[TEnum] = list(cast(Iterable[TEnum], enum_cls)) 

461 

462 if len(members) < 1: 

463 raise EmptyEnumException(f"The provided Enum: '{enum_cls.__name__}' has no members.") 

464 

465 return self.random_element(members)