Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 48 additions & 8 deletions src/dbetto/textdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import json
import logging
import os
import re
import sys
from collections.abc import Iterator
Expand Down Expand Up @@ -68,7 +69,11 @@ class TextDB:
"""

def __init__(
self, path: str | Path, lazy: str | bool = False, hidden: bool = False
self,
path: str | Path,
lazy: str | bool = False,
hidden: bool = False,
allow_up_tree=False,
) -> None:
"""Construct a :class:`.TextDB` object.

Expand All @@ -82,6 +87,8 @@ def __init__(
session.
hidden
ignore hidden (i.e. starting with ".") files of directories.
allow_up_tree
allow paths that go up the tree (i.e. contain "..").
"""
if isinstance(lazy, bool):
self.__lazy__ = lazy
Expand All @@ -92,6 +99,7 @@ def __init__(
raise ValueError(msg)

self.__hidden__ = hidden
self.__allow_up_tree__ = allow_up_tree
self.__path__ = Path(path).expanduser().resolve()

if not self.__path__.is_dir():
Expand Down Expand Up @@ -270,16 +278,39 @@ def group(self, label: str) -> AttrsDict:
def __getitem__(self, item: str | Path) -> TextDB | AttrsDict | list | None:
"""Access files or directories in the database."""
# resolve relative paths / links, but keep it relative to self.__path__
# up the tree ("..") outside self.__path__ is only allowed if
# self.__allow_up_tree__ is True
item = Path(item)

if item.is_absolute() and item.is_relative_to(self.__path__):
item = item.expanduser().resolve().relative_to(self.__path__)
try:
item = item.expanduser().resolve().relative_to(self.__path__)
except ValueError:
if self.__allow_up_tree__:
# Path.relative_to(walkup=True) only in python 3.12+
item = Path(os.path.relpath(item, self.__path__))
else:
raise
elif not item.is_absolute():
item = (
(self.__path__ / item).expanduser().resolve().relative_to(self.__path__)
)
try:
item = (
(self.__path__ / item)
.expanduser()
.resolve()
.relative_to(self.__path__)
)
except ValueError:
if self.__allow_up_tree__:
# Path.relative_to(walkup=True) only in python 3.12+
item = Path(
os.path.relpath(
(self.__path__ / item).expanduser().resolve(), self.__path__
)
)
else:
raise
else:
msg = f"{item} lies outside the database root path {self.__path__!s}"
msg = f"{item} is not relative to the database root path {self.__path__!s}"
raise ValueError(msg)

ext_list = "[" + "|".join(self.__extensions__) + "]"
Expand All @@ -302,12 +333,21 @@ def __getitem__(self, item: str | Path) -> TextDB | AttrsDict | list | None:
obj = db_ptr.__path__ / item.name

# do not consider hidden files
if not self.__hidden__ and obj.name.startswith("."):
if not self.__hidden__ and (
obj.name.startswith(".") and not obj.name.startswith("..")
):
msg = f"skipping hidden file: {obj}"
log.debug(msg)
return None

# if directory, construct another TextDB object
if obj.is_dir():
db_ptr.__store__[item_id] = TextDB(obj, lazy=self.__lazy__)
db_ptr.__store__[item_id] = TextDB(
obj,
lazy=self.__lazy__,
hidden=self.__hidden__,
allow_up_tree=self.__allow_up_tree__,
)

else:
# try to attach an extension if file cannot be found
Expand Down
6 changes: 6 additions & 0 deletions tests/test_textdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ def test_scan():
jdb.scan(recursive=True)

assert sorted(jdb.__dict__.keys()) == [
"__allow_up_tree__",
"__ftypes__",
"__hidden__",
"__lazy__",
Expand All @@ -149,6 +150,7 @@ def test_scan():
jdb.scan(recursive=False)

assert sorted(jdb.__dict__.keys()) == [
"__allow_up_tree__",
"__ftypes__",
"__hidden__",
"__lazy__",
Expand All @@ -164,6 +166,7 @@ def test_scan():
jdb.scan(recursive=False, subdir="dir1")

assert sorted(jdb.__dict__.keys()) == [
"__allow_up_tree__",
"__ftypes__",
"__hidden__",
"__lazy__",
Expand Down Expand Up @@ -292,6 +295,7 @@ def test_lazyness():
jdb = TextDB(testdb, lazy="auto")
assert jdb.__lazy__ is True
assert sorted(jdb.__dict__.keys()) == [
"__allow_up_tree__",
"__ftypes__",
"__hidden__",
"__lazy__",
Expand All @@ -302,6 +306,7 @@ def test_lazyness():
jdb = TextDB(testdb, lazy=True)
assert jdb.__lazy__ is True
assert sorted(jdb.__dict__.keys()) == [
"__allow_up_tree__",
"__ftypes__",
"__hidden__",
"__lazy__",
Expand All @@ -312,6 +317,7 @@ def test_lazyness():
jdb = TextDB(testdb, lazy=False)
assert jdb.__lazy__ is False
assert sorted(jdb.__dict__.keys()) == [
"__allow_up_tree__",
"__ftypes__",
"__hidden__",
"__lazy__",
Expand Down