diff --git a/electrum/tests/test_util.py b/electrum/tests/test_util.py index cf204bb61..4968ca66f 100644 --- a/electrum/tests/test_util.py +++ b/electrum/tests/test_util.py @@ -1,10 +1,11 @@ from decimal import Decimal +from electrum import util from electrum.util import (format_satoshis, format_fee_satoshis, parse_URI, is_hash256_str, chunks, is_ip_address, list_enabled_bits, format_satoshis_plain, is_private_netaddress, is_hex_str, is_integer, is_non_negative_integer, is_int_or_float, - is_non_negative_int_or_float) + is_non_negative_int_or_float, is_subpath) from . import ElectrumTestCase @@ -292,3 +293,25 @@ class TestUtil(ElectrumTestCase): self.assertFalse(is_private_netaddress("[2a00:1450:400e:80d::200e]")) self.assertFalse(is_private_netaddress("8.8.8.8")) self.assertFalse(is_private_netaddress("example.com")) + + def test_is_subpath(self): + self.assertTrue(util.is_subpath("/a/b/c/d/e", "/")) + self.assertTrue(util.is_subpath("/a/b/c/d/e", "/a")) + self.assertTrue(util.is_subpath("/a/b/c/d/e", "/a/")) + self.assertTrue(util.is_subpath("/a/b/c/d/e", "/a/b/c/")) + self.assertTrue(util.is_subpath("/a/b/c/d/e/", "/a/b/c/")) + self.assertTrue(util.is_subpath("/a/b/c/d/e/", "/a/b/c")) + self.assertTrue(util.is_subpath("/a/b/c/d/e/", "/a/b/c/d/e/")) + self.assertTrue(util.is_subpath("/", "/")) + self.assertTrue(util.is_subpath("a/b/c", "a")) + self.assertTrue(util.is_subpath("a/b/c", "a/")) + self.assertTrue(util.is_subpath("a/b/c", "a/b")) + self.assertTrue(util.is_subpath("a/b/c", "a/b/c")) + + self.assertFalse(util.is_subpath("/a/b/c/d/e/", "/b")) + self.assertFalse(util.is_subpath("/a/b/c/d/e/", "/b/c/")) + self.assertFalse(util.is_subpath("/a/b/c", "/a/b/c/d/e/")) + self.assertFalse(util.is_subpath("/a/b/c", "a")) + self.assertFalse(util.is_subpath("/a/b/c", "c")) + self.assertFalse(util.is_subpath("a", "/a/b/c")) + self.assertFalse(util.is_subpath("c", "/a/b/c")) diff --git a/electrum/util.py b/electrum/util.py index b82a650bb..8340d5dd1 100644 --- a/electrum/util.py +++ b/electrum/util.py @@ -1201,6 +1201,17 @@ def make_dir(path, allow_symlink=True): os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) +def is_subpath(long_path: str, short_path: str) -> bool: + """Returns whether long_path is a sub-path of short_path.""" + try: + common = os.path.commonpath([long_path, short_path]) + except ValueError: + return False + short_path = standardize_path(short_path) + common = standardize_path(common) + return short_path == common + + def log_exceptions(func): """Decorator to log AND re-raise exceptions.""" assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine'