diff --git a/PKG-INFO b/PKG-INFO index d6afa6e9ae14e5cf27e10ac25b9a506b8ea77373..6357512042a6ec0f50f1399f8afa75d7010f9849 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: swh.model -Version: 3.1.0 +Version: 3.2.0 Summary: Software Heritage data model Home-page: https://forge.softwareheritage.org/diffusion/DMOD/ Author: Software Heritage developers diff --git a/swh.model.egg-info/PKG-INFO b/swh.model.egg-info/PKG-INFO index d6afa6e9ae14e5cf27e10ac25b9a506b8ea77373..6357512042a6ec0f50f1399f8afa75d7010f9849 100644 --- a/swh.model.egg-info/PKG-INFO +++ b/swh.model.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: swh.model -Version: 3.1.0 +Version: 3.2.0 Summary: Software Heritage data model Home-page: https://forge.softwareheritage.org/diffusion/DMOD/ Author: Software Heritage developers diff --git a/swh/model/from_disk.py b/swh/model/from_disk.py index 450bb68ac55471d8588c0ad6dcb470a813527a36..43a9c7157a9d1bf62e769adeb49679e154b1c844 100644 --- a/swh/model/from_disk.py +++ b/swh/model/from_disk.py @@ -549,6 +549,13 @@ class Directory(MerkleNode): key1, key2 = key.rsplit(b"/", 1) del self[key1][key2] + def __contains__(self, key): + if b"/" not in key: + return super().__contains__(key) + else: + key1, key2 = key.split(b"/", 1) + return super().__contains__(key1) and self[key1].__contains__(key2) + def __repr__(self): return "Directory(id=%s, entries=[%s])" % ( hash_to_hex(self.hash), diff --git a/swh/model/hypothesis_strategies.py b/swh/model/hypothesis_strategies.py index c8644a39134d8199f3431d112e6f7644c52bcafa..513dc93989a16f48c9e5d13f6fcac7c7d0a89f3e 100644 --- a/swh/model/hypothesis_strategies.py +++ b/swh/model/hypothesis_strategies.py @@ -1,10 +1,11 @@ -# Copyright (C) 2019-2020 The Software Heritage developers +# Copyright (C) 2019-2021 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information import datetime import string +from typing import Sequence from hypothesis import assume from hypothesis.extra.dateutil import timezones @@ -75,6 +76,12 @@ def sha1(): return binary(min_size=20, max_size=20) +def binaries_without_bytes(blacklist: Sequence[int]): + """Like hypothesis.strategies.binary, but takes a sequence of bytes that + should not be included.""" + return lists(sampled_from([i for i in range(256) if i not in blacklist])).map(bytes) + + @composite def extended_swhids(draw): object_type = draw(sampled_from(ExtendedObjectType)) @@ -274,7 +281,7 @@ def revisions(): def directory_entries_d(): return builds( dict, - name=binary(), + name=binaries_without_bytes(b"/"), target=sha1_git(), type=sampled_from(["file", "dir", "rev"]), perms=sampled_from([perm.value for perm in DentryPerms]), diff --git a/swh/model/tests/test_from_disk.py b/swh/model/tests/test_from_disk.py index 4a7cb385a2935cd41eed7d99cfa01968b0486316..fa3d8b68270526cc9a935bda60b332ec8a991b9d 100644 --- a/swh/model/tests/test_from_disk.py +++ b/swh/model/tests/test_from_disk.py @@ -978,3 +978,19 @@ class DirectoryManipulation(DataMixin, unittest.TestCase): del d["foo"] with self.assertRaisesRegex(ValueError, "bytes Directory entry"): del d[42] + + def test_directory_contains(self): + d = Directory() + d[b"a"] = Directory() + d[b"a/b"] = Directory() + d[b"a/b/c"] = Directory() + d[b"a/b/c/d"] = Content() + + self.assertIn(b"a", d) + self.assertIn(b"a/b", d) + self.assertIn(b"a/b/c", d) + self.assertIn(b"a/b/c/d", d) + + self.assertNotIn(b"b", d) + self.assertNotIn(b"b/c", d) + self.assertNotIn(b"b/c/d", d)