Skip to content

Commit 061e095

Browse files
authored
fix: allow integers to create and add with Path. Fix #146 (#148)
This effectively treats an integer `x` as if it were the string `"[x]"`, allowing `Path(x)` and `x + Path(...) + x` in addition to current `Path(...)[x]`. There are several possible implementation approaches, including a straightforward `if isinstance(x, int): x = f"[{int(x)}]"` in `__new__`, `__add__` and `__radd__`, then letting it parse as an actual string. However, not only that approach is very inefficient, as it calls the parser and tokenization for a known outcome, but also this was not the approach taken by `__getitem__`, currently the only method that can be used as reference on how to properly handle integers. It is also important be consistent with the current _semantics_ of `Path(x)` and `Path + x`, as they're different from `Path[x]` for strings and Paths, so do not converge to using `Path[x]` for both. Even if for integers it ends up being the same result, as integers can only be a single index, do not rely on that and keep semantics distinct and consistent with the string code path. So in `__new__`, instead of resorting to a `return cls()[x]` shortcut, we call the same classmethod as str and Path do, `cls.from_accessors()`, passing the same argument as __getitem__ uses: a tuple with a single element `ListIndex(index=x)`, which is also, not coincidentally, the same result of parsing `"[x]"`. As for `__add__`/`__radd__`, again we use the same semantics as used by str: `Path(...) + x == Path(...)[Path(x)]`, and `x + Path(...) == Path(x)[Path(...)]` And, as we just defined `Path(x)` for an integer x, we can simply extend the `isinstance(x, str)` check to include `int`, without creating a new case, thus enforcing consistent semantics.
1 parent 099f3fe commit 061e095

File tree

1 file changed

+7
-3
lines changed

1 file changed

+7
-3
lines changed

nbtlib/path.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ def __new__(cls, path=None):
3636
if isinstance(path, Path):
3737
return cls.from_accessors(path)
3838

39+
if isinstance(path, int):
40+
# Handle an integer x as if the string "[x]" were just parsed
41+
return cls.from_accessors((ListIndex(index=int(path)),))
42+
3943
accessors = ()
4044
for accessor in parse_accessors(path):
4145
accessors = extend_accessors(accessors, accessor)
@@ -48,7 +52,7 @@ def __getitem__(self, key):
4852
elif isinstance(key, str):
4953
new_accessors = (NamedKey(key),)
5054
elif isinstance(key, int):
51-
new_accessors = (ListIndex(index=key),)
55+
new_accessors = (ListIndex(index=int(key)),)
5256
elif isinstance(key, slice) and all(
5357
n is None for n in [key.start, key.stop, key.step]
5458
):
@@ -68,15 +72,15 @@ def __getitem__(self, key):
6872
def __add__(self, other):
6973
if isinstance(other, Path):
7074
return self[other]
71-
elif isinstance(other, str):
75+
elif isinstance(other, (str, int)):
7276
return self[Path(other)]
7377
else:
7478
return NotImplemented
7579

7680
def __radd__(self, other):
7781
if isinstance(other, Path):
7882
return other[self]
79-
elif isinstance(other, str):
83+
elif isinstance(other, (str, int)):
8084
return Path(other)[self]
8185
else:
8286
return NotImplemented

0 commit comments

Comments
 (0)