4242# SOFTWARE.
4343
4444import ctypes
45+ import glob
4546import os
4647
4748from commoncode import command
4849from commoncode .system import on_windows
50+ import warnings
4951
5052"""
5153magic2 is minimal and specialized wrapper around a vendored libmagic file
@@ -97,50 +99,72 @@ def logger_debug(*args):
9799TYPECODE_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+
100108def 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
137161def 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