kdschlosser: question.... am I able to use this when calling image.open

```python
image.open(b'\x65\x32\x86\x54\xD2\xA1')
```

the byte data is nothing just for example.

or does it have to explicitly be.

```python
image.open(io.BytesIO(b'\x65\x32\x86\x54\xD2\xA1'))
```

radarhere: @kdschlosser the second one
```python
Image.open(io.BytesIO(b'\x65\x32\x86\x54\xD2\xA1'))
```

The first code sample can be used for a path -
```python
Image.open(b"Tests/images/hopper.jpg")
```
diff --git a/src/PIL/Image.py b/src/PIL/Image.py
index 41e9c9fe8..a636a2bbe 100644
--- a/src/PIL/Image.py
+++ b/src/PIL/Image.py
@@ -2690,10 +2690,17 @@ def open(fp, mode="r"):
     :exception FileNotFoundError: If the file cannot be found.
     :exception PIL.UnidentifiedImageError: If the image cannot be opened and
        identified.
+    :exception ValueError: If the ``mode`` is not "r", or if a ``StringIO``
+       instance is used for ``fp``.
     """
 
     if mode != "r":
         raise ValueError("bad mode %r" % mode)
+    elif isinstance(fp, io.StringIO):
+        raise ValueError(
+            "StringIO cannot be used to open an image. "
+            "Binary data must be used instead."
+        )
 
     exclusive_fp = False
     filename = ""

Loading image from bytes

### What did you do?
Convert string or bytes to PIL.Image

### What did you expect to happen?
have the PIL.Image instance returned

### What actually happened?
Got a Traceback.

### What are your OS, Python and Pillow versions?

* OS: Windows 7 x64
* Python: 2.7 and also 3.7
* Pillow: 6.1.0


OK so this is the skinny. when running python 2.7 everything works as expected.
when I run the same code using Python 3.7 I get the following Traceback

```python
 File "C:\Program Files\Python37\lib\site-packages\PIL\Image.py", line 2822, in open
    raise IOError("cannot identify image file %r" % (filename if filename else fp))
OSError: cannot identify image file <_io.BytesIO object at 0x0000000003465F68>
```


Here is the test code to use.

```python

import sys
from PIL import Image
from io import BytesIO

# PNG data
LEFT_THUMB = (
    '\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52\x00\x00'
    '\x00\x13\x00\x00\x00\x0B\x08\x06\x00\x00\x00\x9D\xD5\xB6\x3A\x00\x00\x01'
    '\x2E\x49\x44\x41\x54\x78\x9C\x95\xD2\x31\x6B\xC2\x40\x00\x05\xE0\x77\x10'
    '\x42\x09\x34\xD0\x29\x21\x82\xC9\x9C\x2E\x72\x4B\x87\x40\x50\xB9\xBF\x5B'
    '\x28\x35\xA1\xA4\x94\x76\x68\x1C\x1C\x74\xCD\x9A\xE8\x20\x0A\x12\xA5\x5A'
    '\xE4\x72\xC9\x75\x10\x6D\xDC\xCE\xF7\x03\x3E\xDE\x83\x47\xA4\x94\x68\x67'
    '\xB5\xD9\x4E\xBF\xBF\x3E\xE8\x78\x3C\x86\x6A\x3C\xCF\x43\x10\x04\x20\x6D'
    '\x6C\xB5\xD9\x4E\x93\xF8\x95\x5A\x96\x05\xC6\x98\x32\x56\x14\x05\x46\xA3'
    '\x11\xB4\x36\x14\xBD\x3C\xD3\x4E\xA7\x03\xC6\x18\x8E\xC7\x23\x9A\xA6\x51'
    '\xC2\x5C\xD7\x45\x9E\xE7\x27\xEC\x0C\x39\x8E\x03\xC6\x18\x0E\x87\x83\x32'
    '\x04\x00\xE7\x75\x1A\xE7\x7C\xF2\xF9\xFE\x46\x6D\xDB\x06\x63\x0C\xFB\xFD'
    '\x1E\x75\x5D\x2B\x43\x57\x58\xF9\xF3\xAB\xAD\xD7\x6B\x98\xA6\x09\x21\x04'
    '\x76\xBB\x1D\x84\x10\x37\x61\x86\x61\x9C\x30\x00\x70\x1C\x07\x49\x92\x80'
    '\x10\x82\x7E\xBF\x8F\xE5\x72\x79\x13\x78\x69\xF6\x70\x6F\x88\x5E\xAF\x37'
    '\x2B\xCB\x92\xC6\x71\x0C\x42\x08\xC2\x30\xC4\x7C\x3E\x57\x06\x2F\x98\xAE'
    '\xEB\x4F\xAE\xEB\x4E\x06\x83\xC1\x4C\x4A\x49\xA3\x28\x82\x94\x12\x61\x18'
    '\x2A\x37\x5B\x2C\x16\xE8\x76\xBB\xFF\x3F\xE3\x9C\x4F\x8A\xA2\xD0\xD2\x34'
    '\xA5\x59\x96\xA1\xAA\x2A\x65\xCC\xB2\x2C\x0C\x87\xC3\xEB\xD3\x9E\xC1\xAA'
    '\xAA\xEE\x38\xE7\x4A\x90\xAE\xEB\x00\x00\xDF\xF7\x1F\xFF\x00\x09\x7C\xA7'
    '\x93\xB1\xFB\xFA\x11\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82'
)

PY3 = sys.version_info[0] > 2

if PY3:
    stream = BytesIO(LEFT_THUMB.encode())
else:
    stream = BytesIO(LEFT_THUMB)

image = Image.open(stream).convert("RGBA")
stream.close()
image.show()
```

Now This is where it goes a little bit sideways. when running Python 2.7 if I use cStringIO.StringIO everything works as expected.. But if I use io.StringIO I get the traceback listed above. I think it has got something to do with the first 16 bytes but I am not 100% sure.

any help is appreciated.


diff --git a/Tests/test_image.py b/Tests/test_image.py
index 83da76b96..47e7420ef 100644
--- a/Tests/test_image.py
+++ b/Tests/test_image.py
@@ -1,3 +1,4 @@
+import io
 import os
 import shutil
 import tempfile
@@ -91,6 +92,9 @@ class TestImage(PillowTestCase):
     def test_bad_mode(self):
         self.assertRaises(ValueError, Image.open, "filename", "bad mode")
 
+    def test_stringio(self):
+        self.assertRaises(ValueError, Image.open, io.StringIO())
+
     def test_pathlib(self):
         from PIL.Image import Path
 

pip install -e .[dev]
parse_log_pytest
requirements.txt
apt-get update apt-get install -y gcc libjpeg-dev zlib1g-dev libtiff5-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev libharfbuzz-dev libfribidi-dev libxcb1-dev
3.9
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.16
babel==2.17.0
black==25.1.0
blessed==1.20.0
build==1.2.2.post1
certifi==2025.1.31
charset-normalizer==3.4.1
check-manifest==0.50
click==8.1.8
coverage==7.8.0
coveralls==4.0.1
docopt==0.6.2
docutils==0.21.2
exceptiongroup==1.2.2
idna==3.10
imagesize==1.4.1
importlib_metadata==8.6.1
iniconfig==2.1.0
jarn.viewdoc==2.7
Jinja2==3.1.6
MarkupSafe==3.0.2
mypy-extensions==1.0.0
olefile==0.47
packaging==24.2
pathspec==0.12.1
-e git+https://github.com/python-pillow/Pillow.git@bbaebe0d20d29328fb20d2e30fa612dcd6b1b87b#egg=Pillow
platformdirs==4.3.7
pluggy==1.5.0
pycodestyle==2.13.0
pyflakes==3.3.1
Pygments==2.19.1
pyproject_hooks==1.2.0
pyroma==4.2
pytest==8.3.5
pytest-cov==6.0.0
requests==2.32.3
six==1.17.0
snowballstemmer==2.2.0
Sphinx==7.4.7
sphinx-rtd-theme==3.0.2
sphinxcontrib-applehelp==2.0.0
sphinxcontrib-devhelp==2.0.0
sphinxcontrib-htmlhelp==2.1.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==2.0.0
sphinxcontrib-serializinghtml==2.0.0
tomli==2.2.1
trove-classifiers==2025.3.19.19
typing_extensions==4.13.0
urllib3==2.3.0
wcwidth==0.2.13
zipp==3.21.0

name: Pillow
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.16
      - babel==2.17.0
      - black==25.1.0
      - blessed==1.20.0
      - build==1.2.2.post1
      - certifi==2025.1.31
      - charset-normalizer==3.4.1
      - check-manifest==0.50
      - click==8.1.8
      - coverage==7.8.0
      - coveralls==4.0.1
      - docopt==0.6.2
      - docutils==0.21.2
      - exceptiongroup==1.2.2
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - jarn-viewdoc==2.7
      - jinja2==3.1.6
      - markupsafe==3.0.2
      - mypy-extensions==1.0.0
      - olefile==0.47
      - packaging==24.2
      - pathspec==0.12.1
      - platformdirs==4.3.7
      - pluggy==1.5.0
      - pycodestyle==2.13.0
      - pyflakes==3.3.1
      - pygments==2.19.1
      - pyproject-hooks==1.2.0
      - pyroma==4.2
      - pytest==8.3.5
      - pytest-cov==6.0.0
      - requests==2.32.3
      - six==1.17.0
      - snowballstemmer==2.2.0
      - sphinx==7.4.7
      - sphinx-rtd-theme==3.0.2
      - sphinxcontrib-applehelp==2.0.0
      - sphinxcontrib-devhelp==2.0.0
      - sphinxcontrib-htmlhelp==2.1.0
      - sphinxcontrib-jquery==4.1
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==2.0.0
      - sphinxcontrib-serializinghtml==2.0.0
      - tomli==2.2.1
      - trove-classifiers==2025.3.19.19
      - typing-extensions==4.13.0
      - urllib3==2.3.0
      - wcwidth==0.2.13
      - zipp==3.21.0
prefix: /opt/conda/envs/Pillow

Tests/test_image.py::TestImage::test_stringio
Tests/test_image.py::TestImage::test__new Tests/test_image.py::TestImage::test_alpha_composite Tests/test_image.py::TestImage::test_alpha_inplace Tests/test_image.py::TestImage::test_bad_mode Tests/test_image.py::TestImage::test_check_size Tests/test_image.py::TestImage::test_comparison_with_other_type Tests/test_image.py::TestImage::test_dump Tests/test_image.py::TestImage::test_effect_mandelbrot Tests/test_image.py::TestImage::test_effect_mandelbrot_bad_arguments Tests/test_image.py::TestImage::test_effect_noise Tests/test_image.py::TestImage::test_effect_spread Tests/test_image.py::TestImage::test_exception_inheritance Tests/test_image.py::TestImage::test_expand_x Tests/test_image.py::TestImage::test_expand_xy Tests/test_image.py::TestImage::test_fp_name Tests/test_image.py::TestImage::test_fromstring Tests/test_image.py::TestImage::test_getbands Tests/test_image.py::TestImage::test_getbbox Tests/test_image.py::TestImage::test_getchannel Tests/test_image.py::TestImage::test_getchannel_wrong_params Tests/test_image.py::TestImage::test_image_modes_fail Tests/test_image.py::TestImage::test_image_modes_success Tests/test_image.py::TestImage::test_internals Tests/test_image.py::TestImage::test_invalid_image Tests/test_image.py::TestImage::test_linear_gradient Tests/test_image.py::TestImage::test_linear_gradient_wrong_mode Tests/test_image.py::TestImage::test_load_on_nonexclusive_multiframe Tests/test_image.py::TestImage::test_ne Tests/test_image.py::TestImage::test_no_resource_warning_on_save Tests/test_image.py::TestImage::test_offset_not_implemented Tests/test_image.py::TestImage::test_overrun Tests/test_image.py::TestImage::test_p_from_rgb_rgba Tests/test_image.py::TestImage::test_pathlib Tests/test_image.py::TestImage::test_radial_gradient Tests/test_image.py::TestImage::test_radial_gradient_wrong_mode Tests/test_image.py::TestImage::test_readonly_save Tests/test_image.py::TestImage::test_register_extensions Tests/test_image.py::TestImage::test_registered_extensions Tests/test_image.py::TestImage::test_registered_extensions_uninitialized Tests/test_image.py::TestImage::test_remap_palette Tests/test_image.py::TestImage::test_sanity Tests/test_image.py::TestImage::test_storage_neg Tests/test_image.py::TestImage::test_tempfile Tests/test_image.py::TestImage::test_unknown_extension Tests/test_image.py::TestImage::test_width_height Tests/test_image.py::TestRegistry::test_encode_registry Tests/test_image.py::TestRegistry::test_encode_registry_fail
src/PIL/Image.py
src/PIL/Image.py