Skip to content

Commit 68a2b7d

Browse files
committed
Format code and streamline failover support #17
Signed-off-by: Philippe Ombredanne <pombredanne@nexb.com>
1 parent b7f7337 commit 68a2b7d

File tree

1 file changed

+92
-47
lines changed

1 file changed

+92
-47
lines changed

src/typecode/magic2.py

Lines changed: 92 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@
4242
# SOFTWARE.
4343

4444
import ctypes
45+
import glob
4546
import os
4647

4748
from commoncode import command
4849
from commoncode.system import on_windows
50+
import warnings
4951

5052
"""
5153
magic2 is minimal and specialized wrapper around a vendored libmagic file
@@ -97,50 +99,72 @@ def logger_debug(*args):
9799
TYPECODE_LIBMAGIC_DB_PATH_ENVVAR = 'TYPECODE_LIBMAGIC_DB_PATH'
98100

99101

102+
class NoMagicLibError(Exception):
103+
"""
104+
Raised when no libmagic library is found.
105+
"""
106+
107+
100108
def load_lib_failover():
101-
# loader from python-magic
102-
libmagic = None
103-
# Let's try to find magic or magic1
104-
dll = (ctypes.util.find_library('magic')
109+
"""
110+
Return a loaded libmagic from well known system installation locations.
111+
This is a function originally from python-magic.
112+
"""
113+
libmagic = None
114+
# Let's try to find magic or magic1
115+
dll = (
116+
ctypes.util.find_library('magic')
105117
or ctypes.util.find_library('magic1')
106118
or ctypes.util.find_library('cygmagic-1')
107119
or ctypes.util.find_library('libmagic-1')
108-
or ctypes.util.find_library('msys-magic-1') # for MSYS2
109-
)
110-
# necessary because find_library returns None if it doesn't find the library
111-
if dll:
112-
libmagic = ctypes.CDLL(dll)
113-
114-
if not libmagic or not libmagic._name:
115-
windows_dlls = ['magic1.dll', 'cygmagic-1.dll', 'libmagic-1.dll', 'msys-magic-1.dll']
116-
platform_to_lib = {'darwin': ['/opt/local/lib/libmagic.dylib',
117-
'/usr/local/lib/libmagic.dylib'] +
118-
# Assumes there will only be one version installed
119-
glob.glob('/usr/local/Cellar/libmagic/*/lib/libmagic.dylib'), # flake8:noqa
120-
'win32': windows_dlls,
121-
'cygwin': windows_dlls,
122-
'linux': ['libmagic.so.1'],
123-
# fallback for some Linuxes (e.g. Alpine) where library search does not work # flake8:noqa
124-
}
125-
platform = 'linux' if sys.platform.startswith('linux') else sys.platform
126-
for dll in platform_to_lib.get(platform, []):
127-
try:
128-
libmagic = ctypes.CDLL(dll)
129-
break
130-
except OSError:
131-
pass
132-
133-
if not libmagic or not libmagic._name:
134-
return None
135-
return libmagic
120+
# for MSYS2
121+
or ctypes.util.find_library('msys-magic-1')
122+
)
123+
# necessary because find_library returns None if it doesn't find the library
124+
if dll:
125+
libmagic = ctypes.CDLL(dll)
126+
127+
if not (libmagic and libmagic._name):
128+
windows_dlls = [
129+
'magic1.dll',
130+
'cygmagic-1.dll',
131+
'libmagic-1.dll',
132+
'msys-magic-1.dll',
133+
]
134+
platform_to_lib = {
135+
'darwin': (
136+
[
137+
'/opt/local/lib/libmagic.dylib',
138+
'/usr/local/lib/libmagic.dylib',
139+
] +
140+
# Assumes there will only be one version installed when using brew
141+
glob.glob('/usr/local/Cellar/libmagic/*/lib/libmagic.dylib')
142+
),
143+
'win32': windows_dlls,
144+
'cygwin': windows_dlls,
145+
'linux': ['libmagic.so.1'],
146+
}
147+
# fallback for some Linuxes (e.g. Alpine) where library search does not
148+
# work # flake8:noqa
149+
platform = 'linux' if sys.platform.startswith('linux') else sys.platform
150+
for dll in platform_to_lib.get(platform, []):
151+
try:
152+
libmagic = ctypes.CDLL(dll)
153+
break
154+
except OSError:
155+
pass
156+
157+
if libmagic and libmagic._name:
158+
return libmagic
159+
136160

137161
def load_lib():
138162
"""
139163
Return the libmagic shared library object loaded from either:
140164
- an environment variable ``TYPECODE_LIBMAGIC_PATH``
141165
- a plugin-provided path,
142166
- the system PATH.
143-
Raise an Exception if no libmagic can be found.
167+
Raise an NoMagicLibError if no libmagic can be found.
144168
"""
145169
from plugincode.location_provider import get_location
146170

@@ -161,23 +185,35 @@ def load_lib():
161185
if not dll_loc:
162186
failover_lib = load_lib_failover()
163187
if failover_lib:
188+
warnings.warn(
189+
'System libmagic found in typical location is used. '
190+
'Install instead a typecode-libmagic plugin for best support.'
191+
)
164192
return failover_lib
165193

166-
167194
# try the PATH
168195
if not dll_loc:
169196
dll = 'libmagic.dll' if on_windows else 'libmagic.so'
170197
dll_loc = command.find_in_path(dll)
171198

199+
if dll_loc:
200+
warnings.warn(
201+
'libmagic found in the PATH. '
202+
'Install instead a typecode-libmagic plugin for best support.'
203+
)
204+
172205
if TRACE and dll_loc:
173206
logger_debug('load_lib:', 'got path magic location:', dll_loc)
174207

175208
if not dll_loc or not os.path.isfile(dll_loc):
176-
raise Exception(
209+
raise NoMagicLibError(
177210
'CRITICAL: libmagic DLL and its magic database are not installed. '
178211
'Unable to continue: you need to install a valid typecode-libmagic '
179-
'plugin with a valid and proper libmagic and magic DB available. '
180-
f'OR set the {TYPECODE_LIBMAGIC_PATH_ENVVAR} environment variable.'
212+
'plugin with a valid and proper libmagic and magic DB available.\n'
213+
f'OR set the {TYPECODE_LIBMAGIC_PATH_ENVVAR} and '
214+
f'{TYPECODE_LIBMAGIC_DB_PATH_ENVVAR} environment variables.\n'
215+
f'OR install libmagic in typical common locations.\n'
216+
f'OR have a libmagic in the system PATH.\n'
181217
)
182218
return command.load_shared_library(dll_loc)
183219

@@ -188,7 +224,7 @@ def get_magicdb_location(_cache=[]):
188224
- an environment variable ``TYPECODE_LIBMAGIC_DB_PATH``,
189225
- a plugin-provided path,
190226
- the system PATH.
191-
Raise an Exception if no magicdb command can be found.
227+
Trigger a warning if no magicdb file is found.
192228
"""
193229
if _cache:
194230
return _cache[0]
@@ -213,16 +249,25 @@ def get_magicdb_location(_cache=[]):
213249
db = 'magic.mgc'
214250
magicdb_loc = command.find_in_path(db)
215251

252+
if magicdb_loc:
253+
warnings.warn(
254+
'magicdb found in the PATH. '
255+
'Install instead a typecode-libmagic plugin for best support.\n'
256+
f'OR set the {TYPECODE_LIBMAGIC_DB_PATH_ENVVAR} environment variable.'
257+
)
258+
216259
if TRACE and magicdb_loc:
217260
logger_debug('get_magicdb_location:', 'got path magicdb location:', magicdb_loc)
218261

219-
if not magicdb_loc or not os.path.isfile(magicdb_loc):
220-
raise Exception(
221-
'CRITICAL: Libmagic magic database is not installed. '
222-
'Unable to continue: you need to install a valid typecode-libmagic '
223-
'plugin with a valid magic database available. '
224-
'OR set the TYPECODE_LIBMAGIC_DB_PATH environment variable.'
225-
)
262+
if not magicdb_loc:
263+
warnings.warn(
264+
'Libmagic magic database not found. '
265+
'A default will be used if possible. '
266+
'Install instead a typecode-libmagic plugin for best support.\n'
267+
f'OR set the {TYPECODE_LIBMAGIC_DB_PATH_ENVVAR} environment variable.'
268+
)
269+
return
270+
226271
_cache.append(magicdb_loc)
227272
return magicdb_loc
228273

@@ -297,11 +342,11 @@ def __init__(self, flags, magic_db_location=None):
297342
self.flags = flags
298343
self.cookie = _magic_open(self.flags)
299344
if not magic_db_location:
300-
# if no plugin, None is returned, and libmagic will load the default db
345+
# Caveat emptor: this may be empty in which case a default will be tried
301346
magic_db_location = get_magicdb_location()
302347

303348
# Note: this location must always be FS-encoded bytes on all OSes
304-
if isinstance(magic_db_location, str):
349+
if magic_db_location and not isinstance(magic_db_location, bytes):
305350
magic_db_location = os.fsencode(magic_db_location)
306351

307352
_magic_load(self.cookie, magic_db_location)

0 commit comments

Comments
 (0)