Skip to content

Commit 3e0ed87

Browse files
committed
fix: fix: replace deprecated stdlib cgi usage with stdlib email
A commonly recommended replacement for the deprecated cgi package in PEP594. Using an email Message object to parse an HTTP header is a little hacky, but since HTTP's Content-Disposition syntax was derived from MIME, they're compatible enough in practice
1 parent f75370f commit 3e0ed87

2 files changed

Lines changed: 21 additions & 3 deletions

File tree

qfieldcloud_sdk/sdk.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
import sys
66
import requests
77
import urllib3
8-
import cgi
98

109
from enum import Enum
1110
from pathlib import Path
1211
from typing import Any, Callable, Dict, List, Optional, TypedDict, Union, cast
1312
from urllib import parse as urlparse
13+
from email.message import Message
1414

1515
from requests.adapters import HTTPAdapter, Retry
1616
from requests_toolbelt.multipart.encoder import MultipartEncoderMonitor
@@ -456,8 +456,7 @@ def get_project_seed_xlsform(
456456

457457
return None
458458

459-
_value, params = cgi.parse_header(content_disposition)
460-
filename = params.get("filename")
459+
filename = self._get_filename_from_content_disposition(content_disposition)
461460

462461
if not filename:
463462
logger.warning(
@@ -471,6 +470,14 @@ def get_project_seed_xlsform(
471470

472471
return str(path)
473472

473+
@staticmethod
474+
def _get_filename_from_content_disposition(
475+
content_disposition: str,
476+
) -> Optional[str]:
477+
message = Message()
478+
message["Content-Disposition"] = content_disposition
479+
return cast(Optional[str], message.get_filename())
480+
474481
def list_remote_files(
475482
self, project_id: str, skip_metadata: bool = True
476483
) -> List[Dict[str, Any]]:

tests/test_cli_client.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ def test_paginated_list_projects_include_public(self):
2121
)
2222
self.assertTrue(0 < len(results) and len(results) <= 50)
2323

24+
def test_parse_content_disposition_filename(self):
25+
filename = Client._get_filename_from_content_disposition(
26+
'attachment; filename="seed.xlsx"'
27+
)
28+
self.assertEqual(filename, "seed.xlsx")
29+
30+
encoded_filename = Client._get_filename_from_content_disposition(
31+
"attachment; filename*=UTF-8''my%20seed.xlsx"
32+
)
33+
self.assertEqual(encoded_filename, "my seed.xlsx")
34+
2435

2536
class TestCLI(unittest.TestCase):
2637
@classmethod

0 commit comments

Comments
 (0)