From 016e70298aa1f78a5dd427ca3512af5da4259a69 Mon Sep 17 00:00:00 2001
From: Moul <moul@moul.re>
Date: Tue, 27 Jun 2023 21:42:23 +0200
Subject: [PATCH] WS2P head: Switch from attrs to dataclass (#88)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Couldn’t come with a beautiful generic implementation
such as the one with attrs expanding the astuple,
since there is no filter exclude in dataclass lib

Appending new attributes values to inheritated super().inline()

Remove attrs dependency
---
 README.md                         |  1 -
 duniterpy/documents/ws2p/heads.py | 62 ++++++++++++++-----------------
 pyproject.toml                    |  1 -
 3 files changed, 27 insertions(+), 37 deletions(-)

diff --git a/README.md b/README.md
index d61e05a..fe5035f 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,6 @@ It is currently used by following programs:
 - [websocket-client](https://pypi.org/project/websocket-client)
 - [jsonschema](https://pypi.org/project/jsonschema)
 - [pyPEG2](https://pypi.org/project/pyPEG2)
-- [attrs](https://pypi.org/project/attrs)
 - [base58](https://pypi.org/project/base58)
 - [libnacl](https://pypi.org/project/libnacl)
 - [pyaes](https://pypi.org/project/pyaes)
diff --git a/duniterpy/documents/ws2p/heads.py b/duniterpy/documents/ws2p/heads.py
index 9f6a3ec..05da42e 100644
--- a/duniterpy/documents/ws2p/heads.py
+++ b/duniterpy/documents/ws2p/heads.py
@@ -14,9 +14,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import re
-
-import attr
-import attrs
+from dataclasses import dataclass
 
 from ...constants import (
     BLOCK_ID_REGEX,
@@ -32,10 +30,10 @@ from ..block_id import BlockID
 from ..document import MalformedDocumentError
 
 
-@attr.s()
+@dataclass
 class API:
-    private = attr.ib(type=str)
-    public = attr.ib(type=str)
+    private: str
+    public: str
 
     re_inline = re.compile(
         f"WS2P({WS2P_PRIVATE_PREFIX_REGEX})?({WS2P_PUBLIC_PREFIX_REGEX})?"
@@ -54,9 +52,9 @@ class API:
         return f"WS2P{self.private}{self.public}"
 
 
-@attr.s()
+@dataclass
 class Head:
-    version = attr.ib(type=int)
+    version: int
 
     re_inline = re.compile(WS2P_HEAD_REGEX)
 
@@ -76,13 +74,13 @@ class Head:
         return "HEAD" if self.version == 0 else f"HEAD:{str(self.version)}"
 
 
-@attr.s()
+@dataclass
 class HeadV0(Head):
-    signature = attr.ib(type=str)
-    api = attr.ib(type=API)
-    head = attr.ib(type=Head)
-    pubkey = attr.ib(type=str)
-    block_id = attr.ib(type=BlockID)
+    signature: str
+    api: API
+    head: Head
+    pubkey: str
+    block_id: BlockID
 
     re_inline = re.compile(
         f"^(WS2P(?:{WS2P_PRIVATE_PREFIX_REGEX})?(?:{WS2P_PUBLIC_PREFIX_REGEX})?):\
@@ -107,19 +105,7 @@ class HeadV0(Head):
             raise MalformedDocumentError("HeadV0") from AttributeError
 
     def inline(self) -> str:
-        values = (
-            str(v)
-            for v in attrs.astuple(
-                self,
-                recurse=False,
-                filter=attrs.filters.exclude(
-                    attrs.fields(HeadV0).version,
-                    attrs.fields(HeadV0).signature,
-                    attrs.fields(HeadV0).api,
-                ),
-            )
-        )
-        return f'{str(self.api)}:{":".join(values)}'
+        return f"{str(self.api)}:{str(self.head)}:{self.pubkey}:{str(self.block_id)}"
 
     def check_signature(self, pubkey: str) -> bool:
         """
@@ -133,12 +119,12 @@ class HeadV0(Head):
         return verifying_key.check_signature(self.inline(), self.signature)
 
 
-@attr.s()
+@dataclass
 class HeadV1(HeadV0):
-    ws2pid = attr.ib(type=str)
-    software = attr.ib(type=str)
-    software_version = attr.ib(type=str)
-    pow_prefix = attr.ib(type=int)
+    ws2pid: str
+    software: str
+    software_version: str
+    pow_prefix: int
 
     re_inline = re.compile(
         "({ws2pid}):({software}):({software_version}):({pow_prefix})(?::)?(.*)".format(
@@ -179,11 +165,14 @@ class HeadV1(HeadV0):
         except AttributeError:
             raise MalformedDocumentError("HeadV1") from AttributeError
 
+    def inline(self) -> str:
+        return f"{super().inline()}:{self.ws2pid}:{self.software}:{self.software_version}:{self.pow_prefix}"
+
 
-@attr.s
+@dataclass
 class HeadV2(HeadV1):
-    free_member_room = attr.ib(type=int)
-    free_mirror_room = attr.ib(type=int)
+    free_member_room: int
+    free_mirror_room: int
 
     re_inline = re.compile(
         "({free_member_room}):({free_mirror_room})(?::)?(.*)".format(
@@ -219,3 +208,6 @@ class HeadV2(HeadV1):
             )
         except AttributeError:
             raise MalformedDocumentError("HeadV2") from AttributeError
+
+    def inline(self) -> str:
+        return f"{super().inline()}:{self.free_member_room}:{self.free_mirror_room}"
diff --git a/pyproject.toml b/pyproject.toml
index e76f67e..d1934a7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -31,7 +31,6 @@ graphql-core = "^3.2.3"
 websocket-client = "^1.5.2"
 jsonschema = "^4.17.3"
 pypeg2 = "^2.15.2"
-attrs = "^23.1.0"
 base58 = "^2.1.1"
 libnacl = "^1.8.0"
 pyaes = "^1.6.1"
-- 
GitLab