Coverage for pandalone/xleash/_parse.py : 99%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
#!/usr/bin/env python # -*- coding: UTF-8 -*- # # Copyright 2014-2019European Commission (JRC); # Licensed under the EUPL (the 'Licence'); # You may not use this work except in compliance with the Licence. # You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl The syntax-parsing part *xleash*.
Prefer accessing the public members from the parent module.
.. currentmodule:: pandalone.xleash """
""" A pair of 1-based strings, denoting the "A1" coordinates of a cell.
The "num" coords (numeric, 0-based) are specified using numpy-arrays (:class:`Coords`). """
cls, row and row.upper(), col and col.upper(), brow and brow.upper(), bcol and bcol.upper(), )
else:
"""Make :class:`Cell` construct with missing 'brow', 'bcol' fields as `None`."""
""" All the infos required to :term:`target` a cell.
An :term:`Edge` contains *A1* :class:`Cell` as `land`.
:param Cell land: the :term:`landing-cell` :param str mov: use None for missing moves. :param str mod: one of (`+`, `-` or `None`) """
"%s(%s%s)" % (self.land, self.mov, self.mod or "") if self.mov else str(self.land) )
# def __repr__(self): # return "Edge('%s')" % str(self)
""" Make a new `Edge` from any non-values supplied, as is capitalized, or nothing.
:param str, None col: ie ``A`` :param str, None row: ie ``1`` :param str, None mov: ie ``RU`` :param str, None mod: ie ``+``
:return: a `Edge` if any non-None :rtype: Edge, None
Examples::
>>> Edge_new('1', 'a', 'Rul', '-') Edge(land=Cell(row='1', col='A'), mov='RUL', mod='-') >>> print(Edge_new('5', '5')) R5C5
No error checking performed::
>>> Edge_new('Any', 'foo', 'BaR', '+_&%') Edge(land=Cell(row='ANY', col='FOO'), mov='BAR', mod='+_&%')
>>> print(Edge_new(None, None, None, None)) None
except were coincidental::
>>> Edge_new(row=0, col=123, mov='BAR', mod=None) Traceback (most recent call last): AttributeError: 'int' object has no attribute 'upper'
>>> Edge_new(row=0, col='A', mov=123, mod=None) Traceback (most recent call last): AttributeError: 'int' object has no attribute 'upper' """
r""" ^\s*(?:(?P<sh_name>[^!]+)?!)? # xl sheet name (?: # 1st-edge (?: (?: (?P<st_col>[A-Z]+|[_^]) # A1-col (?P<st_row>[123456789]\d*|[_^]) # A1-row ) | (?: R(?P<st_row2>-?[123456789]\d*|[_^.]) # RC-row C(?P<st_col2>-?[123456789]\d*|[_^.]) # RC-col ) ) (?:\( (?P<st_mov>L|U|R|D|LD|LU|UL|UR|RU|RD|DL|DR) # moves (?P<st_mod>[+?])? # move modifiers \) )? )? (?:(?P<colon>:) # ':' needed if 2nd (?: # 2nd-edge (?: # target (?: (?P<nd_col>[A-Z]+|[_^.]) # A1-col (?P<nd_row>[123456789]\d*|[_^.]) # A1-row ) | (?: R(?P<nd_row2>-?[123456789]\d*|[_^.]) # RC-row C(?P<nd_col2>-?[123456789]\d*|[_^.]) # RC-col ) ) (?:\( (?P<nd_mov>L|U|R|D|LD|LU|UL|UR|RU|RD|DL|DR) # moves (?P<nd_mod>[+?])? # move-modifiers \) )? )? (?: :(?P<exp_moves>[LURD?123456789]+) # expansion moves )? )? (?: :\s*(?P<js_filt>[{"[].*) # filters )?$ """, re.IGNORECASE | re.X | re.DOTALL, ) """The regex for parsing regular :term:`xl-ref`. """
# TODO: Make exp_moves `?` work different from numbers. r""" ^(?P<moves>[LURD]+) # primitive moves (?P<times>\?|\d+)? # repetition times $""", re.IGNORECASE | re.X, )
"""Excel use these !@#% chars for double-quotes, which are not valid JSON-strings!!"""
"""The :term:`call-specifier` for holding the parsed json-filters."""
"""Make optional the last 2 fields of :class:`CallSpec` ``(args', kwds)`` ."""
""" Parse :term:`call-specifier` from json-filters.
:param call_spec_values: This is a *non-null* structure specifying some function call in the `filter` part, which it can be either:
- string: ``"func_name"`` - list: ``["func_name", ["arg1", "arg2"], {"k1": "v1"}]`` where the last 2 parts are optional and can be given in any order; - object: ``{"func": "func_name", "args": ["arg1"], "kwds": {"k":"v"}}`` where the `args` and `kwds` are optional.
:return: the 3-tuple ``func, args=(), kwds={}`` with the defaults as shown when missing. """
# elif isinstance(call_spec_values, (tuple, list)): ??? else:
""" Returns an iterator that repeats `moves` x `times`, or infinite if unspecified.
Used when parsing primitive :term:`directions`.
:param str moves: the moves to repeat ie ``RU1D?`` :param str times: N of repetitions. If `None` it means infinite repetitions. :return: An iterator of the moves :rtype: iterator
Examples::
>>> list(_repeat_moves('LUR', '3')) ['LUR', 'LUR', 'LUR'] >>> list(_repeat_moves('ABC', '0')) [] >>> _repeat_moves('ABC') ## infinite repetitions repeat('ABC') """
""" Parse rect-expansion into a list of dir-letters iterables.
:param exp_moves: A string with a sequence of primitive moves: es. L1U1R1D1 :type xl_ref: str
:return: A list of primitive-dir chains. :rtype: list
Examples::
>>> res = parse_expansion_moves('lu1urd?') >>> res [repeat('L'), repeat('U', 1), repeat('UR'), repeat('D', 1)]
# infinite generator >>> [next(res[0]) for i in range(10)] ['L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L']
>>> list(res[1]) ['U']
>>> parse_expansion_moves('1LURD') Traceback (most recent call last): ValueError: Invalid rect-expansion(1LURD) due to: 'NoneType' object has no attribute 'groupdict'
"""
_repeat_moves(**_re_exp_moves_parser.match(v).groupdict()) for v in res if v != "" ]
row_a1 or row_rc, col_a1 or col_rc, gs.pop("%s_mov" % prefix), gs.pop("%s_mod" % prefix), default_edge, )
""" Parses a :term:`xl-ref` fragment, anything to the left of the hash(`#`).
:param str xlref_fragment: the url-fragment part of the :term:`xl-ref` string, including the ``'#'`` char. :return: dictionary containing the following parameters:
- sheet: (str, int, None) i.e. ``sheet_name`` - st_edge: (Edge, None) the 1st-ref, with raw cell i.e. ``Edge(land=Cell(row='8', col='UPT'), mov='LU', mod='-')`` - nd_edge: (Edge, None) the 2nd-ref, with raw cell i.e. ``Edge(land=Cell(row='_', col='.'), mov='D', mod='+')`` - exp_moves: (sequence, None), as i.e. ``LDL1`` parsed by :func:`parse_expansion_moves()` - js_filt: dict i.e. ``{"dims: 1}``
:rtype: dict
Examples::
>>> res = parse_xlref_fragment('Sheet1!A1(DR+):Z20(UL):L1U2R1D1:' ... '{"opts":{}, "func": "foo"}') >>> sorted(res.items()) [('call_spec', CallSpec(func='foo', args=[], kwds={})), ('exp_moves', 'L1U2R1D1'), ('nd_edge', Edge(land=Cell(row='20', col='Z'), mov='UL', mod=None)), ('opts', {}), ('sh_name', 'Sheet1'), ('st_edge', Edge(land=Cell(row='1', col='A'), mov='DR', mod='+'))]
Shortcut for all sheet from top-left to bottom-right full-cells::
>>> res = parse_xlref_fragment(':') >>> sorted(res.items()) [('call_spec', None), ('exp_moves', None), ('nd_edge', Edge(land=Cell(row='_', col='_'), mov=None, mod=None)), ('opts', None), ('sh_name', None), ('st_edge', Edge(land=Cell(row='^', col='^'), mov=None, mod=None))]
Errors::
>>> parse_xlref_fragment('A1(DR)Z20(UL)') Traceback (most recent call last): SyntaxError: Not an `xl-ref` syntax: A1(DR)Z20(UL)
"""
""" Like ``_parse_xlref()`` but tries also if `xlreaf` is encased by delimiter chars ``/\\"$%&``.
.. seealso:: _encase_regex """ except SyntaxError: else: else:
""" Parse a :term:`xl-ref` into a dict.
:param str xlref: A url-string abiding to the :term:`xl-ref` syntax. :return: A dict with all fields, with None with those missing. :rtype: dict
Examples::
>>> res = parse_xlref('workbook.xlsx#Sheet1!A1(DR+):Z20(UL):L1U2R1D1:' ... '{"opts":{}, "func": "foo"}') >>> sorted(res.items()) [('call_spec', CallSpec(func='foo', args=[], kwds={})), ('exp_moves', 'L1U2R1D1'), ('nd_edge', Edge(land=Cell(row='20', col='Z'), mov='UL', mod=None)), ('opts', {}), ('sh_name', 'Sheet1'), ('st_edge', Edge(land=Cell(row='1', col='A'), mov='DR', mod='+')), ('url_file', 'workbook.xlsx'), ('xl_ref', 'workbook.xlsx#Sheet1!A1(DR+):Z20(UL):L1U2R1D1:{"opts":{}, "func": "foo"}')]
Shortcut for all sheet from top-left to bottom-right full-cells::
>>> res=parse_xlref('#:') >>> sorted(res.items()) [('call_spec', None), ('exp_moves', None), ('nd_edge', Edge(land=Cell(row='_', col='_'), mov=None, mod=None)), ('opts', None), ('sh_name', None), ('st_edge', Edge(land=Cell(row='^', col='^'), mov=None, mod=None)), ('url_file', None), ('xl_ref', '#:')]
Errors::
>>> parse_xlref('A1(DR)Z20(UL)') Traceback (most recent call last): SyntaxError: No fragment-part (starting with '#'): A1(DR)Z20(UL)
>>> parse_xlref('#A1(DR)Z20(UL)') ## Missing ':'. Traceback (most recent call last): SyntaxError: Not an `xl-ref` syntax: A1(DR)Z20(UL)
But as soon as syntax is matched, subsequent errors raised are :class:`ValueErrors`::
>>> parse_xlref("#A1:B1:{'Bad_JSON_str'}") Traceback (most recent call last): ValueError: Filters are not valid JSON: Expecting property name enclosed in double quotes: line 1 column 2 (char 1) JSON: {'Bad_JSON_str'} """
|