Skip to content

Commit 2093545

Browse files
authored
Merge pull request #183 from radam9/master
fix UnicodeDecodeError when downloading non-json content
2 parents 07d5ef7 + 4fb1e5a commit 2093545

File tree

3 files changed

+33
-11
lines changed

3 files changed

+33
-11
lines changed

00_core.ipynb

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@
8888
" params += [_mk_param(k, **_mk_sig_detls(v)) for k,v in anno_args.items()]\n",
8989
" return Signature(params)\n",
9090
"\n",
91+
"def _decode_response(path: str) -> bool:\n",
92+
" \"checks if a endpoint needs to have it's response from `fastcore.core.urlsend` decoded or just return json\"\n",
93+
" needs_decode = (\n",
94+
" \"/orgs/{org}/migrations/{migration_id}/archive\",\n",
95+
" \"/repos/{owner}/{repo}/actions/artifacts/{artifact_id}/{archive_format}\",\n",
96+
" \"/repos/{owner}/{repo}/tarball/{ref}\",\n",
97+
" \"/repos/{owner}/{repo}/zipball/{ref}\",\n",
98+
" )\n",
99+
" return path not in needs_decode\n",
100+
"\n",
91101
"class _GhObj: pass"
92102
]
93103
},
@@ -99,11 +109,12 @@
99109
"source": [
100110
"#|export\n",
101111
"class _GhVerb(_GhObj):\n",
102-
" __slots__ = 'path,verb,tag,name,summary,url,route_ps,params,data,preview,client,__doc__'.split(',')\n",
112+
" __slots__ = 'path,verb,tag,name,summary,url,route_ps,params,data,preview,client,decode,__doc__'.split(',')\n",
103113
" def __init__(self, path, verb, oper, summary, url, params, data, preview, client, kwargs):\n",
104114
" tag,*name = oper.split('/')\n",
105115
" name = '__'.join(name)\n",
106116
" name = name.replace('-','_')\n",
117+
" decode = _decode_response(path)\n",
107118
" path,_,_ = partial_format(path, **kwargs)\n",
108119
" route_ps = stringfmt_names(path)\n",
109120
" __doc__ = summary\n",
@@ -117,7 +128,7 @@
117128
" for a,b in zip(args,flds): kwargs[b]=a\n",
118129
" route_p,query_p,data_p = [{p:kwargs[p] for p in o if p in kwargs}\n",
119130
" for o in (self.route_ps,self.params,d)]\n",
120-
" return self.client(self.path, self.verb, headers=headers, route=route_p, query=query_p, data=data_p)\n",
131+
" return self.client(self.path, self.verb, headers=headers, decode=self.decode, route=route_p, query=query_p, data=data_p)\n",
121132
"\n",
122133
" def __str__(self): return f'{self.tag}.{self.name}{signature(self)}\\n{self.doc_url}'\n",
123134
" @property\n",
@@ -201,17 +212,17 @@
201212
" self.debug,self.limit_cb,self.limit_rem = debug,limit_cb,5000\n",
202213
" self.gh_host = gh_host or GH_HOST\n",
203214
"\n",
204-
" def __call__(self, path:str, verb:str=None, headers:dict=None, route:dict=None, query:dict=None, data=None, timeout=None):\n",
215+
" def __call__(self, path:str, verb:str=None, headers:dict=None, route:dict=None, query:dict=None, data=None, timeout=None, decode=True):\n",
205216
" \"Call a fully specified `path` using HTTP `verb`, passing arguments to `fastcore.core.urlsend`\"\n",
206217
" if verb is None: verb = 'POST' if data else 'GET'\n",
207218
" headers = {**self.headers,**(headers or {})}\n",
208219
" if not path.startswith(('http://', 'https://')):\n",
209220
" path = self.gh_host + path\n",
210221
" if route:\n",
211222
" for k,v in route.items(): route[k] = quote(str(route[k]))\n",
212-
" return_json = ('json' in headers['Accept'])\n",
223+
" return_json = ('json' in headers['Accept']) and (decode is True)\n",
213224
" debug = self.debug if self.debug else print_summary if os.getenv('GHAPI_DEBUG') else None\n",
214-
" res,self.recv_hdrs = urlsend(path, verb, headers=headers or None, debug=debug, return_headers=True,\n",
225+
" res,self.recv_hdrs = urlsend(path, verb, headers=headers or None, decode=decode, debug=debug, return_headers=True,\n",
215226
" route=route or None, query=query or None, data=data or None, return_json=return_json, timeout=timeout)\n",
216227
" if 'X-RateLimit-Remaining' in self.recv_hdrs:\n",
217228
" newlim = self.recv_hdrs['X-RateLimit-Remaining']\n",

ghapi/core.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,26 @@ def _mk_sig(req_args, opt_args, anno_args):
3636
params += [_mk_param(k, **_mk_sig_detls(v)) for k,v in anno_args.items()]
3737
return Signature(params)
3838

39+
def _decode_response(path: str) -> bool:
40+
"checks if a endpoint needs to have it's response from `fastcore.core.urlsend` decoded or just return json"
41+
needs_decode = (
42+
"/orgs/{org}/migrations/{migration_id}/archive",
43+
"/repos/{owner}/{repo}/actions/artifacts/{artifact_id}/{archive_format}",
44+
"/repos/{owner}/{repo}/tarball/{ref}",
45+
"/repos/{owner}/{repo}/zipball/{ref}",
46+
)
47+
return path not in needs_decode
48+
3949
class _GhObj: pass
4050

4151
# %% ../00_core.ipynb 7
4252
class _GhVerb(_GhObj):
43-
__slots__ = 'path,verb,tag,name,summary,url,route_ps,params,data,preview,client,__doc__'.split(',')
53+
__slots__ = 'path,verb,tag,name,summary,url,route_ps,params,data,preview,client,decode,__doc__'.split(',')
4454
def __init__(self, path, verb, oper, summary, url, params, data, preview, client, kwargs):
4555
tag,*name = oper.split('/')
4656
name = '__'.join(name)
4757
name = name.replace('-','_')
58+
decode = _decode_response(path)
4859
path,_,_ = partial_format(path, **kwargs)
4960
route_ps = stringfmt_names(path)
5061
__doc__ = summary
@@ -58,7 +69,7 @@ def __call__(self, *args, headers=None, **kwargs):
5869
for a,b in zip(args,flds): kwargs[b]=a
5970
route_p,query_p,data_p = [{p:kwargs[p] for p in o if p in kwargs}
6071
for o in (self.route_ps,self.params,d)]
61-
return self.client(self.path, self.verb, headers=headers, route=route_p, query=query_p, data=data_p)
72+
return self.client(self.path, self.verb, headers=headers, decode=self.decode, route=route_p, query=query_p, data=data_p)
6273

6374
def __str__(self): return f'{self.tag}.{self.name}{signature(self)}\n{self.doc_url}'
6475
@property
@@ -107,17 +118,17 @@ def __init__(self, owner=None, repo=None, token=None, jwt_token=None, debug=None
107118
self.debug,self.limit_cb,self.limit_rem = debug,limit_cb,5000
108119
self.gh_host = gh_host or GH_HOST
109120

110-
def __call__(self, path:str, verb:str=None, headers:dict=None, route:dict=None, query:dict=None, data=None, timeout=None):
121+
def __call__(self, path:str, verb:str=None, headers:dict=None, route:dict=None, query:dict=None, data=None, timeout=None, decode=True):
111122
"Call a fully specified `path` using HTTP `verb`, passing arguments to `fastcore.core.urlsend`"
112123
if verb is None: verb = 'POST' if data else 'GET'
113124
headers = {**self.headers,**(headers or {})}
114125
if not path.startswith(('http://', 'https://')):
115126
path = self.gh_host + path
116127
if route:
117128
for k,v in route.items(): route[k] = quote(str(route[k]))
118-
return_json = ('json' in headers['Accept'])
129+
return_json = ('json' in headers['Accept']) and (decode is True)
119130
debug = self.debug if self.debug else print_summary if os.getenv('GHAPI_DEBUG') else None
120-
res,self.recv_hdrs = urlsend(path, verb, headers=headers or None, debug=debug, return_headers=True,
131+
res,self.recv_hdrs = urlsend(path, verb, headers=headers or None, decode=decode, debug=debug, return_headers=True,
121132
route=route or None, query=query or None, data=data or None, return_json=return_json, timeout=timeout)
122133
if 'X-RateLimit-Remaining' in self.recv_hdrs:
123134
newlim = self.recv_hdrs['X-RateLimit-Remaining']

settings.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ language = English
1616
custom_sidebar = False
1717
license = apache2
1818
status = 5
19-
requirements = fastcore>=1.7.0
19+
requirements = fastcore>=1.7.2
2020
dev_requirements = jsonref matplotlib
2121
console_scripts = ghapi=ghapi.cli:ghapi
2222
ghpath=ghapi.cli:ghpath

0 commit comments

Comments
 (0)