def test_iter_jsonpointer_regular(self): self.assertListEqual(list(iter_jsonpointer_parts('/a')), ['a']) self.assertListEqual( list(iter_jsonpointer_parts_relaxed('/a')), ['', 'a']) self.assertListEqual(list(iter_jsonpointer_parts('/a/b')), ['a', 'b']) self.assertListEqual( list(iter_jsonpointer_parts_relaxed('/a/b')), ['', 'a', 'b'])
def test_iter_jsonpointer_regular(self): self.assertListEqual(list(iter_jsonpointer_parts("/a")), ["a"]) self.assertListEqual(list(iter_jsonpointer_parts_relaxed("/a")), ["", "a"]) self.assertListEqual(list(iter_jsonpointer_parts("/a/b")), ["a", "b"]) self.assertListEqual(list(iter_jsonpointer_parts_relaxed("/a/b")), ["", "a", "b"])
def test_iter_jsonpointer_with_spaces(self): self.assertListEqual( list(iter_jsonpointer_parts('/ some ')), [' some ']) self.assertListEqual( list(iter_jsonpointer_parts('/ some / ')), [' some ', ' ']) self.assertListEqual( list(iter_jsonpointer_parts_relaxed(' some ')), [' some ']) self.assertListEqual( list(iter_jsonpointer_parts_relaxed(' some / ')), [' some ', ' '])
def test_iter_jsonpointer_with_spaces(self): self.assertListEqual(list(iter_jsonpointer_parts("/ some ")), [" some "]) self.assertListEqual(list(iter_jsonpointer_parts("/ some / ")), [" some ", " "]) self.assertListEqual(list(iter_jsonpointer_parts_relaxed(" some ")), [" some "]) self.assertListEqual(list(iter_jsonpointer_parts_relaxed(" some / ")), [" some ", " "])
def test_iter_jsonpointer_relaxed_massive(self): cases = [ ('/a', ['', 'a']), ('/a/', ['', 'a', '']), ('/a/b', ['', 'a', 'b']), ('/a/b/', ['', 'a', 'b', '']), ('/a//b', ['', 'a', '', 'b']), ('/', ['', '']), ('', ['']), ('/ some ', ['', ' some ']), ('/ some /', ['', ' some ', '']), ('/ some / ', ['', ' some ', ' ']), (None, AttributeError), ('a', ['a']), ('a/', ['a', '']), ('a/b', ['a', 'b']), ('a/b/', ['a', 'b', '']), ('a/../b/.', ['a', '..', 'b', '.']), ('a/../b/.', ['a', '..', 'b', '.']), (' some ', [' some ']), (' some /', [' some ', '']), (' some / ', [' some ', ' ']), (' some / /', [' some ', ' ', '']), ] for i, (inp, out) in enumerate(cases): msg = 'case #%i' % i try: if issubclass(out, Exception): with self.assertRaises(out, msg=msg): list(iter_jsonpointer_parts_relaxed(inp)) continue except TypeError as ex: if ex.args[0].startswith('issubclass()'): self.assertEqual( list(iter_jsonpointer_parts_relaxed(inp)), out, msg) else: raise ex
def test_iter_jsonpointer_relaxed_massive(self): cases = [ ("/a", ["", "a"]), ("/a/", ["", "a", ""]), ("/a/b", ["", "a", "b"]), ("/a/b/", ["", "a", "b", ""]), ("/a//b", ["", "a", "", "b"]), ("/", ["", ""]), ("", [""]), ("/ some ", ["", " some "]), ("/ some /", ["", " some ", ""]), ("/ some / ", ["", " some ", " "]), (None, AttributeError), ("a", ["a"]), ("a/", ["a", ""]), ("a/b", ["a", "b"]), ("a/b/", ["a", "b", ""]), ("a/../b/.", ["a", "..", "b", "."]), ("a/../b/.", ["a", "..", "b", "."]), (" some ", [" some "]), (" some /", [" some ", ""]), (" some / ", [" some ", " "]), (" some / /", [" some ", " ", ""]), ] for i, (inp, out) in enumerate(cases): msg = "case #%i" % i try: if issubclass(out, Exception): with self.assertRaises(out, msg=msg): list(iter_jsonpointer_parts_relaxed(inp)) continue except TypeError as ex: if ex.args[0].startswith("issubclass()"): self.assertEqual(list(iter_jsonpointer_parts_relaxed(inp)), out, msg) else: raise ex
def map_path(self, path): r""" Maps a '/rooted/path' using all aliases while descending its child pmods. It uses any aliases on all child pmods if found. :param str path: a rooted path to transform :return: the rooted mapped path or '/' if path was '/' :rtype: str or None Examples:: >>> pmods = pmods_from_tuples([ ... ('/a', 'A/AA'), ... ('/~a(\\w*)', r'BB\1'), ... ('/~a\\w*/~d.*', r'D \g<0>'), ... ('/~a(\\d+)', r'C/\1'), ... ('/~a(\\d+)/~(c.*)', r'CC-/\1'), # The 1st group is ignored! ... ('/~a\\d+/~e.*', r'/newroot/\g<0>'), # Rooted mapping. ... ]) >>> pmods.map_path('/a') '/A/AA' >>> pmods.map_path('/a_hi') '/BB_hi' >>> pmods.map_path('/a12') '/C/12' >>> pmods.map_path('/a12/etc') '/newroot/etc' Notice how children from *all* matching prior-steps are merged:: >>> pmods.map_path('/a12/dow') '/C/12/D dow' >>> pmods.map_path('/a12/cow') '/C/12/CC-/cow' To map *root* use '' which matches before the 1st slash('/'):: >>> pmods = pmods_from_tuples([('', 'New/Root'),]) ## Relative >>> pmods pmod({'': pmod('New/Root')}) >>> pmods.map_path('/for/plant') 'New/Root/for/plant' >>> pmods_from_tuples([('', '/New/Root'),]).map_path('/for/plant') '/New/Root/for/plant' .. Note:: Using slash('/') for "from" path will NOT map *root*:: >>> pmods = pmods_from_tuples([('/', 'New/Root'),]) >>> pmods pmod({'': pmod({'': pmod('New/Root')})}) >>> pmods.map_path('/for/plant') '/for/plant' >>> pmods.map_path('//for/plant') '/New/Root/for/plant' '/root' but '' always remains unchanged (whole document):: >>> pmods.map_path('') '' """ is_folder = len(path) > 1 and path.endswith('/') if is_folder: path = path[:-1] steps = tuple(iter_jsonpointer_parts_relaxed(path)) if self._alias is None: nsteps = () else: nsteps = tuple(iter_jsonpointer_parts_relaxed(self._alias)) if steps: pmod = self # Separate last-step from loop below, since # merged child-pmods in `descend` are not needed. # for step in steps[:-1]: if pmod: pmod, alias = pmod.descend(step) if alias is not None: if alias.startswith('.'): nsteps += (step, ) step = alias # XXX: Monkey business here. if len(step) > 1 and step.endswith('/'): step = step[:-1] nsteps += tuple(iter_jsonpointer_parts_relaxed(step)) final_step = steps[-1] if pmod: alias = pmod.alias(final_step) if alias is not None: if alias.startswith('.'): nsteps += (final_step, ) final_step = alias # XXX: Monkey business here. is_folder = len(final_step) > 1 and final_step.endswith('/') if is_folder: final_step = final_step[:-1] nsteps += tuple(iter_jsonpointer_parts_relaxed(final_step)) npath = _join_paths(*nsteps) if is_folder: path += '%s/' % path return npath
def map_path(self, path): r""" Maps a '/rooted/path' using all aliases while descending its child pmods. It uses any aliases on all child pmods if found. :param str path: a rooted path to transform :return: the rooted mapped path or '/' if path was '/' :rtype: str or None Examples:: >>> pmods = pmods_from_tuples([ ... ('/a', 'A/AA'), ... ('/~a(\\w*)', r'BB\1'), ... ('/~a\\w*/~d.*', r'D \g<0>'), ... ('/~a(\\d+)', r'C/\1'), ... ('/~a(\\d+)/~(c.*)', r'CC-/\1'), # The 1st group is ignored! ... ('/~a\\d+/~e.*', r'/newroot/\g<0>'), # Rooted mapping. ... ]) >>> pmods.map_path('/a') '/A/AA' >>> pmods.map_path('/a_hi') '/BB_hi' >>> pmods.map_path('/a12') '/C/12' >>> pmods.map_path('/a12/etc') '/newroot/etc' Notice how children from *all* matching prior-steps are merged:: >>> pmods.map_path('/a12/dow') '/C/12/D dow' >>> pmods.map_path('/a12/cow') '/C/12/CC-/cow' To map *root* use '' which matches before the 1st slash('/'):: >>> pmods = pmods_from_tuples([('', 'New/Root'),]) ## Relative >>> pmods pmod({'': pmod('New/Root')}) >>> pmods.map_path('/for/plant') 'New/Root/for/plant' >>> pmods_from_tuples([('', '/New/Root'),]).map_path('/for/plant') '/New/Root/for/plant' .. Note:: Using slash('/') for "from" path will NOT map *root*:: >>> pmods = pmods_from_tuples([('/', 'New/Root'),]) >>> pmods pmod({'': pmod({'': pmod('New/Root')})}) >>> pmods.map_path('/for/plant') '/for/plant' >>> pmods.map_path('//for/plant') '/New/Root/for/plant' '/root' but '' always remains unchanged (whole document):: >>> pmods.map_path('') '' """ is_folder = len(path) > 1 and path.endswith("/") if is_folder: path = path[:-1] steps = tuple(iter_jsonpointer_parts_relaxed(path)) if self._alias is None: nsteps = () else: nsteps = tuple(iter_jsonpointer_parts_relaxed(self._alias)) if steps: pmod = self # Separate last-step from loop below, since # merged child-pmods in `descend` are not needed. # for step in steps[:-1]: if pmod: pmod, alias = pmod.descend(step) if alias is not None: if alias.startswith("."): nsteps += (step, ) step = alias # XXX: Monkey business here. if len(step) > 1 and step.endswith("/"): step = step[:-1] nsteps += tuple(iter_jsonpointer_parts_relaxed(step)) final_step = steps[-1] if pmod: alias = pmod.alias(final_step) if alias is not None: if alias.startswith("."): nsteps += (final_step, ) final_step = alias # XXX: Monkey business here. is_folder = len(final_step) > 1 and final_step.endswith("/") if is_folder: final_step = final_step[:-1] nsteps += tuple(iter_jsonpointer_parts_relaxed(final_step)) npath = _join_paths(*nsteps) if is_folder: path += "%s/" % path return npath
def test_iter_jsonpointer_None(self): with self.assertRaises(AttributeError): list(iter_jsonpointer_parts(None)) with self.assertRaises(AttributeError): list(iter_jsonpointer_parts_relaxed(None))
def test_iter_jsonpointer_folder(self): self.assertListEqual(list(iter_jsonpointer_parts('/a/')), ['a', '']) self.assertListEqual( list(iter_jsonpointer_parts_relaxed('/a/')), ['', 'a', ''])
def pmods_from_tuples(pmods_tuples): r""" Turns a list of 2-tuples into a *pmods* hierarchy. - Each tuple defines the renaming-or-relocation of the *final* part of some component path onto another one into value-trees, such as:: (/rename/path, foo) --> rename/foo (relocate/path, foo/bar) --> relocate/foo/bar - The "from" path may be: - relative, - absolute(starting with `/`), or - "anywhere"(starting with `//`). - In case a "step" in the "from" path starts with tilda(`~`), it is assumed to be a regular-expression, and it is removed from it. The "to" path can make use of any "from" capture-groups:: ('/~all(.*)/path', 'foo') (r'~some[\d+]/path', 'foo\1') ('//~all(.*)/path', 'foo') :param list(tuple(str, str) pmods_tuples: :return: a root pmod :rtype: Pmod Example:: >>> pmods_from_tuples([ ... ('/a', 'A1/A2'), ... ('/a/b', 'B'), ... ]) pmod({'': pmod({'a': pmod('A1/A2', {'b': pmod('B')})})}) >>> pmods_from_tuples([ ... ('/~a*', 'A1/A2'), ... ('/a/~b[123]', 'B'), ... ]) pmod({'': pmod({'a': pmod({re.compile('b[123]'): pmod('B')})}, {re.compile('a*'): pmod('A1/A2')})}) This is how you map *root*:: >>> pmods = pmods_from_tuples([ ... ('', 'relative/Root'), ## Make all paths relatives. ... ('/a/b', '/Rooted/B'), ## But map `b` would be "rooted". ... ]) >>> pmods pmod({'': pmod('relative/Root', {'a': pmod({'b': pmod('/Rooted/B')})})}) >>> pmods.map_path('/a/c') 'relative/Root/a/c' >>> pmods.map_path('/a/b') '/Rooted/B' But note that '/' maps the 1st "empty-str" step after root:: >>> pmods_from_tuples([ ... ('/', 'New/Root'), ... ]) pmod({'': pmod({'': pmod('New/Root')})}) TODO: Implement "anywhere" matches. """ root = Pmod() for i, (f, t) in enumerate(pmods_tuples): if (f, t) == ("", "") or f is None or t is None: msg = 'pmod-tuple #%i of %i: Invalid "from-to" tuple (%r, %r).' log.warning(msg, i + 1, len(pmods_tuples), f, t) continue pmod = root for srcstep in iter_jsonpointer_parts_relaxed(f): is_regex = srcstep.startswith("~") if is_regex: pmod = pmod._append_into_regxs(srcstep[1:]) else: pmod = pmod._append_into_steps(srcstep) pmod._alias = t return root
def test_iter_jsonpointer_root(self): self.assertListEqual(list(iter_jsonpointer_parts('/')), ['']) self.assertListEqual( list(iter_jsonpointer_parts_relaxed('/')), ['', ''])
def test_iter_jsonpointer_empty(self): self.assertListEqual(list(iter_jsonpointer_parts('')), []) self.assertListEqual(list(iter_jsonpointer_parts_relaxed('')), [''])
def pmods_from_tuples(pmods_tuples): """ Turns a list of 2-tuples into a *pmods* hierarchy. - Each tuple defines the renaming-or-relocation of the *final* part of some component path onto another one into value-trees, such as:: (/rename/path, foo) --> rename/foo (relocate/path, foo/bar) --> relocate/foo/bar - The "from" path may be: - relative, - absolute(starting with `/`), or - "anywhere"(starting with `//`). - In case a "step" in the "from" path starts with tilda(`~`), it is assumed to be a regular-expression, and it is removed from it. The "to" path can make use of any "from" capture-groups:: ('/~all(.*)/path', 'foo') ('~some[\d+]/path', 'foo\1') ('//~all(.*)/path', 'foo') :param list(tuple(str, str) pmods_tuples: :return: a root pmod :rtype: Pmod Example:: >>> pmods_from_tuples([ ... ('/a', 'A1/A2'), ... ('/a/b', 'B'), ... ]) pmod({'': pmod({'a': pmod('A1/A2', {'b': pmod('B')})})}) >>> pmods_from_tuples([ ... ('/~a*', 'A1/A2'), ... ('/a/~b[123]', 'B'), ... ]) pmod({'': pmod({'a': pmod(OrderedDict([(re.compile('b[123]'), pmod('B'))]))}, OrderedDict([(re.compile('a*'), pmod('A1/A2'))]))}) This is how you map *root*:: >>> pmods = pmods_from_tuples([ ... ('', 'relative/Root'), ## Make all paths relatives. ... ('/a/b', '/Rooted/B'), ## But map `b` would be "rooted". ... ]) >>> pmods pmod({'': pmod('relative/Root', {'a': pmod({'b': pmod('/Rooted/B')})})}) >>> pmods.map_path('/a/c') 'relative/Root/a/c' >>> pmods.map_path('/a/b') '/Rooted/B' But note that '/' maps the 1st "empty-str" step after root:: >>> pmods_from_tuples([ ... ('/', 'New/Root'), ... ]) pmod({'': pmod({'': pmod('New/Root')})}) TODO: Implement "anywhere" matches. """ root = Pmod() for i, (f, t) in enumerate(pmods_tuples): if (f, t) == ('', '') or f is None or t is None: msg = 'pmod-tuple #%i of %i: Invalid "from-to" tuple (%r, %r).' log.warning(msg, i + 1, len(pmods_tuples), f, t) continue pmod = root for srcstep in iter_jsonpointer_parts_relaxed(f): is_regex = srcstep.startswith('~') if is_regex: pmod = pmod._append_into_regxs(srcstep[1:]) else: pmod = pmod._append_into_steps(srcstep) pmod._alias = t return root
def test_iter_jsonpointer_folder(self): self.assertListEqual(list(iter_jsonpointer_parts("/a/")), ["a", ""]) self.assertListEqual(list(iter_jsonpointer_parts_relaxed("/a/")), ["", "a", ""])
def test_iter_jsonpointer_empty(self): self.assertListEqual(list(iter_jsonpointer_parts("")), []) self.assertListEqual(list(iter_jsonpointer_parts_relaxed("")), [""])
def test_iter_jsonpointer_root(self): self.assertListEqual(list(iter_jsonpointer_parts("/")), [""]) self.assertListEqual(list(iter_jsonpointer_parts_relaxed("/")), ["", ""])