Source code for octodns.record.chunked

#
#
#

import re

from .base import ValuesMixin
from .validator import ValueValidator


[docs] class ChunkedValueValidator(ValueValidator): ''' Validates values for TXT/SPF-style chunked strings: present, ASCII-only, with no unescaped or double-escaped ``;`` characters. ''' _unescaped_semicolon_re = re.compile(r'\w;') _double_escaped_semicolon_re = re.compile(r'\\\\;')
[docs] def validate(self, value_cls, data, _type): if not data: return ['missing value(s)'] elif not isinstance(data, (list, tuple)): data = (data,) reasons = [] for value in data: if self._unescaped_semicolon_re.search(value): reasons.append(f'unescaped ; in "{value}"') if self._double_escaped_semicolon_re.search(value): reasons.append(f'double escaped ; in "{value}"') try: value.encode('ascii') except UnicodeEncodeError: reasons.append(f'non ASCII character in "{value}"') return reasons
chunked_value_validator = ChunkedValueValidator( 'chunked-value-rfc', sets={'legacy', 'strict'} )
[docs] class _ChunkedValuesMixin(ValuesMixin): CHUNK_SIZE = 255
[docs] def chunked_value(self, value): value = value.replace('"', '\\"') vs = [] i = 0 n = len(value) # until we've processed the whole string while i < n: # start with a full chunk size c = min(self.CHUNK_SIZE, n - i) # make sure that we don't break on escape chars while value[i + c - 1] == '\\': c -= 1 # we have our chunk now vs.append(value[i : i + c]) # and can step over if i += c vs = '" "'.join(vs) return self._value_type(f'"{vs}"')
@property def chunked_values(self): values = [] for v in self.values: values.append(self.chunked_value(v)) return values @property def rr_values(self): return self.chunked_values
[docs] class _ChunkedValue(str): VALIDATORS = [chunked_value_validator]
[docs] @classmethod def parse_rdata_text(cls, value): try: return value.replace(';', '\\;') except AttributeError: return value
[docs] @classmethod def _schema(cls): return {'type': 'string'}
[docs] @classmethod def process(cls, values): ret = [] for v in values: if v and v[0] == '"': v = v[1:-1] ret.append(cls(v.replace('" "', ''))) return ret
@property def rdata_text(self): return self
[docs] def template(self, params): if '{' not in self: return self return self.__class__(self.format(**params))