Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/pptx/opc/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from __future__ import annotations

import collections
import os
from typing import IO, TYPE_CHECKING, DefaultDict, Iterator, Mapping, Set, cast

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

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

# --- otherwise, assume `file` is a file-like object
Expand Down
7 changes: 4 additions & 3 deletions src/pptx/parts/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,12 @@ def from_file(cls, image_file: str | IO[bytes]) -> Image:

`image_file` can be either a path (str) or a file-like object.
"""
if isinstance(image_file, str):
if isinstance(image_file, (str, os.PathLike)):
# treat image_file as a path
with open(image_file, "rb") as f:
image_path = os.fspath(image_file)
with open(image_path, "rb") as f:
blob = f.read()
filename = os.path.basename(image_file)
filename = os.path.basename(image_path)
else:
# assume image_file is a file-like object
# ---reposition file cursor if it has one---
Expand Down
9 changes: 9 additions & 0 deletions tests/opc/test_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import collections
import io
import itertools
from pathlib import Path
from typing import Any

import pytest
Expand Down Expand Up @@ -431,6 +432,14 @@ def it_can_load_a_blob_from_a_file_path_to_help(self):

assert part._blob_from_file(path) == file_bytes

def it_can_load_a_blob_from_a_pathlike_to_help(self):
path = Path(absjoin(test_file_dir, "minimal.pptx"))
with open(path, "rb") as f:
file_bytes = f.read()
part = Part(None, None, None, None)

assert part._blob_from_file(path) == file_bytes

def it_can_load_a_blob_from_a_file_like_object_to_help(self):
part = Part(None, None, None, None)
assert part._blob_from_file(io.BytesIO(b"012345")) == b"012345"
Expand Down
11 changes: 11 additions & 0 deletions tests/parts/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import io
from pathlib import Path

import pytest

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

def it_can_construct_from_a_pathlike(self, from_blob_, image_):
with open(test_image_path, "rb") as f:
blob = f.read()
from_blob_.return_value = image_

image = Image.from_file(Path(test_image_path))

Image.from_blob.assert_called_once_with(blob, "python-icon.jpeg")
assert image is image_

def it_can_construct_from_a_stream(self, from_stream_fixture):
image_file, blob, image_ = from_stream_fixture
image = Image.from_file(image_file)
Expand Down