Skip to content
Snippets Groups Projects
Commit e10b2425 authored by Moul's avatar Moul
Browse files

Fix API.request_url() with Py3.13 (#208)

Since Python 3.13, the IOBase finalizer now logs
any errors raised by the close() method
https://docs.python.org/3/whatsnew/3.13.html#io

According to
https://docs.python.org/3.13/library/io.html#io.IOBase.close
if the file (descriptor) is accessed after being close,
`ValueError` is raised.

The `fd` is copied with `copy.copy()`.
Both gets automatically closed once the function ends,
the second can’t close a second time the same fd,
that’s why we get this error.

Reorganise complex function: don’t use `with` since the function
is designed to allow returning the fd
Drop `copy.copy()` used previously to return the fd, causing the issue
Cleanly separate three return types and function’s returns
parent 58540690
No related branches found
No related tags found
1 merge request!199Fix API.request_url() with Py3.13 (#208)
Pipeline #39250 waiting for manual action
...@@ -13,10 +13,8 @@ ...@@ -13,10 +13,8 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import copy
import json import json
import logging import logging
from http.client import HTTPResponse
from typing import Any, Callable, Dict, Optional, Union from typing import Any, Callable, Dict, Optional, Union
from urllib import parse, request from urllib import parse, request
...@@ -252,39 +250,32 @@ class API: ...@@ -252,39 +250,32 @@ class API:
self.connection_handler.proxy, self.connection_handler.http_scheme self.connection_handler.proxy, self.connection_handler.http_scheme
) )
with request.urlopen( response = request.urlopen( # pylint: disable=consider-using-with
duniter_request, timeout=15 duniter_request, timeout=15
) as response: # type: HTTPResponse )
if response.status != 200: if response.status != 200:
content = response.read().decode("utf-8")
if bma_errors:
try:
error_data = parse_error(content)
raise DuniterError(error_data)
except (TypeError, jsonschema.ValidationError) as exception:
raise ValueError(
f"status code != 200 => {response.status} ({content})"
) from exception
raise ValueError(f"status code != 200 => {response.status} ({content})")
# get response content
return_response = copy.copy(response)
content = response.read().decode("utf-8") content = response.read().decode("utf-8")
if bma_errors:
# if schema supplied... try:
if schema is not None: error_data = parse_error(content)
raise DuniterError(error_data)
except (TypeError, jsonschema.ValidationError) as exception:
raise ValueError(
f"status code != 200 => {response.status} ({content})"
) from exception
raise ValueError(f"status code != 200 => {response.status} ({content})")
if rtype == RESPONSE_HTTP:
return response
# if schema supplied…
content = response.read().decode("utf-8")
if schema:
# validate response # validate response
parse_response(content, schema) parse_response(content, schema)
# return the chosen type
result = return_response # type: Any
if rtype == RESPONSE_TEXT: if rtype == RESPONSE_TEXT:
result = content return content
elif rtype == RESPONSE_JSON: return json.loads(content)
result = json.loads(content)
return result
def connect_ws(self, path: str) -> WSConnection: def connect_ws(self, path: str) -> WSConnection:
""" """
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment