Skip to content

Commit 6a78266

Browse files
authored
Convert competitions to use kagglesdk (#625)
The competitions portion of the API now uses `kagglesdk`. The HTTP client has ben updated to use prod, if needed.
1 parent 192c7e6 commit 6a78266

File tree

12 files changed

+578
-376
lines changed

12 files changed

+578
-376
lines changed

kaggle/api/kaggle_api_extended.py

Lines changed: 242 additions & 165 deletions
Large diffs are not rendered by default.

kaggle/models/kaggle_models_extended.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def __init__(self, init_dict):
136136
self.size = File.get_size(self.totalBytes)
137137

138138
def __repr__(self):
139-
return self.ref
139+
return self.name
140140

141141
@staticmethod
142142
def get_size(size, precision=0):

kagglesdk/common/types/file_download.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ def content_length(self, content_length: int):
8989
self._content_length = content_length
9090

9191

92+
@classmethod
93+
def prepare_from(cls, http_response):
94+
return http_response
95+
9296
FileDownload._fields = [
9397
FieldMetadata("contentType", "content_type", "_content_type", str, "", PredefinedSerializer()),
9498
FieldMetadata("fileName", "file_name", "_file_name", str, "", PredefinedSerializer()),

kagglesdk/kaggle_client.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ class Datasets(object):
2525
def __init__(self, http_client: KaggleHttpClient):
2626
self.dataset_api_client = DatasetApiClient(http_client)
2727

28-
def __init__(self, env: KaggleEnv = None, verbose: bool = False):
29-
self._http_client = http_client = KaggleHttpClient(env, verbose, self._renew_iap_token)
28+
def __init__(self, env: KaggleEnv = None, verbose: bool = False, username: str = None, password: str = None):
29+
self._http_client = http_client = KaggleHttpClient(env, verbose, self._renew_iap_token, username=username, password=password)
3030
self.kernels = KaggleClient.Kernels(http_client)
3131
self.models = KaggleClient.Models(http_client)
3232
self.competitions = KaggleClient.Competitions(http_client)
3333
self.datasets = KaggleClient.Datasets(http_client)
34+
self.username = username
35+
self.password = password
3436

3537
def _renew_iap_token(self):
3638
return self.admin.admin_client.renew_iap_token()

kagglesdk/kaggle_env.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ class KaggleEnv(Enum):
77
STAGING = 1 # staging.kaggle.com
88
ADMIN = 2 # admin.kaggle.com
99
QA = 3 # qa.kaggle.com
10+
# Direct prod access is not allowed to have IAP protection during testing, but we support basic auth.
11+
PROD = 4 # www.kaggle.com
1012

1113

1214
_env_to_endpoint = {
1315
KaggleEnv.LOCAL: 'http://localhost',
1416
KaggleEnv.STAGING: 'https://staging.kaggle.com',
1517
KaggleEnv.ADMIN: 'https://admin.kaggle.com',
1618
KaggleEnv.QA: 'https://qa.kaggle.com',
19+
# See the comment above in KaggleEnv enum.
20+
KaggleEnv.PROD: "https://www.kaggle.com",
1721
}
1822

1923

@@ -33,4 +37,6 @@ def get_env():
3337
return KaggleEnv.STAGING
3438
if env == 'QA':
3539
return KaggleEnv.QA
40+
if env == 'PROD':
41+
return KaggleEnv.PROD
3642
raise Exception(f'Unrecognized value in KAGGLE_API_ENVIRONMENT: "{env}"')

kagglesdk/kaggle_http_client.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import os
22
import json
3+
import urllib.parse
4+
35
import requests
46
from kagglesdk.kaggle_env import get_endpoint, get_env, KaggleEnv
57
from kagglesdk.kaggle_object import KaggleObject
@@ -42,12 +44,16 @@ class KaggleHttpClient(object):
4244
def __init__(self,
4345
env: KaggleEnv = None,
4446
verbose: bool = False,
45-
renew_iap_token = None):
47+
renew_iap_token = None,
48+
username = None,
49+
password = None):
4650
self._env = env or get_env()
4751
self._signed_in = None
4852
self._endpoint = get_endpoint(self._env)
4953
self._verbose = verbose
5054
self._session = None
55+
self._username = username
56+
self._password = password
5157

5258
def call(self, service_name: str, request_name: str, request: KaggleObject, response_type: Type[KaggleObject]):
5359
self._init_session()
@@ -61,10 +67,14 @@ def call(self, service_name: str, request_name: str, request: KaggleObject, resp
6167
def _prepare_request(self, service_name: str, request_name: str, request: KaggleObject):
6268
request_url = self._get_request_url(request)
6369
method = request.method()
64-
if method == 'POST':
65-
data = request.__class__.to_dict(request)
66-
else:
67-
data = []
70+
data = request.__class__.to_dict(request)
71+
if method == 'GET':
72+
request_url = f'{request_url}?{urllib.parse.urlencode(data)}'
73+
data = ''
74+
self._session.headers.update({
75+
'Accept': 'application/json',
76+
'Content-Type': 'text/plain',
77+
})
6878
http_request = requests.Request(
6979
method=method,
7080
url=request_url,
@@ -86,6 +96,10 @@ def _get_xsrf_cookies(self):
8696
def _prepare_response(self, response_type, http_response):
8797
self._print_response(http_response)
8898
http_response.raise_for_status()
99+
if 'application/json' in http_response.headers['Content-Type']:
100+
resp = http_response.json()
101+
if 'code' in resp and resp['code'] >= 400:
102+
raise requests.exceptions.HTTPError(resp['message'], response=http_response)
89103
if response_type is None: # Method doesn't have a return type
90104
return None
91105
return response_type.prepare_from(http_response)
@@ -184,7 +198,10 @@ def _try_fill_auth(self):
184198
self._signed_in = True
185199
return
186200

187-
apikey_creds = _get_apikey_creds()
201+
if self._username and self._password:
202+
apikey_creds = self._username, self._password
203+
else:
204+
apikey_creds = _get_apikey_creds()
188205
if apikey_creds is not None:
189206
self._session.auth = apikey_creds
190207
self._signed_in = True

kagglesdk/kaggle_object.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,15 @@ def _from_str(cls, v):
6565
# return enum_items[v[ix_start:]]
6666
#
6767
# return enum_items[f'{enum_prefix}_{v}']
68-
return cls[v]
68+
try:
69+
return cls[v]
70+
except KeyError:
71+
dct = vars(cls)
72+
n = v.lower()
73+
for key in dct.keys():
74+
if key.lower() == n:
75+
return dct[key]
76+
raise
6977

7078

7179
class ListSerializer(ObjectSerializer):

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ dependencies = [
3333
"text-unidecode",
3434
"tqdm",
3535
"urllib3>=1.15.1",
36-
"webencodings"
36+
"webencodings",
37+
"protobuf"
3738
]
3839

3940
[project.scripts]

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
'python-slugify',
3232
'urllib3',
3333
'bleach',
34+
'protobuf'
3435
],
3536
packages=find_packages(exclude=("src.*", "src")),
3637
license='Apache 2.0')

0 commit comments

Comments
 (0)