From 10180edfb75c06a0b84553c3a6c581f9de71dcff Mon Sep 17 00:00:00 2001 From: jason-kane Date: Wed, 4 Jul 2018 09:49:06 -0700 Subject: [PATCH 1/4] Check common locations for yapf and persist to settings --- PyYapf.py | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/PyYapf.py b/PyYapf.py index 3b2ab9e..c3b6c5d 100644 --- a/PyYapf.py +++ b/PyYapf.py @@ -148,19 +148,7 @@ def __enter__(self): else: self.custom_style_fname = None - # prepare popen arguments - cmd = self.get_setting("yapf_command") - if not cmd: - # always show error in popup - msg = 'Yapf command not configured. Problem with settings?' - sublime.error_message(msg) - raise Exception(msg) - cmd = os.path.expanduser(cmd) - cmd = sublime.expand_variables( - cmd, - sublime.active_window().extract_variables()) - - self.popen_args = [cmd] + self.popen_args = [self.find_yapf()] if self.custom_style_fname: self.popen_args += ['--style', self.custom_style_fname] @@ -190,6 +178,40 @@ def __exit__(self, type, value, traceback): if self.custom_style_fname: os.unlink(self.custom_style_fname) + def find_yapf(self): + """Find the yapf executable.""" + # default to what is in the settings file + cmd = self.get_setting("yapf_command") + if not cmd: + # always show error in popup + msg = 'Yapf command not configured. Problem with settings?' + sublime.error_message(msg) + raise Exception(msg) + + cmd = os.path.expanduser(cmd) + cmd = sublime.expand_variables( + cmd, + sublime.active_window().extract_variables()) + + if not os.path.exists(cmd): + # some common possibilities + for common_location in [ + "/usr/local/bin/yapf", # most common + "/usr/bin/yapf3", # Ubnt 18.04 + "~/Library/Python/2.7/bin/yapf", # OSx installed with --user + ]: + if os.path.exists( + sublime.expand_variables( + os.path.expanduser(common_location), + sublime.active_window().extract_variables())): + cmd = common_location + settings = sublime.load_settings(PLUGIN_SETTINGS_FILE) + settings.set("yapf_command", common_location) + sublime.save_settings(PLUGIN_SETTINGS_FILE) + break + + return cmd + def format(self, edit, selection=None): """ Format selection (if None then formats the entire document). From 289f7b67a35bcf89acc700e7735c5ca06fc046b8 Mon Sep 17 00:00:00 2001 From: jason-kane Date: Wed, 4 Jul 2018 10:24:17 -0700 Subject: [PATCH 2/4] search for yapf the first time, use settings after that --- PyYapf.py | 10 +++------- PyYapf.sublime-settings | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/PyYapf.py b/PyYapf.py index c3b6c5d..732392c 100644 --- a/PyYapf.py +++ b/PyYapf.py @@ -182,23 +182,19 @@ def find_yapf(self): """Find the yapf executable.""" # default to what is in the settings file cmd = self.get_setting("yapf_command") - if not cmd: - # always show error in popup - msg = 'Yapf command not configured. Problem with settings?' - sublime.error_message(msg) - raise Exception(msg) - cmd = os.path.expanduser(cmd) cmd = sublime.expand_variables( cmd, sublime.active_window().extract_variables()) - if not os.path.exists(cmd): + if not cmd: # some common possibilities for common_location in [ "/usr/local/bin/yapf", # most common "/usr/bin/yapf3", # Ubnt 18.04 "~/Library/Python/2.7/bin/yapf", # OSx installed with --user + "C:\\Python\\Scripts\\yapf.exe", # Ugg. + "~/AppData/Local/Programs/Python/Python36-32/Scripts/yapf.exe", ]: if os.path.exists( sublime.expand_variables( diff --git a/PyYapf.sublime-settings b/PyYapf.sublime-settings index 17cc98c..35eba43 100644 --- a/PyYapf.sublime-settings +++ b/PyYapf.sublime-settings @@ -1,6 +1,6 @@ { // full path and command to run yapf - "yapf_command": "/usr/local/bin/yapf", + "yapf_command": "", // reformat entire file if no text is selected "use_entire_file_if_no_selection": true, From 451af08538a3dcdf3c464bee66e9ec2a4ca799be Mon Sep 17 00:00:00 2001 From: jason-kane Date: Wed, 4 Jul 2018 10:56:45 -0700 Subject: [PATCH 3/4] alternative approach (thanks @catch22) --- PyYapf.py | 33 ++++++++--------- backports/LICENSE | 27 ++++++++++++++ backports/__init__.py | 4 ++ backports/shutil_which.py | 77 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+), 17 deletions(-) create mode 100644 backports/LICENSE create mode 100644 backports/__init__.py create mode 100644 backports/shutil_which.py diff --git a/PyYapf.py b/PyYapf.py index 732392c..d031bbf 100644 --- a/PyYapf.py +++ b/PyYapf.py @@ -17,6 +17,11 @@ import sublime import sublime_plugin +try: + from shutil import which +except ImportError: + from backports.shutil_which import which + # make sure we don't choke on unicode when we reformat ourselves u"我爱蟒蛇" @@ -187,24 +192,18 @@ def find_yapf(self): cmd, sublime.active_window().extract_variables()) + save_settings = not cmd + if not cmd: - # some common possibilities - for common_location in [ - "/usr/local/bin/yapf", # most common - "/usr/bin/yapf3", # Ubnt 18.04 - "~/Library/Python/2.7/bin/yapf", # OSx installed with --user - "C:\\Python\\Scripts\\yapf.exe", # Ugg. - "~/AppData/Local/Programs/Python/Python36-32/Scripts/yapf.exe", - ]: - if os.path.exists( - sublime.expand_variables( - os.path.expanduser(common_location), - sublime.active_window().extract_variables())): - cmd = common_location - settings = sublime.load_settings(PLUGIN_SETTINGS_FILE) - settings.set("yapf_command", common_location) - sublime.save_settings(PLUGIN_SETTINGS_FILE) - break + cmd = which("yapf") + + if not cmd: + cmd = which("yapf3") + + if cmd and save_settings: + settings = sublime.load_settings(PLUGIN_SETTINGS_FILE) + settings.set("yapf_command", cmd) + sublime.save_settings(PLUGIN_SETTINGS_FILE) return cmd diff --git a/backports/LICENSE b/backports/LICENSE new file mode 100644 index 0000000..33fc09f --- /dev/null +++ b/backports/LICENSE @@ -0,0 +1,27 @@ +This repo is Copyright (c) 2016 Min RK, and licensed under the MIT license. + +Since the source of `shutil.which` is a backport from a Python standard library module, +the code itself is licensed under the Python Software Foundation (PSF) License. +The backporting part (setup.py, etc.) are MIT. + +The MIT License (MIT) + +Copyright (c) 2016 Min RK + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/backports/__init__.py b/backports/__init__.py new file mode 100644 index 0000000..474b93e --- /dev/null +++ b/backports/__init__.py @@ -0,0 +1,4 @@ +# from https://github.com/minrk/backports.shutil_which + +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) \ No newline at end of file diff --git a/backports/shutil_which.py b/backports/shutil_which.py new file mode 100644 index 0000000..f02706c --- /dev/null +++ b/backports/shutil_which.py @@ -0,0 +1,77 @@ +"""Backport of shutil.which from Python 3.5 + +The function is included unmodified from Python stdlib 3.5.1, +and is (C) Python +""" + +import os +import sys + +__version__ = '3.5.1' + +def backport_which(cmd, mode=os.F_OK | os.X_OK, path=None): + """Given a command, mode, and a PATH string, return the path which + conforms to the given mode on the PATH, or None if there is no such + file. + + `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result + of os.environ.get("PATH"), or can be overridden with a custom search + path. + + """ + # Check that a given file can be accessed with the correct mode. + # Additionally check that `file` is not a directory, as on Windows + # directories pass the os.access check. + def _access_check(fn, mode): + return (os.path.exists(fn) and os.access(fn, mode) + and not os.path.isdir(fn)) + + # If we're given a path with a directory part, look it up directly rather + # than referring to PATH directories. This includes checking relative to the + # current directory, e.g. ./script + if os.path.dirname(cmd): + if _access_check(cmd, mode): + return cmd + return None + + if path is None: + path = os.environ.get("PATH", os.defpath) + if not path: + return None + path = path.split(os.pathsep) + + if sys.platform == "win32": + # The current directory takes precedence on Windows. + if not os.curdir in path: + path.insert(0, os.curdir) + + # PATHEXT is necessary to check on Windows. + pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + # See if the given file matches any of the expected path extensions. + # This will allow us to short circuit when given "python.exe". + # If it does match, only test that one, otherwise we have to try + # others. + if any(cmd.lower().endswith(ext.lower()) for ext in pathext): + files = [cmd] + else: + files = [cmd + ext for ext in pathext] + else: + # On other platforms you don't have things like PATHEXT to tell you + # what file suffixes are executable, so just pass on cmd as-is. + files = [cmd] + + seen = set() + for dir in path: + normdir = os.path.normcase(dir) + if not normdir in seen: + seen.add(normdir) + for thefile in files: + name = os.path.join(dir, thefile) + if _access_check(name, mode): + return name + return None + +try: + from shutil import which +except ImportError: + which = backport_which \ No newline at end of file From 15d083e98948a6907be8ab3a9e4cdfe52cdb675d Mon Sep 17 00:00:00 2001 From: jason-kane Date: Wed, 4 Jul 2018 11:20:04 -0700 Subject: [PATCH 4/4] include windows .exe filenames --- PyYapf.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/PyYapf.py b/PyYapf.py index d031bbf..a351087 100644 --- a/PyYapf.py +++ b/PyYapf.py @@ -194,11 +194,9 @@ def find_yapf(self): save_settings = not cmd - if not cmd: - cmd = which("yapf") - - if not cmd: - cmd = which("yapf3") + for maybe_cmd in ['yapf', 'yapf3', 'yapf.exe', 'yapf3.exe']: + if not cmd: + cmd = which(maybe_cmd) if cmd and save_settings: settings = sublime.load_settings(PLUGIN_SETTINGS_FILE)