diff --git a/pyairtable/api/table.py b/pyairtable/api/table.py index ceba27b6..e9faafcf 100644 --- a/pyairtable/api/table.py +++ b/pyairtable/api/table.py @@ -5,17 +5,7 @@ import urllib.parse import warnings from pathlib import Path -from typing import ( - Any, - BinaryIO, - Dict, - Iterable, - Iterator, - List, - Optional, - Union, - overload, -) +from typing import Any, Dict, Iterable, Iterator, List, Optional, Union, overload import pyairtable.models from pyairtable.api.retrying import Retry @@ -711,7 +701,7 @@ def upload_attachment( record_id: RecordId, field: str, filename: Union[str, Path], - content: Optional[bytes] = None, + content: Optional[Union[str, bytes]] = None, content_type: Optional[str] = None, ) -> UploadAttachmentResultDict: """ """ @@ -730,6 +720,7 @@ def upload_attachment( # TODO: figure out how to handle the atypical subdomain in a more graceful fashion url = f"https://content.airtable.com/v0/{self.base.id}/{record_id}/{field}/uploadAttachment" + content = content.encode() if isinstance(content, str) else content payload = { "contentType": content_type, "filename": filename, diff --git a/pyairtable/orm/lists.py b/pyairtable/orm/lists.py index 97623b39..701da08a 100644 --- a/pyairtable/orm/lists.py +++ b/pyairtable/orm/lists.py @@ -101,7 +101,7 @@ class AttachmentsList(ChangeTrackingList[AttachmentDict]): def upload( self, filename: Union[str, Path], - content: Optional[bytes] = None, + content: Optional[Union[str, bytes]] = None, content_type: Optional[str] = None, ) -> None: """ diff --git a/tests/test_api_table.py b/tests/test_api_table.py index ef9f5dca..4a4f9d4f 100644 --- a/tests/test_api_table.py +++ b/tests/test_api_table.py @@ -618,11 +618,12 @@ def mock_upload_attachment(requests_mock, table): ) -def test_upload_attachment(mock_upload_attachment, table): +@pytest.mark.parametrize("content", [b"Hello, World!", "Hello, World!"]) +def test_upload_attachment(mock_upload_attachment, table, content): """ Test that we can upload an attachment to a record. """ - table.upload_attachment(RECORD_ID, FIELD_ID, "sample.txt", b"Hello, World!") + table.upload_attachment(RECORD_ID, FIELD_ID, "sample.txt", content) assert mock_upload_attachment.last_request.json() == { "contentType": "text/plain", "file": "SGVsbG8sIFdvcmxkIQ==\n", # base64 encoded "Hello, World!" @@ -630,7 +631,7 @@ def test_upload_attachment(mock_upload_attachment, table): } -def test_upload_attachment__no_content(mock_upload_attachment, table, tmp_path): +def test_upload_attachment__no_content_type(mock_upload_attachment, table, tmp_path): """ Test that we can upload an attachment to a record. """ diff --git a/tests/test_orm_lists.py b/tests/test_orm_lists.py index 244df3bc..8d867761 100644 --- a/tests/test_orm_lists.py +++ b/tests/test_orm_lists.py @@ -37,21 +37,23 @@ def mock_upload(): yield m -def test_attachment_upload(mock_upload, tmp_path): +@pytest.mark.parametrize("content", [b"Hello, world!", "Hello, world!"]) +def test_attachment_upload(mock_upload, tmp_path, content): """ Test that we can add an attachment to a record. """ - tmp_file = tmp_path / "a.txt" - tmp_file.write_text("Hello, world!") + fp = tmp_path / "a.txt" + writer = fp.write_text if isinstance(content, str) else fp.write_bytes + writer(content) record = fake_record() instance = Fake.from_record(record) - instance.attachments.upload(tmp_file) + instance.attachments.upload(fp) mock_upload.assert_called_once_with( record["id"], "Files", - filename=tmp_file, + filename=fp, content=None, content_type=None, )