Mercurial > hgrepos > Python > libs > ConfigMix
comparison configmix/__init__.py @ 276:af371f9c016d
Allow deletion of key-value pairs when merging is done.
When encountering the "{{::DEL::}}" special value the corresponding key-value
pair is deleted.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Sat, 03 Oct 2020 17:11:41 +0200 |
| parents | e2fd8fea1a4c |
| children | 57fca7448740 |
comparison
equal
deleted
inserted
replaced
| 275:e2fd8fea1a4c | 276:af371f9c016d |
|---|---|
| 11 """ | 11 """ |
| 12 | 12 |
| 13 from __future__ import division, print_function, absolute_import | 13 from __future__ import division, print_function, absolute_import |
| 14 | 14 |
| 15 | 15 |
| 16 __version__ = "0.10.1.dev1" | 16 __version__ = "0.11.0.dev1" |
| 17 | 17 |
| 18 __revision__ = "|VCSRevision|" | 18 __revision__ = "|VCSRevision|" |
| 19 __date__ = "|VCSJustDate|" | 19 __date__ = "|VCSJustDate|" |
| 20 | 20 |
| 21 __all__ = ["load", "safe_load", | 21 __all__ = ["load", "safe_load", |
| 45 """ | 45 """ |
| 46 | 46 |
| 47 DIR_PREFIX = u("<dir>") | 47 DIR_PREFIX = u("<dir>") |
| 48 """Prefix for configuration values to read other configuration files from | 48 """Prefix for configuration values to read other configuration files from |
| 49 given directory | 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 | |
| 50 | 56 |
| 51 """ | 57 """ |
| 52 | 58 |
| 53 | 59 |
| 54 def load(*files, **kwargs): | 60 def load(*files, **kwargs): |
| 416 **inplace**. | 422 **inplace**. |
| 417 | 423 |
| 418 The configuration in `default` will be changed **inplace** | 424 The configuration in `default` will be changed **inplace** |
| 419 when filtering out comments (which is the default). | 425 when filtering out comments (which is the default). |
| 420 | 426 |
| 427 If a value in `user` is equal to :data:`.DEL_VALUE` | |
| 428 (``{{::DEL::}}``) the corresponding key will be deleted from the | |
| 429 merged output. | |
| 430 | |
| 421 From http://stackoverflow.com/questions/823196/yaml-merge-in-python | 431 From http://stackoverflow.com/questions/823196/yaml-merge-in-python |
| 422 | 432 |
| 423 """ | 433 """ |
| 424 if user is None: | 434 if user is None: |
| 425 if filter_comments: | 435 if filter_comments: |
| 426 _filter_comments(default) | 436 _filter_comments(default) |
| 437 _filter_deletions(default) | |
| 427 return default | 438 return default |
| 428 if filter_comments: | 439 if filter_comments: |
| 429 _filter_comments(user) | 440 _filter_comments(user) |
| 430 if isinstance(user, dict) and isinstance(default, dict): | 441 if isinstance(user, dict) and isinstance(default, dict): |
| 431 for k, v in default.items(): | 442 for k, v in default.items(): |
| 432 if filter_comments and _is_comment(k): | 443 if filter_comments and _is_comment(k): |
| 433 continue | 444 continue |
| 434 if k not in user: | 445 if k in user: |
| 446 if user[k] == DEL_VALUE: | |
| 447 # do not copy | |
| 448 del user[k] | |
| 449 else: | |
| 450 user[k] = _merge(user[k], v, filter_comments) | |
| 451 else: | |
| 435 user[k] = v | 452 user[k] = v |
| 436 else: | 453 _filter_deletions(user) |
| 437 user[k] = _merge(user[k], v, filter_comments) | |
| 438 return user | 454 return user |
| 439 | 455 |
| 440 | 456 |
| 441 def _merge(user, default, filter_comments): | 457 def _merge(user, default, filter_comments): |
| 442 """Recursion helper for :func:`.merge` | 458 """Recursion helper for :func:`.merge` |
| 444 """ | 460 """ |
| 445 if isinstance(user, dict) and isinstance(default, dict): | 461 if isinstance(user, dict) and isinstance(default, dict): |
| 446 for k, v in default.items(): | 462 for k, v in default.items(): |
| 447 if filter_comments and _is_comment(k): | 463 if filter_comments and _is_comment(k): |
| 448 continue | 464 continue |
| 449 if k not in user: | 465 if k in user: |
| 466 if user[k] == DEL_VALUE: | |
| 467 # do not copy | |
| 468 del user[k] | |
| 469 else: | |
| 470 user[k] = _merge(user[k], v, filter_comments) | |
| 471 else: | |
| 450 user[k] = v | 472 user[k] = v |
| 451 else: | |
| 452 user[k] = _merge(user[k], v, filter_comments) | |
| 453 return user | 473 return user |
| 454 | 474 |
| 455 | 475 |
| 456 def safe_merge(user, default, filter_comments=True): | 476 def safe_merge(user, default, filter_comments=True): |
| 457 """A more safe version of :func:`.merge` that makes deep copies of | 477 """A more safe version of :func:`.merge` that makes deep copies of |
| 464 | 484 |
| 465 """ | 485 """ |
| 466 if user is None: | 486 if user is None: |
| 467 if filter_comments: | 487 if filter_comments: |
| 468 _filter_comments(default) | 488 _filter_comments(default) |
| 489 _filter_deletions(default) | |
| 469 return copy.deepcopy(default) | 490 return copy.deepcopy(default) |
| 470 user = copy.deepcopy(user) | 491 user = copy.deepcopy(user) |
| 471 if filter_comments: | 492 if filter_comments: |
| 472 _filter_comments(user) | 493 _filter_comments(user) |
| 473 if isinstance(user, dict) and isinstance(default, dict): | 494 if isinstance(user, dict) and isinstance(default, dict): |
| 474 for k, v in default.items(): | 495 for k, v in default.items(): |
| 475 if filter_comments and _is_comment(k): | 496 if filter_comments and _is_comment(k): |
| 476 continue | 497 continue |
| 477 if k not in user: | 498 if k in user: |
| 499 if user[k] == DEL_VALUE: | |
| 500 # do not copy | |
| 501 del user[k] | |
| 502 else: | |
| 503 user[k] = _safe_merge(user[k], v, filter_comments) | |
| 504 else: | |
| 478 user[k] = copy.deepcopy(v) | 505 user[k] = copy.deepcopy(v) |
| 479 else: | 506 _filter_deletions(user) |
| 480 user[k] = _safe_merge(user[k], v, filter_comments) | |
| 481 return user | 507 return user |
| 482 | 508 |
| 483 | 509 |
| 484 def _safe_merge(user, default, filter_comments): | 510 def _safe_merge(user, default, filter_comments): |
| 485 """Recursion helper for :func:`safe_merge` | 511 """Recursion helper for :func:`safe_merge` |
| 487 """ | 513 """ |
| 488 if isinstance(user, dict) and isinstance(default, dict): | 514 if isinstance(user, dict) and isinstance(default, dict): |
| 489 for k, v in default.items(): | 515 for k, v in default.items(): |
| 490 if filter_comments and _is_comment(k): | 516 if filter_comments and _is_comment(k): |
| 491 continue | 517 continue |
| 492 if k not in user: | 518 if k in user: |
| 519 if user[k] == DEL_VALUE: | |
| 520 # do not copy | |
| 521 del user[k] | |
| 522 else: | |
| 523 user[k] = _safe_merge(user[k], v, filter_comments) | |
| 524 else: | |
| 493 user[k] = copy.deepcopy(v) | 525 user[k] = copy.deepcopy(v) |
| 494 else: | |
| 495 user[k] = _safe_merge(user[k], v, filter_comments) | |
| 496 return user | 526 return user |
| 497 | 527 |
| 498 | 528 |
| 499 def _filter_comments(d): | 529 def _filter_comments(d): |
| 500 """Recursively filter comments keys in the dict `d`. | 530 """Recursively filter comments keys in the dict `d`. |
| 523 # non-string key | 553 # non-string key |
| 524 return False | 554 return False |
| 525 return False | 555 return False |
| 526 | 556 |
| 527 | 557 |
| 558 def _filter_deletions(d): | |
| 559 """Recursively filter deletions in the dict `d`. | |
| 560 | |
| 561 Deletions have values that equal :data:`.DEL_VALUE`. | |
| 562 | |
| 563 """ | |
| 564 if not isinstance(d, dict): | |
| 565 return | |
| 566 # use a copy of the items because we change `d` while iterating | |
| 567 for k, v in list(d.items()): | |
| 568 if v == DEL_VALUE: | |
| 569 del d[k] | |
| 570 else: | |
| 571 if isinstance(d[k], dict): | |
| 572 _filter_deletions(d[k]) | |
| 573 | |
| 574 | |
| 528 # | 575 # |
| 529 # Init loader defaults: mode->loader and extension->mode | 576 # Init loader defaults: mode->loader and extension->mode |
| 530 # | 577 # |
| 531 mode_loaders.update(DEFAULT_MODE_LOADERS) | 578 mode_loaders.update(DEFAULT_MODE_LOADERS) |
| 532 for _pattern, _mode in DEFAULT_ASSOC: | 579 for _pattern, _mode in DEFAULT_ASSOC: |
