Skip to content

fix: allow uploading a file from an in-memory stream - #148

Open
LukasGold wants to merge 1 commit into
mainfrom
fix/in-memory-file-upload
Open

fix: allow uploading a file from an in-memory stream#148
LukasGold wants to merge 1 commit into
mainfrom
fix/in-memory-file-upload

Conversation

@LukasGold

Copy link
Copy Markdown
Contributor

Closes #140.

Uploading from an open file-like object was documented but never worked. Two
independent bugs, both on the path from osw_upload_file(source=<stream>) to
InMemoryController.

Changes

  • src/osw/express.py: replace the isinstance(source, IO) branch with a
    duck-typed hasattr(source, "read") check. typing.IO is not
    runtime-checkable, so that isinstance was always False and a stream source
    silently fell through to the path-handling branch.
  • src/osw/controller/file/memory.py: drop the __init__ that assigned
    self.stream = StringIO() before super().__init__(**kwargs). It ran before
    the pydantic model was initialised and overwrote any caller-supplied stream.
  • Default the stream to BytesIO via Field(default_factory=BytesIO), matching
    the byte-oriented get/put (shutil.copyfileobj). StringIO was a
    text/bytes mismatch with both.
  • Declare IO in the OswExpress.upload_file / osw_upload_file signatures and
    docstrings, which previously accepted only str and Path.

Verification

Both bugs were reproduced against the unfixed code before fixing: the
isinstance bug fails 1 of the new tests, the __init__ bug fails 4.

tests/test_in_memory_upload.py adds 5 tests covering a caller-supplied stream
surviving construction, the empty-buffer default, put copying into the stream,
a stream being wrapped in an InMemoryController on upload, and a non-file-like
source being rejected.

Full unit suite: 282 passed, 1 skipped.

@SimonStier for review.

Closes #140.

- express.py: replace the unreachable isinstance(source, IO) check with a
  duck-typed one; typing.IO is not runtime-checkable
- InMemoryController: drop the __init__ that assigned stream before the
  model was initialised and overwrote a caller-supplied stream
- default the stream to BytesIO, matching the byte-oriented get/put
- declare IO in the upload_file / osw_upload_file signatures
@github-actions

Copy link
Copy Markdown
Contributor

Release preview

Merging this PR would release v2.0.3 (current: v2.0.2).

Changelog preview (truncated)
## v2.0.3 (2026-08-28)

### Bug Fixes

- Allow uploading a file from an in-memory stream
  ([`64d99d6`](https://github.com/OpenSemanticLab/osw-python/commit/64d99d66ea45bc56bdaa6d12f2502df0bbbaf88c))

### Testing

- Rename oold.py to oold_test.py so its tests are collected
  ([`20072a9`](https://github.com/OpenSemanticLab/osw-python/commit/20072a9249cd97126a222c62a70f84e0433343ef))

Preview via python-semantic-release and conventional commits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Uploading a file from an in-memory stream is impossible: both code paths are broken

1 participant