about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authortoonn <toonn@toonn.io>2022-02-05 19:31:22 +0100
committertoonn <toonn@toonn.io>2022-02-05 19:31:22 +0100
commit7a5f820e760c7ce2f93e2a5f0e91677b46252ea9 (patch)
tree6f4130ac9594adda5d6a1dd3fb97e87dc7fcc3ac
parentfcd0d50f8b952cc70f65639742cee5e194d84401 (diff)
parente3c90e2f758ebd94de8cd228d27ef5e1e4cda817 (diff)
downloadranger-7a5f820e760c7ce2f93e2a5f0e91677b46252ea9.tar.gz
Merge branch 'pylint'
-rw-r--r--.github/workflows/pylint.yml4
-rw-r--r--.github/workflows/python.yml2
-rw-r--r--.pylintrc2
-rwxr-xr-xdoc/tools/convert_papermode_to_metadata.py2
-rw-r--r--examples/plugin_ipc.py1
-rw-r--r--ranger/config/.pylintrc2
-rw-r--r--ranger/container/file.py2
-rw-r--r--ranger/container/tags.py1
-rw-r--r--ranger/core/actions.py5
-rw-r--r--ranger/core/loader.py3
-rw-r--r--ranger/core/runner.py2
-rw-r--r--ranger/ext/img_display.py4
-rw-r--r--ranger/ext/macrodict.py2
-rw-r--r--ranger/ext/popen_forked.py2
-rwxr-xr-xranger/ext/rifle.py4
-rw-r--r--ranger/ext/vcs/vcs.py8
-rwxr-xr-xtests/manpage_completion_test.py1
-rw-r--r--tests/pylint/py2_compat.py8
-rw-r--r--tests/pylint/test_py2_compat.py25
19 files changed, 66 insertions, 14 deletions
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index eac9aa5c..c15f8f64 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -27,6 +27,6 @@ jobs:
       run: |
         python -m pip install --upgrade pip
         pip install -r requirements.txt
-    - name: Lint with pylint
+    - name: Lint with pylint, test with pytest
       run: |
-        make test_pylint
+        make test_pylint test_pytest
diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml
index b96d6812..dc41318e 100644
--- a/.github/workflows/python.yml
+++ b/.github/workflows/python.yml
@@ -31,4 +31,4 @@ jobs:
         pip install -r requirements.txt
     - name: Flake8 and test
       run: |
-        make test_flake8 test_pytest test_doctest test_other
+        make test_flake8 test_doctest test_other
diff --git a/.pylintrc b/.pylintrc
index 994ddf62..9b4ad466 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -13,7 +13,7 @@ max-branches=16
 [FORMAT]
 max-line-length = 99
 enable=no-absolute-import,old-division
-disable=cyclic-import,duplicate-code,fixme,import-outside-toplevel,locally-disabled,locally-enabled,missing-docstring,no-else-break,no-else-continue,no-else-raise,no-else-return,raise-missing-from,redefined-variable-type,stop-iteration-return,super-with-arguments,useless-object-inheritance
+disable=consider-using-f-string,cyclic-import,duplicate-code,fixme,import-outside-toplevel,locally-disabled,locally-enabled,missing-docstring,no-else-break,no-else-continue,no-else-raise,no-else-return,raise-missing-from,redefined-variable-type,stop-iteration-return,super-with-arguments,useless-object-inheritance
 
 [TYPECHECK]
 ignored-classes=ranger.core.actions.Actions
diff --git a/doc/tools/convert_papermode_to_metadata.py b/doc/tools/convert_papermode_to_metadata.py
index 0a1b21ce..58371193 100755
--- a/doc/tools/convert_papermode_to_metadata.py
+++ b/doc/tools/convert_papermode_to_metadata.py
@@ -39,6 +39,7 @@ def replace(source, target):
     result = {}
 
     # Read the input file and convert it to a dictionary
+    # pylint: disable=unspecified-encoding
     with open(".paperinfo", "r") as infile:
         reader = csv.reader(infile, skipinitialspace=True)
         for lineno, row in enumerate(reader):
@@ -61,6 +62,7 @@ def replace(source, target):
     if result:
         # There's no way to specify encoding in 2.x even though in this case we
         # could choose to write in UTF-8.
+        # pylint: disable=unspecified-encoding
         with open(".metadata.json", "w") as outfile:
             json.dump(result, outfile, indent=2)
     else:
diff --git a/examples/plugin_ipc.py b/examples/plugin_ipc.py
index da6a19b0..0e7de3bb 100644
--- a/examples/plugin_ipc.py
+++ b/examples/plugin_ipc.py
@@ -32,6 +32,7 @@ def hook_init(fm):
             while True:
                 # The IPC encoding depends on the system locale so we can't
                 # guess here.
+                # pylint: disable=unspecified-encoding
                 with open(filepath, 'r') as fifo:
                     line = fifo.read()
                     fm.execute_console(line.strip())
diff --git a/ranger/config/.pylintrc b/ranger/config/.pylintrc
index 316bf189..509c6f31 100644
--- a/ranger/config/.pylintrc
+++ b/ranger/config/.pylintrc
@@ -5,4 +5,4 @@ class-rgx=[a-z][a-z0-9_]{1,30}$
 [FORMAT]
 max-line-length = 99
 max-module-lines=3000
-disable=duplicate-code,fixme,import-outside-toplevel,locally-disabled,locally-enabled,missing-docstring,no-else-return,super-with-arguments
+disable=consider-using-f-string,duplicate-code,fixme,import-outside-toplevel,locally-disabled,locally-enabled,missing-docstring,no-else-return,super-with-arguments
diff --git a/ranger/container/file.py b/ranger/container/file.py
index 9477abe7..4cc29887 100644
--- a/ranger/container/file.py
+++ b/ranger/container/file.py
@@ -86,7 +86,7 @@ class File(FileSystemObject):
             return True
         if PREVIEW_BLACKLIST.search(self.basename):
             return False
-        if self.path == '/dev/core' or self.path == '/proc/kcore':
+        if self.path in ('/dev/core', '/proc/kcore'):
             return False
         if self.is_binary():
             return False
diff --git a/ranger/container/tags.py b/ranger/container/tags.py
index e1ceec6f..4d4e6c59 100644
--- a/ranger/container/tags.py
+++ b/ranger/container/tags.py
@@ -123,6 +123,7 @@ class Tags(FileManagerAware):
             elif path.startswith(path_old + sep):
                 pnew = path_new + path[len(path_old):]
             if pnew:
+                # pylint: disable=unnecessary-dict-index-lookup
                 del self.tags[path]
                 self.tags[pnew] = tag
                 changed = True
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index 6cbc9ef7..a552e328 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -372,6 +372,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
         """
         filename = os.path.expanduser(filename)
         LOG.debug("Sourcing config file '%s'", filename)
+        # pylint: disable=unspecified-encoding
         with open(filename, 'r', encoding="utf-8") as fobj:
             for line in fobj:
                 line = line.strip(" \r\n")
@@ -1053,6 +1054,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
                 # XXX: properly determine file's encoding
                 # Disable the lint because the preview is read outside the
                 # local scope.
+                # pylint: disable=consider-using-with
                 return codecs.open(path, 'r', errors='ignore')
             # IOError for Python2, OSError for Python3
             except (IOError, OSError):
@@ -1438,6 +1440,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
             contexts = 'browser', 'console', 'pager', 'taskview'
 
         # Disable lint because TemporaryFiles are removed on close
+        # pylint: disable=consider-using-with
         temporary_file = tempfile.NamedTemporaryFile()
 
         def write(string):  # pylint: disable=redefined-outer-name
@@ -1464,6 +1467,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
 
     def dump_commands(self):
         # Disable lint because TemporaryFiles are removed on close
+        # pylint: disable=consider-using-with
         temporary_file = tempfile.NamedTemporaryFile()
 
         def write(string):  # pylint: disable=redefined-outer-name
@@ -1490,6 +1494,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
 
     def dump_settings(self):
         # Disable lint because TemporaryFiles are removed on close
+        # pylint: disable=consider-using-with
         temporary_file = tempfile.NamedTemporaryFile()
 
         def write(string):  # pylint: disable=redefined-outer-name
diff --git a/ranger/core/loader.py b/ranger/core/loader.py
index 866902fb..19611c7b 100644
--- a/ranger/core/loader.py
+++ b/ranger/core/loader.py
@@ -175,6 +175,7 @@ class CommandLoader(  # pylint: disable=too-many-instance-attributes
         # pylint: disable=too-many-branches,too-many-statements
         # TODO: Check whether we can afford to wait for processes and use a
         #       with-statement for Popen.
+        # pylint: disable=consider-using-with
         popenargs = {} if self.popenArgs is None else self.popenArgs
         popenargs['stdout'] = popenargs['stderr'] = PIPE
         popenargs['stdin'] = (
@@ -191,7 +192,7 @@ class CommandLoader(  # pylint: disable=too-many-instance-attributes
             try:
                 stdin.write(self.input)
             except IOError as ex:
-                if ex.errno != errno.EPIPE and ex.errno != errno.EINVAL:
+                if ex.errno not in (errno.EPIPE, errno.EINVAL):
                     raise
             stdin.close()
         if self.silent and not self.read:  # pylint: disable=too-many-nested-blocks
diff --git a/ranger/core/runner.py b/ranger/core/runner.py
index acd85fb8..1d2b91f7 100644
--- a/ranger/core/runner.py
+++ b/ranger/core/runner.py
@@ -193,6 +193,7 @@ class Runner(object):  # pylint: disable=too-few-public-methods
             context.wait = False
         if 's' in context.flags:
             # Using a with-statement for these is inconvenient.
+            # pylint: disable=consider-using-with
             devnull_writable = open(os.devnull, 'w', encoding="utf-8")
             devnull_readable = open(os.devnull, 'r', encoding="utf-8")
             for key in ('stdout', 'stderr'):
@@ -243,6 +244,7 @@ class Runner(object):  # pylint: disable=too-few-public-methods
                 if 'f' in context.flags and 'r' not in context.flags:
                     # This can fail and return False if os.fork() is not
                     # supported, but we assume it is, since curses is used.
+                    # pylint: disable=consider-using-with
                     Popen_forked(**popen_kws)
                 else:
                     process = Popen(**popen_kws)
diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py
index 97b8f43f..61e10f96 100644
--- a/ranger/ext/img_display.py
+++ b/ranger/ext/img_display.py
@@ -143,6 +143,7 @@ class W3MImageDisplayer(ImageDisplayer, FileManagerAware):
         self.binary_path = None
         self.binary_path = self._find_w3mimgdisplay_executable()  # may crash
         # We cannot close the process because that stops the preview.
+        # pylint: disable=consider-using-with
         self.process = Popen([self.binary_path] + W3MIMGDISPLAY_OPTIONS, cwd=self.working_dir,
                              stdin=PIPE, stdout=PIPE, universal_newlines=True)
         self.is_initialized = True
@@ -642,7 +643,7 @@ class KittyImageDisplayer(ImageDisplayer, FileManagerAware):
             image = image.resize((int(scale * image.width), int(scale * image.height)),
                                  self.backend.LANCZOS)
 
-        if image.mode != 'RGB' and image.mode != 'RGBA':
+        if image.mode not in ('RGB', 'RGBA'):
             image = image.convert('RGB')
         # start_x += ((box[0] - image.width) // 2) // self.pix_row
         # start_y += ((box[1] - image.height) // 2) // self.pix_col
@@ -744,6 +745,7 @@ class UeberzugImageDisplayer(ImageDisplayer):
             return
 
         # We cannot close the process because that stops the preview.
+        # pylint: disable=consider-using-with
         self.process = Popen(['ueberzug', 'layer', '--silent'], cwd=self.working_dir,
                              stdin=PIPE, universal_newlines=True)
         self.is_initialized = True
diff --git a/ranger/ext/macrodict.py b/ranger/ext/macrodict.py
index b4613fbc..924fe5a9 100644
--- a/ranger/ext/macrodict.py
+++ b/ranger/ext/macrodict.py
@@ -15,7 +15,7 @@ def macro_val(thunk, fallback=MACRO_FAIL):
 try:
     from collections.abc import MutableMapping  # pylint: disable=no-name-in-module
 except ImportError:
-    from collections import MutableMapping
+    from collections import MutableMapping  # pylint: disable=deprecated-class
 
 
 class MacroDict(MutableMapping):
diff --git a/ranger/ext/popen_forked.py b/ranger/ext/popen_forked.py
index 46054e9c..40a5b833 100644
--- a/ranger/ext/popen_forked.py
+++ b/ranger/ext/popen_forked.py
@@ -24,7 +24,7 @@ def Popen_forked(*args, **kwargs):  # pylint: disable=invalid-name
         ) as null_w:
             kwargs['stdin'] = null_r
             kwargs['stdout'] = kwargs['stderr'] = null_w
-            Popen(*args, **kwargs)
+            Popen(*args, **kwargs)  # pylint: disable=consider-using-with
         os._exit(0)  # pylint: disable=protected-access
     else:
         os.wait()
diff --git a/ranger/ext/rifle.py b/ranger/ext/rifle.py
index 53800ed6..fbfe7c5a 100755
--- a/ranger/ext/rifle.py
+++ b/ranger/ext/rifle.py
@@ -141,12 +141,13 @@ except ImportError:
             return False
         if pid == 0:
             os.setsid()
+            # pylint: disable=unspecified-encoding
             with open(os.devnull, "r") as null_r, open(
                 os.devnull, "w"
             ) as null_w:
                 kwargs["stdin"] = null_r
                 kwargs["stdout"] = kwargs["stderr"] = null_w
-                Popen(*args, **kwargs)
+                Popen(*args, **kwargs)  # pylint: disable=consider-using-with
             os._exit(0)  # pylint: disable=protected-access
         return True
 
@@ -223,6 +224,7 @@ class Rifle(object):  # pylint: disable=too-many-instance-attributes
         """Replace the current configuration with the one in config_file"""
         if config_file is None:
             config_file = self.config_file
+        # pylint: disable=unspecified-encoding
         with open(config_file, "r") as fobj:
             self.rules = []
             for line in fobj:
diff --git a/ranger/ext/vcs/vcs.py b/ranger/ext/vcs/vcs.py
index 455bd175..93fb1d0b 100644
--- a/ranger/ext/vcs/vcs.py
+++ b/ranger/ext/vcs/vcs.py
@@ -88,6 +88,7 @@ class Vcs(object):  # pylint: disable=too-many-instance-attributes
         if self.root:
             if self.is_root:
                 self.rootvcs = self
+                # pylint: disable=invalid-class-object
                 self.__class__ = globals()[self.REPOTYPES[self.repotype]['class'] + 'Root']
 
                 if not os.access(self.repodir, os.R_OK):
@@ -101,6 +102,7 @@ class Vcs(object):  # pylint: disable=too-many-instance-attributes
                 if self.rootvcs is None or self.rootvcs.root is None:
                     return
                 self.rootvcs.links |= self.links
+                # pylint: disable=invalid-class-object
                 self.__class__ = globals()[self.REPOTYPES[self.repotype]['class']]
                 self.track = self.rootvcs.track
 
@@ -461,10 +463,10 @@ class VcsThread(threading.Thread):  # pylint: disable=too-many-instance-attribut
             self.paused.set()
             self._advance.wait()
             self._awoken.wait()
-            if self.__stop.isSet():
+            if self.__stop.is_set():
                 self.stopped.set()
                 return
-            if not self._advance.isSet():
+            if not self._advance.is_set():
                 continue
             self._awoken.clear()
             self.paused.clear()
@@ -489,7 +491,7 @@ class VcsThread(threading.Thread):  # pylint: disable=too-many-instance-attribut
         self._advance.set()
         self._awoken.set()
         self.stopped.wait(1)
-        return self.stopped.isSet()
+        return self.stopped.is_set()
 
     def pause(self):
         """Pause thread"""
diff --git a/tests/manpage_completion_test.py b/tests/manpage_completion_test.py
index 3b067b46..1a01f944 100755
--- a/tests/manpage_completion_test.py
+++ b/tests/manpage_completion_test.py
@@ -26,6 +26,7 @@ def get_path_of_man_page():
 
 def read_manpage():
     path = get_path_of_man_page()
+    # pylint: disable=unspecified-encoding
     with open(path, 'r') as man_page:
         return man_page.read()
 
diff --git a/tests/pylint/py2_compat.py b/tests/pylint/py2_compat.py
index 7e136148..e0353260 100644
--- a/tests/pylint/py2_compat.py
+++ b/tests/pylint/py2_compat.py
@@ -51,6 +51,9 @@ class Py2CompatibilityChecker(BaseChecker):
                   "Python 2 subprocess.Popen objects were not contextmanagers,"
                   "popen23.Popen wraps them to enable use with"
                   "with-statements."),
+        "E4240": ("Use format method",
+                  "use-format-method",
+                  "Python 2 (and <3.6) does not support f-strings."),
     }
     # This class variable declares the options
     # that are configurable by the user.
@@ -121,6 +124,11 @@ class Py2CompatibilityChecker(BaseChecker):
                     self.add_message("implicit-format-spec", node=node,
                                      confidence=HIGH)
 
+    def visit_joinedstr(self, node):
+        """Make sure we don't use f-strings"""
+        if isinstance(node, astroid.nodes.JoinedStr):
+            self.add_message("use-format-method", node=node, confidence=HIGH)
+
     def visit_with(self, node):
         """Make sure subprocess.Popen objects aren't used in with-statements"""
         for (cm, _) in node.items:
diff --git a/tests/pylint/test_py2_compat.py b/tests/pylint/test_py2_compat.py
index 2eb51599..33fc5681 100644
--- a/tests/pylint/test_py2_compat.py
+++ b/tests/pylint/test_py2_compat.py
@@ -4,6 +4,7 @@ import py2_compat
 
 import astroid
 import pylint.testutils
+from pylint.interfaces import HIGH
 
 from sys import version_info
 PY2 = version_info[0] < 3
@@ -25,6 +26,7 @@ class TestPy2CompatibilityChecker(pylint.testutils.CheckerTestCase):
             pylint.testutils.MessageTest(
                 msg_id='old-style-class',
                 node=oldstyle_class,
+                confidence=HIGH,
             ),
         ):
             self.checker.visit_classdef(oldstyle_class)
@@ -56,6 +58,7 @@ class TestPy2CompatibilityChecker(pylint.testutils.CheckerTestCase):
             pylint.testutils.MessageTest(
                 msg_id='print-without-import',
                 node=print_function_call,
+                confidence=HIGH,
             ),
         ):
             self.checker.visit_call(print_function_call)
@@ -95,6 +98,7 @@ class TestPy2CompatibilityChecker(pylint.testutils.CheckerTestCase):
             pylint.testutils.MessageTest(
                 msg_id='print-without-import',
                 node=early_print_function_call,
+                confidence=HIGH,
             ),
         ):
             self.checker.visit_call(early_print_function_call)
@@ -111,6 +115,7 @@ class TestPy2CompatibilityChecker(pylint.testutils.CheckerTestCase):
             pylint.testutils.MessageTest(
                 msg_id='implicit-format-spec',
                 node=implicit_format_spec,
+                confidence=HIGH,
             ),
         ):
             self.checker.visit_call(implicit_format_spec)
@@ -134,6 +139,7 @@ class TestPy2CompatibilityChecker(pylint.testutils.CheckerTestCase):
             pylint.testutils.MessageTest(
                 msg_id='with-popen23',
                 node=with_Popen,
+                confidence=HIGH,
             ),
         ):
             self.checker.visit_with(with_subprocess_Popen)
@@ -141,6 +147,25 @@ class TestPy2CompatibilityChecker(pylint.testutils.CheckerTestCase):
         with self.assertNoMessages():
             self.checker.visit_with(with_Popen23)
 
+    def test_use_format(self):
+        old_format, new_format, f_string = astroid.extract_node("""
+            "2 + 2 is %s" % (2+2) #@
+            "2 + 2 is {0}".format(2+2) #@
+            f"2 + 2 is {2+2}" #@
+        """)
+
+        with self.assertAddsMessages(
+            pylint.testutils.MessageTest(
+                msg_id='use-format-method',
+                node=f_string,
+                confidence=HIGH,
+            ),
+        ):
+            self.checker.visit_joinedstr(f_string)
+        with self.assertNoMessages():
+            self.checker.visit_joinedstr(old_format)
+            self.checker.visit_joinedstr(new_format)
+
     # # These checks still exist as old-division and no-absolute-import
     # def test_division_without_import(self):
     #     division = astroid.extract_node("""