diff --git a/swh/model/model.py b/swh/model/model.py --- a/swh/model/model.py +++ b/swh/model/model.py @@ -88,6 +88,45 @@ name = attr.ib(type=Optional[bytes]) email = attr.ib(type=Optional[bytes]) + @classmethod + def from_fullname(cls, fullname: bytes): + """Returns a Person object, by guessing the name and email from the + fullname, in the `name ` format. + + The fullname is left unchanged.""" + if fullname is None: + raise TypeError('fullname is None.') + + name: Optional[bytes] + email: Optional[bytes] + + try: + open_bracket = fullname.index(b'<') + except ValueError: + name = fullname + email = None + else: + raw_name = fullname[:open_bracket] + raw_email = fullname[open_bracket+1:] + + if not raw_name: + name = None + else: + name = raw_name.strip() + + try: + close_bracket = raw_email.rindex(b'>') + except ValueError: + email = raw_email + else: + email = raw_email[:close_bracket] + + return Person( + name=name or None, + email=email or None, + fullname=fullname, + ) + @attr.s(frozen=True) class Timestamp(BaseModel): diff --git a/swh/model/tests/test_model.py b/swh/model/tests/test_model.py --- a/swh/model/tests/test_model.py +++ b/swh/model/tests/test_model.py @@ -13,7 +13,7 @@ from swh.model.model import ( Content, SkippedContent, Directory, Revision, Release, Snapshot, Timestamp, TimestampWithTimezone, - MissingData, + MissingData, Person ) from swh.model.hashutil import hash_to_bytes, MultiHash from swh.model.hypothesis_strategies import objects, origins, origin_visits @@ -108,6 +108,96 @@ ) +def test_person_from_fullname(): + """The author should have name, email and fullname filled. + + """ + actual_person = Person.from_fullname(b'tony ') + assert actual_person == Person( + fullname=b'tony ', + name=b'tony', + email=b'ynot@dagobah', + ) + + +def test_person_from_fullname_no_email(): + """The author and fullname should be the same as the input (author). + + """ + actual_person = Person.from_fullname(b'tony') + assert actual_person == Person( + fullname=b'tony', + name=b'tony', + email=None, + ) + + +def test_person_from_fullname_empty_person(): + """Empty person has only its fullname filled with the empty + byte-string. + + """ + actual_person = Person.from_fullname(b'') + assert actual_person == Person( + fullname=b'', + name=None, + email=None, + ) + + +def test_git_author_line_to_author(): + # edge case out of the way + with pytest.raises(TypeError): + Person.from_fullname(None) + + tests = { + b'a ': Person( + name=b'a', + email=b'b@c.com', + fullname=b'a ', + ), + b'': Person( + name=None, + email=b'foo@bar.com', + fullname=b'', + ), + b'malformed ': Person( + name=b'malformed', + email=b'"', + ), + b'trailing ': Person( + name=b'trailing', + email=b'sp@c.e', + fullname=b'trailing ', + ), + b'no': Person( + name=b'no', + email=b'sp@c.e', + fullname=b'no', + ), + b' more ': Person( + name=b'more', + email=b'sp@c.es', + fullname=b' more ', + ), + b' <>': Person( + name=None, + email=None, + fullname=b' <>', + ), + } + + for person in sorted(tests): + expected_person = tests[person] + assert expected_person == Person.from_fullname(person) + + def test_content_get_hash(): hashes = dict( sha1=b'foo', sha1_git=b'bar', sha256=b'baz', blake2s256=b'qux')