Skip to content

Commit fc89634

Browse files
Support PathLike file inputs
1 parent 278b47b commit fc89634

4 files changed

Lines changed: 30 additions & 8 deletions

File tree

src/pptx/opc/package.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from __future__ import annotations
88

99
import collections
10+
import os
1011
from typing import IO, TYPE_CHECKING, DefaultDict, Iterator, Mapping, Set, cast
1112

1213
from pptx.opc.constants import RELATIONSHIP_TARGET_MODE as RTM
@@ -360,11 +361,11 @@ def rels(self) -> _Relationships:
360361
# --- this must be public to allow the part graph to be traversed ---
361362
return self._rels
362363

363-
def _blob_from_file(self, file: str | IO[bytes]) -> bytes:
364-
"""Return bytes of `file`, which is either a str path or a file-like object."""
365-
# --- a str `file` is assumed to be a path ---
366-
if isinstance(file, str):
367-
with open(file, "rb") as f:
364+
def _blob_from_file(self, file: str | os.PathLike[str] | IO[bytes]) -> bytes:
365+
"""Return bytes of `file`, which is either a path or a file-like object."""
366+
# --- a path-like `file` is assumed to be a path ---
367+
if isinstance(file, (str, os.PathLike)):
368+
with open(os.fspath(file), "rb") as f:
368369
return f.read()
369370

370371
# --- otherwise, assume `file` is a file-like object

src/pptx/parts/image.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,12 @@ def from_file(cls, image_file: str | IO[bytes]) -> Image:
158158
159159
`image_file` can be either a path (str) or a file-like object.
160160
"""
161-
if isinstance(image_file, str):
161+
if isinstance(image_file, (str, os.PathLike)):
162162
# treat image_file as a path
163-
with open(image_file, "rb") as f:
163+
image_path = os.fspath(image_file)
164+
with open(image_path, "rb") as f:
164165
blob = f.read()
165-
filename = os.path.basename(image_file)
166+
filename = os.path.basename(image_path)
166167
else:
167168
# assume image_file is a file-like object
168169
# ---reposition file cursor if it has one---

tests/opc/test_package.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import collections
88
import io
99
import itertools
10+
from pathlib import Path
1011
from typing import Any
1112

1213
import pytest
@@ -431,6 +432,14 @@ def it_can_load_a_blob_from_a_file_path_to_help(self):
431432

432433
assert part._blob_from_file(path) == file_bytes
433434

435+
def it_can_load_a_blob_from_a_pathlike_to_help(self):
436+
path = Path(absjoin(test_file_dir, "minimal.pptx"))
437+
with open(path, "rb") as f:
438+
file_bytes = f.read()
439+
part = Part(None, None, None, None)
440+
441+
assert part._blob_from_file(path) == file_bytes
442+
434443
def it_can_load_a_blob_from_a_file_like_object_to_help(self):
435444
part = Part(None, None, None, None)
436445
assert part._blob_from_file(io.BytesIO(b"012345")) == b"012345"

tests/parts/test_image.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import io
6+
from pathlib import Path
67

78
import pytest
89

@@ -101,6 +102,16 @@ def it_can_construct_from_a_path(self, from_blob_, image_):
101102
Image.from_blob.assert_called_once_with(blob, "python-icon.jpeg")
102103
assert image is image_
103104

105+
def it_can_construct_from_a_pathlike(self, from_blob_, image_):
106+
with open(test_image_path, "rb") as f:
107+
blob = f.read()
108+
from_blob_.return_value = image_
109+
110+
image = Image.from_file(Path(test_image_path))
111+
112+
Image.from_blob.assert_called_once_with(blob, "python-icon.jpeg")
113+
assert image is image_
114+
104115
def it_can_construct_from_a_stream(self, from_stream_fixture):
105116
image_file, blob, image_ = from_stream_fixture
106117
image = Image.from_file(image_file)

0 commit comments

Comments
 (0)