|
282 | 282 | " @classmethod\n", |
283 | 283 | " def load_dotenv(cls, json_schema: Union[str, dict]=None, dotenv_path: str=None, storage_driver: FS=None):\n", |
284 | 284 | " storage_driver = storage_driver or cls.DEFAULT_STORAGE_DRIVER\n", |
285 | | - " with storage_driver.open(dotenv_path) as ifile:\n", |
286 | | - " config = dotenv.dotenv_values(stream=ifile)\n", |
| 285 | + " if dotenv_path is None:\n", |
| 286 | + " maybe_dotenv_path = dotenv.find_dotenv() # '' if not exist\n", |
| 287 | + " if maybe_dotenv_path:\n", |
| 288 | + " logger.debug(f'using detected dotenv path; {maybe_dotenv_path}')\n", |
| 289 | + " dotenv_path = maybe_dotenv_path\n", |
| 290 | + " if dotenv_path:\n", |
| 291 | + " with storage_driver.open(dotenv_path) as ifile:\n", |
| 292 | + " config = dotenv.dotenv_values(stream=ifile)\n", |
| 293 | + " else:\n", |
| 294 | + " config = {}\n", |
| 295 | + " loaded_config = config.copy()\n", |
| 296 | + " \n", |
| 297 | + " json_schema_dict = cls.load_json(json_schema, storage_driver=storage_driver) or {}\n", |
| 298 | + " for key in json_schema_dict.get('properties', []):\n", |
| 299 | + " if key not in os.environ:\n", |
| 300 | + " continue\n", |
| 301 | + " if key in config and config[key] != os.environ[key]:\n", |
| 302 | + " logger.debug(f'os.environ key \"{key}\" overriding value present in {dotenv_path}')\n", |
| 303 | + " config[key] = os.environ[key]\n", |
287 | 304 | " return cls.load_validated_config(\n", |
288 | 305 | " json_schema or cls.get_default_json_schema(storage_driver=storage_driver),\n", |
289 | 306 | " config, storage_driver=storage_driver)" |
|
350 | 367 | "cell_type": "code", |
351 | 368 | "execution_count": null, |
352 | 369 | "metadata": {}, |
353 | | - "outputs": [ |
354 | | - { |
355 | | - "name": "stderr", |
356 | | - "output_type": "stream", |
357 | | - "text": [ |
358 | | - "$:\t'string_value_with_enum' is a required property\n", |
359 | | - "$:\t'MY_INTEGER_VALUE' is a required property\n", |
360 | | - "$:\t'A_NUMERIC_VALUE' is a required property\n" |
361 | | - ] |
362 | | - } |
363 | | - ], |
| 370 | + "outputs": [], |
364 | 371 | "source": [ |
365 | 372 | "#| hide\n", |
366 | 373 | "test_fail(ConfigValidator.load_validated_config, args=(example_properties_schema, {}))" |
|
408 | 415 | "cell_type": "code", |
409 | 416 | "execution_count": null, |
410 | 417 | "metadata": {}, |
411 | | - "outputs": [ |
412 | | - { |
413 | | - "name": "stderr", |
414 | | - "output_type": "stream", |
415 | | - "text": [ |
416 | | - "$.string_value_with_enum:\t'blah-blah' is not one of ['it', 'can', 'only', 'be', 'one', 'of', 'these']\n" |
417 | | - ] |
418 | | - } |
419 | | - ], |
| 418 | + "outputs": [], |
420 | 419 | "source": [ |
421 | 420 | "#| hide\n", |
422 | 421 | "test_fail(ConfigValidator.load_validated_config,\n", |
|
432 | 431 | "cell_type": "code", |
433 | 432 | "execution_count": null, |
434 | 433 | "metadata": {}, |
435 | | - "outputs": [ |
436 | | - { |
437 | | - "name": "stderr", |
438 | | - "output_type": "stream", |
439 | | - "text": [ |
440 | | - "$.MY_INTEGER_VALUE:\t'5555.999' is not of type 'integer'\n" |
441 | | - ] |
442 | | - } |
443 | | - ], |
| 434 | + "outputs": [], |
444 | 435 | "source": [ |
445 | 436 | "#| hide\n", |
446 | 437 | "test_fail(ConfigValidator.load_validated_config,\n", |
|
456 | 447 | "cell_type": "code", |
457 | 448 | "execution_count": null, |
458 | 449 | "metadata": {}, |
459 | | - "outputs": [ |
460 | | - { |
461 | | - "name": "stderr", |
462 | | - "output_type": "stream", |
463 | | - "text": [ |
464 | | - "$.A_NUMERIC_VALUE:\t'WHAT???' is not of type 'number'\n" |
465 | | - ] |
466 | | - } |
467 | | - ], |
| 450 | + "outputs": [], |
468 | 451 | "source": [ |
469 | 452 | "#| hide\n", |
470 | 453 | "test_fail(ConfigValidator.load_validated_config,\n", |
|
480 | 463 | "cell_type": "code", |
481 | 464 | "execution_count": null, |
482 | 465 | "metadata": {}, |
483 | | - "outputs": [ |
484 | | - { |
485 | | - "name": "stderr", |
486 | | - "output_type": "stream", |
487 | | - "text": [ |
488 | | - "$.A_NUMERIC_VALUE:\t13.0 is less than the minimum of 22\n" |
489 | | - ] |
490 | | - } |
491 | | - ], |
| 466 | + "outputs": [], |
492 | 467 | "source": [ |
493 | 468 | "#| hide\n", |
494 | 469 | "test_fail(ConfigValidator.load_validated_config,\n", |
|
590 | 565 | "test_fail(validator.load_dotenv, kwargs={'dotenv_path': 'non-existent-location-own.env'})" |
591 | 566 | ] |
592 | 567 | }, |
| 568 | + { |
| 569 | + "cell_type": "code", |
| 570 | + "execution_count": null, |
| 571 | + "metadata": {}, |
| 572 | + "outputs": [], |
| 573 | + "source": [ |
| 574 | + "#| hide\n", |
| 575 | + "# test data load precedence\n", |
| 576 | + "\n", |
| 577 | + "with memfs.open('bar.schema.json', 'w') as ofile:\n", |
| 578 | + " ofile.write(json.dumps({\n", |
| 579 | + " 'type': 'object',\n", |
| 580 | + " 'properties': {\n", |
| 581 | + " 'A_VALUE_TO_OVERRIDE': { 'type': 'string', 'default': 'change me' },\n", |
| 582 | + " }\n", |
| 583 | + " }))\n", |
| 584 | + " \n", |
| 585 | + "memfs.makedirs('precedence-test', recreate=True)\n", |
| 586 | + "with memfs.open('precedence-test/.env', 'w') as ofile:\n", |
| 587 | + " ofile.write('\\n'.join([\n", |
| 588 | + " 'A_VALUE_TO_OVERRIDE=in dotenv',\n", |
| 589 | + " ]))\n", |
| 590 | + "\n", |
| 591 | + "os.environ.pop('A_VALUE_TO_OVERRIDE', None)\n", |
| 592 | + "validated_dotenv = ConfigValidator.load_dotenv(\n", |
| 593 | + " json_schema='bar.schema.json',\n", |
| 594 | + " dotenv_path='precedence-test/.env',\n", |
| 595 | + " storage_driver=memfs,\n", |
| 596 | + ")\n", |
| 597 | + "test_eq(validated_dotenv, {\n", |
| 598 | + " 'A_VALUE_TO_OVERRIDE': 'in dotenv',\n", |
| 599 | + "})\n", |
| 600 | + "\n", |
| 601 | + "os.environ['A_VALUE_TO_OVERRIDE'] = 'overrode from environ'\n", |
| 602 | + "test_eq(ConfigValidator.load_dotenv(\n", |
| 603 | + " json_schema='bar.schema.json',\n", |
| 604 | + " storage_driver=memfs,\n", |
| 605 | + "), {\n", |
| 606 | + " 'A_VALUE_TO_OVERRIDE': 'overrode from environ',\n", |
| 607 | + "})\n", |
| 608 | + "\n", |
| 609 | + "test_eq(ConfigValidator.load_dotenv(\n", |
| 610 | + " json_schema='bar.schema.json',\n", |
| 611 | + " dotenv_path='precedence-test/.env',\n", |
| 612 | + " storage_driver=memfs,\n", |
| 613 | + "), {\n", |
| 614 | + " 'A_VALUE_TO_OVERRIDE': 'overrode from environ',\n", |
| 615 | + "})\n", |
| 616 | + "\n", |
| 617 | + "os.environ.pop('A_VALUE_TO_OVERRIDE', None)\n", |
| 618 | + "test_eq(ConfigValidator.load_dotenv(\n", |
| 619 | + " json_schema='bar.schema.json',\n", |
| 620 | + " dotenv_path='precedence-test/.env',\n", |
| 621 | + " storage_driver=memfs,\n", |
| 622 | + "), {\n", |
| 623 | + " 'A_VALUE_TO_OVERRIDE': 'in dotenv',\n", |
| 624 | + "})" |
| 625 | + ] |
| 626 | + }, |
593 | 627 | { |
594 | 628 | "cell_type": "code", |
595 | 629 | "execution_count": null, |
|
603 | 637 | ], |
604 | 638 | "metadata": { |
605 | 639 | "kernelspec": { |
606 | | - "display_name": "python3", |
| 640 | + "display_name": "Python 3 (ipykernel)", |
607 | 641 | "language": "python", |
608 | 642 | "name": "python3" |
| 643 | + }, |
| 644 | + "language_info": { |
| 645 | + "codemirror_mode": { |
| 646 | + "name": "ipython", |
| 647 | + "version": 3 |
| 648 | + }, |
| 649 | + "file_extension": ".py", |
| 650 | + "mimetype": "text/x-python", |
| 651 | + "name": "python", |
| 652 | + "nbconvert_exporter": "python", |
| 653 | + "pygments_lexer": "ipython3", |
| 654 | + "version": "3.10.8" |
609 | 655 | } |
610 | 656 | }, |
611 | 657 | "nbformat": 4, |
|
0 commit comments