Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/ahupp/python-magic into a…
Browse files Browse the repository at this point in the history
…bi3-wheels

* 'master' of https://github.com/ahupp/python-magic:
  log warning on ctypes load error, adapted from ahupp#279
  smartos support, adapted from ahupp#132
  rename no_json test to avoid duplicate function definitions
  handle unknown platforms gracefully in loader.py
  Clean up loader.py
  • Loading branch information
ddelange committed May 26, 2024
2 parents 9357f27 + a9e6276 commit 53d099b
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 55 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ brew install libmagic
port install file
```

### SmartOS:
- Install libmagic for source https://github.com/threatstack/libmagic/
- Depending on your ./configure --prefix settings set your LD_LIBRARY_PATH to <prefix>/lib

### Troubleshooting

- 'MagicException: could not find any magic files!': some
Expand Down
136 changes: 82 additions & 54 deletions magic/loader.py
Original file line number Diff line number Diff line change
@@ -1,74 +1,102 @@
from ctypes.util import find_library
import ctypes
import sys
import glob
import os
import logging
import subprocess
import sys

logger = logging.getLogger(__name__)
here = os.path.dirname(__file__)

def _lib_candidates_linux():
"""Yield possible libmagic library names on Linux."""
prefixes = ('libmagic.so.1', 'libmagic.so')

for i in prefixes:
# libmagic bundled in the wheel
yield os.path.join(here, i)
# libmagic in the current working directory
yield os.path.join(os.path.abspath('.'), i)
# libmagic install from source default destination path
yield os.path.join('/usr/local/lib', i)
# on some linux systems (musl/alpine), find_library('magic') returns None
# first try finding libmagic using ldconfig
# otherwise fall back to /usr/lib/
yield subprocess.check_output(
"( ldconfig -p | grep '{0}' | grep -o '/.*' ) || echo '/usr/lib/{0}'".format(i),
shell=True,
universal_newlines=True,
).strip()


def _lib_candidates_macos():
"""Yield possible libmagic library names on macOS."""
paths = [
# libmagic bundled in the wheel
here,
# libmagic in the current working directory
os.path.abspath('.'),
# libmagic in other common sources like homebrew
'/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')


def _lib_candidates_windows():
"""Yield possible libmagic library names on Windows."""
prefixes = (
"libmagic",
"magic1",
"magic-1",
"cygmagic-1",
"libmagic-1",
"msys-magic-1",
)

for i in prefixes:
# libmagic bundled in the wheel
yield os.path.join(here, '%s.dll' % i)
# libmagic in the current working directory
yield os.path.join(os.path.abspath('.'), '%s.dll' % i)
# find_library searches in %PATH% but not the current directory
yield find_library(i)


def _lib_candidates():
here = os.path.dirname(__file__)

if sys.platform == 'darwin':

paths = [
# libmagic bundled in the wheel
here,
# libmagic in the current working directory
os.path.abspath('.'),
# libmagic in other common sources like homebrew
'/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', 'magic-1', 'cygmagic-1', 'libmagic-1', 'msys-magic-1']

for i in prefixes:
# libmagic bundled in the wheel
yield os.path.join(here, '%s.dll' % i)
# libmagic in the current working directory
yield os.path.join(os.path.abspath('.'), '%s.dll' % i)
# find_library searches in %PATH% but not the current directory
yield find_library(i)

elif sys.platform == 'linux':

prefixes = ['libmagic.so.1', 'libmagic.so']

for i in prefixes:
# libmagic bundled in the wheel
yield os.path.join(here, i)
# libmagic in the current working directory
yield os.path.join(os.path.abspath('.'), i)
# libmagic install from source default destination path
yield os.path.join('/usr/local/lib', i)
# on some linux systems (musl/alpine), find_library('magic') returns None
# first try finding libmagic using ldconfig
# otherwise fall back to /usr/lib/
yield subprocess.check_output(
"( ldconfig -p | grep '{0}' | grep -o '/.*' ) || echo '/usr/lib/{0}'".format(i),
shell=True,
universal_newlines=True,
).strip()
func = {
"cygwin": _lib_candidates_windows,
"darwin": _lib_candidates_macos,
"linux": _lib_candidates_linux,
"win32": _lib_candidates_windows,
"sunos5": _lib_candidates_linux,
}.get(sys.platform)
if func is None:
raise ImportError("python-magic: Unsupported platform: " + sys.platform)
# When we drop legacy Python, we can just `yield from func()`
for path in func():
yield path

# fallback
yield find_library('magic')


def load_lib():

for lib in _lib_candidates():
# find_library returns None when lib not found
if lib is None:
continue
if not os.path.exists(lib):
continue

try:
return ctypes.CDLL(lib)
except OSError: # file not found
pass
raise ImportError('failed to find libmagic. Check your installation')
except OSError:
logger.warning("Failed to load: " + lib, exc_info=True)

# It is better to raise an ImportError since we are importing magic module
raise ImportError("python-magic: failed to find libmagic. Check your installation")
3 changes: 2 additions & 1 deletion test/python_magic_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@ def test_descriptions_no_json(self):
buf_equals_file=True,
)

def test_descriptions_no_json(self):
def test_descriptions_no_json_unchanged(self):
# verify non-json results are unchanged
m = magic.Magic(check_json=False)
os.environ["TZ"] = "UTC" # To get last modified date of test.gz in UTC
try:
Expand Down

0 comments on commit 53d099b

Please sign in to comment.