diff --git a/PKG-INFO b/PKG-INFO index 468d926da6dfe3a712738ce507759f2f978dfec9..cd3b55ba1a9c82d643e23a13f02c19d5a3db0d66 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,14 +1,14 @@ Metadata-Version: 2.1 Name: swh.model -Version: 0.0.36 +Version: 0.0.37 Summary: Software Heritage data model Home-page: https://forge.softwareheritage.org/diffusion/DMOD/ Author: Software Heritage developers Author-email: swh-devel@inria.fr License: UNKNOWN -Project-URL: Source, https://forge.softwareheritage.org/source/swh-model -Project-URL: Bug Reports, https://forge.softwareheritage.org/maniphest Project-URL: Funding, https://www.softwareheritage.org/donate +Project-URL: Bug Reports, https://forge.softwareheritage.org/maniphest +Project-URL: Source, https://forge.softwareheritage.org/source/swh-model Description: swh-model ========= diff --git a/requirements-swh.txt b/requirements-swh.txt index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..32aa75ec6ce17c3177f18150967201f13ef774bb 100644 --- a/requirements-swh.txt +++ b/requirements-swh.txt @@ -0,0 +1 @@ +swh.core >= 0.0.60 diff --git a/setup.py b/setup.py index c28e4bf0ad4ef5a6931ae5e986ab0b66b64569b0..0e24d22bb117992517682eaf87c785644cabae8c 100755 --- a/setup.py +++ b/setup.py @@ -73,6 +73,8 @@ setup( entry_points=''' [console_scripts] swh-identify=swh.model.cli:identify + [swh.cli.subcommands] + identify=swh.model.cli:identify ''', classifiers=[ "Programming Language :: Python :: 3", diff --git a/swh.model.egg-info/PKG-INFO b/swh.model.egg-info/PKG-INFO index 468d926da6dfe3a712738ce507759f2f978dfec9..cd3b55ba1a9c82d643e23a13f02c19d5a3db0d66 100644 --- a/swh.model.egg-info/PKG-INFO +++ b/swh.model.egg-info/PKG-INFO @@ -1,14 +1,14 @@ Metadata-Version: 2.1 Name: swh.model -Version: 0.0.36 +Version: 0.0.37 Summary: Software Heritage data model Home-page: https://forge.softwareheritage.org/diffusion/DMOD/ Author: Software Heritage developers Author-email: swh-devel@inria.fr License: UNKNOWN -Project-URL: Source, https://forge.softwareheritage.org/source/swh-model -Project-URL: Bug Reports, https://forge.softwareheritage.org/maniphest Project-URL: Funding, https://www.softwareheritage.org/donate +Project-URL: Bug Reports, https://forge.softwareheritage.org/maniphest +Project-URL: Source, https://forge.softwareheritage.org/source/swh-model Description: swh-model ========= diff --git a/swh.model.egg-info/entry_points.txt b/swh.model.egg-info/entry_points.txt index 9c74f3660b342d539bac70c1973a1cea205cbb7a..03eb1114b8c543f6bcdd3518f05ecbd259b5c3e9 100644 --- a/swh.model.egg-info/entry_points.txt +++ b/swh.model.egg-info/entry_points.txt @@ -1,4 +1,6 @@ [console_scripts] swh-identify=swh.model.cli:identify + [swh.cli.subcommands] + identify=swh.model.cli:identify \ No newline at end of file diff --git a/swh.model.egg-info/requires.txt b/swh.model.egg-info/requires.txt index af8f5be4c20b71a50eb1d2e0478234978170c263..d8298156bb5103e8de007f314803501c9caaeae4 100644 --- a/swh.model.egg-info/requires.txt +++ b/swh.model.egg-info/requires.txt @@ -3,6 +3,7 @@ Click attrs hypothesis python-dateutil +swh.core>=0.0.60 [testing] pytest diff --git a/swh/model/cli.py b/swh/model/cli.py index 82af76f8cd5827167133edc1bb0556145d5d688e..de04bb17696b26bb0ec39460f9b1de7ba9098049 100644 --- a/swh/model/cli.py +++ b/swh/model/cli.py @@ -9,6 +9,8 @@ import sys from functools import partial +from swh.core.cli import CONTEXT_SETTINGS + from swh.model import identifiers as pids from swh.model.exceptions import ValidationError from swh.model.from_disk import Content, Directory @@ -62,7 +64,7 @@ def identify_object(obj_type, follow_symlinks, obj): return (obj, pid) -@click.command() +@click.command(context_settings=CONTEXT_SETTINGS) @click.option('--dereference/--no-dereference', 'follow_symlinks', default=True, help='follow (or not) symlinks for OBJECTS passed as arguments ' @@ -74,7 +76,7 @@ def identify_object(obj_type, follow_symlinks, obj): help='type of object to identify (default: auto)') @click.option('--verify', '-v', metavar='PID', type=PidParamType(), help='reference identifier to be compared with computed one') -@click.argument('objects', nargs=-1, +@click.argument('objects', nargs=-1, required=True, type=click.Path(exists=True, readable=True, allow_dash=True, path_type=bytes)) def identify(obj_type, verify, show_filename, follow_symlinks, objects): @@ -90,13 +92,13 @@ def identify(obj_type, verify, show_filename, follow_symlinks, objects): Examples: \b - $ swh-identify fork.c kmod.c sched/deadline.c + $ swh identify fork.c kmod.c sched/deadline.c swh:1:cnt:2e391c754ae730bd2d8520c2ab497c403220c6e3 fork.c swh:1:cnt:0277d1216f80ae1adeed84a686ed34c9b2931fc2 kmod.c swh:1:cnt:57b939c81bce5d06fa587df8915f05affbe22b82 sched/deadline.c \b - $ swh-identify --no-filename /usr/src/linux/kernel/ + $ swh identify --no-filename /usr/src/linux/kernel/ swh:1:dir:f9f858a48d663b3809c9e2f336412717496202ab """ diff --git a/swh/model/hypothesis_strategies.py b/swh/model/hypothesis_strategies.py index 3e006c8afaae1d5ec8f51c6135ae7023fbabc062..1b99957a737030dab80f3f01f204a925e6d2621f 100644 --- a/swh/model/hypothesis_strategies.py +++ b/swh/model/hypothesis_strategies.py @@ -20,6 +20,15 @@ from .model import ( from .identifiers import snapshot_identifier, identifier_to_bytes +pgsql_alphabet = characters( + blacklist_categories=('Cs', ), + blacklist_characters=['\u0000']) # postgresql does not like these + + +def pgsql_text(): + return text(alphabet=pgsql_alphabet) + + def sha1_git(): return binary(min_size=20, max_size=20) @@ -89,10 +98,7 @@ def releases(draw): def revision_metadata(): - alphabet = characters( - blacklist_categories=('Cs', ), - blacklist_characters=['\u0000']) # postgresql does not like these - return dictionaries(text(alphabet=alphabet), text(alphabet=alphabet)) + return dictionaries(pgsql_text(), pgsql_text()) def revisions(): @@ -125,7 +131,7 @@ def directories(): def contents(draw): (status, data, reason) = draw(one_of( tuples(just('visible'), binary(), none()), - tuples(just('absent'), none(), text()), + tuples(just('absent'), none(), pgsql_text()), tuples(just('hidden'), none(), none()), )) @@ -143,7 +149,7 @@ def contents(draw): def branch_names(): - return binary() + return binary(min_size=1) def branch_targets_object(): diff --git a/swh/model/model.py b/swh/model/model.py index 25a565b4c5560f7fb894887064995b1f4bd923d2..6e3fd0e0a7d3f14bede8569e54629022a2df817c 100644 --- a/swh/model/model.py +++ b/swh/model/model.py @@ -16,20 +16,6 @@ from .identifiers import normalize_timestamp Sha1Git = bytes -def contains_optional_validator(validator): - """Inspects an attribute's validator to find its type. - Inspired by `hypothesis/searchstrategy/attrs.py`.""" - if isinstance(validator, attr.validators._OptionalValidator): - return True - elif isinstance(validator, attr.validators._AndValidator): - for validator in validator._validators: - res = contains_optional_validator(validator) - if res: - return True - else: - return False - - class BaseModel: """Base class for SWH model classes. @@ -45,31 +31,7 @@ class BaseModel: def from_dict(cls, d): """Takes a dictionary representing a tree of SWH objects, and recursively builds the corresponding objects.""" - if not isinstance(d, dict): - raise TypeError( - '%s.from_dict expects a dict, not %r' % (cls.__name__, d)) - kwargs = {} - for (name, attribute) in attr.fields_dict(cls).items(): - type_ = attribute.type - - # Heuristic to detect `Optional[X]` and unwrap it to `X`. - if contains_optional_validator(attribute.validator): - if name not in d: - continue - if d[name] is None: - continue - else: - type_ = type_.__args__[0] - - # Construct an object of the expected type - if issubclass(type_, BaseModel): - kwargs[name] = type_.from_dict(d[name]) - elif issubclass(type_, Enum): - kwargs[name] = type_(d[name]) - else: - kwargs[name] = d[name] - - return cls(**kwargs) + return cls(**d) @attr.s @@ -119,7 +81,11 @@ class TimestampWithTimezone(BaseModel): def from_dict(cls, d): """Builds a TimestampWithTimezone from any of the formats accepted by :py:`swh.model.normalize_timestamp`.""" - return super().from_dict(normalize_timestamp(d)) + d = normalize_timestamp(d) + return cls( + timestamp=Timestamp.from_dict(d['timestamp']), + offset=d['offset'], + negative_utc=d['negative_utc']) @attr.s @@ -197,6 +163,12 @@ class SnapshotBranch(BaseModel): branch['target_type'] = branch['target_type'].value return branch + @classmethod + def from_dict(cls, d): + return cls( + target=d['target'], + target_type=TargetType(d['target_type'])) + @attr.s class Snapshot(BaseModel): @@ -215,14 +187,12 @@ class Snapshot(BaseModel): @classmethod def from_dict(cls, d): - d = { - **d, - 'branches': { + return cls( + id=d['id'], + branches={ name: SnapshotBranch.from_dict(branch) for (name, branch) in d['branches'].items() - } - } - return cls(**d) + }) @attr.s @@ -253,6 +223,17 @@ class Release(BaseModel): rel['target_type'] = rel['target_type'].value return rel + @classmethod + def from_dict(cls, d): + d = d.copy() + if d.get('author'): + d['author'] = Person.from_dict(d['author']) + if d.get('date'): + d['date'] = TimestampWithTimezone.from_dict(d['date']) + return cls( + target_type=ObjectType(d.pop('target_type')), + **d) + class RevisionType(Enum): GIT = 'git' @@ -286,6 +267,22 @@ class Revision(BaseModel): rev['type'] = rev['type'].value return rev + @classmethod + def from_dict(cls, d): + return cls( + id=d['id'], + message=d['message'], + author=Person.from_dict(d['author']), + committer=Person.from_dict(d['committer']), + date=TimestampWithTimezone.from_dict(d['date']), + committer_date=TimestampWithTimezone.from_dict( + d['committer_date']), + type=RevisionType(d['type']), + directory=d['directory'], + synthetic=d['synthetic'], + metadata=d['metadata'], + parents=d['parents']) + @attr.s class DirectoryEntry(BaseModel): @@ -309,11 +306,10 @@ class Directory(BaseModel): @classmethod def from_dict(cls, d): - d = { - **d, - 'entries': list(map(DirectoryEntry.from_dict, d['entries'])) - } - return super().from_dict(d) + return cls( + id=d['id'], + entries=[DirectoryEntry.from_dict(entry) + for entry in d['entries']]) @attr.s diff --git a/version.txt b/version.txt index bcfdf8a972c71985a51394b567f98b129ec6fa28..6f92dd26a77e0fb4056cdafaf732853b6f097457 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v0.0.36-0-gfc3d3c1 \ No newline at end of file +v0.0.37-0-gd7ec4a6 \ No newline at end of file