comparison configmix/__init__.py @ 303:2a2f5b86fe34

Move some important public constants into the .constants sub-module
author Franz Glasner <fzglas.hg@dom66.de>
date Sun, 25 Apr 2021 16:09:00 +0200
parents 5648e4611383
children ce7aea9eac4a
comparison
equal deleted inserted replaced
302:063099b188cd 303:2a2f5b86fe34
29 import copy 29 import copy
30 import io 30 import io
31 import os 31 import os
32 import re 32 import re
33 33
34 from .compat import u, u2fs 34 from .compat import u2fs
35 from .config import Configuration 35 from .config import Configuration
36 36 from . import constants
37
38 COMMENTS = [
39 u("__comment"),
40 u("__doc"),
41 ]
42 """Prefixes for comment configuration keys that are to be handled as
43 comments
44
45 """
46
47 DIR_PREFIX = u("<dir>")
48 """Prefix for configuration values to read other configuration files from
49 given directory
50
51 """
52
53 DEL_VALUE = u("{{::DEL::}}")
54 """Value for configuration items to signal that the corresponding
55 key-value is to be deleted when configurations are merged
56
57 """
58 37
59 38
60 def load(*files, **kwargs): 39 def load(*files, **kwargs):
61 """Load the given configuration files, merge them in the given order 40 """Load the given configuration files, merge them in the given order
62 and return the resulting configuration dictionary. 41 and return the resulting configuration dictionary.
88 if defaults is None: 67 if defaults is None:
89 ex = Configuration() 68 ex = Configuration()
90 else: 69 else:
91 ex = merge(None, Configuration(defaults)) 70 ex = merge(None, Configuration(defaults))
92 for f in files: 71 for f in files:
93 if f.startswith(DIR_PREFIX): 72 if f.startswith(constants.DIR_PREFIX):
94 for f2 in _get_configuration_files_from_dir(f[5:]): 73 for f2 in _get_configuration_files_from_dir(f[5:]):
95 nx = _load_cfg_from_file(f2, ignore_unknown=True, strict=strict) 74 nx = _load_cfg_from_file(f2, ignore_unknown=True, strict=strict)
96 if nx is not None: 75 if nx is not None:
97 ex = merge(nx, ex) 76 ex = merge(nx, ex)
98 else: 77 else:
115 if defaults is None: 94 if defaults is None:
116 ex = Configuration() 95 ex = Configuration()
117 else: 96 else:
118 ex = safe_merge(None, Configuration(defaults)) 97 ex = safe_merge(None, Configuration(defaults))
119 for f in files: 98 for f in files:
120 if f.startswith(DIR_PREFIX): 99 if f.startswith(constants.DIR_PREFIX):
121 for f2 in _get_configuration_files_from_dir(f[5:]): 100 for f2 in _get_configuration_files_from_dir(f[5:]):
122 nx = _load_cfg_from_file(f2, ignore_unknown=True, strict=strict) 101 nx = _load_cfg_from_file(f2, ignore_unknown=True, strict=strict)
123 if nx is not None: 102 if nx is not None:
124 ex = safe_merge(nx, ex) 103 ex = safe_merge(nx, ex)
125 else: 104 else:
427 **inplace**. 406 **inplace**.
428 407
429 The configuration in `default` will be changed **inplace** 408 The configuration in `default` will be changed **inplace**
430 when filtering out comments (which is the default). 409 when filtering out comments (which is the default).
431 410
432 If a value in `user` is equal to :data:`.DEL_VALUE` 411 If a value in `user` is equal to :data:`.constants.DEL_VALUE`
433 (``{{::DEL::}}``) the corresponding key will be deleted from the 412 (``{{::DEL::}}``) the corresponding key will be deleted from the
434 merged output. 413 merged output.
435 414
436 From http://stackoverflow.com/questions/823196/yaml-merge-in-python 415 From http://stackoverflow.com/questions/823196/yaml-merge-in-python
437 416
446 if isinstance(user, dict) and isinstance(default, dict): 425 if isinstance(user, dict) and isinstance(default, dict):
447 for k, v in default.items(): 426 for k, v in default.items():
448 if filter_comments and _is_comment(k): 427 if filter_comments and _is_comment(k):
449 continue 428 continue
450 if k in user: 429 if k in user:
451 if user[k] == DEL_VALUE: 430 if user[k] == constants.DEL_VALUE:
452 # do not copy 431 # do not copy
453 del user[k] 432 del user[k]
454 else: 433 else:
455 user[k] = _merge(user[k], v, filter_comments) 434 user[k] = _merge(user[k], v, filter_comments)
456 else: 435 else:
466 if isinstance(user, dict) and isinstance(default, dict): 445 if isinstance(user, dict) and isinstance(default, dict):
467 for k, v in default.items(): 446 for k, v in default.items():
468 if filter_comments and _is_comment(k): 447 if filter_comments and _is_comment(k):
469 continue 448 continue
470 if k in user: 449 if k in user:
471 if user[k] == DEL_VALUE: 450 if user[k] == constants.DEL_VALUE:
472 # do not copy 451 # do not copy
473 del user[k] 452 del user[k]
474 else: 453 else:
475 user[k] = _merge(user[k], v, filter_comments) 454 user[k] = _merge(user[k], v, filter_comments)
476 else: 455 else:
499 if isinstance(user, dict) and isinstance(default, dict): 478 if isinstance(user, dict) and isinstance(default, dict):
500 for k, v in default.items(): 479 for k, v in default.items():
501 if filter_comments and _is_comment(k): 480 if filter_comments and _is_comment(k):
502 continue 481 continue
503 if k in user: 482 if k in user:
504 if user[k] == DEL_VALUE: 483 if user[k] == constants.DEL_VALUE:
505 # do not copy 484 # do not copy
506 del user[k] 485 del user[k]
507 else: 486 else:
508 user[k] = _safe_merge(user[k], v, filter_comments) 487 user[k] = _safe_merge(user[k], v, filter_comments)
509 else: 488 else:
519 if isinstance(user, dict) and isinstance(default, dict): 498 if isinstance(user, dict) and isinstance(default, dict):
520 for k, v in default.items(): 499 for k, v in default.items():
521 if filter_comments and _is_comment(k): 500 if filter_comments and _is_comment(k):
522 continue 501 continue
523 if k in user: 502 if k in user:
524 if user[k] == DEL_VALUE: 503 if user[k] == constants.DEL_VALUE:
525 # do not copy 504 # do not copy
526 del user[k] 505 del user[k]
527 else: 506 else:
528 user[k] = _safe_merge(user[k], v, filter_comments) 507 user[k] = _safe_merge(user[k], v, filter_comments)
529 else: 508 else:
533 512
534 def _filter_comments(d): 513 def _filter_comments(d):
535 """Recursively filter comments keys in the dict `d`. 514 """Recursively filter comments keys in the dict `d`.
536 515
537 Comment keys are keys that start with any of the items in 516 Comment keys are keys that start with any of the items in
538 :data:`.COMMENTS`. 517 :data:`.constants.COMMENTS`.
539 518
540 """ 519 """
541 if not isinstance(d, dict): 520 if not isinstance(d, dict):
542 return 521 return
543 # use a copy of the keys because we change `d` while iterating 522 # use a copy of the keys because we change `d` while iterating
548 if isinstance(d[k], dict): 527 if isinstance(d[k], dict):
549 _filter_comments(d[k]) 528 _filter_comments(d[k])
550 529
551 530
552 def _is_comment(k): 531 def _is_comment(k):
553 for i in COMMENTS: 532 for i in constants.COMMENTS:
554 try: 533 try:
555 if k.startswith(i): 534 if k.startswith(i):
556 return True 535 return True
557 except AttributeError: 536 except AttributeError:
558 # non-string key 537 # non-string key
561 540
562 541
563 def _filter_deletions(d): 542 def _filter_deletions(d):
564 """Recursively filter deletions in the dict `d`. 543 """Recursively filter deletions in the dict `d`.
565 544
566 Deletions have values that equal :data:`.DEL_VALUE`. 545 Deletions have values that equal :data:`.constants.DEL_VALUE`.
567 546
568 """ 547 """
569 if not isinstance(d, dict): 548 if not isinstance(d, dict):
570 return 549 return
571 # use a copy of the items because we change `d` while iterating 550 # use a copy of the items because we change `d` while iterating
572 for k, v in list(d.items()): 551 for k, v in list(d.items()):
573 if v == DEL_VALUE: 552 if v == constants.DEL_VALUE:
574 del d[k] 553 del d[k]
575 else: 554 else:
576 if isinstance(d[k], dict): 555 if isinstance(d[k], dict):
577 _filter_deletions(d[k]) 556 _filter_deletions(d[k])
578 557