Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F11023668
D2819.id10271.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
12 KB
Subscribers
None
D2819.id10271.diff
View Options
diff --git a/mypy.ini b/mypy.ini
--- a/mypy.ini
+++ b/mypy.ini
@@ -2,9 +2,11 @@
namespace_packages = True
warn_unused_ignores = True
-
# 3rd party libraries without stubs (yet)
+[mypy-attrs_strict.*] # a bit sad, but...
+ignore_missing_imports = True
+
[mypy-django.*] # false positive, only used my hypotesis' extras
ignore_missing_imports = True
diff --git a/requirements.txt b/requirements.txt
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,6 +3,7 @@
# dependency lines, see https://pip.readthedocs.org/en/1.1/requirements.html
vcversioner
attrs
+attrs_strict
hypothesis
python-dateutil
iso8601
diff --git a/swh/model/model.py b/swh/model/model.py
--- a/swh/model/model.py
+++ b/swh/model/model.py
@@ -7,9 +7,10 @@
from abc import ABCMeta, abstractmethod
from enum import Enum
-from typing import List, Optional, Dict, Union
+from typing import Dict, List, Optional, Union
import attr
+from attrs_strict import type_validator
import dateutil.parser
import iso8601
@@ -84,9 +85,15 @@
@attr.s(frozen=True)
class Person(BaseModel):
"""Represents the author/committer of a revision or release."""
- fullname = attr.ib(type=bytes)
- name = attr.ib(type=Optional[bytes])
- email = attr.ib(type=Optional[bytes])
+ fullname = attr.ib(
+ type=bytes,
+ validator=type_validator())
+ name = attr.ib(
+ type=Optional[bytes],
+ validator=type_validator())
+ email = attr.ib(
+ type=Optional[bytes],
+ validator=type_validator())
@classmethod
def from_fullname(cls, fullname: bytes):
@@ -131,8 +138,12 @@
@attr.s(frozen=True)
class Timestamp(BaseModel):
"""Represents a naive timestamp from a VCS."""
- seconds = attr.ib(type=int)
- microseconds = attr.ib(type=int)
+ seconds = attr.ib(
+ type=int,
+ validator=type_validator())
+ microseconds = attr.ib(
+ type=int,
+ validator=type_validator())
@seconds.validator
def check_seconds(self, attribute, value):
@@ -150,9 +161,15 @@
@attr.s(frozen=True)
class TimestampWithTimezone(BaseModel):
"""Represents a TZ-aware timestamp from a VCS."""
- timestamp = attr.ib(type=Timestamp)
- offset = attr.ib(type=int)
- negative_utc = attr.ib(type=bool)
+ timestamp = attr.ib(
+ type=Timestamp,
+ validator=type_validator())
+ offset = attr.ib(
+ type=int,
+ validator=type_validator())
+ negative_utc = attr.ib(
+ type=bool,
+ validator=type_validator())
@offset.validator
def check_offset(self, attribute, value):
@@ -193,25 +210,38 @@
@attr.s(frozen=True)
class Origin(BaseModel):
"""Represents a software source: a VCS and an URL."""
- url = attr.ib(type=str)
+ url = attr.ib(
+ type=str,
+ validator=type_validator())
@attr.s(frozen=True)
class OriginVisit(BaseModel):
"""Represents a visit of an origin at a given point in time, by a
SWH loader."""
- origin = attr.ib(type=str)
- date = attr.ib(type=datetime.datetime)
+ origin = attr.ib(
+ type=str,
+ validator=type_validator())
+ date = attr.ib(
+ type=datetime.datetime,
+ validator=type_validator())
status = attr.ib(
type=str,
validator=attr.validators.in_(['ongoing', 'full', 'partial']))
- type = attr.ib(type=str)
- snapshot = attr.ib(type=Optional[Sha1Git])
- metadata = attr.ib(type=Optional[Dict[str, object]],
- default=None)
-
- visit = attr.ib(type=Optional[int],
- default=None)
+ type = attr.ib(
+ type=str,
+ validator=type_validator())
+ snapshot = attr.ib(
+ type=Optional[Sha1Git],
+ validator=type_validator())
+ metadata = attr.ib(
+ type=Optional[Dict[str, object]],
+ validator=type_validator(),
+ default=None)
+ visit = attr.ib(
+ type=Optional[int],
+ validator=type_validator(),
+ default=None)
"""Should not be set before calling 'origin_visit_add()'."""
def to_dict(self):
@@ -225,13 +255,10 @@
@classmethod
def from_dict(cls, d):
"""Parses the date from a string, and accepts missing visit ids."""
- d = d.copy()
- date = d.pop('date')
- return cls(
- date=(date
- if isinstance(date, datetime.datetime)
- else dateutil.parser.parse(date)),
- **d)
+ if isinstance(d['date'], str):
+ d = d.copy()
+ d['date'] = dateutil.parser.parse(d['date'])
+ return super().from_dict(d)
class TargetType(Enum):
@@ -257,8 +284,12 @@
@attr.s(frozen=True)
class SnapshotBranch(BaseModel):
"""Represents one of the branches of a snapshot."""
- target = attr.ib(type=bytes)
- target_type = attr.ib(type=TargetType)
+ target = attr.ib(
+ type=bytes,
+ validator=type_validator())
+ target_type = attr.ib(
+ type=TargetType,
+ validator=type_validator())
@target.validator
def check_target(self, attribute, value):
@@ -279,8 +310,13 @@
@attr.s(frozen=True)
class Snapshot(BaseModel, HashableObject):
"""Represents the full state of an origin at a given point in time."""
- branches = attr.ib(type=Dict[bytes, Optional[SnapshotBranch]])
- id = attr.ib(type=Sha1Git, default=b'')
+ branches = attr.ib(
+ type=Dict[bytes, Optional[SnapshotBranch]],
+ validator=type_validator())
+ id = attr.ib(
+ type=Sha1Git,
+ validator=type_validator(),
+ default=b'')
@staticmethod
def compute_hash(object_dict):
@@ -299,18 +335,37 @@
@attr.s(frozen=True)
class Release(BaseModel, HashableObject):
- name = attr.ib(type=bytes)
- message = attr.ib(type=bytes)
- target = attr.ib(type=Optional[Sha1Git])
- target_type = attr.ib(type=ObjectType)
- synthetic = attr.ib(type=bool)
- author = attr.ib(type=Optional[Person],
- default=None)
- date = attr.ib(type=Optional[TimestampWithTimezone],
- default=None)
- metadata = attr.ib(type=Optional[Dict[str, object]],
- default=None)
- id = attr.ib(type=Sha1Git, default=b'')
+ name = attr.ib(
+ type=bytes,
+ validator=type_validator())
+ message = attr.ib(
+ type=bytes,
+ validator=type_validator())
+ target = attr.ib(
+ type=Optional[Sha1Git],
+ validator=type_validator())
+ target_type = attr.ib(
+ type=ObjectType,
+ validator=type_validator())
+ synthetic = attr.ib(
+ type=bool,
+ validator=type_validator())
+ author = attr.ib(
+ type=Optional[Person],
+ validator=type_validator(),
+ default=None)
+ date = attr.ib(
+ type=Optional[TimestampWithTimezone],
+ validator=type_validator(),
+ default=None)
+ metadata = attr.ib(
+ type=Optional[Dict[str, object]],
+ validator=type_validator(),
+ default=None)
+ id = attr.ib(
+ type=Sha1Git,
+ validator=type_validator(),
+ default=b'')
@staticmethod
def compute_hash(object_dict):
@@ -350,19 +405,42 @@
@attr.s(frozen=True)
class Revision(BaseModel, HashableObject):
- message = attr.ib(type=bytes)
- author = attr.ib(type=Person)
- committer = attr.ib(type=Person)
- date = attr.ib(type=Optional[TimestampWithTimezone])
- committer_date = attr.ib(type=Optional[TimestampWithTimezone])
- type = attr.ib(type=RevisionType)
- directory = attr.ib(type=Sha1Git)
- synthetic = attr.ib(type=bool)
- metadata = attr.ib(type=Optional[Dict[str, object]],
- default=None)
- parents = attr.ib(type=List[Sha1Git],
- default=attr.Factory(list))
- id = attr.ib(type=Sha1Git, default=b'')
+ message = attr.ib(
+ type=bytes,
+ validator=type_validator())
+ author = attr.ib(
+ type=Person,
+ validator=type_validator())
+ committer = attr.ib(
+ type=Person,
+ validator=type_validator())
+ date = attr.ib(
+ type=Optional[TimestampWithTimezone],
+ validator=type_validator())
+ committer_date = attr.ib(
+ type=Optional[TimestampWithTimezone],
+ validator=type_validator())
+ type = attr.ib(
+ type=RevisionType,
+ validator=type_validator())
+ directory = attr.ib(
+ type=Sha1Git,
+ validator=type_validator())
+ synthetic = attr.ib(
+ type=bool,
+ validator=type_validator())
+ metadata = attr.ib(
+ type=Optional[Dict[str, object]],
+ validator=type_validator(),
+ default=None)
+ parents = attr.ib(
+ type=List[Sha1Git],
+ validator=type_validator(),
+ default=attr.Factory(list))
+ id = attr.ib(
+ type=Sha1Git,
+ validator=type_validator(),
+ default=b'')
@staticmethod
def compute_hash(object_dict):
@@ -391,18 +469,30 @@
@attr.s(frozen=True)
class DirectoryEntry(BaseModel):
- name = attr.ib(type=bytes)
- type = attr.ib(type=str,
- validator=attr.validators.in_(['file', 'dir', 'rev']))
- target = attr.ib(type=Sha1Git)
- perms = attr.ib(type=int)
+ name = attr.ib(
+ type=bytes,
+ validator=type_validator())
+ type = attr.ib(
+ type=str,
+ validator=attr.validators.in_(['file', 'dir', 'rev']))
+ target = attr.ib(
+ type=Sha1Git,
+ validator=type_validator())
+ perms = attr.ib(
+ type=int,
+ validator=type_validator())
"""Usually one of the values of `swh.model.from_disk.DentryPerms`."""
@attr.s(frozen=True)
class Directory(BaseModel, HashableObject):
- entries = attr.ib(type=List[DirectoryEntry])
- id = attr.ib(type=Sha1Git, default=b'')
+ entries = attr.ib(
+ type=List[DirectoryEntry],
+ validator=type_validator())
+ id = attr.ib(
+ type=Sha1Git,
+ validator=type_validator(),
+ default=b'')
@staticmethod
def compute_hash(object_dict):
@@ -461,22 +551,37 @@
@attr.s(frozen=True)
class Content(BaseContent):
- sha1 = attr.ib(type=bytes)
- sha1_git = attr.ib(type=Sha1Git)
- sha256 = attr.ib(type=bytes)
- blake2s256 = attr.ib(type=bytes)
-
- length = attr.ib(type=int)
+ sha1 = attr.ib(
+ type=bytes,
+ validator=type_validator())
+ sha1_git = attr.ib(
+ type=Sha1Git,
+ validator=type_validator())
+ sha256 = attr.ib(
+ type=bytes,
+ validator=type_validator())
+ blake2s256 = attr.ib(
+ type=bytes,
+ validator=type_validator())
+
+ length = attr.ib(
+ type=int,
+ validator=type_validator())
status = attr.ib(
type=str,
- default='visible',
- validator=attr.validators.in_(['visible', 'hidden']))
+ validator=attr.validators.in_(['visible', 'hidden']),
+ default='visible')
- data = attr.ib(type=Optional[bytes], default=None)
+ data = attr.ib(
+ type=Optional[bytes],
+ validator=type_validator(),
+ default=None)
- ctime = attr.ib(type=Optional[datetime.datetime],
- default=None)
+ ctime = attr.ib(
+ type=Optional[datetime.datetime],
+ validator=type_validator(),
+ default=None)
@length.validator
def check_length(self, attribute, value):
@@ -518,24 +623,40 @@
@attr.s(frozen=True)
class SkippedContent(BaseContent):
- sha1 = attr.ib(type=Optional[bytes])
- sha1_git = attr.ib(type=Optional[Sha1Git])
- sha256 = attr.ib(type=Optional[bytes])
- blake2s256 = attr.ib(type=Optional[bytes])
-
- length = attr.ib(type=Optional[int])
+ sha1 = attr.ib(
+ type=Optional[bytes],
+ validator=type_validator())
+ sha1_git = attr.ib(
+ type=Optional[Sha1Git],
+ validator=type_validator())
+ sha256 = attr.ib(
+ type=Optional[bytes],
+ validator=type_validator())
+ blake2s256 = attr.ib(
+ type=Optional[bytes],
+ validator=type_validator())
+
+ length = attr.ib(
+ type=Optional[int],
+ validator=type_validator())
status = attr.ib(
type=str,
validator=attr.validators.in_(['absent']))
- reason = attr.ib(type=Optional[str],
- default=None)
-
- origin = attr.ib(type=Optional[Origin],
- default=None)
-
- ctime = attr.ib(type=Optional[datetime.datetime],
- default=None)
+ reason = attr.ib(
+ type=Optional[str],
+ validator=type_validator(),
+ default=None)
+
+ origin = attr.ib(
+ type=Optional[Origin],
+ validator=type_validator(),
+ default=None)
+
+ ctime = attr.ib(
+ type=Optional[datetime.datetime],
+ validator=type_validator(),
+ default=None)
@reason.validator
def check_reason(self, attribute, value):
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Sep 17, 4:55 PM (6 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3222811
Attached To
D2819: model: use attrs_static to enforce type validation of model objects
Event Timeline
Log In to Comment