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

128 statements  

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

1import re 

2import string 

3 

4from collections import OrderedDict 

5from typing import Any, Collection, List, Optional, Sequence, TypeVar, Union 

6 

7from ..generator import Generator 

8from ..typing import OrderedDictType 

9from ..utils.distribution import choices_distribution, choices_distribution_unique 

10 

11_re_hash = re.compile(r"#") 

12_re_perc = re.compile(r"%") 

13_re_excl = re.compile(r"!") 

14_re_at = re.compile(r"@") 

15_re_qm = re.compile(r"\?") 

16_re_cir = re.compile(r"\^") 

17 

18T = TypeVar("T") 

19ElementsType = Union[Collection[str], Collection[T], OrderedDictType[T, float]] 

20 

21 

22class BaseProvider: 

23 

24 __provider__ = "base" 

25 __lang__: Optional[str] = None 

26 __use_weighting__ = False 

27 

28 # Locales supported by Linux Mint from `/usr/share/i18n/SUPPORTED` 

29 language_locale_codes = { 

30 "aa": ("DJ", "ER", "ET"), 

31 "af": ("ZA",), 

32 "ak": ("GH",), 

33 "am": ("ET",), 

34 "an": ("ES",), 

35 "apn": ("IN",), 

36 "ar": ( 

37 "AE", 

38 "BH", 

39 "DJ", 

40 "DZ", 

41 "EG", 

42 "EH", 

43 "ER", 

44 "IL", 

45 "IN", 

46 "IQ", 

47 "JO", 

48 "KM", 

49 "KW", 

50 "LB", 

51 "LY", 

52 "MA", 

53 "MR", 

54 "OM", 

55 "PS", 

56 "QA", 

57 "SA", 

58 "SD", 

59 "SO", 

60 "SS", 

61 "SY", 

62 "TD", 

63 "TN", 

64 "YE", 

65 ), 

66 "as": ("IN",), 

67 "ast": ("ES",), 

68 "ayc": ("PE",), 

69 "az": ("AZ", "IN"), 

70 "be": ("BY",), 

71 "bem": ("ZM",), 

72 "ber": ("DZ", "MA"), 

73 "bg": ("BG",), 

74 "bhb": ("IN",), 

75 "bho": ("IN",), 

76 "bn": ("BD", "IN"), 

77 "bo": ("CN", "IN"), 

78 "br": ("FR",), 

79 "brx": ("IN",), 

80 "bs": ("BA",), 

81 "byn": ("ER",), 

82 "ca": ("AD", "ES", "FR", "IT"), 

83 "ce": ("RU",), 

84 "ckb": ("IQ",), 

85 "cmn": ("TW",), 

86 "crh": ("UA",), 

87 "cs": ("CZ",), 

88 "csb": ("PL",), 

89 "cv": ("RU",), 

90 "cy": ("GB",), 

91 "da": ("DK",), 

92 "de": ("AT", "BE", "CH", "DE", "LI", "LU"), 

93 "doi": ("IN",), 

94 "dv": ("MV",), 

95 "dz": ("BT",), 

96 "el": ("GR", "CY"), 

97 "en": ( 

98 "AG", 

99 "AU", 

100 "BW", 

101 "CA", 

102 "DK", 

103 "GB", 

104 "HK", 

105 "IE", 

106 "IN", 

107 "NG", 

108 "NZ", 

109 "PH", 

110 "SG", 

111 "US", 

112 "ZA", 

113 "ZM", 

114 "ZW", 

115 ), 

116 "eo": ("US",), 

117 "es": ( 

118 "AR", 

119 "BO", 

120 "CL", 

121 "CO", 

122 "CR", 

123 "CU", 

124 "DO", 

125 "EC", 

126 "ES", 

127 "GT", 

128 "HN", 

129 "MX", 

130 "NI", 

131 "PA", 

132 "PE", 

133 "PR", 

134 "PY", 

135 "SV", 

136 "US", 

137 "UY", 

138 "VE", 

139 ), 

140 "et": ("EE",), 

141 "eu": ("ES", "FR"), 

142 "fa": ("IR",), 

143 "ff": ("SN",), 

144 "fi": ("FI",), 

145 "fil": ("PH",), 

146 "fo": ("FO",), 

147 "fr": ("CA", "CH", "FR", "LU"), 

148 "fur": ("IT",), 

149 "fy": ("NL", "DE"), 

150 "ga": ("IE",), 

151 "gd": ("GB",), 

152 "gez": ("ER", "ET"), 

153 "gl": ("ES",), 

154 "gu": ("IN",), 

155 "gv": ("GB",), 

156 "ha": ("NG",), 

157 "hak": ("TW",), 

158 "he": ("IL",), 

159 "hi": ("IN",), 

160 "hne": ("IN",), 

161 "hr": ("HR",), 

162 "hsb": ("DE",), 

163 "ht": ("HT",), 

164 "hu": ("HU",), 

165 "hy": ("AM",), 

166 "ia": ("FR",), 

167 "id": ("ID",), 

168 "ig": ("NG",), 

169 "ik": ("CA",), 

170 "is": ("IS",), 

171 "it": ("CH", "IT"), 

172 "iu": ("CA",), 

173 "iw": ("IL",), 

174 "ja": ("JP",), 

175 "ka": ("GE",), 

176 "kk": ("KZ",), 

177 "kl": ("GL",), 

178 "km": ("KH",), 

179 "kn": ("IN",), 

180 "ko": ("KR",), 

181 "kok": ("IN",), 

182 "ks": ("IN",), 

183 "ku": ("TR",), 

184 "kw": ("GB",), 

185 "ky": ("KG",), 

186 "lb": ("LU",), 

187 "lg": ("UG",), 

188 "li": ("BE", "NL"), 

189 "lij": ("IT",), 

190 "ln": ("CD",), 

191 "lo": ("LA",), 

192 "lt": ("LT",), 

193 "lv": ("LV",), 

194 "lzh": ("TW",), 

195 "mag": ("IN",), 

196 "mai": ("IN",), 

197 "mg": ("MG",), 

198 "mhr": ("RU",), 

199 "mi": ("NZ",), 

200 "mk": ("MK",), 

201 "ml": ("IN",), 

202 "mn": ("MN",), 

203 "mni": ("IN",), 

204 "mr": ("IN",), 

205 "ms": ("MY",), 

206 "mt": ("MT",), 

207 "my": ("MM",), 

208 "nan": ("TW",), 

209 "nb": ("NO",), 

210 "nds": ("DE", "NL"), 

211 "ne": ("NP",), 

212 "nhn": ("MX",), 

213 "niu": ("NU", "NZ"), 

214 "nl": ("AW", "BE", "NL"), 

215 "nn": ("NO",), 

216 "nr": ("ZA",), 

217 "nso": ("ZA",), 

218 "oc": ("FR",), 

219 "om": ("ET", "KE"), 

220 "or": ("IN",), 

221 "os": ("RU",), 

222 "pa": ("IN", "PK"), 

223 "pap": ("AN", "AW", "CW"), 

224 "pl": ("PL",), 

225 "ps": ("AF",), 

226 "pt": ("BR", "PT"), 

227 "quz": ("PE",), 

228 "raj": ("IN",), 

229 "ro": ("RO",), 

230 "ru": ("RU", "UA"), 

231 "rw": ("RW",), 

232 "sa": ("IN",), 

233 "sat": ("IN",), 

234 "sc": ("IT",), 

235 "sd": ("IN", "PK"), 

236 "se": ("NO",), 

237 "shs": ("CA",), 

238 "si": ("LK",), 

239 "sid": ("ET",), 

240 "sk": ("SK",), 

241 "sl": ("SI",), 

242 "so": ("DJ", "ET", "KE", "SO"), 

243 "sq": ("AL", "ML"), 

244 "sr": ("ME", "RS"), 

245 "ss": ("ZA",), 

246 "st": ("ZA",), 

247 "sv": ("FI", "SE"), 

248 "sw": ("KE", "TZ"), 

249 "szl": ("PL",), 

250 "ta": ("IN", "LK"), 

251 "tcy": ("IN",), 

252 "te": ("IN",), 

253 "tg": ("TJ",), 

254 "th": ("TH",), 

255 "the": ("NP",), 

256 "ti": ("ER", "ET"), 

257 "tig": ("ER",), 

258 "tk": ("TM",), 

259 "tl": ("PH",), 

260 "tn": ("ZA",), 

261 "tr": ("CY", "TR"), 

262 "ts": ("ZA",), 

263 "tt": ("RU",), 

264 "ug": ("CN",), 

265 "uk": ("UA",), 

266 "unm": ("US",), 

267 "ur": ("IN", "PK"), 

268 "uz": ("UZ",), 

269 "ve": ("ZA",), 

270 "vi": ("VN",), 

271 "wa": ("BE",), 

272 "wae": ("CH",), 

273 "wal": ("ET",), 

274 "wo": ("SN",), 

275 "xh": ("ZA",), 

276 "yi": ("US",), 

277 "yo": ("NG",), 

278 "yue": ("HK",), 

279 "zh": ("CN", "HK", "SG", "TW"), 

280 "zu": ("ZA",), 

281 } 

282 

283 def __init__(self, generator: Any) -> None: 

284 """ 

285 Base class for fake data providers 

286 :param generator: `Generator` instance 

287 """ 

288 self.generator = generator 

289 

290 def locale(self) -> str: 

291 """Generate a random underscored i18n locale code (e.g. en_US).""" 

292 

293 language_code = self.language_code() 

294 return ( 

295 language_code 

296 + "_" 

297 + self.random_element( 

298 BaseProvider.language_locale_codes[language_code], 

299 ) 

300 ) 

301 

302 def language_code(self) -> str: 

303 """Generate a random i18n language code (e.g. en).""" 

304 

305 return self.random_element(BaseProvider.language_locale_codes.keys()) 

306 

307 def random_int(self, min: int = 0, max: int = 9999, step: int = 1) -> int: 

308 """Generate a random integer between two integers ``min`` and ``max`` inclusive 

309 while observing the provided ``step`` value. 

310 

311 This method is functionally equivalent to randomly sampling an integer 

312 from the sequence ``range(min, max + 1, step)``. 

313 

314 :sample: min=0, max=15 

315 :sample: min=0, max=15, step=3 

316 """ 

317 return self.generator.random.randrange(min, max + 1, step) 

318 

319 def random_digit(self) -> int: 

320 """Generate a random digit (0 to 9).""" 

321 

322 return self.generator.random.randint(0, 9) 

323 

324 def random_digit_not_null(self) -> int: 

325 """Generate a random non-zero digit (1 to 9).""" 

326 

327 return self.generator.random.randint(1, 9) 

328 

329 def random_digit_or_empty(self) -> Union[int, str]: 

330 """Generate a random digit (0 to 9) or an empty string. 

331 

332 This method will return an empty string 50% of the time, 

333 and each digit has a 1/20 chance of being generated. 

334 """ 

335 

336 if self.generator.random.randint(0, 1): 

337 return self.generator.random.randint(0, 9) 

338 else: 

339 return "" 

340 

341 def random_digit_not_null_or_empty(self) -> Union[int, str]: 

342 """Generate a random non-zero digit (1 to 9) or an empty string. 

343 

344 This method will return an empty string 50% of the time, 

345 and each digit has a 1/18 chance of being generated. 

346 """ 

347 

348 if self.generator.random.randint(0, 1): 

349 return self.generator.random.randint(1, 9) 

350 else: 

351 return "" 

352 

353 def random_number(self, digits: Optional[int] = None, fix_len: bool = False) -> int: 

354 """Generate a random integer according to the following rules: 

355 

356 - If ``digits`` is ``None`` (default), its value will be set to a random 

357 integer from 1 to 9. 

358 - If ``fix_len`` is ``False`` (default), all integers that do not exceed 

359 the number of ``digits`` can be generated. 

360 - If ``fix_len`` is ``True``, only integers with the exact number of 

361 ``digits`` can be generated. 

362 

363 :sample: fix_len=False 

364 :sample: fix_len=True 

365 :sample: digits=3 

366 :sample: digits=3, fix_len=False 

367 :sample: digits=3, fix_len=True 

368 """ 

369 if digits is None: 

370 digits = self.random_digit_not_null() 

371 if digits < 0: 

372 raise ValueError("The digit parameter must be greater than or equal to 0.") 

373 if fix_len: 

374 if digits > 0: 

375 return self.generator.random.randint(pow(10, digits - 1), pow(10, digits) - 1) 

376 else: 

377 raise ValueError("A number of fixed length cannot have less than 1 digit in it.") 

378 else: 

379 return self.generator.random.randint(0, pow(10, digits) - 1) 

380 

381 def random_letter(self) -> str: 

382 """Generate a random ASCII letter (a-z and A-Z).""" 

383 

384 return self.generator.random.choice(getattr(string, "letters", string.ascii_letters)) 

385 

386 def random_letters(self, length: int = 16) -> Sequence[str]: 

387 """Generate a list of random ASCII letters (a-z and A-Z) of the specified ``length``. 

388 

389 :sample: length=10 

390 """ 

391 return self.random_choices( 

392 getattr(string, "letters", string.ascii_letters), 

393 length=length, 

394 ) 

395 

396 def random_lowercase_letter(self) -> str: 

397 """Generate a random lowercase ASCII letter (a-z).""" 

398 

399 return self.generator.random.choice(string.ascii_lowercase) 

400 

401 def random_uppercase_letter(self) -> str: 

402 """Generate a random uppercase ASCII letter (A-Z).""" 

403 

404 return self.generator.random.choice(string.ascii_uppercase) 

405 

406 def random_elements( 

407 self, 

408 elements: ElementsType[T] = ("a", "b", "c"), # type: ignore[assignment] 

409 length: Optional[int] = None, 

410 unique: bool = False, 

411 use_weighting: Optional[bool] = None, 

412 ) -> Sequence[T]: 

413 """Generate a list of randomly sampled objects from ``elements``. 

414 

415 Set ``unique`` to ``False`` for random sampling with replacement, and set ``unique`` to 

416 ``True`` for random sampling without replacement. 

417 

418 If ``length`` is set to ``None`` or is omitted, ``length`` will be set to a random 

419 integer from 1 to the size of ``elements``. 

420 

421 The value of ``length`` cannot be greater than the number of objects 

422 in ``elements`` if ``unique`` is set to ``True``. 

423 

424 The value of ``elements`` can be any sequence type (``list``, ``tuple``, ``set``, 

425 ``string``, etc) or an ``OrderedDict`` type. If it is the latter, the keys will be 

426 used as the objects for sampling, and the values will be used as weighted probabilities 

427 if ``unique`` is set to ``False``. For example: 

428 

429 .. code-block:: python 

430 

431 # Random sampling with replacement 

432 fake.random_elements( 

433 elements=OrderedDict([ 

434 ("variable_1", 0.5), # Generates "variable_1" 50% of the time 

435 ("variable_2", 0.2), # Generates "variable_2" 20% of the time 

436 ("variable_3", 0.2), # Generates "variable_3" 20% of the time 

437 ("variable_4": 0.1), # Generates "variable_4" 10% of the time 

438 ]), unique=False 

439 ) 

440 

441 # Random sampling without replacement (defaults to uniform distribution) 

442 fake.random_elements( 

443 elements=OrderedDict([ 

444 ("variable_1", 0.5), 

445 ("variable_2", 0.2), 

446 ("variable_3", 0.2), 

447 ("variable_4": 0.1), 

448 ]), unique=True 

449 ) 

450 

451 :sample: elements=('a', 'b', 'c', 'd'), unique=False 

452 :sample: elements=('a', 'b', 'c', 'd'), unique=True 

453 :sample: elements=('a', 'b', 'c', 'd'), length=10, unique=False 

454 :sample: elements=('a', 'b', 'c', 'd'), length=4, unique=True 

455 :sample: elements=OrderedDict([ 

456 ("a", 0.45), 

457 ("b", 0.35), 

458 ("c", 0.15), 

459 ("d", 0.05), 

460 ]), length=20, unique=False 

461 :sample: elements=OrderedDict([ 

462 ("a", 0.45), 

463 ("b", 0.35), 

464 ("c", 0.15), 

465 ("d", 0.05), 

466 ]), unique=True 

467 """ 

468 use_weighting = use_weighting if use_weighting is not None else self.__use_weighting__ 

469 

470 if isinstance(elements, dict) and not isinstance(elements, OrderedDict): 470 ↛ 471line 470 didn't jump to line 471, because the condition on line 470 was never true

471 raise ValueError("Use OrderedDict only to avoid dependency on PYTHONHASHSEED (See #363).") 

472 

473 fn = choices_distribution_unique if unique else choices_distribution 

474 

475 if length is None: 475 ↛ 476line 475 didn't jump to line 476, because the condition on line 475 was never true

476 length = self.generator.random.randint(1, len(elements)) 

477 

478 if unique and length > len(elements): 478 ↛ 479line 478 didn't jump to line 479, because the condition on line 478 was never true

479 raise ValueError("Sample length cannot be longer than the number of unique elements to pick from.") 

480 

481 if isinstance(elements, dict): 

482 if not hasattr(elements, "_key_cache"): 

483 elements._key_cache = tuple(elements.keys()) # type: ignore 

484 

485 choices = elements._key_cache # type: ignore[attr-defined, union-attr] 

486 probabilities = tuple(elements.values()) if use_weighting else None 

487 else: 

488 if unique: 488 ↛ 490line 488 didn't jump to line 490, because the condition on line 488 was never true

489 # shortcut 

490 return self.generator.random.sample(elements, length) 

491 choices = elements 

492 probabilities = None 

493 

494 return fn( 

495 tuple(choices), 

496 probabilities, 

497 self.generator.random, 

498 length=length, 

499 ) 

500 

501 def random_choices( 

502 self, 

503 elements: ElementsType[T] = ("a", "b", "c"), # type: ignore[assignment] 

504 length: Optional[int] = None, 

505 ) -> Sequence[T]: 

506 """Generate a list of objects randomly sampled from ``elements`` with replacement. 

507 

508 For information on the ``elements`` and ``length`` arguments, please refer to 

509 :meth:`random_elements() <faker.providers.BaseProvider.random_elements>` which 

510 is used under the hood with the ``unique`` argument explicitly set to ``False``. 

511 

512 :sample: elements=('a', 'b', 'c', 'd') 

513 :sample: elements=('a', 'b', 'c', 'd'), length=10 

514 :sample: elements=OrderedDict([ 

515 ("a", 0.45), 

516 ("b", 0.35), 

517 ("c", 0.15), 

518 ("d", 0.05), 

519 ]) 

520 :sample: elements=OrderedDict([ 

521 ("a", 0.45), 

522 ("b", 0.35), 

523 ("c", 0.15), 

524 ("d", 0.05), 

525 ]), length=20 

526 """ 

527 return self.random_elements(elements, length, unique=False) 

528 

529 def random_element(self, elements: ElementsType[T] = ("a", "b", "c")) -> T: 

530 """Generate a randomly sampled object from ``elements``. 

531 

532 For information on the ``elements`` argument, please refer to 

533 :meth:`random_elements() <faker.providers.BaseProvider.random_elements>` which 

534 is used under the hood with the ``unique`` argument set to ``False`` and the 

535 ``length`` argument set to ``1``. 

536 

537 :sample: elements=('a', 'b', 'c', 'd') 

538 :sample size=10: elements=OrderedDict([ 

539 ("a", 0.45), 

540 ("b", 0.35), 

541 ("c", 0.15), 

542 ("d", 0.05), 

543 ]) 

544 """ 

545 

546 return self.random_elements(elements, length=1)[0] 

547 

548 def random_sample( 

549 self, elements: ElementsType[T] = ("a", "b", "c"), length: Optional[int] = None # type: ignore[assignment] 

550 ) -> Sequence[T]: 

551 """Generate a list of objects randomly sampled from ``elements`` without replacement. 

552 

553 For information on the ``elements`` and ``length`` arguments, please refer to 

554 :meth:`random_elements() <faker.providers.BaseProvider.random_elements>` which 

555 is used under the hood with the ``unique`` argument explicitly set to ``True``. 

556 

557 :sample: elements=('a', 'b', 'c', 'd', 'e', 'f') 

558 :sample: elements=('a', 'b', 'c', 'd', 'e', 'f'), length=3 

559 """ 

560 return self.random_elements(elements, length, unique=True) 

561 

562 def randomize_nb_elements( 

563 self, 

564 number: int = 10, 

565 le: bool = False, 

566 ge: bool = False, 

567 min: Optional[int] = None, 

568 max: Optional[int] = None, 

569 ) -> int: 

570 """Generate a random integer near ``number`` according to the following rules: 

571 

572 - If ``le`` is ``False`` (default), allow generation up to 140% of ``number``. 

573 If ``True``, upper bound generation is capped at 100%. 

574 - If ``ge`` is ``False`` (default), allow generation down to 60% of ``number``. 

575 If ``True``, lower bound generation is capped at 100%. 

576 - If a numerical value for ``min`` is provided, generated values less than ``min`` 

577 will be clamped at ``min``. 

578 - If a numerical value for ``max`` is provided, generated values greater than 

579 ``max`` will be clamped at ``max``. 

580 - If both ``le`` and ``ge`` are ``True``, the value of ``number`` will automatically 

581 be returned, regardless of the values supplied for ``min`` and ``max``. 

582 

583 :sample: number=100 

584 :sample: number=100, ge=True 

585 :sample: number=100, ge=True, min=120 

586 :sample: number=100, le=True 

587 :sample: number=100, le=True, max=80 

588 :sample: number=79, le=True, ge=True, min=80 

589 """ 

590 if le and ge: 

591 return number 

592 _min = 100 if ge else 60 

593 _max = 100 if le else 140 

594 nb = int(number * self.generator.random.randint(_min, _max) / 100) 

595 if min is not None and nb < min: 

596 nb = min 

597 if max is not None and nb > max: 

598 nb = max 

599 return nb 

600 

601 def numerify(self, text: str = "###") -> str: 

602 """Generate a string with each placeholder in ``text`` replaced according 

603 to the following rules: 

604 

605 - Number signs ('#') are replaced with a random digit (0 to 9). 

606 - Percent signs ('%') are replaced with a random non-zero digit (1 to 9). 

607 - Exclamation marks ('!') are replaced with a random digit or an empty string. 

608 - At symbols ('@') are replaced with a random non-zero digit or an empty string. 

609 

610 Under the hood, this method uses :meth:`random_digit() <faker.providers.BaseProvider.random_digit>`, 

611 :meth:`random_digit_not_null() <faker.providers.BaseProvider.random_digit_not_null>`, 

612 :meth:`random_digit_or_empty() <faker.providers.BaseProvider.random_digit_or_empty>`, 

613 and :meth:`random_digit_not_null_or_empty() <faker.providers.BaseProvider.random_digit_not_null_or_empty>` 

614 to generate the random values. 

615 

616 :sample: text='Intel Core i%-%%##K vs AMD Ryzen % %%##X' 

617 :sample: text='!!! !!@ !@! !@@ @!! @!@ @@! @@@' 

618 """ 

619 text = _re_hash.sub(lambda x: str(self.random_digit()), text) 

620 text = _re_perc.sub(lambda x: str(self.random_digit_not_null()), text) 

621 text = _re_excl.sub(lambda x: str(self.random_digit_or_empty()), text) 

622 text = _re_at.sub(lambda x: str(self.random_digit_not_null_or_empty()), text) 

623 return text 

624 

625 def lexify(self, text: str = "????", letters: str = string.ascii_letters) -> str: 

626 """Generate a string with each question mark ('?') in ``text`` 

627 replaced with a random character from ``letters``. 

628 

629 By default, ``letters`` contains all ASCII letters, uppercase and lowercase. 

630 

631 :sample: text='Random Identifier: ??????????' 

632 :sample: text='Random Identifier: ??????????', letters='ABCDE' 

633 """ 

634 return _re_qm.sub(lambda x: self.random_element(letters), text) 

635 

636 def bothify(self, text: str = "## ??", letters: str = string.ascii_letters) -> str: 

637 """Generate a string with each placeholder in ``text`` replaced according to the following rules: 

638 

639 - Number signs ('#') are replaced with a random digit (0 to 9). 

640 - Question marks ('?') are replaced with a random character from ``letters``. 

641 

642 By default, ``letters`` contains all ASCII letters, uppercase and lowercase. 

643 

644 Under the hood, this method uses :meth:`numerify() <faker.providers.BaseProvider.numerify>` and 

645 and :meth:`lexify() <faker.providers.BaseProvider.lexify>` to generate random values for number 

646 signs and question marks respectively. 

647 

648 :sample: letters='ABCDE' 

649 :sample: text='Product Number: ????-########' 

650 :sample: text='Product Number: ????-########', letters='ABCDE' 

651 """ 

652 return self.lexify(self.numerify(text), letters=letters) 

653 

654 def hexify(self, text: str = "^^^^", upper: bool = False) -> str: 

655 """Generate a string with each circumflex ('^') in ``text`` 

656 replaced with a random hexadecimal character. 

657 

658 By default, ``upper`` is set to False. If set to ``True``, output 

659 will be formatted using uppercase hexadecimal characters. 

660 

661 :sample: text='MAC Address: ^^:^^:^^:^^:^^:^^' 

662 :sample: text='MAC Address: ^^:^^:^^:^^:^^:^^', upper=True 

663 """ 

664 letters = string.hexdigits[:-6] 

665 if upper: 

666 letters = letters.upper() 

667 return _re_cir.sub(lambda x: self.random_element(letters), text) 

668 

669 

670class DynamicProvider(BaseProvider): 

671 def __init__( 

672 self, 

673 provider_name: str, 

674 elements: Optional[List] = None, 

675 generator: Optional[Any] = None, 

676 ): 

677 """ 

678 A faker Provider capable of getting a list of elements to randomly select from, 

679 instead of using the predefined list of elements which exist in the default providers in faker. 

680 

681 :param provider_name: Name of provider, which would translate into the function name e.g. faker.my_fun(). 

682 :param elements: List of values to randomly select from 

683 :param generator: Generator object. If missing, the default Generator is used. 

684 

685 :example: 

686 >>>from faker import Faker 

687 >>>from faker.providers import DynamicProvider 

688 

689 >>>medical_professions_provider = DynamicProvider( 

690 >>> provider_name="medical_profession", 

691 >>> elements=["dr.", "doctor", "nurse", "surgeon", "clerk"], 

692 >>>) 

693 >>>fake = Faker() 

694 >>>fake.add_provider(medical_professions_provider) 

695 

696 >>>fake.medical_profession() 

697 "dr." 

698 

699 """ 

700 

701 if not generator: 

702 generator = Generator() 

703 super().__init__(generator) 

704 if provider_name.startswith("__"): 

705 raise ValueError("Provider name cannot start with __ as it would be ignored by Faker") 

706 

707 self.provider_name = provider_name 

708 

709 self.elements = [] 

710 if elements: 

711 self.elements = elements 

712 

713 setattr(self, provider_name, self.get_random_value) # Add a method for the provider_name value 

714 

715 def add_element(self, element: str) -> None: 

716 """Add new element.""" 

717 self.elements.append(element) 

718 

719 def get_random_value(self) -> Any: 

720 

721 if not self.elements or len(self.elements) == 0: 

722 raise ValueError("Elements should be a list of values the provider samples from") 

723 

724 return self.random_element(self.elements)