forked from frictionlessdata/tableschema-py
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfield.py
More file actions
165 lines (138 loc) · 5.29 KB
/
Copy pathfield.py
File metadata and controls
165 lines (138 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# -*- coding: utf-8 -*-
from __future__ import division
from __future__ import print_function
from __future__ import absolute_import
from __future__ import unicode_literals
from functools import partial
from .profile import Profile
from . import constraints
from . import exceptions
from . import helpers
from . import config
from . import types
# Module API
class Field(object):
"""Table Schema field representation.
"""
# Public
def __init__(self, descriptor, missing_values=config.DEFAULT_MISSING_VALUES,
# Internal
schema=None):
"""https://github.com/frictionlessdata/tableschema-py#field
"""
# Process descriptor
descriptor = helpers.expand_field_descriptor(descriptor)
# Set attributes
self.__descriptor = descriptor
self.__missing_values = missing_values
self.__schema = schema
self.__cast_function = self.__get_cast_function()
self.__check_functions = self.__get_check_functions()
@property
def schema(self):
"""https://github.com/frictionlessdata/tableschema-py#field
"""
return self.__schema
@property
def name(self):
"""https://github.com/frictionlessdata/tableschema-py#field
"""
return self.__descriptor.get('name')
@property
def type(self):
"""https://github.com/frictionlessdata/tableschema-py#field
"""
return self.__descriptor.get('type')
@property
def format(self):
"""https://github.com/frictionlessdata/tableschema-py#field
"""
return self.__descriptor.get('format')
@property
def required(self):
"""https://github.com/frictionlessdata/tableschema-py#field
"""
return self.constraints.get('required', False)
@property
def constraints(self):
"""https://github.com/frictionlessdata/tableschema-py#field
"""
return self.__descriptor.get('constraints', {})
@property
def descriptor(self):
"""https://github.com/frictionlessdata/tableschema-py#field
"""
return self.__descriptor
def cast_value(self, value, constraints=True, preserve_missing_values=False):
"""https://github.com/frictionlessdata/tableschema-py#field
"""
# Null value
if value in self.__missing_values:
# If missing_values should be preserved without being cast
if preserve_missing_values:
return value
value = None
# Cast value
cast_value = value
if value is not None:
cast_value = self.__cast_function(value)
if cast_value == config.ERROR:
raise exceptions.CastError((
'Field "{field.name}" can\'t cast value "{value}" '
'for type "{field.type}" with format "{field.format}"'
).format(field=self, value=value))
# Check value
if constraints:
for name, check in self.__check_functions.items():
if isinstance(constraints, list):
if name not in constraints:
continue
passed = check(cast_value)
if not passed:
raise exceptions.CastError((
'Field "{field.name}" has constraint "{name}" '
'which is not satisfied for value "{value}"'
).format(field=self, name=name, value=value))
return cast_value
def test_value(self, value, constraints=True):
"""https://github.com/frictionlessdata/tableschema-py#field
"""
try:
self.cast_value(value, constraints=constraints)
except exceptions.CastError:
return False
return True
# Private
def __get_cast_function(self):
options = {}
# Get cast options
for key in ['decimalChar', 'groupChar', 'bareNumber', 'trueValues', 'falseValues']:
value = self.descriptor.get(key)
if value is not None:
options[key] = value
cast = getattr(types, 'cast_%s' % self.type)
cast = partial(cast, self.format, **options)
return cast
def __get_check_functions(self):
checks = {}
cast = partial(self.cast_value, constraints=False)
whitelist = _get_field_constraints(self.type)
for name, constraint in self.constraints.items():
if name in whitelist:
# Cast enum constraint
if name in ['enum']:
constraint = list(map(cast, constraint))
# Cast maximum/minimum constraint
if name in ['maximum', 'minimum']:
constraint = cast(constraint)
check = getattr(constraints, 'check_%s' % name)
checks[name] = partial(check, constraint)
return checks
# Internal
def _get_field_constraints(type):
# Extract list of constraints for given type from jsonschema
jsonschema = Profile('table-schema').jsonschema
profile_types = jsonschema['properties']['fields']['items']['anyOf']
for profile_type in profile_types:
if type in profile_type['properties']['type']['enum']:
return profile_type['properties']['constraints']['properties'].keys()