diff options
-rw-r--r-- | .github/workflows/pylint.yml | 4 | ||||
-rw-r--r-- | .github/workflows/python.yml | 2 | ||||
-rw-r--r-- | .pylintrc | 2 | ||||
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | README.md | 6 | ||||
-rw-r--r-- | doc/ranger.1 | 23 | ||||
-rw-r--r-- | doc/ranger.pod | 23 | ||||
-rw-r--r-- | ranger/config/.pylintrc | 2 | ||||
-rw-r--r-- | ranger/container/file.py | 2 | ||||
-rw-r--r-- | ranger/core/loader.py | 2 | ||||
-rwxr-xr-x | ranger/data/scope.sh | 38 | ||||
-rw-r--r-- | ranger/ext/img_display.py | 2 | ||||
-rw-r--r-- | ranger/ext/vcs/vcs.py | 6 | ||||
-rw-r--r-- | tests/pylint/py2_compat.py | 8 | ||||
-rw-r--r-- | tests/pylint/test_py2_compat.py | 39 |
15 files changed, 115 insertions, 48 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/Makefile b/Makefile index 3479bb2d..568ebd42 100644 --- a/Makefile +++ b/Makefile @@ -135,13 +135,13 @@ test_other: test: test_py test_shellcheck @echo "$(bold)Finished testing: All tests passed!$(normal)" -doc/ranger.1: doc/ranger.pod README.md +doc/ranger.1: doc/ranger.pod pod2man --stderr --center='ranger manual' \ --date='$(NAME)-$(VERSION)' \ --release=$(shell date -u '+%Y-%m-%d') \ doc/ranger.pod doc/ranger.1 -doc/rifle.1: doc/rifle.pod README.md +doc/rifle.1: doc/rifle.pod pod2man --stderr --center='rifle manual' \ --date='$(NAME_RIFLE)-$(VERSION_RIFLE)' \ --release=$(shell date -u '+%Y-%m-%d') \ diff --git a/README.md b/README.md index bc811d12..6598b655 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,9 @@ For enhanced file previews (with `scope.sh`): * `img2txt` (from `caca-utils`) for ASCII-art image previews * `w3mimgdisplay`, `ueberzug`, `mpv`, `iTerm2`, `kitty`, `terminology` or `urxvt` for image previews -* `convert` (from `imagemagick`) to auto-rotate images and for SVG previews +* `convert` (from `imagemagick`) to auto-rotate images +* `rsvg-convert` (from [`librsvg`](https://wiki.gnome.org/Projects/LibRsvg)) + for SVG previews * `ffmpeg`, or `ffmpegthumbnailer` for video thumbnails * `highlight`, `bat` or `pygmentize` for syntax highlighting of code * `atool`, `bsdtar`, `unrar` and/or `7z` to preview archives @@ -107,6 +109,8 @@ For enhanced file previews (with `scope.sh`): * `jupyter nbconvert` for Jupyter Notebooks * `fontimage` for font previews * `openscad` for 3D model previews (`stl`, `off`, `dxf`, `scad`, `csg`) +* `draw.io` for [draw.io](https://app.diagrams.net/) diagram previews + (`drawio` extension) Installing ---------- diff --git a/doc/ranger.1 b/doc/ranger.1 index a3d13994..cc166604 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -1,4 +1,4 @@ -.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) +.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.42) .\" .\" Standard preamble: .\" ======================================================================== @@ -133,7 +133,7 @@ .\" ======================================================================== .\" .IX Title "RANGER 1" -.TH RANGER 1 "ranger-1.9.3" "2021-11-13" "ranger manual" +.TH RANGER 1 "ranger-1.9.3" "2022-03-18" "ranger manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -308,7 +308,9 @@ are automatically used when available but completely optional. \&\f(CW\*(C`w3mimgdisplay\*(C'\fR, \f(CW\*(C`ueberzug\*(C'\fR, \f(CW\*(C`mpv\*(C'\fR, \f(CW\*(C`iTerm2\*(C'\fR, \f(CW\*(C`kitty\*(C'\fR, \f(CW\*(C`terminology\*(C'\fR or \&\f(CW\*(C`urxvt\*(C'\fR for image previews .IP "\-" 2 -\&\f(CW\*(C`convert\*(C'\fR (from \f(CW\*(C`imagemagick\*(C'\fR) to auto-rotate images and for \s-1SVG\s0 previews +\&\f(CW\*(C`convert\*(C'\fR (from \f(CW\*(C`imagemagick\*(C'\fR) to auto-rotate images +.IP "\-" 2 +\&\f(CW\*(C`rsvg\-convert\*(C'\fR (from \f(CW\*(C`librsvg\*(C'\fR) for \s-1SVG\s0 previews .IP "\-" 2 \&\f(CW\*(C`ffmpegthumbnailer\*(C'\fR for video thumbnails .IP "\-" 2 @@ -316,11 +318,13 @@ are automatically used when available but completely optional. .IP "\-" 2 \&\f(CW\*(C`atool\*(C'\fR, \f(CW\*(C`bsdtar\*(C'\fR, \f(CW\*(C`unrar\*(C'\fR and/or \f(CW\*(C`7z\*(C'\fR to preview archives .IP "\-" 2 -\&\f(CW\*(C`bsdtar\*(C'\fR, \f(CW\*(C`tar\*(C'\fR, \f(CW\*(C`unrar\*(C'\fR, \f(CW\*(C`unzip\*(C'\fR and/or \f(CW\*(C`zipinfo\*(C'\fR (and \f(CW\*(C`sed\*(C'\fR) to preview archives as their first image +\&\f(CW\*(C`bsdtar\*(C'\fR, \f(CW\*(C`tar\*(C'\fR, \f(CW\*(C`unrar\*(C'\fR, \f(CW\*(C`unzip\*(C'\fR and/or \f(CW\*(C`zipinfo\*(C'\fR (and \f(CW\*(C`sed\*(C'\fR) to preview +archives as their first image .IP "\-" 2 \&\f(CW\*(C`lynx\*(C'\fR, \f(CW\*(C`w3m\*(C'\fR or \f(CW\*(C`elinks\*(C'\fR to preview html pages .IP "\-" 2 -\&\f(CW\*(C`pdftotext\*(C'\fR or \f(CW\*(C`mutool\*(C'\fR (and \f(CW\*(C`fmt\*(C'\fR) for textual pdf previews, \f(CW\*(C`pdftoppm\*(C'\fR to preview as image +\&\f(CW\*(C`pdftotext\*(C'\fR or \f(CW\*(C`mutool\*(C'\fR (and \f(CW\*(C`fmt\*(C'\fR) for textual pdf previews, \f(CW\*(C`pdftoppm\*(C'\fR to +preview as image .IP "\-" 2 \&\f(CW\*(C`djvutxt\*(C'\fR for textual DjVu previews, \f(CW\*(C`ddjvu\*(C'\fR to preview as image .IP "\-" 2 @@ -335,6 +339,11 @@ are automatically used when available but completely optional. \&\f(CW\*(C`python\*(C'\fR or \f(CW\*(C`jq\*(C'\fR for \s-1JSON\s0 files .IP "\-" 2 \&\f(CW\*(C`fontimage\*(C'\fR for font previews +.IP "\-" 2 +\&\f(CW\*(C`openscad\*(C'\fR for 3D model previews (stl, off, dxf, scad, csg) +.IP "\-" 2 +\&\f(CW\*(C`draw.io\*(C'\fR for draw.io <https://app.diagrams.net/> diagram previews (drawio +extension) .RE .RS 2 .RE @@ -1445,8 +1454,8 @@ following \f(CW\*(C`FILTER_TYPE\*(C'\fRs are available: Filter files so only files that have duplicates in the same directory are shown. Useful when cleaning up identical songs and memes that were saved using distinct file names. -.IP "filename \s-1NAME\s0" 2 -.IX Item "filename NAME" +.IP "name \s-1NAME\s0" 2 +.IX Item "name NAME" Filter files that contain \s-1NAME\s0 in the filename, regular expression syntax is allowed. .IP "hash \s-1PATH\s0" 2 diff --git a/doc/ranger.pod b/doc/ranger.pod index 2484581c..f3413856 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -234,7 +234,11 @@ C<urxvt> for image previews =item - -C<convert> (from C<imagemagick>) to auto-rotate images and for SVG previews +C<convert> (from C<imagemagick>) to auto-rotate images + +=item - + +C<rsvg-convert> (from C<librsvg>) for SVG previews =item - @@ -250,7 +254,8 @@ C<atool>, C<bsdtar>, C<unrar> and/or C<7z> to preview archives =item - -C<bsdtar>, C<tar>, C<unrar>, C<unzip> and/or C<zipinfo> (and C<sed>) to preview archives as their first image +C<bsdtar>, C<tar>, C<unrar>, C<unzip> and/or C<zipinfo> (and C<sed>) to preview +archives as their first image =item - @@ -258,7 +263,8 @@ C<lynx>, C<w3m> or C<elinks> to preview html pages =item - -C<pdftotext> or C<mutool> (and C<fmt>) for textual pdf previews, C<pdftoppm> to preview as image +C<pdftotext> or C<mutool> (and C<fmt>) for textual pdf previews, C<pdftoppm> to +preview as image =item - @@ -288,6 +294,15 @@ C<python> or C<jq> for JSON files C<fontimage> for font previews +=item - + +C<openscad> for 3D model previews (stl, off, dxf, scad, csg) + +=item - + +C<draw.io> for draw.io L<https://app.diagrams.net/> diagram previews (drawio +extension) + =back =back @@ -1560,7 +1575,7 @@ Filter files so only files that have duplicates in the same directory are shown. Useful when cleaning up identical songs and memes that were saved using distinct file names. -=item filename NAME +=item name NAME Filter files that contain NAME in the filename, regular expression syntax is allowed. 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/core/loader.py b/ranger/core/loader.py index 5c9e28a5..19611c7b 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -192,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/data/scope.sh b/ranger/data/scope.sh index 36c462c6..2e9983ee 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -140,9 +140,11 @@ handle_image() { local mimetype="${1}" case "${mimetype}" in ## SVG - # image/svg+xml|image/svg) - # convert -- "${FILE_PATH}" "${IMAGE_CACHE_PATH}" && exit 6 - # exit 1;; + image/svg+xml|image/svg) + rsvg-convert --keep-aspect-ratio --width "${DEFAULT_SIZE%x*}" "${FILE_PATH}" -o "${IMAGE_CACHE_PATH}.png" \ + && mv "${IMAGE_CACHE_PATH}.png" "${IMAGE_CACHE_PATH}" \ + && exit 6 + exit 1;; ## DjVu # image/vnd.djvu) @@ -268,19 +270,23 @@ handle_image() { # mv "${TMPPNG}" "${IMAGE_CACHE_PATH}" # } - # case "${FILE_EXTENSION_LOWER}" in - # ## 3D models - # ## OpenSCAD only supports png image output, and ${IMAGE_CACHE_PATH} - # ## is hardcoded as jpeg. So we make a tempfile.png and just - # ## move/rename it to jpg. This works because image libraries are - # ## smart enough to handle it. - # csg|scad) - # openscad_image "${FILE_PATH}" && exit 6 - # ;; - # 3mf|amf|dxf|off|stl) - # openscad_image <(echo "import(\"${FILE_PATH}\");") && exit 6 - # ;; - # esac + case "${FILE_EXTENSION_LOWER}" in + ## 3D models + ## OpenSCAD only supports png image output, and ${IMAGE_CACHE_PATH} + ## is hardcoded as jpeg. So we make a tempfile.png and just + ## move/rename it to jpg. This works because image libraries are + ## smart enough to handle it. + # csg|scad) + # openscad_image "${FILE_PATH}" && exit 6 + # ;; + # 3mf|amf|dxf|off|stl) + # openscad_image <(echo "import(\"${FILE_PATH}\");") && exit 6 + # ;; + drawio) + draw.io -x "${FILE_PATH}" -o "${IMAGE_CACHE_PATH}" \ + --width "${DEFAULT_SIZE%x*}" && exit 6 + exit 1;; + esac } handle_mime() { diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py index 43e4203d..61e10f96 100644 --- a/ranger/ext/img_display.py +++ b/ranger/ext/img_display.py @@ -643,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 diff --git a/ranger/ext/vcs/vcs.py b/ranger/ext/vcs/vcs.py index 5619ed19..93fb1d0b 100644 --- a/ranger/ext/vcs/vcs.py +++ b/ranger/ext/vcs/vcs.py @@ -463,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() @@ -491,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/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 7156aba7..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 @@ -22,9 +23,10 @@ class TestPy2CompatibilityChecker(pylint.testutils.CheckerTestCase): """) with self.assertAddsMessages( - pylint.testutils.Message( + pylint.testutils.MessageTest( msg_id='old-style-class', node=oldstyle_class, + confidence=HIGH, ), ): self.checker.visit_classdef(oldstyle_class) @@ -53,9 +55,10 @@ class TestPy2CompatibilityChecker(pylint.testutils.CheckerTestCase): """) with self.assertAddsMessages( - pylint.testutils.Message( + pylint.testutils.MessageTest( msg_id='print-without-import', node=print_function_call, + confidence=HIGH, ), ): self.checker.visit_call(print_function_call) @@ -92,9 +95,10 @@ class TestPy2CompatibilityChecker(pylint.testutils.CheckerTestCase): """) with self.assertAddsMessages( - pylint.testutils.Message( + pylint.testutils.MessageTest( msg_id='print-without-import', node=early_print_function_call, + confidence=HIGH, ), ): self.checker.visit_call(early_print_function_call) @@ -108,9 +112,10 @@ class TestPy2CompatibilityChecker(pylint.testutils.CheckerTestCase): """) with self.assertAddsMessages( - pylint.testutils.Message( + pylint.testutils.MessageTest( msg_id='implicit-format-spec', node=implicit_format_spec, + confidence=HIGH, ), ): self.checker.visit_call(implicit_format_spec) @@ -131,9 +136,10 @@ class TestPy2CompatibilityChecker(pylint.testutils.CheckerTestCase): """) with self.assertAddsMessages( - pylint.testutils.Message( + 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(""" @@ -148,7 +173,7 @@ class TestPy2CompatibilityChecker(pylint.testutils.CheckerTestCase): # """) # with self.assertAddsMessages( - # pylint.testutils.Message( + # pylint.testutils.MessageTest( # msg_id='division-without-import', # node=division, # ), @@ -170,7 +195,7 @@ class TestPy2CompatibilityChecker(pylint.testutils.CheckerTestCase): # """) # with self.assertAddsMessages( - # pylint.testutils.Message( + # pylint.testutils.MessageTest( # msg_id='old-no-absolute-import', # node=no_import, # ), |