Repository: python-pillow/Pillow

Issue:
ImportError from distutils in exe built with cx_Freeze in virtualenv
Running a trivial Python program using Pillow built into a Windows EXE file using cx_Freeze out of a virtualenv fails with `ImportError: cannot import name 'dist' from 'distutils'`

Running the same directly using Python, without building the EXE, works.

I guess this is caused by https://github.com/python-pillow/Pillow/pull/3350 (which introduced a dependency on distutils.version) and the solution could possibly be https://github.com/python-pillow/Pillow/pull/3552, which was proposed already in https://github.com/python-pillow/Pillow/issues/3547 as a solution to another virtualenv-related issue.

The underlying cause seems to be that the distutils in the virtualenv is not normal distutils but something fake from virtualenv. Why it works outside of the built EXE is a mystery to me, I'm guessing some magic with sys.path or import hooks. Why it doesn't work in the EXE file seems clearer - distutils.version is simply not included.

To reproduce:
repro.cmd:
```cmd
virtualenv .
call Scripts\activate
pip install cx_freeze pillow
python setup.py bdist
call Scripts\deactivate
build\exe.win-amd64-3.7\pok.exe
```

setup.py:
```python
from cx_Freeze import setup, Executable

setup(
    options = {"build_exe": {
        "include_msvcr": True,
        "packages": ["PIL"],
        "zip_include_packages": "*",
        "zip_exclude_packages": "",
        "excludes": ["tkinter"]
        }
    },
    executables = [Executable("pok.py")],
)
```

pok.py:
```python
import PIL.Image
import PIL.TiffImagePlugin
```

`pip list`:
```
Package    Version
---------- -------
cx-Freeze  6.0
Pillow     6.2.1
pip        19.3.1
setuptools 41.6.0
wheel      0.33.6
```

Python version: Python 3.7.4 (tags/v3.7.4:e09359112e, Jul  8 2019, 20:34:20) [MSC v.1916 64 bit (AMD64)] on win32


Discussion:
You might be interested in https://gist.github.com/nicoddemus/ca0acd93a20acbc42d1d and https://github.com/anthony-tuininga/cx_Freeze/issues/443.
Oh wow, how did I fail to find this? Thanks.
Still, as this is at least the second issue raised due to the use of distutils, would you consider eliminating it, even though it is not technically Pillow's problem? Especially as the solution seems easy and already on offer...
PR #3552 was one attempt at doing so. I've now created PR #4305 to try again in a different way.

Resolving patch:
diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py
--- a/src/PIL/TiffImagePlugin.py
+++ b/src/PIL/TiffImagePlugin.py
@@ -38,7 +38,6 @@
 #
 # See the README file for information on usage and redistribution.
 #
-import distutils.version
 import io
 import itertools
 import os
@@ -264,10 +263,6 @@ def _limit_rational(val, max_val):
     return n_d[::-1] if inv else n_d
 
 
-def _libtiff_version():
-    return Image.core.libtiff_version.split("\n")[0].split("Version ")[1]
-
-
 ##
 # Wrapper for TIFF IFDs.
 
@@ -1559,11 +1554,10 @@ def _save(im, fp, filename):
             # Custom items are supported for int, float, unicode, string and byte
             # values. Other types and tuples require a tagtype.
             if tag not in TiffTags.LIBTIFF_CORE:
-                if TiffTags.lookup(tag).type == TiffTags.UNDEFINED:
-                    continue
-                if distutils.version.StrictVersion(
-                    _libtiff_version()
-                ) < distutils.version.StrictVersion("4.0"):
+                if (
+                    TiffTags.lookup(tag).type == TiffTags.UNDEFINED
+                    or not Image.core.libtiff_support_custom_tags
+                ):
                     continue
 
                 if tag in ifd.tagtype:


Test patch:
diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py
--- a/Tests/test_file_libtiff.py
+++ b/Tests/test_file_libtiff.py
@@ -1,5 +1,4 @@
 import base64
-import distutils.version
 import io
 import itertools
 import logging
@@ -272,12 +271,8 @@ def test_custom_metadata(self):
             )
         }
 
-        libtiff_version = TiffImagePlugin._libtiff_version()
-
         libtiffs = [False]
-        if distutils.version.StrictVersion(
-            libtiff_version
-        ) >= distutils.version.StrictVersion("4.0"):
+        if Image.core.libtiff_support_custom_tags:
             libtiffs.append(True)
 
         for libtiff in libtiffs: