Skip to content

Commit 03ffb08

Browse files
authored
Add stubs (dfurtado#39)
1 parent ea57a61 commit 03ffb08

14 files changed

+76
-28
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ dist
1717
.eggs
1818
.vscode
1919
gmon.out
20+
.vim
21+
pyproject.toml

dataclass_csv/__init__.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .dataclass_reader import DataclassReader as DataclassReader
2+
from .dataclass_writer import DataclassWriter as DataclassWriter
3+
from .decorators import accept_whitespaces as accept_whitespaces, dateformat as dateformat
4+
from .exceptions import CsvValueError as CsvValueError

dataclass_csv/dataclass_reader.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,27 +28,27 @@ def __init__(
2828
if cls is None or not dataclasses.is_dataclass(cls):
2929
raise ValueError('cls argument needs to be a dataclass.')
3030

31-
self.cls = cls
32-
self.optional_fields = self._get_optional_fields()
33-
self.field_mapping: Dict[str, Dict[str, Any]] = {}
31+
self._cls = cls
32+
self._optional_fields = self._get_optional_fields()
33+
self._field_mapping: Dict[str, Dict[str, Any]] = {}
3434

35-
self.reader = csv.DictReader(
35+
self._reader = csv.DictReader(
3636
f, fieldnames, restkey, restval, dialect, *args, **kwds
3737
)
3838

3939
def _get_optional_fields(self):
4040
return [
4141
field.name
42-
for field in dataclasses.fields(self.cls)
42+
for field in dataclasses.fields(self._cls)
4343
if not isinstance(field.default, dataclasses._MISSING_TYPE)
4444
or not isinstance(field.default_factory, dataclasses._MISSING_TYPE)
4545
]
4646

4747
def _add_to_mapping(self, property_name, csv_fieldname):
48-
self.field_mapping[property_name] = csv_fieldname
48+
self._field_mapping[property_name] = csv_fieldname
4949

5050
def _get_metadata_option(self, field, key):
51-
option = field.metadata.get(key, getattr(self.cls, f'__{key}__', None))
51+
option = field.metadata.get(key, getattr(self._cls, f'__{key}__', None))
5252
return option
5353

5454
def _get_default_value(self, field):
@@ -69,9 +69,9 @@ def _get_value(self, row, field):
6969
is_field_mapped = False
7070

7171
try:
72-
if field.name in self.field_mapping.keys():
72+
if field.name in self._field_mapping.keys():
7373
is_field_mapped = True
74-
key = self.field_mapping.get(field.name)
74+
key = self._field_mapping.get(field.name)
7575
else:
7676
key = field.name
7777

@@ -83,17 +83,17 @@ def _get_value(self, row, field):
8383
value = row[key]
8484

8585
except KeyError:
86-
if field.name in self.optional_fields:
86+
if field.name in self._optional_fields:
8787
return self._get_default_value(field)
8888
else:
8989
keyerror_message = f'The value for the column `{field.name}` is missing in the CSV file.'
9090
if is_field_mapped:
9191
keyerror_message = f'The value for the mapped column `{key}` is missing in the CSV file'
9292
raise KeyError(keyerror_message)
9393
else:
94-
if not value and field.name in self.optional_fields:
94+
if not value and field.name in self._optional_fields:
9595
return self._get_default_value(field)
96-
elif not value and field.name not in self.optional_fields:
96+
elif not value and field.name not in self._optional_fields:
9797
raise ValueError(f'The field `{field.name}` is required.')
9898
elif (
9999
value
@@ -139,15 +139,15 @@ def _parse_date_value(self, field, date_value):
139139
def _process_row(self, row):
140140
values = []
141141

142-
for field in dataclasses.fields(self.cls):
142+
for field in dataclasses.fields(self._cls):
143143
if not field.init:
144144
continue
145145

146146
try:
147147
value = self._get_value(row, field)
148148
except ValueError as ex:
149149
raise CsvValueError(
150-
ex, line_number=self.reader.line_num
150+
ex, line_number=self._reader.line_num
151151
) from None
152152

153153
if not value and field.default is None:
@@ -174,7 +174,7 @@ def _process_row(self, row):
174174
transformed_value = self._parse_date_value(field, value)
175175
except ValueError as ex:
176176
raise CsvValueError(
177-
ex, line_number=self.reader.line_num
177+
ex, line_number=self._reader.line_num
178178
) from None
179179
else:
180180
values.append(transformed_value)
@@ -189,7 +189,7 @@ def _process_row(self, row):
189189
)
190190
except ValueError as ex:
191191
raise CsvValueError(
192-
ex, line_number=self.reader.line_num
192+
ex, line_number=self._reader.line_num
193193
) from None
194194
else:
195195
values.append(transformed_value)
@@ -203,14 +203,14 @@ def _process_row(self, row):
203203
f'The field `{field.name}` is defined as {field.type} '
204204
f'but received a value of type {type(value)}.'
205205
),
206-
line_number=self.reader.line_num,
206+
line_number=self._reader.line_num,
207207
) from e
208208
else:
209209
values.append(transformed_value)
210-
return self.cls(*values)
210+
return self._cls(*values)
211211

212212
def __next__(self):
213-
row = next(self.reader)
213+
row = next(self._reader)
214214
return self._process_row(row)
215215

216216
def __iter__(self):

dataclass_csv/dataclass_reader.pyi

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from .exceptions import CsvValueError as CsvValueError
2+
from .field_mapper import FieldMapper as FieldMapper
3+
from typing import Any, Dict, List, Optional, Sequence, Type
4+
5+
class DataclassReader:
6+
def __init__(self, f: Any, cls: Type[object], fieldnames: Optional[Sequence[str]]=..., restkey: Optional[str]=..., restval: Optional[Any]=..., dialect: str=..., *args: List[Any], **kwds: Dict[str, Any]) -> None: ...
7+
def __next__(self): ...
8+
def __iter__(self) -> Any: ...
9+
def map(self, csv_fieldname: str) -> FieldMapper: ...

dataclass_csv/dataclass_writer.pyi

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from .header_mapper import HeaderMapper as HeaderMapper
2+
from typing import Any, Dict, List, Type
3+
4+
class DataclassWriter:
5+
def __init__(self, f: Any, data: List[Any], cls: Type[object], dialect: str=..., **fmtparams: Dict[str, Any]) -> None: ...
6+
def write(self, skip_header: bool=...) -> Any: ...
7+
def map(self, propname: str) -> HeaderMapper: ...

dataclass_csv/decorators.pyi

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from typing import Any, Callable, Type, TypeVar
2+
3+
F = TypeVar('F', bound=Callable[..., Any])
4+
5+
def dateformat(date_format: str) -> Callable[[F], F]: ...
6+
def accept_whitespaces(_cls: Type[Any]=...) -> Callable[[F], F]: ...

dataclass_csv/exceptions.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
from typing import Any
2+
3+
14
class CsvValueError(Exception):
25
"""Error when a value in the CSV file cannot be parsed."""
36

4-
def __init__(self, error, line_number):
5-
self.error = error
6-
self.line_number = line_number
7+
def __init__(self, error: Any, line_number: int):
8+
self.error: Any = error
9+
self.line_number: int = line_number
710

811
def __str__(self):
912
return f'{self.error} [CSV Line number: {self.line_number}]'

dataclass_csv/exceptions.pyi

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from typing import Any
2+
3+
class CsvValueError(Exception):
4+
error: Any = ...
5+
line_number: Any = ...
6+
def __init__(self, error: Any, line_number: int) -> None: ...

dataclass_csv/field_mapper.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ class FieldMapper:
66
in the CSV file to a specific `dataclass` field.
77
"""
88

9-
def __init__(self, callback: Callable[[str], Any]):
10-
def to(property_name):
9+
def __init__(self, callback: Callable[[str], None]):
10+
def to(property_name: str) -> None:
1111
"""Specify the dataclass field to receive the value
1212
:param property_name: The dataclass property that
1313
will receive the csv value.
1414
"""
1515

1616
callback(property_name)
1717

18-
self.to = to
18+
self.to: Callable[[str], None] = to

dataclass_csv/field_mapper.pyi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from typing import Any, Callable
2+
3+
class FieldMapper:
4+
to: Any = ...
5+
def __init__(self, callback: Callable[[str], None]) -> None: ...

0 commit comments

Comments
 (0)