Skip to content

Commit 6d56d30

Browse files
hf-kkleinKonstantinCopilot
authored
chore: switch from attrs to pydantic (#388)
* chore: switch from attrs to pydantic * Update src/ebdamame/__init__.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * rm keyword only --------- Co-authored-by: Konstantin <konstantin.klein+github@hochfrequenz.de> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent e0bc841 commit 6d56d30

6 files changed

Lines changed: 41 additions & 48 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ dependencies = [
2222
"rebdhuhn>=0.16.0",
2323
"python-docx>=1.1.2",
2424
"more_itertools>=10.5.0",
25-
"attrs>=24.3.0",
25+
"pydantic>=2.0",
2626
"click>=8.1.8"
2727
# add all the dependencies from requirements.in here, too
2828
]

requirements.in

Lines changed: 0 additions & 5 deletions
This file was deleted.

requirements.txt

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
#
55
# pip-compile pyproject.toml
66
#
7+
annotated-types==0.7.0
8+
# via pydantic
79
attrs==25.4.0
810
# via
9-
# -r requirements.in
1011
# cattrs
1112
# rebdhuhn
1213
cattrs==25.3.0
@@ -16,7 +17,9 @@ certifi==2025.10.5
1617
charset-normalizer==3.4.3
1718
# via requests
1819
click==8.3.1
19-
# via -r requirements.in
20+
# via ebdamame (pyproject.toml)
21+
colorama==0.4.6
22+
# via click
2023
idna==3.10
2124
# via requests
2225
lxml==6.0.2
@@ -25,20 +28,29 @@ lxml==6.0.2
2528
# rebdhuhn
2629
# svgutils
2730
more-itertools==10.8.0
28-
# via -r requirements.in
31+
# via ebdamame (pyproject.toml)
2932
networkx==3.5
3033
# via rebdhuhn
34+
pydantic==2.12.5
35+
# via ebdamame (pyproject.toml)
36+
pydantic-core==2.41.5
37+
# via pydantic
3138
python-docx==1.2.0
32-
# via -r requirements.in
39+
# via ebdamame (pyproject.toml)
3340
rebdhuhn==0.17.5
34-
# via -r requirements.in
41+
# via ebdamame (pyproject.toml)
3542
requests==2.32.5
3643
# via rebdhuhn
3744
svgutils==0.3.4
3845
# via rebdhuhn
3946
typing-extensions==4.15.0
4047
# via
4148
# cattrs
49+
# pydantic
50+
# pydantic-core
4251
# python-docx
52+
# typing-inspection
53+
typing-inspection==0.4.2
54+
# via pydantic
4355
urllib3==2.6.0
4456
# via requests

src/ebdamame/__init__.py

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
from pathlib import Path
1212
from typing import Generator, Iterable, Optional, Union
1313

14-
import attrs
1514
import docx
1615
from docx.document import Document as DocumentType
1716
from docx.oxml.table import CT_Tbl
1817
from docx.oxml.text.paragraph import CT_P
1918
from docx.table import Table, _Cell
2019
from docx.text.paragraph import Paragraph
20+
from pydantic import BaseModel, ConfigDict, Field
2121

2222
_logger = logging.getLogger(__name__)
2323

@@ -149,14 +149,15 @@ def _table_is_first_ebd_table(table: Table) -> bool:
149149
return "prüfende rolle" in table.rows[0].cells[0].text.lower()
150150

151151

152-
@attrs.define(kw_only=True, frozen=True)
153-
class EbdNoTableSection:
152+
class EbdNoTableSection(BaseModel):
154153
"""
155154
Represents an empty section in the document
156155
"""
157156

158-
ebd_key: str = attrs.field(validator=attrs.validators.instance_of(str))
159-
remark: str = attrs.field(validator=attrs.validators.instance_of(str))
157+
model_config = ConfigDict(frozen=True, extra="forbid")
158+
159+
ebd_key: str
160+
remark: str
160161

161162

162163
# pylint:disable=too-many-branches
@@ -272,8 +273,7 @@ def get_ebd_docx_tables(docx_file_path: Path, ebd_key: str) -> list[Table] | Ebd
272273

273274

274275
# pylint:disable=too-few-public-methods
275-
@attrs.define(kw_only=True, frozen=True)
276-
class EbdChapterInformation:
276+
class EbdChapterInformation(BaseModel):
277277
"""
278278
Contains information about where an EBD is located within the document.
279279
If the heading is e.g. "5.2.1" we denote this as:
@@ -282,22 +282,14 @@ class EbdChapterInformation:
282282
* subsection 1
283283
"""
284284

285-
chapter: int = attrs.field(
286-
validator=attrs.validators.and_(attrs.validators.instance_of(int), attrs.validators.ge(1))
287-
)
288-
chapter_title: Optional[str] = attrs.field(validator=attrs.validators.optional(attrs.validators.instance_of(str)))
289-
section: int = attrs.field(
290-
validator=attrs.validators.and_(attrs.validators.instance_of(int), attrs.validators.ge(1))
291-
)
292-
293-
section_title: Optional[str] = attrs.field(validator=attrs.validators.optional(attrs.validators.instance_of(str)))
294-
subsection: int = attrs.field(
295-
validator=attrs.validators.and_(attrs.validators.instance_of(int), attrs.validators.ge(1))
296-
)
297-
298-
subsection_title: Optional[str] = attrs.field(
299-
validator=attrs.validators.optional(attrs.validators.instance_of(str))
300-
)
285+
model_config = ConfigDict(frozen=True)
286+
287+
chapter: int = Field(ge=1)
288+
chapter_title: Optional[str] = None
289+
section: int = Field(ge=1)
290+
section_title: Optional[str] = None
291+
subsection: int = Field(ge=1)
292+
subsection_title: Optional[str] = None
301293

302294

303295
def _enrich_paragraphs_with_sections(

src/ebdamame/docxtableconverter.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
from itertools import cycle, groupby
99
from typing import Generator, Literal, Optional
1010

11-
import attrs
1211
from docx.table import Table, _Cell, _Row
1312
from more_itertools import first, first_true, last
13+
from pydantic import BaseModel, ConfigDict
1414
from rebdhuhn.models.ebd_table import (
1515
_STEP_NUMBER_REGEX,
1616
EbdCheckResult,
@@ -120,33 +120,28 @@ class _EbdSubRowPosition(Enum):
120120

121121

122122
# pylint:disable=too-few-public-methods
123-
@attrs.define
124-
class _EnhancedDocxTableLine:
123+
class _EnhancedDocxTableLine(BaseModel):
125124
"""
126125
A structure that primarily contains a single row from a DOCX table but also meta information about previous and
127126
following elements in the table. It gathers information that are not directly accessible when only looking at one
128127
single row.
129128
"""
130129

131-
row: _Row = attrs.field(validator=attrs.validators.instance_of(_Row))
130+
model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True)
131+
132+
row: _Row
132133
"""
133134
The row that is currently being processed
134135
"""
135-
sub_row_position: _EbdSubRowPosition = attrs.field(validator=attrs.validators.instance_of(_EbdSubRowPosition))
136+
sub_row_position: _EbdSubRowPosition
136137
"""
137138
denotes if row is an upper/lower sub row
138139
"""
139-
cells: list[_Cell] = attrs.field(
140-
validator=attrs.validators.deep_iterable(
141-
member_validator=attrs.validators.instance_of(_Cell), iterable_validator=attrs.validators.instance_of(list)
142-
)
143-
)
140+
cells: list[_Cell]
144141
"""
145142
the (sanitized) cells of the row
146143
"""
147-
multi_step_instruction_text: Optional[str] = attrs.field(
148-
validator=attrs.validators.optional(attrs.validators.instance_of(str))
149-
)
144+
multi_step_instruction_text: Optional[str] = None
150145
"""
151146
a multistep instruction text that may be applicable to this row (if not None)
152147
"""

tox.ini

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,5 @@ deps =
9999
pre-commit
100100
commands =
101101
python -m pip install --upgrade pip
102-
pip-compile requirements.in
103102
pip install -r requirements.txt
104103
pre-commit install

0 commit comments

Comments
 (0)