Skip to content

Commit 4404a31

Browse files
committed
upd: prefer os.environ variables when found; bump to 0.0.5
1 parent 8c4bcc6 commit 4404a31

File tree

5 files changed

+426
-58
lines changed

5 files changed

+426
-58
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ schematized config
99
pip install python_schematized_config
1010
```
1111

12+
# development
13+
14+
assuming you have [nix](https://nixos.org/download.html) installed and
15+
ready, make sure `nix-command` and `flake` are enabled (oneliner: run
16+
`export NIX_CONFIG="experimental-features = nix-command flakes"` in the
17+
terminal), then enter the dev shell using `nix develop`
18+
19+
start the jupyter notebook using the provided alias or just
20+
`jupyter notebook`, and hack away
21+
1222
## How to use
1323

1424
``` python

nbs/00_core.ipynb

Lines changed: 96 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,25 @@
282282
" @classmethod\n",
283283
" def load_dotenv(cls, json_schema: Union[str, dict]=None, dotenv_path: str=None, storage_driver: FS=None):\n",
284284
" 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",
287304
" return cls.load_validated_config(\n",
288305
" json_schema or cls.get_default_json_schema(storage_driver=storage_driver),\n",
289306
" config, storage_driver=storage_driver)"
@@ -350,17 +367,7 @@
350367
"cell_type": "code",
351368
"execution_count": null,
352369
"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": [],
364371
"source": [
365372
"#| hide\n",
366373
"test_fail(ConfigValidator.load_validated_config, args=(example_properties_schema, {}))"
@@ -408,15 +415,7 @@
408415
"cell_type": "code",
409416
"execution_count": null,
410417
"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": [],
420419
"source": [
421420
"#| hide\n",
422421
"test_fail(ConfigValidator.load_validated_config,\n",
@@ -432,15 +431,7 @@
432431
"cell_type": "code",
433432
"execution_count": null,
434433
"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": [],
444435
"source": [
445436
"#| hide\n",
446437
"test_fail(ConfigValidator.load_validated_config,\n",
@@ -456,15 +447,7 @@
456447
"cell_type": "code",
457448
"execution_count": null,
458449
"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": [],
468451
"source": [
469452
"#| hide\n",
470453
"test_fail(ConfigValidator.load_validated_config,\n",
@@ -480,15 +463,7 @@
480463
"cell_type": "code",
481464
"execution_count": null,
482465
"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": [],
492467
"source": [
493468
"#| hide\n",
494469
"test_fail(ConfigValidator.load_validated_config,\n",
@@ -590,6 +565,65 @@
590565
"test_fail(validator.load_dotenv, kwargs={'dotenv_path': 'non-existent-location-own.env'})"
591566
]
592567
},
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+
},
593627
{
594628
"cell_type": "code",
595629
"execution_count": null,
@@ -603,9 +637,21 @@
603637
],
604638
"metadata": {
605639
"kernelspec": {
606-
"display_name": "python3",
640+
"display_name": "Python 3 (ipykernel)",
607641
"language": "python",
608642
"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"
609655
}
610656
},
611657
"nbformat": 4,

schematized_config/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.0.4"
1+
__version__ = "0.0.5"

0 commit comments

Comments
 (0)