Blob Blame History Raw
From 4245de0b978a4d28bb8c833c2f2f5a15a260bd22 Mon Sep 17 00:00:00 2001
From: Pavel Moravec <pmoravec@redhat.com>
Date: Mon, 12 Sep 2022 15:30:16 +0200
Subject: [PATCH] [utilities] Relax from hard dependency of python3-magic

For compatibility reasons on some distros, sos should not have a hard
dependency on 'magic' python library. It should attempt to use it for
detection of binary file content, but should fall back to previous "read
the very first byte" method otherwise.

Resolves: #3025
Relates: #3021

Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
---
 requirements.txt |  1 -
 setup.py         |  2 +-
 sos.spec         |  2 +-
 sos/utilities.py | 50 +++++++++++++++++++++++++++++++++++-------------
 4 files changed, 39 insertions(+), 16 deletions(-)

diff --git a/requirements.txt b/requirements.txt
index c6ba1162..39f42161 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,5 +2,4 @@ pycodestyle>=2.4.0
 coverage>=4.0.3
 Sphinx>=1.3.5
 pexpect>=4.0.0
-python_magic>=0.4.20
 pyyaml
diff --git a/setup.py b/setup.py
index 2a70802d..f2f9ecbe 100644
--- a/setup.py
+++ b/setup.py
@@ -107,7 +107,7 @@ setup(
     ],
     cmdclass=cmdclass,
     command_options=command_options,
-    requires=['pexpect', 'python_magic', 'pyyaml']
+    requires=['pexpect', 'pyyaml']
     )
 
 
diff --git a/sos.spec b/sos.spec
index 748b9fd5..08499816 100644
--- a/sos.spec
+++ b/sos.spec
@@ -16,7 +16,7 @@ Requires: python3-rpm
 Requires: tar
 Requires: xz
 Requires: python3-pexpect
-Requires: python3-magic
+Recommends: python3-magic
 Recommends: python3-pyyaml
 Obsoletes: sos-collector <= 1.9
 
diff --git a/sos/utilities.py b/sos/utilities.py
index 2046c8fd..21c815d9 100644
--- a/sos/utilities.py
+++ b/sos/utilities.py
@@ -19,11 +19,26 @@ import tempfile
 import threading
 import time
 import io
-import magic
-
 from contextlib import closing
 from collections import deque
 
+# try loading magic>=0.4.20 which implements detect_from_filename method
+magic_mod = False
+try:
+    import magic
+    magic.detect_from_filename(__file__)
+    magic_mod = True
+except (ImportError, AttributeError):
+    log = logging.getLogger('sos')
+    from textwrap import fill
+    msg = ("""\
+WARNING: Failed to load 'magic' module version >= 0.4.20 which sos aims to \
+use for detecting binary files. A less effective method will be used. It is \
+recommended to install proper python3-magic package with the module.
+""")
+    log.warn('\n' + fill(msg, 72, replace_whitespace=False) + '\n')
+
+
 TIMEOUT_DEFAULT = 300
 
 
@@ -75,17 +90,26 @@ def file_is_binary(fname):
     :returns:   True if binary, else False
     :rtype:     ``bool``
     """
-    try:
-        _ftup = magic.detect_from_filename(fname)
-        _mimes = ['text/', 'inode/']
-        return (
-            _ftup.encoding == 'binary' and not
-            any(_ftup.mime_type.startswith(_mt) for _mt in _mimes)
-        )
-    except Exception:
-        # if for some reason this check fails, don't blindly remove all files
-        # but instead rely on other checks done by the component
-        return False
+    if magic_mod:
+        try:
+            _ftup = magic.detect_from_filename(fname)
+            _mimes = ['text/', 'inode/']
+            return (
+                _ftup.encoding == 'binary' and not
+                any(_ftup.mime_type.startswith(_mt) for _mt in _mimes)
+            )
+        except Exception:
+            pass
+    # if for some reason the above check fails or magic>=0.4.20 is not present,
+    # fail over to checking the very first byte of the file content
+    with open(fname, 'tr') as tfile:
+        try:
+            # when opened as above (tr), reading binary content will raise
+            # an exception
+            tfile.read(1)
+            return False
+        except UnicodeDecodeError:
+            return True
 
 
 def find(file_pattern, top_dir, max_depth=None, path_pattern=None):
-- 
2.37.3