Skip to content

Commit 313922c

Browse files
committed
dep
1 parent 5a1178d commit 313922c

File tree

2 files changed

+1
-199
lines changed

2 files changed

+1
-199
lines changed

00_core.ipynb

Lines changed: 0 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,204 +1339,6 @@
13391339
"test_eq(rel.name, 'v0.0.1')"
13401340
]
13411341
},
1342-
{
1343-
"cell_type": "markdown",
1344-
"id": "c49a4347",
1345-
"metadata": {
1346-
"solveit_ai": true
1347-
},
1348-
"source": [
1349-
"```\n",
1350-
" File \"/Users/jhoward/aai-ws/ghapi/ghapi/core.py\", line 63, in __call__\n",
1351-
" return self.client(self.path, self.verb, headers=headers, route=route_p, query=query_p, data=data_p)\n",
1352-
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
1353-
" File \"/Users/jhoward/aai-ws/ghapi/ghapi/core.py\", line 121, in __call__\n",
1354-
" res,self.recv_hdrs = urlsend(path, verb, headers=headers or None, decode=False, debug=debug, return_headers=True,\n",
1355-
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
1356-
" File \"/Users/jhoward/aai-ws/fastcore/fastcore/net.py\", line 221, in urlsend\n",
1357-
" return urlread(req, decode=decode, return_json=return_json, return_headers=return_headers, timeout=timeout)\n",
1358-
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
1359-
" File \"/Users/jhoward/aai-ws/fastcore/fastcore/net.py\", line 120, in urlread\n",
1360-
" with urlopen(url, data=data, headers=headers, timeout=timeout, **kwargs) as u: res,hdrs = u.read(),u.headers\n",
1361-
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
1362-
" File \"/Users/jhoward/aai-ws/fastcore/fastcore/net.py\", line 111, in urlopen\n",
1363-
" try: return urlopener().open(urlwrap(url, data=data, headers=headers), timeout=timeout)\n",
1364-
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
1365-
" File \"/Users/jhoward/.local/share/uv/python/cpython-3.12.0-macos-aarch64-none/lib/python3.12/urllib/request.py\", line 521, in open\n",
1366-
" response = meth(req, response)\n",
1367-
" ^^^^^^^^^^^^^^^^^^^\n",
1368-
" File \"/Users/jhoward/.local/share/uv/python/cpython-3.12.0-macos-aarch64-none/lib/python3.12/urllib/request.py\", line 630, in http_response\n",
1369-
" response = self.parent.error(\n",
1370-
" ^^^^^^^^^^^^^^^^^^\n",
1371-
" File \"/Users/jhoward/.local/share/uv/python/cpython-3.12.0-macos-aarch64-none/lib/python3.12/urllib/request.py\", line 553, in error\n",
1372-
" result = self._call_chain(*args)\n",
1373-
" ^^^^^^^^^^^^^^^^^^^^^^^\n",
1374-
" File \"/Users/jhoward/.local/share/uv/python/cpython-3.12.0-macos-aarch64-none/lib/python3.12/urllib/request.py\", line 492, in _call_chain\n",
1375-
" result = func(*args)\n",
1376-
" ^^^^^^^^^^^\n",
1377-
" File \"/Users/jhoward/.local/share/uv/python/cpython-3.12.0-macos-aarch64-none/lib/python3.12/urllib/request.py\", line 724, in http_error_302\n",
1378-
" new = self.redirect_request(req, fp, code, msg, headers, newurl)\n",
1379-
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
1380-
" File \"/Users/jhoward/.local/share/uv/python/cpython-3.12.0-macos-aarch64-none/lib/python3.12/urllib/request.py\", line 662, in redirect_request\n",
1381-
" raise HTTPError(req.full_url, code, msg, headers, fp)\n",
1382-
"urllib.error.HTTPError: HTTP Error 307: Temporary Redirect\n",
1383-
"====Error Body====\n",
1384-
"{\n",
1385-
" \"message\": \"Moved Permanently\",\n",
1386-
" \"url\": \"https://api.github.com/repositories/314706418/releases\",\n",
1387-
" \"documentation_url\": \"https://docs.github.com/rest/guides/best-practices-for-using-the-rest-api#follow-redirects\"\n",
1388-
"}\n",
1389-
"```\n",
1390-
"Sometimes I get this error. Can you look through fastcore to see what we need to change there or here to enable redirects? I think we should have them on by default.\n",
1391-
"\n",
1392-
"##### 🤖Reply🤖<!-- SOLVEIT_SEPARATOR_7f3a9b2c -->\n",
1393-
"\n",
1394-
"🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠\n",
1395-
"\n",
1396-
"Let me look at the fastcore networking code to understand how redirects are handled:\n",
1397-
"<details class='token-usage-details'><summary>Cache hit: 0.0% | Tokens: total=55,463 input=55,250 (+0 cached, 43,847 new) output=213</summary>\n",
1398-
"\n",
1399-
"`Usage(completion_tokens=213, prompt_tokens=55250, total_tokens=55463, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=213, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=0, text_tokens=None, image_tokens=None, cache_creation_tokens=43847), cache_creation_input_tokens=43847, cache_read_input_tokens=0)`\n",
1400-
"\n",
1401-
"</details>\n",
1402-
"\n",
1403-
"\n",
1404-
"<details class='tool-usage-details'>\n",
1405-
"<summary>view(path='/Users/jhoward/aai-ws/fastcore/fastcore/net.py', nums=True)</summary>\n",
1406-
"\n",
1407-
"```json\n",
1408-
"{\n",
1409-
" \"id\": \"toolu_017R1tVfTRvatQ3c6ECSeyXV\",\n",
1410-
" \"call\": {\n",
1411-
" \"function\": \"view\",\n",
1412-
" \"arguments\": {\n",
1413-
" \"path\": \"/Users/jhoward/aai-ws/fastcore/fastcore/net.py\",\n",
1414-
" \"nums\": \"True\"\n",
1415-
" }\n",
1416-
" },\n",
1417-
" \"result\": \"<TRUNCATED>\\u2026work, HTTP, and URL functions\\\"\\\"\\\"\\n 2 \\u2502 \\n 3 \\u2502 # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/03b_net.ipynb.\\n 4 \\u2502 \\n 5 \\u2502 # %% auto #0\\n 6 \\u2502 __all__ = ['url_default_headers', 'ExceptionsHTTP', 'urlquote', 'urlwrap', 'HTTP4xxClientError', 'HTTP5xxServerError',\\n 7 \\u2502 'urlopener', 'urlopen', 'urlread', 'urljson', 'urlcheck', 'urlclean', 'urlretrieve', 'urldest', 'urlsave',\\n 8 \\u2502 'urlvalid', 'urlrequest', 'urlsend', 'do_request', 'start_server', 'start_client', 'tobytes',\\n 9 \\u2502 'http_response', 'recv_once', 'HTTP400BadRequestError', 'HTTP401UnauthorizedError',\\n 10 \\u2502 'HTTP402PaymentRequiredError', 'HTTP403ForbiddenError', 'HTTP404NotFoundError',\\n 11 \\u2502 'HTTP405MethodNotAllowedError', 'HTTP406NotAcceptableError', 'HTTP407ProxyAuthRequiredError',\\n 12 \\u2502 'HTTP408RequestTimeoutError', 'HTTP409ConflictError', 'HTTP410GoneError', 'HTTP411LengthRequiredError',\\n 13 \\u2502 \\u2026</TRUNCATED>\"\n",
1418-
"}\n",
1419-
"```\n",
1420-
"\n",
1421-
"</details>\n",
1422-
"\n",
1423-
"\n",
1424-
"\n",
1425-
"🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠\n",
1426-
"\n",
1427-
"I can see the issue. On line 77-82, `_SafeRedirectHandler.redirect_request` calls `super()`, which is Python's stdlib `HTTPRedirectHandler`. The stdlib raises `HTTPError` for 307/308 with anything other than GET/HEAD — it doesn't preserve the method and body as 307/308 require.\n",
1428-
"\n",
1429-
"The fix is to handle 307/308 directly in `_SafeRedirectHandler` before calling `super()`. Let me check the source notebook:\n",
1430-
"<details class='token-usage-details'><summary>Cache hit: 70.9% | Tokens: total=64,472 input=61,812 (+43,847 cached, 11,400 new) output=2,660</summary>\n",
1431-
"\n",
1432-
"`Usage(completion_tokens=2660, prompt_tokens=61812, total_tokens=64472, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=2660, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=43847, text_tokens=None, image_tokens=None, cache_creation_tokens=11400), cache_creation_input_tokens=11400, cache_read_input_tokens=43847)`\n",
1433-
"\n",
1434-
"</details>\n",
1435-
"\n",
1436-
"\n",
1437-
"<details class='tool-usage-details'>\n",
1438-
"<summary>find_msgs(dname='/aai-ws/fastcore/nbs/03b_net', re_pattern='_SafeRedirectHandler')</summary>\n",
1439-
"\n",
1440-
"```json\n",
1441-
"{\n",
1442-
" \"id\": \"toolu_017jfsewPEFWG6nirtVVzw2V\",\n",
1443-
" \"call\": {\n",
1444-
" \"function\": \"find_msgs\",\n",
1445-
" \"arguments\": {\n",
1446-
" \"dname\": \"/aai-ws/fastcore/nbs/03b_net\",\n",
1447-
" \"re_pattern\": \"_SafeRedirectHandler\"\n",
1448-
" }\n",
1449-
" },\n",
1450-
" \"result\": \"[{'id': '_128b5f4a', 'time_run': '', 'is_exported': 1, 'skipped': 0, 'hidden': False, 'bookmark': {}, 'i_collapsed': 0, 'o_collapsed': 0, 'heading_collapsed': False, 'i_clamp': False, 'o_clamp': False, 'pinned': 0, 'run': {}, 'content': \\\"class _SafeRedirectHandler(urllib.request.HTTPRedirectHandler):\\\\n def redirect_request(self, req, fp, code, msg, headers, newurl):\\\\n new_req = super().redirect_request(req, fp, code, msg, headers, newurl)\\\\n if new_req and urlparse(newurl).netloc != urlparse(req.full_url).netloc:\\\\n new_req.remove_header('Authorization')\\\\n return new_req\\\\n\\\\ndef urlopener():\\\\n _opener = urllib.request.build_opener(_SafeRedirectHandler)\\\\n _opener.addheaders = list(url_default_headers.items())\\\\n return _opener\\\", 'output': '', 'msg_type': 'code', 'input_tokens': 211, 'output_tokens': {}}]\"\n",
1451-
"}\n",
1452-
"```\n",
1453-
"\n",
1454-
"</details>\n",
1455-
"\n",
1456-
"\n",
1457-
"<details class='token-usage-details'><summary>Cache hit: 85.3% | Tokens: total=64,851 input=64,759 (+55,247 cached, 215 new) output=92</summary>\n",
1458-
"\n",
1459-
"`Usage(completion_tokens=92, prompt_tokens=64759, total_tokens=64851, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=92, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=55247, text_tokens=None, image_tokens=None, cache_creation_tokens=215), cache_creation_input_tokens=215, cache_read_input_tokens=55247)`\n",
1460-
"\n",
1461-
"</details>\n",
1462-
"\n",
1463-
"\n",
1464-
"<details class='tool-usage-details'>\n",
1465-
"<summary>view_msg(id='_128b5f4a', dname='/aai-ws/fastcore/nbs/03b_net')</summary>\n",
1466-
"\n",
1467-
"```json\n",
1468-
"{\n",
1469-
" \"id\": \"toolu_0176XwAtruq7tFxjbvwS62S7\",\n",
1470-
" \"call\": {\n",
1471-
" \"function\": \"view_msg\",\n",
1472-
" \"arguments\": {\n",
1473-
" \"id\": \"_128b5f4a\",\n",
1474-
" \"dname\": \"/aai-ws/fastcore/nbs/03b_net\"\n",
1475-
" }\n",
1476-
" },\n",
1477-
" \"result\": \"1 \\u2502 class _SafeRedirectHandler(urllib.request.HTTPRedirectHandler):\\n 2 \\u2502 def redirect_request(self, req, fp, code, msg, headers, newurl):\\n 3 \\u2502 new_req = super().redirect_request(req, fp, code, msg, headers, newurl)\\n 4 \\u2502 if new_req and urlparse(newurl).netloc != urlparse(req.full_url).netloc:\\n 5 \\u2502 new_req.remove_header('Authorization')\\n 6 \\u2502 return new_req\\n 7 \\u2502 \\n 8 \\u2502 def urlopener():\\n 9 \\u2502 _opener = urllib.request.build_opener(_SafeRedirectHandler)\\n 10 \\u2502 _opener.addheaders = list(url_default_headers.items())\\n 11 \\u2502 return _opener\"\n",
1478-
"}\n",
1479-
"```\n",
1480-
"\n",
1481-
"</details>\n",
1482-
"\n",
1483-
"The problem: `super().redirect_request()` on line 3 calls Python's `HTTPRedirectHandler.redirect_request`, which raises `HTTPError` for 307/308 with any method other than GET/HEAD. Per the HTTP spec, 307/308 must preserve the original method and body — so we need to handle them before calling `super()`.\n",
1484-
"\n",
1485-
"Here's the fix:\n",
1486-
"\n",
1487-
"```python\n",
1488-
"class _SafeRedirectHandler(urllib.request.HTTPRedirectHandler):\n",
1489-
" def redirect_request(self, req, fp, code, msg, headers, newurl):\n",
1490-
" if code in (307, 308):\n",
1491-
" new_req = Request(newurl, data=req.data, method=req.get_method())\n",
1492-
" for k,v in req.headers.items(): new_req.add_header(k, v)\n",
1493-
" else:\n",
1494-
" new_req = super().redirect_request(req, fp, code, msg, headers, newurl)\n",
1495-
" if new_req and urlparse(newurl).netloc != urlparse(req.full_url).netloc:\n",
1496-
" new_req.remove_header('Authorization')\n",
1497-
" return new_req\n",
1498-
"```\n",
1499-
"\n",
1500-
"The key changes:\n",
1501-
"- For **307/308**: build a new `Request` manually, preserving `req.data` and `req.get_method()` (plus headers) — this is what the spec requires\n",
1502-
"- For **301/302/303**: fall through to `super()` as before (these convert to GET, which is the standard behavior)\n",
1503-
"- The **Authorization stripping** on cross-origin redirects still applies to both paths\n",
1504-
"<details class='token-usage-details'><summary>Cache hit: 85.2% | Tokens: total=65,439 input=65,071 (+55,462 cached, 8,974 new) output=368</summary>\n",
1505-
"\n",
1506-
"`Usage(completion_tokens=368, prompt_tokens=65071, total_tokens=65439, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=368, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=55462, text_tokens=None, image_tokens=None, cache_creation_tokens=8974), cache_creation_input_tokens=8974, cache_read_input_tokens=55462)`\n",
1507-
"\n",
1508-
"</details>\n"
1509-
]
1510-
},
1511-
{
1512-
"cell_type": "markdown",
1513-
"id": "a43c002e",
1514-
"metadata": {
1515-
"solveit_ai": true
1516-
},
1517-
"source": [
1518-
"We can't use `super().redirect_request` for 307/8 and pass in the data/method?\n",
1519-
"\n",
1520-
"##### 🤖Reply🤖<!-- SOLVEIT_SEPARATOR_7f3a9b2c -->\n",
1521-
"\n",
1522-
"🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠\n",
1523-
"\n",
1524-
"No — the stdlib's `HTTPRedirectHandler.redirect_request` explicitly raises `HTTPError` for 307/308 when the method isn't GET or HEAD:\n",
1525-
"\n",
1526-
"```python\n",
1527-
"if (not (code in (301, 302, 303, 307, 308) and m in (\"GET\", \"HEAD\")\n",
1528-
" or code in (301, 302, 303) and m == \"POST\")):\n",
1529-
" raise HTTPError(req.full_url, code, msg, headers, fp)\n",
1530-
"```\n",
1531-
"\n",
1532-
"So for a POST to a GitHub endpoint that returns 307, `super()` will always raise before it gets a chance to build the redirect request. That's why we have to handle 307/308 ourselves before calling `super()`.\n",
1533-
"<details class='token-usage-details'><summary>Cache hit: 73.2% | Tokens: total=57,585 input=57,043 (+41,777 cached, 15,058 new) output=542</summary>\n",
1534-
"\n",
1535-
"`Usage(completion_tokens=542, prompt_tokens=57043, total_tokens=57585, completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=0, rejected_prediction_tokens=None, text_tokens=542, image_tokens=None), prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=None, cached_tokens=41777, text_tokens=None, image_tokens=None, cache_creation_tokens=15058), cache_creation_input_tokens=15058, cache_read_input_tokens=41777)`\n",
1536-
"\n",
1537-
"</details>\n"
1538-
]
1539-
},
15401342
{
15411343
"cell_type": "code",
15421344
"execution_count": null,

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ license = {text = "Apache-2.0"}
1212
authors = [{name = "Jeremy Howard", email = "info@fast.ai"}]
1313
keywords = ['github', 'api']
1414
classifiers = ["Natural Language :: English", "Intended Audience :: Developers", "Development Status :: 5 - Production/Stable", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only"]
15-
dependencies = ['fastcore>=1.7.2']
15+
dependencies = ['fastcore>=1.12.19']
1616

1717
[project.urls]
1818
Repository = "https://github.com/fastai/ghapi"

0 commit comments

Comments
 (0)