From b23055fda0aa8baf29731e4a4f29b009d42ad14d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Paduszyn=CC=81ski?= Date: Thu, 2 Nov 2023 14:17:34 +0100 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20Define=20the=20data?= =?UTF-8?q?=20model=20classes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/gitmojis/model.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/gitmojis/model.py diff --git a/src/gitmojis/model.py b/src/gitmojis/model.py new file mode 100644 index 0000000..a67e31d --- /dev/null +++ b/src/gitmojis/model.py @@ -0,0 +1,22 @@ +from collections import UserList +from dataclasses import dataclass +from typing import Iterable, Literal + + +@dataclass(frozen=True, kw_only=True) +class Gitmoji: + emoji: str + entity: str + code: str + description: str + name: str + semver: Literal["major", "minor", "patch"] | None + + +class Guide(UserList[Gitmoji]): + def __init__(self, *, gitmojis: Iterable[Gitmoji] | None = None) -> None: + super().__init__(gitmojis) + + @property + def gitmojis(self) -> list[Gitmoji]: + return self.data From 2b803b7c18bc7eac0476654ff81c125c49d7b7bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Paduszyn=CC=81ski?= Date: Thu, 2 Nov 2023 14:18:03 +0100 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=A7=B1=20Add=20the=20Gitmoji=20data?= =?UTF-8?q?=20model=20classes=20to=20the=20package=20index?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/gitmojis/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/gitmojis/__init__.py b/src/gitmojis/__init__.py index e69de29..faf89ac 100644 --- a/src/gitmojis/__init__.py +++ b/src/gitmojis/__init__.py @@ -0,0 +1,6 @@ +from .model import Gitmoji, Guide + +__all__ = [ + "Gitmoji", + "Guide", +] From cb27530258b5369d15089cf1ee2654f13f3ee97b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Paduszyn=CC=81ski?= Date: Thu, 2 Nov 2023 14:39:35 +0100 Subject: [PATCH 3/5] =?UTF-8?q?=E2=9C=85=20Add=20tests=20covering=20the=20?= =?UTF-8?q?defined=20data=20model=20classes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_gitmoji.py | 36 +++++++++++++++++++++++++ tests/test_guide.py | 62 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 tests/test_gitmoji.py create mode 100644 tests/test_guide.py diff --git a/tests/test_gitmoji.py b/tests/test_gitmoji.py new file mode 100644 index 0000000..be89ce9 --- /dev/null +++ b/tests/test_gitmoji.py @@ -0,0 +1,36 @@ +from dataclasses import FrozenInstanceError, fields + +import pytest + +from gitmojis.model import Gitmoji + + +@pytest.fixture() +def gitmoji_json(): + return { + "emoji": "🐛", + "entity": "🐛", + "code": ":bug:", + "description": "Fix a bug.", + "name": "bug", + "semver": "patch", + } + + +@pytest.fixture() +def gitmoji(gitmoji_json): + return Gitmoji(**gitmoji_json) + + +def test_gitmoji_init_populates_fields_from_kwargs(gitmoji_json, gitmoji): + for key, value in gitmoji_json.items(): + assert getattr(gitmoji, key) == value + + +@pytest.mark.parametrize( + "field_name", + [field.name for field in fields(Gitmoji)], +) +def test_gitmoji_is_immutable(gitmoji, field_name): + with pytest.raises(FrozenInstanceError): + setattr(gitmoji, field_name, getattr(gitmoji, field_name)) diff --git a/tests/test_guide.py b/tests/test_guide.py new file mode 100644 index 0000000..e51fe95 --- /dev/null +++ b/tests/test_guide.py @@ -0,0 +1,62 @@ +import pytest + +from gitmojis.model import Gitmoji, Guide + + +@pytest.fixture() +def gitmojis_json(): + return [ + { + "emoji": "💥", + "entity": "💥", + "code": ":boom:", + "description": "Introduce breaking changes.", + "name": "boom", + "semver": "major", + }, + { + "emoji": "✨", + "entity": "✨", + "code": ":sparkles:", + "description": "Introduce new features.", + "name": "sparkles", + "semver": "minor", + }, + { + "emoji": "🐛", + "entity": "🐛", + "code": ":bug:", + "description": "Fix a bug.", + "name": "bug", + "semver": "patch", + }, + { + "emoji": "📝", + "entity": "📝", + "code": ":memo:", + "description": "Add or update documentation.", + "name": "memo", + "semver": None, + }, + ] + + +@pytest.fixture() +def guide(gitmojis_json): + return Guide(gitmojis=[Gitmoji(**gitmoji_json) for gitmoji_json in gitmojis_json]) + + +def test_guide_init_raises_if_called_with_positional_argument(): + with pytest.raises(TypeError): + Guide([]) + + +def test_guide_init_succeeds_if_called_with_keyword_argument(): + try: + Guide(gitmojis=[]) + except TypeError: + pytest.fail() + + +def test_guide_gitmojis_returns_data(guide): + assert guide.gitmojis == guide.data From 1219860c891db9c71548c8721b51819843c8c5f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Paduszyn=CC=81ski?= Date: Thu, 2 Nov 2023 14:41:05 +0100 Subject: [PATCH 4/5] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/gitmojis/model.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/gitmojis/model.py b/src/gitmojis/model.py index a67e31d..da9284b 100644 --- a/src/gitmojis/model.py +++ b/src/gitmojis/model.py @@ -5,6 +5,22 @@ @dataclass(frozen=True, kw_only=True) class Gitmoji: + """Represents a single Gitmoji and its data. + + The data model is adapted from the schema of the original Gitmoji project. + + Attributes: + emoji: The emoji symbol representing the Gitmoji. + entity: The HTML entity corresponding to the Gitmoji. + code: The Markdown code of the Gitmoji's emoji. + description: A brief description of changes introduced by commits and pull + requests marked by the Gitmoji. + name: The user-defined name or identifier of the Gitmoji. + semver: The Semantic Versioning level affected by the commits/PRs marked by the + emoji associated with the Gitmoji, if specified. May be `None` or one of + the following: `"major"`, `"minor"`, `"patch"`. + """ + emoji: str entity: str code: str @@ -14,9 +30,24 @@ class Gitmoji: class Guide(UserList[Gitmoji]): + """Represents a list of (a "guide" through) `Gitmoji` objects. + + This class is used to create a collection of `Gitmoji` objects, providing a simple + framework for accessing various Gitmojis and their data. + """ + def __init__(self, *, gitmojis: Iterable[Gitmoji] | None = None) -> None: + """Construct a new `Guide` object. + + Args: + gitmojis: An optional iterable of `Gitmoji` objects used to create + the guide. If `None`, the guide is initialized as empty. Note + that the data must be passed as a keyword argument, in contrast + to the implementation provided by the base class. + """ super().__init__(gitmojis) @property def gitmojis(self) -> list[Gitmoji]: + """Return the guide's data with a semantically meaningful attribute name.""" return self.data From bfca2821f0a28c70e6235271acca771b59195f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Paduszyn=CC=81ski?= Date: Thu, 2 Nov 2023 17:13:58 +0100 Subject: [PATCH 5/5] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Rephrase=20`Gitmoji`?= =?UTF-8?q?=20class=20docstring=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/gitmojis/model.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gitmojis/model.py b/src/gitmojis/model.py index da9284b..2cf5117 100644 --- a/src/gitmojis/model.py +++ b/src/gitmojis/model.py @@ -16,9 +16,9 @@ class Gitmoji: description: A brief description of changes introduced by commits and pull requests marked by the Gitmoji. name: The user-defined name or identifier of the Gitmoji. - semver: The Semantic Versioning level affected by the commits/PRs marked by the - emoji associated with the Gitmoji, if specified. May be `None` or one of - the following: `"major"`, `"minor"`, `"patch"`. + semver: The Semantic Versioning level affected by the commits or pull requests + marked by the emoji associated with the Gitmoji, if specified. May be `None` + or one of the following: `"major"`, `"minor"`, `"patch"`. """ emoji: str