Skip to content

Commit 6affe37

Browse files
WIP
1 parent 1f74ac9 commit 6affe37

File tree

15 files changed

+114
-153
lines changed

15 files changed

+114
-153
lines changed

frictionless/detector/detector.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ def detect_schema(
392392
):
393393
field = runner["field"].to_copy()
394394
field.name = name
395+
field.descriptor.name = name
395396
field.schema = schema
396397
fields[index] = field
397398
break

frictionless/fields/__spec__/test_number.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@
4141
("default", "10#000#000.50", Decimal(10000000.5), {"groupChar": "#"}),
4242
("default", "10.50", Decimal(10.5), {"groupChar": "#"}),
4343
("default", "1#000", Decimal(1000), {"groupChar": "#"}),
44-
("default", "10#000@00", Decimal(10000), {"groupChar": "#", "decimalChar": "@"}),
44+
(
45+
"default",
46+
"10#000@00",
47+
Decimal(10000),
48+
{"groupChar": "#", "decimalChar": "@"},
49+
),
4550
(
4651
"default",
4752
"10#000#000@50",
@@ -50,7 +55,12 @@
5055
),
5156
("default", "10@50", Decimal(10.5), {"groupChar": "#", "decimalChar": "@"}),
5257
("default", "1#000", Decimal(1000), {"groupChar": "#", "decimalChar": "@"}),
53-
("default", "10,000.00", Decimal(10000), {"groupChar": ",", "bareNumber": False}),
58+
(
59+
"default",
60+
"10,000.00",
61+
Decimal(10000),
62+
{"groupChar": ",", "bareNumber": False},
63+
),
5464
(
5565
"default",
5666
"10,000,000.00",
@@ -70,7 +80,12 @@
7080
Decimal(10000),
7181
{"groupChar": ",", "bareNumber": False},
7282
),
73-
("default", "10 000,00", Decimal(10000), {"groupChar": " ", "decimalChar": ","}),
83+
(
84+
"default",
85+
"10 000,00",
86+
Decimal(10000),
87+
{"groupChar": " ", "decimalChar": ","},
88+
),
7489
(
7590
"default",
7691
"10 000 000,00",
@@ -114,7 +129,9 @@ def test_number_read_cell(format, source, target, options):
114129

115130

116131
def test_number_group_char_issue_1444():
117-
field = fields.NumberField(name="name", decimal_char=",", group_char=".")
132+
field = Field(
133+
{"type": "number", "name": "name", "decimalChar": ",", "group_char": "."}
134+
)
118135
cell = field.read_cell("8.699,8")[0]
119136
assert cell == Decimal("8699.8")
120137
cell = field.write_cell(cell)[0]

frictionless/fields/boolean.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ class BooleanReadWriter:
1212
_descriptor: BooleanFieldDescriptor
1313

1414
def true_values(self) -> List[str]:
15-
return self._descriptor.trueValues or settings.DEFAULT_TRUE_VALUES
15+
return self._descriptor.true_values or settings.DEFAULT_TRUE_VALUES
1616

1717
def false_values(self) -> List[str]:
18-
return self._descriptor.falseValues or settings.DEFAULT_FALSE_VALUES
18+
return self._descriptor.false_values or settings.DEFAULT_FALSE_VALUES
1919

2020
def create_value_reader(self):
2121
# Create mapping

frictionless/fields/field_descriptor.py

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
import datetime
4-
from typing import List, Literal, Optional, Union
4+
from typing import Any, Dict, List, Literal, Optional, Union
55

66
import pydantic
77

@@ -32,21 +32,23 @@ class BaseFieldDescriptor(pydantic.BaseModel):
3232
A description for this field e.g. “The recipient of the funds”
3333
"""
3434

35-
missingValues: Optional[List[str]] = None
35+
missing_values: Optional[List[str]] = pydantic.Field(
36+
default=None, alias="missingValues"
37+
)
3638
"""
3739
A list of field values to consider as null values
3840
"""
3941

40-
# @pydantic.model_validator(mode="before")
41-
# @classmethod
42-
# def compat(cls, data: Dict[str, Any]) -> Dict[str, Any]:
43-
# # field.format
44-
# format = data.get("format")
45-
# if format:
46-
# if format.startswith("fmt:"):
47-
# data["format"] = format[4:]
42+
@pydantic.model_validator(mode="before")
43+
@classmethod
44+
def compat(cls, data: Dict[str, Any]) -> Dict[str, Any]:
45+
# field.format
46+
format_ = data.get("format")
47+
if format_:
48+
if format_.startswith("fmt:"):
49+
data["format"] = format_[4:]
4850

49-
# return data
51+
return data
5052

5153

5254
class BooleanFieldDescriptor(BaseFieldDescriptor):
@@ -57,12 +59,11 @@ class BooleanFieldDescriptor(BaseFieldDescriptor):
5759
format: Optional[Literal["default"]] = None
5860
constraints: Optional[BaseConstraints[bool]] = None
5961

60-
trueValues: Optional[List[str]] = None
61-
"""
62-
Values to be interpreted as “true” for boolean fields
62+
true_values: Optional[List[str]] = pydantic.Field(default=None, alias="trueValues")
6363
"""
64+
Values to be interpreted as “true” for boolean fields """
6465

65-
falseValues: Optional[List[str]] = None
66+
false_values: Optional[List[str]] = pydantic.Field(default=None, alias="falseValues")
6667
"""
6768
Values to be interpreted as “false” for boolean fields
6869
"""
@@ -76,7 +77,9 @@ class ArrayFieldDescriptor(BaseFieldDescriptor):
7677
constraints: Optional[JSONConstraints] = None
7778

7879
# TODO type is not accurate : array item are unnamed, not described etc
79-
arrayItem: Optional[FieldDescriptor] = None
80+
array_item: Optional[FieldDescriptor] = pydantic.Field(
81+
default=None, alias="arrayItem"
82+
)
8083

8184

8285
class AnyFieldDescriptor(BaseFieldDescriptor):
@@ -156,18 +159,20 @@ class IntegerFieldDescriptor(BaseFieldDescriptor):
156159
Property to restrict the field to a finite set of possible values
157160
"""
158161

159-
categoriesOrdered: bool = False
162+
categories_ordered: Optional[bool] = pydantic.Field(
163+
default=None, alias="categoriesOrdered"
164+
)
160165
"""
161166
When categoriesOrdered is true, implementations SHOULD regard the order of
162167
appearance of the values in the categories property as their natural order.
163168
"""
164169

165-
groupChar: Optional[str] = None
170+
group_char: Optional[str] = pydantic.Field(default=None, alias="groupChar")
166171
"""
167172
String whose value is used to group digits for integer/number fields
168173
"""
169174

170-
bareNumber: bool = True
175+
bare_number: Optional[bool] = pydantic.Field(default=None, alias="bareNumber")
171176
"""
172177
If false leading and trailing non numbers will be removed for integer/number fields
173178
"""
@@ -200,7 +205,7 @@ class ListFieldDescriptor(BaseFieldDescriptor):
200205
Specifies the character sequence which separates lexically represented list items.
201206
"""
202207

203-
itemType: Optional[IItemType] = None
208+
item_type: Optional[IItemType] = pydantic.Field(default=None, alias="itemType")
204209
"""
205210
Specifies the list item type in terms of existent Table Schema types.
206211
"""
@@ -213,17 +218,17 @@ class NumberFieldDescriptor(BaseFieldDescriptor):
213218
format: Optional[Literal["default"]] = None
214219
constraints: Optional[ValueConstraints[float]] = None
215220

216-
decimalChar: Optional[str] = None
221+
decimal_char: Optional[str] = pydantic.Field(default=None, alias="decimalChar")
217222
"""
218223
String whose value is used to represent a decimal point for number fields
219224
"""
220225

221-
groupChar: Optional[str] = None
226+
group_char: Optional[str] = pydantic.Field(default=None, alias="groupChar")
222227
"""
223228
String whose value is used to group digits for integer/number fields
224229
"""
225230

226-
bareNumber: Optional[bool] = None
231+
bare_number: Optional[bool] = pydantic.Field(default=None, alias="bareNumber")
227232
"""
228233
If false leading and trailing non numbers will be removed for integer/number fields
229234
"""
@@ -260,7 +265,7 @@ class StringFieldDescriptor(BaseFieldDescriptor):
260265
Property to restrict the field to a finite set of possible values
261266
"""
262267

263-
categoriesOrdered: bool = False
268+
categoriesOrdered: Optional[bool] = None
264269
"""
265270
When categoriesOrdered is true, implementations SHOULD regard the order of
266271
appearance of the values in the categories property as their natural order.

frictionless/fields/geopoint.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from __future__ import annotations
22

3+
from dataclasses import dataclass
34
from decimal import Decimal
45
from typing import Any, NamedTuple
56

67
from .. import settings
78
from .field_descriptor import GeoPointFieldDescriptor
89

910

11+
@dataclass
1012
class GeoPointReadWriter:
1113
_descriptor: GeoPointFieldDescriptor
1214

frictionless/fields/integer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class IntegerReadWriter:
1414
_descriptor: IntegerFieldDescriptor
1515

1616
def bare_number(self):
17-
return self._descriptor.bareNumber or settings.DEFAULT_BARE_NUMBER
17+
return self._descriptor.bare_number or settings.DEFAULT_BARE_NUMBER
1818

1919
# Read
2020

frictionless/fields/number.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ class NumberReadWriter:
1414
_descriptor: NumberFieldDescriptor
1515

1616
def bare_number(self):
17-
return self._descriptor.bareNumber or settings.DEFAULT_BARE_NUMBER
17+
return self._descriptor.bare_number or settings.DEFAULT_BARE_NUMBER
1818

1919
def decimal_char(self):
20-
return self._descriptor.decimalChar or settings.DEFAULT_DECIMAL_CHAR
20+
return self._descriptor.decimal_char or settings.DEFAULT_DECIMAL_CHAR
2121

2222
def group_char(self):
23-
return self._descriptor.groupChar or settings.DEFAULT_GROUP_CHAR
23+
return self._descriptor.group_char or settings.DEFAULT_GROUP_CHAR
2424

2525
# Read
2626

@@ -39,17 +39,17 @@ def value_reader(cell: Any):
3939

4040
# Process the cell
4141
any_non_default = (
42-
self._descriptor.bareNumber is not None
43-
or self._descriptor.groupChar is not None
44-
or self._descriptor.decimalChar is not None
42+
self._descriptor.bare_number is not None
43+
or self._descriptor.group_char is not None
44+
or self._descriptor.decimal_char is not None
4545
)
4646
if any_non_default:
4747
if pattern:
4848
cell = pattern.sub("", cell)
49-
cell = cell.replace(self.group_char, "")
49+
cell = cell.replace(self.group_char(), "")
5050
if self.decimal_char() != "." and "." in cell:
5151
return None
52-
cell = cell.replace(self.decimal_char, ".")
52+
cell = cell.replace(self.decimal_char(), ".")
5353

5454
if cell is None:
5555
return None
@@ -78,14 +78,14 @@ def value_reader(cell: Any):
7878
def create_value_writer(self):
7979
# Create writer
8080
def value_writer(cell: Any):
81-
if self._descriptor.groupChar is not None:
81+
if self._descriptor.group_char is not None:
8282
cell = f"{cell:,}".replace(",", "g")
8383
else:
8484
cell = str(cell)
85-
if self._descriptor.decimalChar is not None:
86-
cell = cell.replace(".", self.decimal_char)
87-
if self._descriptor.groupChar is not None:
88-
cell = cell.replace("g", self.group_char)
85+
if self._descriptor.decimal_char is not None:
86+
cell = cell.replace(".", self.decimal_char())
87+
if self._descriptor.group_char is not None:
88+
cell = cell.replace("g", self.group_char())
8989
return cell
9090

9191
return value_writer

frictionless/fields/time.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
from dataclasses import dataclass
34
from datetime import datetime, time
45
from typing import Any
56

@@ -8,6 +9,7 @@
89
from .field_descriptor import TimeFieldDescriptor
910

1011

12+
@dataclass
1113
class TimeReadWriter:
1214
_descriptor: TimeFieldDescriptor
1315

frictionless/fields/yearmonth.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,6 @@
44

55

66
class YearmonthReadWriter:
7-
type = "yearmonth"
8-
builtin = True
9-
supported_constraints = [
10-
"required",
11-
"minimum",
12-
"maximum",
13-
"enum",
14-
]
15-
16-
# Read
17-
187
def create_value_reader(self):
198
# Create reader
209
def value_reader(cell: Any):

frictionless/metadata/metadata.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ def to_descriptor_source(self) -> Union[types.IDescriptor, str]:
292292

293293
def to_copy(self, **options: Any) -> Self:
294294
"""Create a copy of the metadata"""
295-
return type(self).from_descriptor(self.to_descriptor(), **options)
295+
return deepcopy(self)
296296

297297
def to_dict(self) -> types.IDescriptor:
298298
"""Export metadata as dictionary (alias for "to_descriptor")"""

0 commit comments

Comments
 (0)