Browse Source

Merge upstream version 0.4.24

Christoph Biedl 2 years ago
parent
commit
90b7616f27

+ 16 - 1
CHANGELOG

@@ -1,6 +1,21 @@
+Changes to 0.4.24:
+ - Fix regression in library loading on some Alpine docker images.
+
+Changes to 0.4.23
+
+ - Include a `py.typed` sentinal to enable type checking
+ - Improve fix for attribute error during destruction
+ - Cleanup library loading logic
+ - Add new homebrew library dir for OSX
+
+Changes to 0.4.21, 0.4.22
+
+ - Unify dll loader between the standard and compat library, fixing load
+ failures on some previously supported platforms.
+
 Changes to 0.4.20
 
-- merge in a compatability layer for the upstream libmagic python binding.
+- merge in a compatibility layer for the upstream libmagic python binding.
   Since both this package and that one are called 'magic', this compat layer
   removes a very common source of runtime errors.  Use of that libmagic API will
   produce a deprecation warning.

+ 2 - 2
COMPAT.md

@@ -1,10 +1,10 @@
 There are two python modules named 'magic' that do the same thing, but
 with incompatible APIs.  One of these ships with libmagic, and (this one) is
 distributed through pypi.  Both have been around for many years and have
-substantial user bases.  This incompatability is a major source of pain for
+substantial user bases.  This incompatibility is a major source of pain for
 users, and bug reports for me.
 
-To mitigate this pain, python-magic has added a compatability layer to export
+To mitigate this pain, python-magic has added a compatibility layer to export
 the libmagic python API parallel to the existing one.
 
 The mapping between the libmagic and python-magic functions is:

+ 3 - 3
README.md

@@ -1,6 +1,6 @@
 # python-magic
 [![PyPI version](https://badge.fury.io/py/python-magic.svg)](https://badge.fury.io/py/python-magic)
-[![Build Status](https://travis-ci.org/ahupp/python-magic.svg?branch=master)](https://travis-ci.org/ahupp/python-magic)
+[![Build Status](https://travis-ci.org/ahupp/python-magic.svg?branch=master)](https://travis-ci.org/ahupp/python-magic) [![Join the chat at https://gitter.im/ahupp/python-magic](https://badges.gitter.im/ahupp/python-magic.svg)](https://gitter.im/ahupp/python-magic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 
 python-magic is a Python interface to the libmagic file type
 identification library.  libmagic identifies file types by checking
@@ -119,9 +119,9 @@ To run against a specific python version:
 LC_ALL=en_US.UTF-8 python3 test/test.py
 ```
 
-## libmagic and python-magic
+## libmagic python API compatibility
 
-See [COMPAT.md](COMPAT.md) for a guide to libmagic / python-magic compatability.
+The python bindings shipped with libmagic use a module name that conflicts with this package.  To work around this, python-magic includes a compatibility layer for the libmagic API.  See [COMPAT.md](COMPAT.md) for a guide to libmagic / python-magic compatibility.
 
 ## Versioning
 

+ 4 - 39
magic/__init__.py

@@ -53,8 +53,6 @@ class Magic:
         raw - Do not try to decode "non-printable" chars.
         extension - Print a slash-separated list of valid extensions for the file type found.
         """
-
-        self.cookie = None
         self.flags = MAGIC_NONE
         if mime:
             self.flags |= MAGIC_MIME_TYPE
@@ -152,7 +150,7 @@ class Magic:
         # incorrect fix for a threading problem, however I'm leaving
         # it in because it's harmless and I'm slightly afraid to
         # remove it.
-        if self.cookie and magic_close:
+        if hasattr(self, 'cookie') and self.cookie and magic_close:
             magic_close(self.cookie)
             self.cookie = None
 
@@ -206,41 +204,8 @@ def from_descriptor(fd, mime=False):
     m = _get_magic_type(mime)
     return m.from_descriptor(fd)
 
-
-libmagic = None
-# Let's try to find magic or magic1
-dll = ctypes.util.find_library('magic') \
-      or ctypes.util.find_library('magic1') \
-      or ctypes.util.find_library('cygmagic-1') \
-      or ctypes.util.find_library('libmagic-1') \
-      or ctypes.util.find_library('msys-magic-1')  # for MSYS2
-
-# necessary because find_library returns None if it doesn't find the library
-if dll:
-    libmagic = ctypes.CDLL(dll)
-
-if not libmagic or not libmagic._name:
-    windows_dlls = ['magic1.dll', 'cygmagic-1.dll', 'libmagic-1.dll', 'msys-magic-1.dll']
-    platform_to_lib = {'darwin': ['/opt/local/lib/libmagic.dylib',
-                                  '/usr/local/lib/libmagic.dylib'] +
-                                 # Assumes there will only be one version installed
-                                 glob.glob('/usr/local/Cellar/libmagic/*/lib/libmagic.dylib'),  # flake8:noqa
-                       'win32': windows_dlls,
-                       'cygwin': windows_dlls,
-                       'linux': ['libmagic.so.1'],
-                       # fallback for some Linuxes (e.g. Alpine) where library search does not work # flake8:noqa
-                       }
-    platform = 'linux' if sys.platform.startswith('linux') else sys.platform
-    for dll in platform_to_lib.get(platform, []):
-        try:
-            libmagic = ctypes.CDLL(dll)
-            break
-        except OSError:
-            pass
-
-if not libmagic or not libmagic._name:
-    # It is better to raise an ImportError since we are importing magic module
-    raise ImportError('failed to find libmagic.  Check your installation')
+from . import loader
+libmagic = loader.load_lib()
 
 magic_t = ctypes.c_void_p
 
@@ -453,7 +418,7 @@ def _add_compat(to_module):
     def deprecation_wrapper(fn):
         def _(*args, **kwargs):
             warnings.warn(
-                "Using compatability mode with libmagic's python binding. "
+                "Using compatibility mode with libmagic's python binding. "
                 "See https://github.com/ahupp/python-magic/blob/master/COMPAT.md for details.",
                 PendingDeprecationWarning)
 

+ 2 - 8
magic/compat.py

@@ -12,16 +12,10 @@ from ctypes import *
 from ctypes.util import find_library
 
 
-def _init():
-    """
-    Loads the shared library through ctypes and returns a library
-    L{ctypes.CDLL} instance
-    """
-    return ctypes.cdll.LoadLibrary(find_library('magic'))
-
+from . import loader
 
 _libraries = {}
-_libraries['magic'] = _init()
+_libraries['magic'] = loader.load_lib()
 
 # Flag constants for open and setflags
 MAGIC_NONE = NONE = 0

+ 50 - 0
magic/loader.py

@@ -0,0 +1,50 @@
+from ctypes.util import find_library
+import ctypes
+import sys
+import glob
+import os.path
+
+def _lib_candidates():
+
+  yield find_library('magic')
+
+  if sys.platform == 'darwin':
+
+    paths = [
+      '/opt/local/lib',
+      '/usr/local/lib',
+      '/opt/homebrew/lib',
+    ] + glob.glob('/usr/local/Cellar/libmagic/*/lib')
+
+    for i in paths:
+      yield os.path.join(i, 'libmagic.dylib')
+
+  elif sys.platform in ('win32', 'cygwin'):
+
+    prefixes = ['libmagic', 'magic1', 'cygmagic-1', 'libmagic-1', 'msys-magic-1']
+
+    for i in prefixes:
+      # find_library searches in %PATH% but not the current directory,
+      # so look for both
+      yield './%s.dll' % (i,)
+      yield find_library(i)
+
+  elif sys.platform == 'linux':
+    # This is necessary because alpine is bad
+    yield 'libmagic.so.1'
+
+
+def load_lib():
+
+  for lib in _lib_candidates():
+    # find_library returns None when lib not found
+    if lib is None:
+      continue
+    try:
+      return ctypes.CDLL(lib)
+    except OSError:
+      pass
+  else:
+    # It is better to raise an ImportError since we are importing magic module
+    raise ImportError('failed to find libmagic.  Check your installation')
+

+ 0 - 0
magic/py.typed


+ 4 - 1
setup.py

@@ -18,10 +18,13 @@ setuptools.setup(
     author='Adam Hupp',
     author_email='adam@hupp.org',
     url="http://github.com/ahupp/python-magic",
-    version='0.4.20',
+    version='0.4.24',
     long_description=read('README.md'),
     long_description_content_type='text/markdown',
     packages=['magic'],
+    package_data={
+        'magic': ['py.typed'],
+    },
     keywords="mime magic file",
     license="MIT",
     python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*',

+ 0 - 11
test.ps1

@@ -1,11 +0,0 @@
-
-
-function TestInContainer($name) {
-    $TAG="python_magic/${name}:latest"
-    docker build -t $TAG -f "test/Dockerfile_${name}" .
-    docker run "python_magic/${name}:latest"
-}
-
-TestInContainer "xenial"
-TestInContainer "bionic"
-TestInContainer "focal"

+ 0 - 5
test/Dockerfile_centos8

@@ -1,5 +0,0 @@
-FROM centos:8
-RUN yum -y update
-RUN yum -y install file-libs python3 python2 which
-COPY . /python-magic
-CMD cd /python-magic/test && python3 ./run.py

+ 4 - 0
test/docker/alpine

@@ -0,0 +1,4 @@
+FROM python:3.8-alpine3.12
+RUN apk add python3 python2 libmagic
+COPY . /python-magic
+CMD cd /python-magic/test && python3 ./run.py

+ 1 - 1
test/Dockerfile_archlinux

@@ -1,4 +1,4 @@
-FROM archlinux:20200505
+FROM archlinux:latest
 RUN yes | pacman -Syyu --overwrite '*'
 RUN yes | pacman -S python python2 file which
 COPY . /python-magic

test/Dockerfile_bionic → test/docker/bionic


test/Dockerfile_centos7 → test/docker/centos7


+ 9 - 0
test/docker/centos8

@@ -0,0 +1,9 @@
+FROM centos:8
+RUN yum -y update
+RUN yum -y install file-libs python3 python2 which glibc-locale-source
+RUN yum reinstall glibc-common -y && \
+  localedef -i en_US -f UTF-8 en_US.UTF-8 && \
+  echo "LANG=en_US.UTF-8" > /etc/locale.conf
+ENV LANG en_US.UTF-8
+COPY . /python-magic
+CMD cd /python-magic/test && python3 ./run.py

test/Dockerfile_focal → test/docker/focal


test/Dockerfile_xenial → test/docker/xenial


+ 2 - 2
test/libmagic_test.py

@@ -11,12 +11,12 @@ class MagicTestCase(unittest.TestCase):
     filename = 'testdata/test.pdf'
     expected_mime_type = 'application/pdf'
     expected_encoding = 'us-ascii'
-    expected_name = 'PDF document, version 1.2'
+    expected_name = ('PDF document, version 1.2', 'PDF document, version 1.2, 2 pages')
 
     def assert_result(self, result):
         self.assertEqual(result.mime_type, self.expected_mime_type)
         self.assertEqual(result.encoding, self.expected_encoding)
-        self.assertEqual(result.name, self.expected_name)
+        self.assertIn(result.name, self.expected_name)
 
     def test_detect_from_filename(self):
         result = magic.detect_from_filename(self.filename)

+ 2 - 1
test/test.py

@@ -107,7 +107,8 @@ class MagicTest(unittest.TestCase):
         try:
             self.assert_values(m, {
                 'magic._pyc_': 'python 2.4 byte-compiled',
-                'test.pdf': 'PDF document, version 1.2',
+                'test.pdf': ('PDF document, version 1.2',
+                             'PDF document, version 1.2, 2 pages'),
                 'test.gz':
                     ('gzip compressed data, was "test", from Unix, last '
                      'modified: Sun Jun 29 01:32:52 2008',

+ 4 - 13
test_docker.sh

@@ -5,17 +5,8 @@
 
 set -e
 
-function TestInContainer {
-    local name="$1"
-    local TAG="python_magic/${name}:latest"
-    docker build -t $TAG -f "test/Dockerfile_${name}" .
-    docker run "python_magic/${name}:latest"
-}
-
-TestInContainer "xenial"
-TestInContainer "bionic"
-TestInContainer "focal"
-TestInContainer "centos7"
-TestInContainer "centos8"
-TestInContainer "archlinux"
+NAME=`basename $1`
+TAG="python_magic/${NAME}:latest"
+docker build -t $TAG -f $1 .
+docker run $TAG