fauna.query.template

 1import re as _re
 2from typing import Optional, Tuple, Iterator, Match
 3
 4
 5class FaunaTemplate:
 6  """A template class that supports variables marked with a ${}-sigil. Its primary purpose
 7    is to expose an iterator for the template parts that support composition of FQL queries.
 8
 9    Implementation adapted from https://github.com/python/cpython/blob/main/Lib/string.py
10
11    :param template: A string template e.g. "${my_var} { name }"
12    :type template: str
13    """
14
15  _delimiter = '$'
16  _idpattern = r'[_a-zA-Z][_a-zA-Z0-9]*'
17  _flags = _re.VERBOSE
18
19  def __init__(self, template: str):
20    """The initializer"""
21    delim = _re.escape(self._delimiter)
22    pattern = fr"""
23        {delim}(?:
24          (?P<escaped>{delim})  |   # Escape sequence of two delimiters
25          {{(?P<braced>{self._idpattern})}} |   # delimiter and a braced identifier
26          (?P<invalid>)             # Other ill-formed delimiter exprs
27        ) 
28        """
29    self._pattern = _re.compile(pattern, self._flags)
30    self._template = template
31
32  def iter(self) -> Iterator[Tuple[Optional[str], Optional[str]]]:
33    """A method that returns an iterator over tuples representing template parts. The
34        first value of the tuple, if not None, is a template literal. The second value of
35        the tuple, if not None, is a template variable. If both are not None, then the
36        template literal comes *before* the variable.
37
38        :raises ValueError: If there is an invalid template placeholder
39
40        :return: An iterator of template parts
41        :rtype: collections.Iterable[Tuple[Optional[str], Optional[str]]]
42        """
43    match_objects = self._pattern.finditer(self._template)
44    cur_pos = 0
45    for mo in match_objects:
46      if mo.group("invalid") is not None:
47        self._handle_invalid(mo)
48
49      span_start_pos = mo.span()[0]
50      span_end_pos = mo.span()[1]
51      escaped_part = mo.group("escaped") or ""
52      variable_part = mo.group("braced")
53      literal_part: Optional[str] = None
54
55      if cur_pos != span_start_pos:
56        literal_part = \
57            self._template[cur_pos:span_start_pos] \
58                + escaped_part
59
60      cur_pos = span_end_pos
61
62      yield literal_part, variable_part
63
64    if cur_pos != len(self._template):
65      yield self._template[cur_pos:], None
66
67  def _handle_invalid(self, mo: Match) -> None:
68    i = mo.start("invalid")
69    lines = self._template[:i].splitlines(keepends=True)
70
71    if not lines:
72      colno = 1
73      lineno = 1
74    else:
75      colno = i - len(''.join(lines[:-1]))
76      lineno = len(lines)
77
78    raise ValueError(
79        f"Invalid placeholder in template: line {lineno}, col {colno}")
class FaunaTemplate:
 6class FaunaTemplate:
 7  """A template class that supports variables marked with a ${}-sigil. Its primary purpose
 8    is to expose an iterator for the template parts that support composition of FQL queries.
 9
10    Implementation adapted from https://github.com/python/cpython/blob/main/Lib/string.py
11
12    :param template: A string template e.g. "${my_var} { name }"
13    :type template: str
14    """
15
16  _delimiter = '$'
17  _idpattern = r'[_a-zA-Z][_a-zA-Z0-9]*'
18  _flags = _re.VERBOSE
19
20  def __init__(self, template: str):
21    """The initializer"""
22    delim = _re.escape(self._delimiter)
23    pattern = fr"""
24        {delim}(?:
25          (?P<escaped>{delim})  |   # Escape sequence of two delimiters
26          {{(?P<braced>{self._idpattern})}} |   # delimiter and a braced identifier
27          (?P<invalid>)             # Other ill-formed delimiter exprs
28        ) 
29        """
30    self._pattern = _re.compile(pattern, self._flags)
31    self._template = template
32
33  def iter(self) -> Iterator[Tuple[Optional[str], Optional[str]]]:
34    """A method that returns an iterator over tuples representing template parts. The
35        first value of the tuple, if not None, is a template literal. The second value of
36        the tuple, if not None, is a template variable. If both are not None, then the
37        template literal comes *before* the variable.
38
39        :raises ValueError: If there is an invalid template placeholder
40
41        :return: An iterator of template parts
42        :rtype: collections.Iterable[Tuple[Optional[str], Optional[str]]]
43        """
44    match_objects = self._pattern.finditer(self._template)
45    cur_pos = 0
46    for mo in match_objects:
47      if mo.group("invalid") is not None:
48        self._handle_invalid(mo)
49
50      span_start_pos = mo.span()[0]
51      span_end_pos = mo.span()[1]
52      escaped_part = mo.group("escaped") or ""
53      variable_part = mo.group("braced")
54      literal_part: Optional[str] = None
55
56      if cur_pos != span_start_pos:
57        literal_part = \
58            self._template[cur_pos:span_start_pos] \
59                + escaped_part
60
61      cur_pos = span_end_pos
62
63      yield literal_part, variable_part
64
65    if cur_pos != len(self._template):
66      yield self._template[cur_pos:], None
67
68  def _handle_invalid(self, mo: Match) -> None:
69    i = mo.start("invalid")
70    lines = self._template[:i].splitlines(keepends=True)
71
72    if not lines:
73      colno = 1
74      lineno = 1
75    else:
76      colno = i - len(''.join(lines[:-1]))
77      lineno = len(lines)
78
79    raise ValueError(
80        f"Invalid placeholder in template: line {lineno}, col {colno}")

A template class that supports variables marked with a ${}-sigil. Its primary purpose is to expose an iterator for the template parts that support composition of FQL queries.

Implementation adapted from https://github.com/python/cpython/blob/main/Lib/string.py

Parameters
  • template: A string template e.g. "${my_var} { name }"
FaunaTemplate(template: str)
20  def __init__(self, template: str):
21    """The initializer"""
22    delim = _re.escape(self._delimiter)
23    pattern = fr"""
24        {delim}(?:
25          (?P<escaped>{delim})  |   # Escape sequence of two delimiters
26          {{(?P<braced>{self._idpattern})}} |   # delimiter and a braced identifier
27          (?P<invalid>)             # Other ill-formed delimiter exprs
28        ) 
29        """
30    self._pattern = _re.compile(pattern, self._flags)
31    self._template = template

The initializer

def iter(self) -> Iterator[Tuple[Optional[str], Optional[str]]]:
33  def iter(self) -> Iterator[Tuple[Optional[str], Optional[str]]]:
34    """A method that returns an iterator over tuples representing template parts. The
35        first value of the tuple, if not None, is a template literal. The second value of
36        the tuple, if not None, is a template variable. If both are not None, then the
37        template literal comes *before* the variable.
38
39        :raises ValueError: If there is an invalid template placeholder
40
41        :return: An iterator of template parts
42        :rtype: collections.Iterable[Tuple[Optional[str], Optional[str]]]
43        """
44    match_objects = self._pattern.finditer(self._template)
45    cur_pos = 0
46    for mo in match_objects:
47      if mo.group("invalid") is not None:
48        self._handle_invalid(mo)
49
50      span_start_pos = mo.span()[0]
51      span_end_pos = mo.span()[1]
52      escaped_part = mo.group("escaped") or ""
53      variable_part = mo.group("braced")
54      literal_part: Optional[str] = None
55
56      if cur_pos != span_start_pos:
57        literal_part = \
58            self._template[cur_pos:span_start_pos] \
59                + escaped_part
60
61      cur_pos = span_end_pos
62
63      yield literal_part, variable_part
64
65    if cur_pos != len(self._template):
66      yield self._template[cur_pos:], None

A method that returns an iterator over tuples representing template parts. The first value of the tuple, if not None, is a template literal. The second value of the tuple, if not None, is a template variable. If both are not None, then the template literal comes before the variable.

Raises
  • ValueError: If there is an invalid template placeholder
Returns

An iterator of template parts