Skip to content

Commit 001c831

Browse files
committed
Implement new library loading approach
We now load the libmagic native library and its database from paths found in: 1. environment variables, 2. OR a location provider plugin, 3. OR the system PATH, 4. OR we fail with an informative error message. Based on original code contributed by @priv-kweihmann to scancode-plugins in aboutcode-org/scancode-plugins#9 and moved here and adapted for use in the core code rather than in a plugin. Contributed-by: Konrad Weihmann <kweihmann@outlook.com> Signed-off-by: Philippe Ombredanne <pombredanne@nexb.com>
1 parent 45011e9 commit 001c831

1 file changed

Lines changed: 107 additions & 18 deletions

File tree

src/typecode/magic2.py

Lines changed: 107 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,33 @@
4545
import os
4646

4747
from commoncode import command
48-
from plugincode.location_provider import get_location
49-
50-
from os import fsencode
51-
52-
TRACE = False
48+
from commoncode.system import on_windows
5349

5450
"""
5551
magic2 is minimal and specialized wrapper around a vendored libmagic file
5652
identification library. This is NOT thread-safe. It is based on python-magic
5753
by Adam Hup and adapted to the specific needs of ScanCode.
5854
"""
55+
56+
# Tracing flag
57+
TRACE = True
58+
59+
60+
def logger_debug(*args):
61+
pass
62+
63+
64+
if TRACE:
65+
import logging
66+
import sys
67+
68+
logger = logging.getLogger(__name__)
69+
logging.basicConfig(stream=sys.stdout)
70+
logger.setLevel(logging.DEBUG)
71+
72+
def logger_debug(*args):
73+
return logger.debug(' '.join(isinstance(a, str) and a or repr(a) for a in args))
74+
5975
#
6076
# Cached detectors
6177
#
@@ -74,24 +90,97 @@
7490
DETECT_ENC = MAGIC_NONE | MAGIC_MIME | MAGIC_MIME_ENCODING
7591

7692
# keys for plugin-provided locations
77-
TYPECODE_LIBMAGIC_LIBDIR = 'typecode.libmagic.libdir'
7893
TYPECODE_LIBMAGIC_DLL = 'typecode.libmagic.dll'
79-
TYPECODE_LIBMAGIC_DATABASE = 'typecode.libmagic.db'
94+
TYPECODE_LIBMAGIC_DB = 'typecode.libmagic.db'
95+
96+
TYPECODE_LIBMAGIC_PATH_ENVVAR = 'TYPECODE_LIBMAGIC_PATH'
97+
TYPECODE_LIBMAGIC_DB_PATH_ENVVAR = 'TYPECODE_LIBMAGIC_DB_PATH'
8098

8199

82100
def load_lib():
83101
"""
84-
Return the loaded libmagic shared library object from plugin-provided path.
102+
Return the libmagic shared library object loaded from either:
103+
- an environment variable ``TYPECODE_LIBMAGIC_PATH``
104+
- a plugin-provided path,
105+
- the system PATH.
106+
Raise an Exception if no libmagic can be found.
85107
"""
86-
dll = get_location(TYPECODE_LIBMAGIC_DLL)
87-
libdir = get_location(TYPECODE_LIBMAGIC_LIBDIR)
88-
if not (dll and libdir) or not os.path.isfile(dll) or not os.path.isdir(libdir):
108+
from plugincode.location_provider import get_location
109+
110+
# try the environment first
111+
dll_loc = os.environ.get(TYPECODE_LIBMAGIC_PATH_ENVVAR)
112+
113+
if TRACE and dll_loc:
114+
logger_debug('load_lib:', 'got environ magic location:', dll_loc)
115+
116+
# try a plugin-provided path second
117+
if not dll_loc:
118+
dll_loc = get_location(TYPECODE_LIBMAGIC_DLL)
119+
120+
if TRACE and dll_loc:
121+
logger_debug('load_lib:', 'got plugin magic location:', dll_loc)
122+
123+
# try the PATH
124+
if not dll_loc:
125+
dll = 'libmagic.dll' if on_windows else 'libmagic.so'
126+
dll_loc = command.find_in_path(dll)
127+
128+
if TRACE and dll_loc:
129+
logger_debug('load_lib:', 'got path magic location:', dll_loc)
130+
131+
if not dll_loc or not os.path.isfile(dll_loc):
132+
raise Exception(
133+
'CRITICAL: libmagic DLL and its magic database are not installed. '
134+
'Unable to continue: you need to install a valid typecode-libmagic '
135+
'plugin with a valid and proper libmagic and magic DB available. '
136+
f'OR set the {TYPECODE_LIBMAGIC_PATH_ENVVAR} environment variable.'
137+
)
138+
return command.load_shared_library(dll_loc)
139+
140+
141+
def get_magicdb_location(_cache=[]):
142+
"""
143+
Return the location of the magicdb loaded from either:
144+
- an environment variable ``TYPECODE_LIBMAGIC_DB_PATH``,
145+
- a plugin-provided path,
146+
- the system PATH.
147+
Raise an Exception if no magicdb command can be found.
148+
"""
149+
if _cache:
150+
return _cache[0]
151+
152+
from plugincode.location_provider import get_location
153+
154+
# try the environment first
155+
magicdb_loc = os.environ.get(TYPECODE_LIBMAGIC_DB_PATH_ENVVAR)
156+
157+
if TRACE and magicdb_loc:
158+
logger_debug('get_magicdb_location:', 'got environ magicdb location:', magicdb_loc)
159+
160+
# try a plugin-provided path second
161+
if not magicdb_loc:
162+
magicdb_loc = get_location(TYPECODE_LIBMAGIC_DB)
163+
164+
if TRACE and magicdb_loc:
165+
logger_debug('get_magicdb_location:', 'got plugin magicdb location:', magicdb_loc)
166+
167+
# try the PATH
168+
if not magicdb_loc:
169+
db = 'magic.mgc'
170+
magicdb_loc = command.find_in_path(db)
171+
172+
if TRACE and magicdb_loc:
173+
logger_debug('get_magicdb_location:', 'got path magicdb location:', magicdb_loc)
174+
175+
if not magicdb_loc or not os.path.isfile(magicdb_loc):
89176
raise Exception(
90-
'CRITICAL: libmagic DLL and is magic database are not installed. '
177+
'CRITICAL: Libmagic magic database is not installed. '
91178
'Unable to continue: you need to install a valid typecode-libmagic '
92-
'plugin with a valid and proper libmagic and magic DB available.'
179+
'plugin with a valid magic database available. '
180+
'OR set the TYPECODE_LIBMAGIC_DB_PATH environment variable.'
93181
)
94-
return command.load_shared_library(dll, libdir)
182+
_cache.append(magicdb_loc)
183+
return magicdb_loc
95184

96185

97186
if TRACE:
@@ -164,11 +253,11 @@ def __init__(self, flags, magic_db_location=None):
164253
self.flags = flags
165254
self.cookie = _magic_open(self.flags)
166255
if not magic_db_location:
167-
magic_db_location = get_location(TYPECODE_LIBMAGIC_DATABASE)
256+
magic_db_location = get_magicdb_location()
168257

169-
# Note: this location must always be bytes on Python2 and 3, all OSes
258+
# Note: this location must always be FS-encoded bytes on all OSes
170259
if isinstance(magic_db_location, str):
171-
magic_db_location = fsencode(magic_db_location)
260+
magic_db_location = os.fsencode(magic_db_location)
172261

173262
_magic_load(self.cookie, magic_db_location)
174263

@@ -190,7 +279,7 @@ def get(self, location):
190279
# location string may therefore be mangled and the file not accessible
191280
# anymore by libmagic in some cases.
192281
try:
193-
uloc = fsencode(location)
282+
uloc = os.fsencode(location)
194283
return _magic_file(self.cookie, uloc)
195284
except:
196285
# if all fails, read the start of the file instead

0 commit comments

Comments
 (0)