What's happening
Uploads.upload() (actionkit/uploads.py:19-34):
def upload(self, file_name, import_page):
m = MultipartEncoderMonitor.from_fields(
fields={
'page': import_page,
'upload': (file_name, open(file_name, 'rb'), 'text/csv'),
'autocreate_user_fields': 'false',
},
callback=progressing,
)
upload_url = self.post(data=m, headers={'Content-Type': m.content_type})
upload = self.poll(upload_url)
while not upload['is_completed']:
upload = self.poll(upload_url)
time.sleep(1)
Two issues:
- No timeout / max-attempt count on the polling loop. If ActionKit's import job never completes (or the
is_completed field is never present/true for some reason), this hangs the calling process forever.
- The file handle from
open(file_name, 'rb') is never explicitly closed. It's only passed into MultipartEncoderMonitor.from_fields, which doesn't take ownership/close it after the request completes.
Suggested fix direction
- Add a max-attempt count or overall timeout to the polling loop, raising a clear exception (or returning a "still pending" result) if exceeded.
- Use a
with open(file_name, 'rb') as fh: block (or otherwise ensure the handle is closed) around the request.
Test coverage
Pinned (not fixed) by tests/test_uploads.py::test_upload_polls_until_is_completed, which documents the unbounded-loop behavior explicitly in its docstring — it only terminates because the mocked poll() sequence is finite. Should be revisited once a timeout/max-attempts is added.
What's happening
Uploads.upload()(actionkit/uploads.py:19-34):Two issues:
is_completedfield is never present/true for some reason), this hangs the calling process forever.open(file_name, 'rb')is never explicitly closed. It's only passed intoMultipartEncoderMonitor.from_fields, which doesn't take ownership/close it after the request completes.Suggested fix direction
with open(file_name, 'rb') as fh:block (or otherwise ensure the handle is closed) around the request.Test coverage
Pinned (not fixed) by
tests/test_uploads.py::test_upload_polls_until_is_completed, which documents the unbounded-loop behavior explicitly in its docstring — it only terminates because the mockedpoll()sequence is finite. Should be revisited once a timeout/max-attempts is added.