kaggle 1.7.3b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. kaggle/LICENSE +201 -0
  2. kaggle/__init__.py +6 -0
  3. kaggle/api/__init__.py +0 -0
  4. kaggle/api/kaggle_api.py +614 -0
  5. kaggle/api/kaggle_api_extended.py +4657 -0
  6. kaggle/cli.py +1606 -0
  7. kaggle/configuration.py +206 -0
  8. kaggle/models/__init__.py +0 -0
  9. kaggle/models/api_blob_type.py +4 -0
  10. kaggle/models/dataset_column.py +228 -0
  11. kaggle/models/dataset_new_request.py +385 -0
  12. kaggle/models/dataset_new_version_request.py +287 -0
  13. kaggle/models/dataset_update_settings_request.py +310 -0
  14. kaggle/models/kaggle_models_extended.py +276 -0
  15. kaggle/models/kernel_push_request.py +556 -0
  16. kaggle/models/model_instance_new_version_request.py +145 -0
  17. kaggle/models/model_instance_update_request.py +351 -0
  18. kaggle/models/model_new_instance_request.py +417 -0
  19. kaggle/models/model_new_request.py +314 -0
  20. kaggle/models/model_update_request.py +282 -0
  21. kaggle/models/start_blob_upload_request.py +232 -0
  22. kaggle/models/start_blob_upload_response.py +137 -0
  23. kaggle/models/upload_file.py +169 -0
  24. kaggle/test/__init__.py +0 -0
  25. kaggle/test/test_authenticate.py +43 -0
  26. kaggle-1.7.3b1.dist-info/METADATA +348 -0
  27. kaggle-1.7.3b1.dist-info/RECORD +89 -0
  28. kaggle-1.7.3b1.dist-info/WHEEL +4 -0
  29. kaggle-1.7.3b1.dist-info/entry_points.txt +2 -0
  30. kaggle-1.7.3b1.dist-info/licenses/LICENSE.txt +201 -0
  31. kagglesdk/LICENSE +201 -0
  32. kagglesdk/__init__.py +2 -0
  33. kagglesdk/admin/__init__.py +0 -0
  34. kagglesdk/admin/services/__init__.py +0 -0
  35. kagglesdk/admin/services/inbox_file_service.py +22 -0
  36. kagglesdk/admin/types/__init__.py +0 -0
  37. kagglesdk/admin/types/inbox_file_service.py +74 -0
  38. kagglesdk/blobs/__init__.py +0 -0
  39. kagglesdk/blobs/services/__init__.py +0 -0
  40. kagglesdk/blobs/services/blob_api_service.py +25 -0
  41. kagglesdk/blobs/types/__init__.py +0 -0
  42. kagglesdk/blobs/types/blob_api_service.py +177 -0
  43. kagglesdk/common/__init__.py +0 -0
  44. kagglesdk/common/types/__init__.py +0 -0
  45. kagglesdk/common/types/file_download.py +102 -0
  46. kagglesdk/common/types/http_redirect.py +105 -0
  47. kagglesdk/competitions/__init__.py +0 -0
  48. kagglesdk/competitions/services/__init__.py +0 -0
  49. kagglesdk/competitions/services/competition_api_service.py +129 -0
  50. kagglesdk/competitions/types/__init__.py +0 -0
  51. kagglesdk/competitions/types/competition_api_service.py +1874 -0
  52. kagglesdk/competitions/types/competition_enums.py +53 -0
  53. kagglesdk/competitions/types/submission_status.py +9 -0
  54. kagglesdk/datasets/__init__.py +0 -0
  55. kagglesdk/datasets/services/__init__.py +0 -0
  56. kagglesdk/datasets/services/dataset_api_service.py +170 -0
  57. kagglesdk/datasets/types/__init__.py +0 -0
  58. kagglesdk/datasets/types/dataset_api_service.py +2777 -0
  59. kagglesdk/datasets/types/dataset_enums.py +82 -0
  60. kagglesdk/datasets/types/dataset_types.py +646 -0
  61. kagglesdk/education/__init__.py +0 -0
  62. kagglesdk/education/services/__init__.py +0 -0
  63. kagglesdk/education/services/education_api_service.py +19 -0
  64. kagglesdk/education/types/__init__.py +0 -0
  65. kagglesdk/education/types/education_api_service.py +248 -0
  66. kagglesdk/education/types/education_service.py +139 -0
  67. kagglesdk/kaggle_client.py +66 -0
  68. kagglesdk/kaggle_env.py +42 -0
  69. kagglesdk/kaggle_http_client.py +316 -0
  70. kagglesdk/kaggle_object.py +293 -0
  71. kagglesdk/kernels/__init__.py +0 -0
  72. kagglesdk/kernels/services/__init__.py +0 -0
  73. kagglesdk/kernels/services/kernels_api_service.py +109 -0
  74. kagglesdk/kernels/types/__init__.py +0 -0
  75. kagglesdk/kernels/types/kernels_api_service.py +1951 -0
  76. kagglesdk/kernels/types/kernels_enums.py +33 -0
  77. kagglesdk/models/__init__.py +0 -0
  78. kagglesdk/models/services/__init__.py +0 -0
  79. kagglesdk/models/services/model_api_service.py +255 -0
  80. kagglesdk/models/services/model_service.py +19 -0
  81. kagglesdk/models/types/__init__.py +0 -0
  82. kagglesdk/models/types/model_api_service.py +3719 -0
  83. kagglesdk/models/types/model_enums.py +60 -0
  84. kagglesdk/models/types/model_service.py +275 -0
  85. kagglesdk/models/types/model_types.py +286 -0
  86. kagglesdk/test/test_client.py +45 -0
  87. kagglesdk/users/__init__.py +0 -0
  88. kagglesdk/users/types/__init__.py +0 -0
  89. kagglesdk/users/types/users_enums.py +22 -0
@@ -0,0 +1,316 @@
1
+ import binascii
2
+ import codecs
3
+ import json
4
+ import os
5
+ import urllib.parse
6
+ from io import BytesIO
7
+
8
+ import requests
9
+ from urllib3.fields import RequestField
10
+
11
+ from kagglesdk.kaggle_env import get_endpoint, get_env, KaggleEnv
12
+ from kagglesdk.kaggle_object import KaggleObject
13
+ from typing import Type
14
+
15
+ # TODO (http://b/354237483) Generate the client from the existing one.
16
+ # This was created from kaggle_api_client.py, prior to recent changes to
17
+ # auth handling. The new client requires KAGGLE_API_TOKEN, so it is not
18
+ # currently usable by the CLI.
19
+
20
+ # TODO: Extend kapigen to add a boolean to these requests indicating that they use forms.
21
+ REQUESTS_REQUIRING_FORMS = ['ApiUploadDatasetFileRequest', 'ApiCreateSubmissionRequest', 'ApiStartSubmissionUploadRequest', 'ApiUploadModelFileRequest']
22
+
23
+ def _headers_to_str(headers):
24
+ return '\n'.join(f'{k}: {v}' for k, v in headers.items())
25
+
26
+
27
+ def _get_apikey_creds():
28
+ apikey_filename = os.path.expanduser('~/.kaggle/kaggle.json')
29
+ if not os.path.exists(apikey_filename):
30
+ return None
31
+
32
+ kaggle_json = None
33
+ with open(apikey_filename) as apikey_file:
34
+ kaggle_json = apikey_file.read()
35
+
36
+ if not kaggle_json or not kaggle_json.strip():
37
+ return None
38
+
39
+ api_key_data = json.loads(kaggle_json)
40
+ username = api_key_data['username']
41
+ api_key = api_key_data['key']
42
+ return username, api_key
43
+
44
+
45
+ def clean_data(data):
46
+ if isinstance(data, dict):
47
+ return {to_lower_camel_case(k): clean_data(v) for k, v in data.items() if v is not None}
48
+ if isinstance(data, list):
49
+ return [clean_data(v) for v in data if v is not None]
50
+ if data is True:
51
+ return 'true'
52
+ if data is False:
53
+ return 'false'
54
+ return data
55
+
56
+ def find_words(source, left='{', right='}'):
57
+ words = []
58
+ split_str = source.split(left)
59
+
60
+ for s in split_str[1:]:
61
+ split_s = s.split(right)
62
+ if len(split_s) > 1:
63
+ words.append(split_s[0])
64
+
65
+ return words
66
+
67
+ def to_camel_case(snake_str):
68
+ return "".join(x.capitalize() for x in snake_str.lower().split("_"))
69
+
70
+ def to_lower_camel_case(snake_str):
71
+ # https://stackoverflow.com/questions/19053707/converting-snake-case-to-lower-camel-case-lowercamelcase
72
+ # We capitalize the first letter of each component except the first one
73
+ # with the 'capitalize' method and join them together.
74
+ camel_string = to_camel_case(snake_str)
75
+ return snake_str[0].lower() + camel_string[1:]
76
+
77
+ class KaggleHttpClient(object):
78
+ _xsrf_cookie_name = 'XSRF-TOKEN'
79
+ _csrf_cookie_name = "CSRF-TOKEN"
80
+ _xsrf_cookies = (_xsrf_cookie_name, _csrf_cookie_name)
81
+ _xsrf_header_name = 'X-XSRF-TOKEN'
82
+
83
+ def __init__(self,
84
+ env: KaggleEnv = None,
85
+ verbose: bool = False,
86
+ renew_iap_token=None,
87
+ username=None,
88
+ password=None):
89
+ self._env = env or get_env()
90
+ self._signed_in = None
91
+ self._endpoint = get_endpoint(self._env)
92
+ self._verbose = verbose
93
+ self._session = None
94
+ self._username = username
95
+ self._password = password
96
+
97
+ def call(self, service_name: str, request_name: str, request: KaggleObject,
98
+ response_type: Type[KaggleObject]):
99
+ self._init_session()
100
+ http_request = self._prepare_request(service_name, request_name, request)
101
+
102
+ http_response = self._session.send(http_request)
103
+
104
+ response = self._prepare_response(response_type, http_response)
105
+ return response
106
+
107
+ def _prepare_request(self, service_name: str, request_name: str,
108
+ request: KaggleObject):
109
+ request_url = self._get_request_url(request)
110
+ method = request.method()
111
+ data= ''
112
+ if method == 'GET':
113
+ data = request.__class__.to_dict(request, ignore_defaults=False)
114
+ if request.endpoint_path():
115
+ words = find_words(request.endpoint_path())
116
+ list(map(data.pop, [to_lower_camel_case(w) for w in words]))
117
+ if len(data) == 0:
118
+ data = None
119
+ if data:
120
+ request_url = f'{request_url}?{urllib.parse.urlencode(clean_data(data))}'
121
+ data = ''
122
+ self._session.headers.update({
123
+ 'Accept': 'application/json',
124
+ 'Content-Type': 'text/plain',
125
+ })
126
+ elif method == 'POST':
127
+ data = request.to_field_map(request, ignore_defaults=True)
128
+ if isinstance(data, dict):
129
+ fields = request.body_fields()
130
+ if fields is not None:
131
+ if fields != '*':
132
+ data = data[fields]
133
+ data = clean_data(data)
134
+ if self.requires_form(request):
135
+ data, content_type = self.make_form(data)
136
+ else:
137
+ content_type = 'application/json'
138
+ data = json.dumps(data)
139
+ self._session.headers.update({
140
+ 'Accept': 'application/json',
141
+ 'Content-Type': content_type,
142
+ })
143
+ http_request = requests.Request(
144
+ method=method,
145
+ url=request_url,
146
+ data=data,
147
+ headers=self._session.headers,
148
+ # cookies=self._get_xsrf_cookies(),
149
+ auth=self._session.auth)
150
+ prepared_request = http_request.prepare()
151
+ self._print_request(prepared_request)
152
+ return prepared_request
153
+
154
+ def _get_xsrf_cookies(self):
155
+ cookies = requests.cookies.RequestsCookieJar()
156
+ for cookie in self._session.cookies:
157
+ if cookie.name in KaggleHttpClient._xsrf_cookies:
158
+ cookies[cookie.name] = cookie.value
159
+ return cookies
160
+
161
+ def _prepare_response(self, response_type, http_response):
162
+ self._print_response(http_response)
163
+ http_response.raise_for_status()
164
+ if 'application/json' in http_response.headers['Content-Type']:
165
+ resp = http_response.json()
166
+ if 'code' in resp and resp['code'] >= 400:
167
+ raise requests.exceptions.HTTPError(
168
+ resp['message'], response=http_response)
169
+ if response_type is None: # Method doesn't have a return type
170
+ return None
171
+ return response_type.prepare_from(http_response)
172
+
173
+ def _print_request(self, request):
174
+ if not self._verbose:
175
+ return
176
+ self._print('---------------------Request----------------------')
177
+ self._print(
178
+ f'{request.method} {request.url}\n{_headers_to_str(request.headers)}\n\n{request.body}'
179
+ )
180
+ self._print('--------------------------------------------------')
181
+
182
+ def _print_response(self, response, body=True):
183
+ if not self._verbose:
184
+ return
185
+ self._print('---------------------Response---------------------')
186
+ self._print(f'{response.status_code}\n{_headers_to_str(response.headers)}')
187
+ if body:
188
+ self._print(f'\n{response.text}')
189
+ self._print('--------------------------------------------------')
190
+
191
+ def _print(self, message: str):
192
+ if self._verbose:
193
+ print(message)
194
+
195
+ def __enter__(self):
196
+ self._init_session()
197
+ return self
198
+
199
+ def __exit__(self, exc_type, exc_value, tb):
200
+ if self._session is not None:
201
+ self._session.close()
202
+
203
+ def _init_session(self):
204
+ if self._session is not None:
205
+ return self._session
206
+
207
+ self._session = requests.Session()
208
+ self._session.headers.update({
209
+ 'User-Agent': 'kaggle-api/v1.7.0', # Was: V2
210
+ 'Content-Type': 'application/x-www-form-urlencoded', # Was: /json
211
+ })
212
+
213
+ iap_token = self._get_iap_token_if_required()
214
+ if iap_token is not None:
215
+ self._session.headers.update({
216
+ # https://cloud.google.com/iap/docs/authentication-howto#authenticating_from_proxy-authorization_header
217
+ 'Proxy-Authorization': f'Bearer {iap_token}',
218
+ })
219
+
220
+ self._try_fill_auth()
221
+ # self._fill_xsrf_token(iap_token) # TODO Make this align with original handler.
222
+
223
+ def _get_iap_token_if_required(self):
224
+ if self._env not in (KaggleEnv.STAGING, KaggleEnv.ADMIN):
225
+ return None
226
+ iap_token = os.getenv('KAGGLE_IAP_TOKEN')
227
+ if iap_token is None:
228
+ raise Exception(f'Must set KAGGLE_IAP_TOKEN to access "{self._endpoint}"')
229
+ return iap_token
230
+
231
+ def _fill_xsrf_token(self, iap_token):
232
+ initial_get_request = requests.Request(
233
+ method='GET',
234
+ url=self._endpoint,
235
+ headers=self._session.headers,
236
+ auth=self._session.auth)
237
+ prepared_request = initial_get_request.prepare()
238
+ self._print_request(prepared_request)
239
+
240
+ http_response = self._session.send(prepared_request)
241
+
242
+ self._print_response(http_response, body=False)
243
+ if iap_token is not None and http_response.status_code in (401, 403):
244
+ raise requests.exceptions.HTTPError('IAP token invalid or expired')
245
+ http_response.raise_for_status()
246
+
247
+ self._session.headers.update({
248
+ KaggleHttpClient._xsrf_header_name:
249
+ self._session.cookies[KaggleHttpClient._xsrf_cookie_name],
250
+ })
251
+
252
+ class BearerAuth(requests.auth.AuthBase):
253
+
254
+ def __init__(self, token):
255
+ self.token = token
256
+
257
+ def __call__(self, r):
258
+ r.headers["Authorization"] = f"Bearer {self.token}"
259
+ return r
260
+
261
+ def _try_fill_auth(self):
262
+ if self._signed_in is not None:
263
+ return
264
+
265
+ api_token = os.getenv('KAGGLE_API_TOKEN')
266
+ if api_token is not None:
267
+ self._session.auth = KaggleHttpClient.BearerAuth(api_token)
268
+ self._signed_in = True
269
+ return
270
+
271
+ if self._username and self._password:
272
+ apikey_creds = self._username, self._password
273
+ else:
274
+ apikey_creds = _get_apikey_creds()
275
+ if apikey_creds is not None:
276
+ self._session.auth = apikey_creds
277
+ self._signed_in = True
278
+ return
279
+
280
+ self._signed_in = False
281
+
282
+ def _get_request_url(self, request):
283
+ return f'{self._endpoint}{request.endpoint()}'
284
+
285
+ @staticmethod
286
+ def make_form(fields):
287
+ body = BytesIO()
288
+ boundary = binascii.hexlify(os.urandom(16)).decode()
289
+ writer = codecs.lookup("utf-8")[3]
290
+
291
+ for field in fields.items():
292
+ field = RequestField.from_tuples(*field)
293
+ body.write(f"--{boundary}\r\n".encode("latin-1"))
294
+
295
+ writer(body).write(field.render_headers())
296
+ data = field.data
297
+
298
+ if isinstance(data, int):
299
+ data = str(data)
300
+
301
+ if isinstance(data, str):
302
+ writer(body).write(data)
303
+ else:
304
+ body.write(data)
305
+
306
+ body.write(b"\r\n")
307
+
308
+ body.write(f"--{boundary}--\r\n".encode("latin-1"))
309
+
310
+ content_type = f"multipart/form-data; boundary={boundary}"
311
+
312
+ return body.getvalue(), content_type
313
+
314
+ @staticmethod
315
+ def requires_form(request):
316
+ return type(request).__name__ in REQUESTS_REQUIRING_FORMS
@@ -0,0 +1,293 @@
1
+ import json
2
+ import re
3
+ from datetime import datetime, timedelta
4
+ from google.protobuf.field_mask_pb2 import FieldMask
5
+
6
+
7
+ class ObjectSerializer(object):
8
+ def __init__(self, to_dict_value, from_dict_value):
9
+ self.to_dict_value = to_dict_value
10
+ self.from_dict_value = from_dict_value
11
+
12
+
13
+ class PredefinedSerializer(ObjectSerializer):
14
+ def __init__(self):
15
+ """Predefined objects such as int, float etc are serialized/deserialized directly."""
16
+ ObjectSerializer.__init__(self, lambda cls, v, _: v, lambda cls, v: v)
17
+
18
+
19
+ # Adapted from https://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-snake-case
20
+ _pascal_to_upper_snake_case_regex = re.compile('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))')
21
+
22
+
23
+ def _pascal_case_to_upper_snake_case(string):
24
+ return _pascal_to_upper_snake_case_regex.sub(r'_\1', string).upper()
25
+
26
+
27
+ def _convert (camel_input):
28
+ words = re.findall(r'[A-Z]?[a-z]+|[A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$)|\d+', camel_input)
29
+ return '_'.join(map(str.lower, words))
30
+
31
+
32
+ class EnumSerializer(ObjectSerializer):
33
+ def __init__(self):
34
+ """
35
+ Enum objects are serialized using their ".name" field and deserialized by indexing the string in the Enum type.
36
+ Example:
37
+ class Foo(Enum):
38
+ TEST = 1
39
+ foo = Foo.TEST
40
+ foo.name # => returns "TEST"
41
+ Foo["TEST"] # => returns Foo.TEST enum value.
42
+ """
43
+ ObjectSerializer.__init__(self,
44
+ lambda cls, v, _: EnumSerializer._to_str(cls, v),
45
+ lambda cls, v: EnumSerializer._from_str(cls, v))
46
+
47
+ @staticmethod
48
+ def _to_str(cls, v):
49
+ # "v" corresponds to an enum instance: Example foo or Foo.Test above.
50
+ # "cls" corresponds to the enum type Foo above.
51
+ #enum_prefix = f'{_pascal_case_to_upper_snake_case(cls.__name__)}_'
52
+ #if v.name.startswith(enum_prefix):
53
+ # return v.name
54
+ #return f'{enum_prefix}{v.name}'
55
+ enum_prefix = f'{_pascal_case_to_upper_snake_case(cls.__name__)}_'
56
+ if v.name.find(enum_prefix) == 0:
57
+ return v.name[len(enum_prefix):].lower()
58
+ return v.name
59
+
60
+ @staticmethod
61
+ def _from_str(cls, v):
62
+ # "v" corresponds to enum string: Example "TEST" above.
63
+ # "cls" corresponds to the enum type Foo above.
64
+ # enum_items = {item.name: item for item in cls}
65
+ # if v in enum_items:
66
+ # return enum_items[v]
67
+ #
68
+ # # Try with enum prefix. Example: EnvironmentType.JSON -> "ENVIRONMENT_TYPE_JSON"
69
+ # enum_prefix = _pascal_case_to_upper_snake_case(cls.__name__)
70
+ # if v.startswith(enum_prefix):
71
+ # ix_start = len(enum_prefix) + 1
72
+ # return enum_items[v[ix_start:]]
73
+ #
74
+ # return enum_items[f'{enum_prefix}_{v}']
75
+ try:
76
+ return cls[v]
77
+ except KeyError:
78
+ dct = vars(cls)
79
+ n = v.lower()
80
+ nn = _convert(v).lower()
81
+ enum_prefix = _pascal_case_to_upper_snake_case(cls.__name__).lower()
82
+ for key in dct.keys():
83
+ k = key.lower()
84
+ if k == n:
85
+ return dct[key]
86
+ if k.startswith(enum_prefix) and k.endswith(n) or k.endswith(nn):
87
+ return dct[key]
88
+ raise
89
+
90
+
91
+ class ListSerializer(ObjectSerializer):
92
+ def __init__(self, item_serializer: ObjectSerializer):
93
+ """
94
+ Lists are serialized based on the type they contain. Since objects are generated from proto files, a list always
95
+ contains objects of the same type, which is serialized/deserialized using "item_serializer".
96
+ """
97
+ ObjectSerializer.__init__(self,
98
+ lambda cls, l, ignore_defaults: [item_serializer.to_dict_value(cls, v, ignore_defaults) for v in l],
99
+ lambda cls, l: [item_serializer.from_dict_value(cls, v) for v in l])
100
+
101
+
102
+ class MapSerializer(ObjectSerializer):
103
+ def __init__(self, item_serializer: ObjectSerializer):
104
+ """
105
+ Maps are serialized based on type of their values. Since maps keys are always predefined types, we don't need a
106
+ serializer for them.
107
+ """
108
+ ObjectSerializer.__init__(self,
109
+ lambda cls, d, ignore_defaults: {k: item_serializer.to_dict_value(cls, v, ignore_defaults) for k, v in d.items()},
110
+ lambda cls, d: {k: item_serializer.from_dict_value(cls, v) for k, v in d.items()})
111
+
112
+
113
+ class DateTimeSerializer(ObjectSerializer):
114
+ def __init__(self):
115
+ """Date times are serialized/deserialized as a string in iso format"""
116
+ ObjectSerializer.__init__(self,
117
+ lambda cls, dt, _: DateTimeSerializer._to_str(dt),
118
+ lambda _, v: DateTimeSerializer._from_str(v))
119
+
120
+ @staticmethod
121
+ def _to_str(dt):
122
+ return dt.isoformat(timespec='milliseconds') + 'Z'
123
+
124
+ @staticmethod
125
+ def _from_str(v):
126
+ v = v.rstrip('Z')
127
+ fields = v.rsplit('.', maxsplit=1)
128
+ if len(fields) == 1:
129
+ return datetime.fromisoformat(v)
130
+ (dt, nanos) = fields
131
+ millis = nanos[:3]
132
+ try:
133
+ return datetime.fromisoformat(f'{dt}.{millis}')
134
+ except ValueError:
135
+ return datetime.fromisoformat(dt) # Python 3.9, 3.10
136
+
137
+
138
+ class TimeDeltaSerializer(ObjectSerializer):
139
+ def __init__(self):
140
+ """Time deltas are serialized/deserialized as a string in "mm:ss" format"""
141
+ ObjectSerializer.__init__(self,
142
+ lambda cls, t, _: TimeDeltaSerializer._to_dict_value(t),
143
+ lambda cls, v: TimeDeltaSerializer._from_dict_value(v))
144
+
145
+ @staticmethod
146
+ def _to_dict_value(delta):
147
+ seconds = int(delta.total_seconds())
148
+ minutes = seconds // 60
149
+ seconds -= minutes * 60
150
+ return '{}:{:02}'.format(int(minutes), int(seconds))
151
+
152
+ @staticmethod
153
+ def _from_dict_value(value):
154
+ (minutes, seconds) = value.split(':')
155
+ return timedelta(minutes=int(minutes), seconds=int(seconds))
156
+
157
+
158
+ class FieldMaskSerializer(ObjectSerializer):
159
+ def __init__(self):
160
+ """Field masks are serialized/deserialized as a string that contains a list of paths with a comma delimiter"""
161
+ ObjectSerializer.__init__(self,
162
+ lambda cls, m, _: m.ToJsonString(),
163
+ lambda cls, v: FieldMaskSerializer._from_joined_paths(v))
164
+
165
+ @staticmethod
166
+ def _from_joined_paths(joined_paths):
167
+ mask = FieldMask()
168
+ mask.FromJsonString(joined_paths)
169
+ return mask
170
+
171
+
172
+ class KaggleObjectSerializer(ObjectSerializer):
173
+ def __init__(self):
174
+ """
175
+ Kaggle objects (i.e., proto-generated types that inherit from KaggleObject) have custom "to_dict" and "from_dict"
176
+ methods that serialize/deserialize them to/from dictionaries.
177
+ """
178
+ ObjectSerializer.__init__(self,
179
+ # "v" is an instance of a KaggleObject. For example: "req = ListCompetitionsRequest()".
180
+ # So "req.to_dict()" returns a dictionary with keys as json field names. Example:
181
+ # '{"pageSize": 10, "page": 2}'
182
+ lambda cls, v, ignore_defaults: cls.to_dict(v, ignore_defaults),
183
+ # "cls" is the type of a KaggleObject. For example: ListCompetitionsRequest. All
184
+ # generated Kaggle objects have "from_dict" class method that takes a dict to create a
185
+ # new instance of the object. See "KaggleObject" class definition below.
186
+ lambda cls, v: cls.from_dict(v))
187
+
188
+
189
+ class FieldMetadata(object):
190
+ def __init__(self, json_name, field_name, private_field_name, field_type, default_value, serializer, optional=False):
191
+ self.json_name = json_name
192
+ self.field_name = field_name
193
+ self.private_field_name = private_field_name
194
+ self.field_type = field_type
195
+ self.default_value = default_value
196
+ self.serializer = serializer
197
+ self.optional = optional
198
+
199
+ def get_as_dict_item(self, instance, ignore_defaults=True):
200
+ value = getattr(instance, self.private_field_name)
201
+ if ignore_defaults and value == self.default_value:
202
+ return None
203
+ if value is None:
204
+ return None
205
+ return self.serializer.to_dict_value(self.field_type, value, ignore_defaults)
206
+
207
+ def set_from_dict(self, instance, json_dict):
208
+ if self.json_name not in json_dict:
209
+ return # Ignore unknown fields
210
+ value = json_dict[self.json_name]
211
+ if value == self.default_value:
212
+ return # Ignore default values
213
+ try:
214
+ setattr(instance, self.private_field_name, self.serializer.from_dict_value(self.field_type, value))
215
+ except Exception as e:
216
+ raise
217
+
218
+
219
+ class KaggleObject(object):
220
+ def endpoint(self):
221
+ raise 'Error: endpoint must be defined by the request object'
222
+
223
+ @staticmethod
224
+ def endpoint_path():
225
+ return None
226
+
227
+ @staticmethod
228
+ def body_fields():
229
+ return None
230
+
231
+ @classmethod
232
+ def prepare_from(cls, http_response):
233
+ return cls.from_json(http_response.text)
234
+
235
+ @staticmethod
236
+ def method():
237
+ return "GET"
238
+
239
+ def _freeze(self):
240
+ self._is_frozen = True
241
+
242
+ def __setattr__(self, key, value):
243
+ if hasattr(self, '_is_frozen') and not hasattr(self, key):
244
+ raise AttributeError(f'Unknown field for {self.__class__.__name__}: {key}')
245
+ object.__setattr__(self, key, value)
246
+
247
+ def to_dict(self, ignore_defaults=True):
248
+ kv_pairs = [(field.json_name, field.get_as_dict_item(self, ignore_defaults)) for field in self._fields]
249
+ return {k: v for (k, v) in kv_pairs if not ignore_defaults or v is not None}
250
+
251
+ @staticmethod
252
+ def to_field_map(self, ignore_defaults=True):
253
+ kv_pairs = [(field.field_name, field.get_as_dict_item(self, ignore_defaults)) for field in self._fields]
254
+ return {k: v for (k, v) in kv_pairs if not ignore_defaults or v is not None}
255
+
256
+ @classmethod
257
+ def from_dict(cls, json_dict):
258
+ instance = cls()
259
+ for field in cls._fields:
260
+ field.set_from_dict(instance, json_dict)
261
+ return instance
262
+
263
+ @classmethod
264
+ def from_json(cls, json_str):
265
+ return cls.from_dict(json.loads(json_str))
266
+
267
+ @staticmethod
268
+ def to_json(self, ignore_defaults=True):
269
+ return json.dumps(KaggleObject.to_dict(self, ignore_defaults))
270
+
271
+ def __str__(self):
272
+ return KaggleObject.to_json(self, ignore_defaults=False)
273
+
274
+ def __repr__(self):
275
+ return KaggleObject.to_json(self, ignore_defaults=False)
276
+
277
+ def __contains__(self, field_name):
278
+ field = self._get_field(field_name)
279
+ value = getattr(self, field.private_field_name)
280
+ if field.optional:
281
+ return value is not None
282
+ else:
283
+ return value != field.default_value
284
+
285
+ def __delattr__(self, field_name):
286
+ field = self._get_field(field_name)
287
+ setattr(self, field.private_field_name, field.default_value)
288
+
289
+ def _get_field(self, field_name):
290
+ field = next((f for f in self._fields if f.field_name == field_name), None)
291
+ if field is None:
292
+ raise ValueError(f'Protocol message {self.__class__.__name__} has no "{field_name}" field.')
293
+ return field
File without changes
File without changes