Browse Source

Add YamlProvider.order_mode natrual is default, adds simple

pull/1207/head
Ross McFarland 1 year ago
parent
commit
bd5f148e75
No known key found for this signature in database GPG Key ID: 943B179E15D3B22A
4 changed files with 75 additions and 7 deletions
  1. +5
    -0
      CHANGELOG.md
  2. +12
    -2
      octodns/provider/yaml.py
  3. +29
    -5
      octodns/yaml.py
  4. +29
    -0
      tests/test_octodns_yaml.py

+ 5
- 0
CHANGELOG.md View File

@ -1,3 +1,8 @@
## v1.?.? - 2024-??-?? - ???
* Add YamlProvider.order_mode to allow picking between natrual (human)
the default when enforce_order=True and simple `sort`.
## v1.10.0 - 2024-10-06 - Lots of little stuff
* Zone name validation checking for double dots, and throwing InvalidNameError


+ 12
- 2
octodns/provider/yaml.py View File

@ -34,6 +34,11 @@ class YamlProvider(BaseProvider):
# Whether or not to enforce sorting order when loading yaml
# (optional, default True)
enforce_order: true
# What sort mode to employ when enforcing order
# - simple: `sort`
# - natual: https://pypi.org/project/natsort/
# (optional, default natual)
order_mode: natrual
# Whether duplicate records should replace rather than error
# (optional, default False)
@ -174,6 +179,7 @@ class YamlProvider(BaseProvider):
directory,
default_ttl=3600,
enforce_order=True,
order_mode='natrual',
populate_should_replace=False,
supports_root_ns=True,
split_extension=False,
@ -186,11 +192,12 @@ class YamlProvider(BaseProvider):
klass = self.__class__.__name__
self.log = logging.getLogger(f'{klass}[{id}]')
self.log.debug(
'__init__: id=%s, directory=%s, default_ttl=%d, enforce_order=%d, populate_should_replace=%s, supports_root_ns=%s, split_extension=%s, split_catchall=%s, shared_filename=%s, disable_zonefile=%s',
'__init__: id=%s, directory=%s, default_ttl=%d, enforce_order=%d, order_mode=%s, populate_should_replace=%s, supports_root_ns=%s, split_extension=%s, split_catchall=%s, shared_filename=%s, disable_zonefile=%s',
id,
directory,
default_ttl,
enforce_order,
order_mode,
populate_should_replace,
supports_root_ns,
split_extension,
@ -202,6 +209,7 @@ class YamlProvider(BaseProvider):
self.directory = directory
self.default_ttl = default_ttl
self.enforce_order = enforce_order
self.order_mode = order_mode
self.populate_should_replace = populate_should_replace
self.supports_root_ns = supports_root_ns
self.split_extension = split_extension
@ -304,7 +312,9 @@ class YamlProvider(BaseProvider):
def _populate_from_file(self, filename, zone, lenient):
with open(filename, 'r') as fh:
yaml_data = safe_load(fh, enforce_order=self.enforce_order)
yaml_data = safe_load(
fh, enforce_order=self.enforce_order, order_mode=self.order_mode
)
if yaml_data:
for name, data in yaml_data.items():
if not isinstance(data, list):


+ 29
- 5
octodns/yaml.py View File

@ -44,11 +44,12 @@ ContextLoader.add_constructor(
# Found http://stackoverflow.com/a/21912744 which guided me on how to hook in
# here
class SortEnforcingLoader(ContextLoader):
def _construct(self, node):
ret, pairs, context = self._pairs(node)
keys = [d[0] for d in pairs]
keys_sorted = sorted(keys, key=_natsort_key)
keys_sorted = sorted(keys, key=self.KEYGEN)
for key in keys:
expected = keys_sorted.pop(0)
if key != expected:
@ -62,13 +63,36 @@ class SortEnforcingLoader(ContextLoader):
return ret
SortEnforcingLoader.add_constructor(
SortEnforcingLoader.DEFAULT_MAPPING_TAG, SortEnforcingLoader._construct
class NaturalSortEnforcingLoader(SortEnforcingLoader):
KEYGEN = _natsort_key
NaturalSortEnforcingLoader.add_constructor(
SortEnforcingLoader.DEFAULT_MAPPING_TAG,
NaturalSortEnforcingLoader._construct,
)
def safe_load(stream, enforce_order=True):
return load(stream, SortEnforcingLoader if enforce_order else ContextLoader)
class SimpleSortEnforcingLoader(SortEnforcingLoader):
KEYGEN = lambda _, s: s
SimpleSortEnforcingLoader.add_constructor(
SortEnforcingLoader.DEFAULT_MAPPING_TAG,
SimpleSortEnforcingLoader._construct,
)
def safe_load(stream, enforce_order=True, order_mode='natrual'):
if enforce_order:
loader = {
'natrual': NaturalSortEnforcingLoader,
'simple': SimpleSortEnforcingLoader,
}[order_mode]
else:
loader = ContextLoader
return load(stream, loader)
class SortingDumper(SafeDumper):


+ 29
- 0
tests/test_octodns_yaml.py View File

@ -86,3 +86,32 @@ class TestYaml(TestCase):
"[Errno 2] No such file or directory: 'tests/config/include/does-not-exist.yaml'",
str(ctx.exception),
)
def test_order_mode(self):
self.assertEqual(
{'*.1.2': 'a', '*.10.1': 'c', '*.11.2': 'd', '*.2.2': 'b'},
safe_load(
'''
'*.1.2': 'a'
'*.10.1': 'c'
'*.11.2': 'd'
'*.2.2': 'b'
''',
order_mode='simple',
),
)
# natrual sort throws error
with self.assertRaises(ConstructorError) as ctx:
safe_load(
'''
'*.1.2': 'a'
'*.2.2': 'b'
'*.10.1': 'c'
'*.11.2': 'd'
''',
order_mode='simple',
)
self.assertTrue(
'keys out of order: expected *.10.1 got *.2.2 at'
in ctx.exception.problem
)

Loading…
Cancel
Save