Scott Wilson avatar Scott Wilson committed 68cf2a6

Extend path lookup notation to allow '.' element.

Comments (0)

Files changed (2)

flatland/schema/paths.py

 
 TOP = symbol('TOP')
 UP = symbol('UP')
+HERE = symbol('HERE')
 SLICE = symbol('SLICE')
 NAME = symbol('NAME')
 
       (?<!\\)\[(-?\d*:?-?\d*\:?-?\d*)(?<!\\)\](?=$|/|\[)
     | # [bogus]
       (?<!\\)\[[^\]]*(?<!\\)\](?=$|/|\[)
-    | # .. at start
-      ^\.\.
-    | # .. in expression
-      (?<=[^\\]/)\.\.
+    | # . or .. at start
+      ^\.\.?
+    | # . or .. in expression
+      (?<=[^\\]/)\.\.?
     |
       \[
     )
                 elif op is UP:
                     if el.parent:
                         el = el.parent
+                elif op is HERE:
+                    pass
                 elif op is NAME:
                     try:
                         el = el._index(data)
     """Parse *path* and return a list of (OP, data) pairs."""
     tokens = []
     last, last_type = None, None
-    upped = False
+    canonical = True
 
     for token, slice_spec in _tokenize_re.findall(path):
         if token == '/':
             elif last == '/':
                 tokens.append((NAME, None))
 
+        # . -> here
+        elif token == '.':
+            canonical = False
+            tokens.append((HERE, None))
+
         # foo/../bar -> 'foo', up, 'bar'
         elif token == '..':
-            upped = True
+            canonical = False
             tokens.append((UP, None))
 
         # [:] or [123] or [1:] or [5] etc.
         last = token
         last_type = tokens[-1][0]
 
-    if not upped:
+    if canonical:
         return tokens
     # foo/../bar -> bar
     return _canonicalize(tokens)
 
 
 def _canonicalize(tokens):
-    """Collapse redundant steps from token lists containing UP ops."""
+    """Collapse redundant steps from token lists containing UP or HERE ops."""
     canonical = []
     for token in tokens:
+        if token[0] is HERE and len(tokens) > 1:
+            continue
         if token[0] is not UP or not canonical:
             canonical.append(token)
             continue

tests/schema/test_paths.py

     SLICE,
     TOP,
     UP,
+    HERE,
     pathexpr,
     tokenize,
     )
 
 top = (TOP, None)
 up = (UP, None)
+here = (HERE, None)
 name = lambda x: (NAME, x)
 sl = lambda x: (SLICE, x)
 
 
 def test_tokenize():
     _tokencases = [
+        ('.', [here]),
         ('/', [top]),
         ('..', [up]),
         ('/..', [top]),
         ('foo/', [name('foo')]),
         ('foo/bar', [name('foo'), name('bar')]),
         ('foo/../bar', [name('bar')]),
+        ('foo/./bar', [name('foo'), name('bar')]),
+        ('foo/./bar/.', [name('foo'), name('bar')]),
         ('foo/bar[bogus]', [name('foo'), name('bar[bogus]')]),
         ('a[b][c:d][0]', [name('a[b][c:d]'), name('0')]),
+        ('.[1]', [name('1')]),
         ('foo[1]', [name('foo'), name('1')]),
         ('foo[1]/', [name('foo'), name('1')]),
+        ('./foo[1]/', [name('foo'), name('1')]),
         ('foo[1][2][3]', [name('foo'), name('1'), name('2'), name('3')]),
         ('[1][2][3]', [name('1'), name('2'), name('3')]),
         ('[1]/foo/[2]', [name('1'), name('foo'), name('2')]),
 
 def test_tokenize_escapes():
     _tokencases = [
+        (r'\.', [name('.')]),
         (r'\/', [name('/')]),
         (r'\.\.', [name('..')]),
         (r'/\.\.', [top, name('..')]),
     today = date.today()
 
     _finders = [
+        (el['i1'], '.', [0]),
         (el, 'i1', [0]),
         (el, '/i1', [0]),
         (el, '../i1', [0]),
+        (el, '../i1/.', [0]),
         (el['i1'], '../i1', [0]),
         (el, 'd1/d1i1', [1]),
         (el, '/d1/d1i2', [2]),
         (el, '/l1[:]', [3, 3]),
         (el, '/l1[2:]', []),
         (el, '/l1[0]', [3]),
+        (el, './l1[0]', [3]),
+        (el, 'l1/.[0]', [3]),
         (el, '/l2[:]/l2i1', [4, 4, 4]),
         (el, '/l3[:]', [[6, 6], [6, 6]]),
         (el, '/l3[:][:]', [6, 6, 6, 6]),
         (el, 'a1[::-2]', [15, 13, 11]),
         (el, 'dt1', [today]),
         (el, 'dt1/year', [today.year]),
+        (el, 'dt1/./year', [today.year]),
         ]
     for element, path, expected in _finders:
         yield _finds, element, path, expected
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.