From 68a61bbd81196bfe7d753e9e205aa81e630d6c3e Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Tue, 22 May 2018 13:10:37 +0700 Subject: [PATCH 01/16] Update setup.py --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 00c905b..43c4cc1 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ #__requires__ = [] __extra_requires__ = { ':sys_platform == "win32"': [ - 'pypiwin32', + 'pywin32', 'winshell', ], } @@ -58,4 +58,4 @@ 'console_scripts': [ 'shortcut = shortcut:main' ]}, - zip_safe=False) \ No newline at end of file + zip_safe=False) From ae40f3f659b1c3d02e6bf1519cb91f89c96978bc Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Tue, 22 May 2018 16:45:40 +0700 Subject: [PATCH 02/16] Update linux.py --- shortcut/linux.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shortcut/linux.py b/shortcut/linux.py index f0a11a0..28fae63 100644 --- a/shortcut/linux.py +++ b/shortcut/linux.py @@ -6,7 +6,8 @@ class ShortCutterLinux(ShortCutter): def _get_desktop_folder(self): - return os.path.join(os.path.join(os.path.expanduser('~')), 'Desktop') + import subprocess + return subprocess.check_output(['xdg-user-dir', 'DESKTOP']).decode('utf-8') def _get_menu_folder(self): return os.path.join(os.path.join(os.path.expanduser('~')), '.local', 'share', 'applications') From 801efbd27aaac6dc716b82bc361bcb643ab4d198 Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Tue, 22 May 2018 16:49:17 +0700 Subject: [PATCH 03/16] Update linux.py --- shortcut/linux.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shortcut/linux.py b/shortcut/linux.py index 28fae63..46796e6 100644 --- a/shortcut/linux.py +++ b/shortcut/linux.py @@ -7,7 +7,7 @@ class ShortCutterLinux(ShortCutter): def _get_desktop_folder(self): import subprocess - return subprocess.check_output(['xdg-user-dir', 'DESKTOP']).decode('utf-8') + return subprocess.check_output(['xdg-user-dir', 'DESKTOP']).decode('utf-8').replace('\n', '') def _get_menu_folder(self): return os.path.join(os.path.join(os.path.expanduser('~')), '.local', 'share', 'applications') From e941dc9d3f388e564aea5705db6e9e6e983519ed Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Tue, 22 May 2018 16:59:38 +0700 Subject: [PATCH 04/16] Update linux.py --- shortcut/linux.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/shortcut/linux.py b/shortcut/linux.py index 46796e6..e6f550c 100644 --- a/shortcut/linux.py +++ b/shortcut/linux.py @@ -2,13 +2,16 @@ import stat from .exception import * from .base import ShortCutter - + class ShortCutterLinux(ShortCutter): - + def _get_desktop_folder(self): import subprocess - return subprocess.check_output(['xdg-user-dir', 'DESKTOP']).decode('utf-8').replace('\n', '') - + return subprocess.check_output([ + 'xdg-user-dir', + 'DESKTOP' + ]).decode('utf-8').replace('\n', '') + def _get_menu_folder(self): return os.path.join(os.path.join(os.path.expanduser('~')), '.local', 'share', 'applications') @@ -29,9 +32,9 @@ def _create_shortcut_file(self, target_name, target_path, shortcut_directory): # make the launch file executable st = os.stat(shortcut_file_path) os.chmod(shortcut_file_path, st.st_mode | stat.S_IEXEC) - + return shortcut_file_path - + def _is_file_the_target(self, target, file_name, file_path): match = False if file_name == target: From f37a1e52180534a8cc48ad959f7a03f1788a5f57 Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Wed, 23 May 2018 12:15:48 +0700 Subject: [PATCH 05/16] Update linux.py --- shortcut/linux.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shortcut/linux.py b/shortcut/linux.py index e6f550c..f032398 100644 --- a/shortcut/linux.py +++ b/shortcut/linux.py @@ -10,7 +10,7 @@ def _get_desktop_folder(self): return subprocess.check_output([ 'xdg-user-dir', 'DESKTOP' - ]).decode('utf-8').replace('\n', '') + ]).decode('utf-8').strip() def _get_menu_folder(self): return os.path.join(os.path.join(os.path.expanduser('~')), '.local', 'share', 'applications') From 34fbcb3488e54e5510ffefdf4f4ef5fac86bb892 Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Thu, 24 May 2018 01:32:46 +0700 Subject: [PATCH 06/16] Update linux.py --- shortcut/linux.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shortcut/linux.py b/shortcut/linux.py index f032398..d6a00bf 100644 --- a/shortcut/linux.py +++ b/shortcut/linux.py @@ -25,7 +25,7 @@ def _create_shortcut_file(self, target_name, target_path, shortcut_directory): with open(shortcut_file_path, "w") as shortcut: shortcut.write("[Desktop Entry]\n") shortcut.write("Name={}\n".format(target_name)) - shortcut.write("Exec={}\n".format(target_path)) + shortcut.write("Exec={} %F\n".format(target_path)) shortcut.write("Terminal=true\n") shortcut.write("Type=Application\n") From e3e4d419c467cd3bda5a5c8ad8ff6192afdcc8fd Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Sat, 2 Jun 2018 16:11:59 +0700 Subject: [PATCH 07/16] Update macos.py --- shortcut/macos.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/shortcut/macos.py b/shortcut/macos.py index 8c987f7..82f97e8 100644 --- a/shortcut/macos.py +++ b/shortcut/macos.py @@ -5,10 +5,13 @@ import subprocess class ShortCutterMacOS(ShortCutterLinux): - + + def _get_desktop_folder(self): + return os.path.join(os.path.join(os.path.expanduser('~')), 'Desktop') + def _get_menu_folder(self): return os.path.join('/', 'Applications') - + def _create_shortcut_file(self, target_name, target_path, shortcut_directory): """ Creates a MacOS app which opens an executable via the terminal From d2303e39c4b0667385d5f8ecb2ecd47a5f284395 Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Sun, 3 Jun 2018 01:22:30 +0700 Subject: [PATCH 08/16] shortcuts to folders, set shortcut name, error handling change --- shortcut/base.py | 131 ++++++++++++++++++++++++++++++++++++-------- shortcut/linux.py | 18 +++++- shortcut/macos.py | 4 +- shortcut/windows.py | 32 ++++++++--- 4 files changed, 151 insertions(+), 34 deletions(-) diff --git a/shortcut/base.py b/shortcut/base.py index 708bb78..a17159c 100644 --- a/shortcut/base.py +++ b/shortcut/base.py @@ -2,6 +2,8 @@ import os import stat from .exception import * +import traceback + class ShortCutter(object): """ @@ -15,19 +17,20 @@ class ShortCutter(object): s.create_menu_shortcut("python") """ - def __init__(self): + def __init__(self, err_file=None): + self.err_file = sys.stderr if (err_file is None) else err_file self._desktop_folder = self._get_desktop_folder() self._menu_folder = self._get_menu_folder() # should be overridden def _get_desktop_folder(self): raise ShortcutError("_get_desktop_folder needs overriding") - + # should be overridden def _get_menu_folder(self): raise ShortcutError("_get_menu_folder needs overriding") - def create_desktop_shortcut(self, target): + def create_desktop_shortcut(self, target, target_name=None, target_is_dir=False, virtual=True): """ Creates a desktop shortcut to a target. @@ -35,15 +38,25 @@ def create_desktop_shortcut(self, target): The target to create a shortcut for, it can be a fully qualified file path `/path/to/my_program` or a simple application name `my_program`. - - Returns a tuple of (target_name, target_path, shortcut_file_path) + :param str target_name: + Name of the shortcut without extension (.lnk would be appended if needed). + :param bool target_is_dir: + whether it's a shortcut to a directory + :param bool virtual: + whether to allow shortcuts to yet non-existing files + (shortcuts to dirs always work on non-existing dirs) + + Returns a tuple of (target_name, target_path, shortcut_file_path) or None """ if not os.path.isdir(self._desktop_folder): - raise ShortcutNoDesktopError("Desktop folder '{}' not found".format(self._desktop_folder)) - - return self.create_shortcut(target, self._desktop_folder) + print("Desktop folder '{}' not found.\n".format(self._desktop_folder), file=self.err_file) + else: + if target_is_dir: + return self.create_shortcut_to_dir(target, self._desktop_folder, target_name) + else: + return self.create_shortcut(target, self._desktop_folder, target_name, virtual) - def create_menu_shortcut(self, target): + def create_menu_shortcut(self, target, target_name=None, target_is_dir=False, virtual=True): """ Creates a menu shortcut to a target. @@ -51,15 +64,25 @@ def create_menu_shortcut(self, target): The target to create a shortcut for, it can be a fully qualified file path `/path/to/my_program` or a simple application name `my_program`. - - Returns a tuple of (target_name, target_path, shortcut_file_path) + :param str target_name: + Name of the shortcut without extension (.lnk would be appended if needed). + :param bool target_is_dir: + whether it's a shortcut to a directory + :param bool virtual: + whether to allow shortcuts to yet non-existing files + (shortcuts to dirs always work on non-existing dirs) + + Returns a tuple of (target_name, target_path, shortcut_file_path) or None """ if not os.path.isdir(self._menu_folder): - raise ShortcutNoMenuError("Menu folder '{}' not found".format(self._menu_folder)) - - return self.create_shortcut(target, self._menu_folder) + print("Menu folder '{}' not found.\n".format(self._menu_folder), file=self.err_file) + else: + if target_is_dir: + return self.create_shortcut_to_dir(target, self._menu_folder, target_name) + else: + return self.create_shortcut(target, self._menu_folder, target_name, virtual) - def create_shortcut(self, target, shortcut_directory): + def create_shortcut(self, target, shortcut_directory, target_name=None, virtual=True): """ Creates a shortcut to a target. @@ -67,25 +90,87 @@ def create_shortcut(self, target, shortcut_directory): The target to create a shortcut for, it can be a fully qualified file path `/path/to/my_program` or a simple application name `my_program`. - :param str shortcut_directory: The directory path where the shortcut should be created. + :param str target_name: + Name of the shortcut without extension (.lnk would be appended if needed). + :param bool virtual: + whether to allow shortcuts to yet non-existing files Returns a tuple of (target_name, target_path, shortcut_file_path) """ - # get the target name by getting the file name and removing the extension - target_name = os.path.splitext(os.path.basename(target))[0] + if target_name is None: + # get the target name by getting the file name and removing the extension + target_name = os.path.splitext(os.path.basename(target))[0] # find for the target path target_path = self.find_target(target) - shortcut_file_path = self._create_shortcut_file(target_name, target_path, shortcut_directory) + # Create temporal file in order to create shortcut in virtual mode: + clean = False + if virtual and (target_path is None): + try: + if not os.path.isdir(os.path.dirname(target)): + os.makedirs(os.path.dirname(target)) + open(target, 'a').close() + target_path = self.find_target(target) + clean = True + except (OSError, IOError): + print(''.join(traceback.format_exc()), file=self.err_file) + + # Create shortcut to the target_path: + try: + shortcut_file_path = self._create_shortcut_file(target_name, target_path, shortcut_directory) + except: + shortcut_file_path = None + print(''.join(traceback.format_exc()), file=self.err_file) + + # Delete temporal file: + if clean: + os.remove(target) + + return (target_name, target_path, shortcut_file_path) + + def create_shortcut_to_dir(self, target_path, shortcut_directory, target_name=None): + """ + Creates a shortcut to a direcrory. + + :param str target_path: + The target directory path to create a shortcut for. + :param str shortcut_directory: + The directory path where the shortcut should be created. + :param str target_name: + Name of the shortcut without extension (.lnk would be appended if needed). + + Returns a tuple of (target_name, target_path, shortcut_file_path) + """ + if target_name is None: + # get the target_name by getting the target dir name + target_name = os.path.basename(target_path) + + # Create target_path if it doesn't exist: + if not os.path.isdir(target_path): + try: + os.makedirs(target_path) + except OSError: + print(''.join(traceback.format_exc()), file=self.err_file) + + # Create shortcut to the target_path: + try: + shortcut_file_path = self._create_shortcut_to_dir(target_name, target_path, shortcut_directory) + except: + shortcut_file_path = None + print(''.join(traceback.format_exc()), file=self.err_file) return (target_name, target_path, shortcut_file_path) - #needs overriding + # should be overridden + def _create_shortcut_to_dir(self, target_name, target_path, shortcut_directory): + raise ShortcutError("_create_shortcut_to_dir needs overriding") + + # should be overridden def _create_shortcut_file(self, target_name, target_path, shortcut_directory): - raise ShortcutError("create_shortcut_file needs overriding") + raise ShortcutError("_create_shortcut_file needs overriding") def find_target(self, target): """ @@ -151,7 +236,7 @@ def _get_paths(self): """ # get folders from PATH paths = os.environ['PATH'].split(os.pathsep) - + return paths @property @@ -174,4 +259,4 @@ def menu_directory(self): @menu_directory.setter def menu_directory(self, value): - self._menu_folder = value \ No newline at end of file + self._menu_folder = value diff --git a/shortcut/linux.py b/shortcut/linux.py index d6a00bf..551dca6 100644 --- a/shortcut/linux.py +++ b/shortcut/linux.py @@ -2,9 +2,11 @@ import stat from .exception import * from .base import ShortCutter +import traceback +import sys -class ShortCutterLinux(ShortCutter): +class ShortCutterLinux(ShortCutter): def _get_desktop_folder(self): import subprocess return subprocess.check_output([ @@ -15,11 +17,23 @@ def _get_desktop_folder(self): def _get_menu_folder(self): return os.path.join(os.path.join(os.path.expanduser('~')), '.local', 'share', 'applications') + def _create_shortcut_to_dir(self, target_name, target_path, shortcut_directory): + """ + Creates a Unix shortcut to a directory via symbolic link. + + Returns shortcut_file_path + """ + shortcut_path = os.path.join(shortcut_directory, target_name) + if os.path.islink(shortcut_path): + os.remove(shortcut_path) + os.symlink(target_path, shortcut_path) + return shortcut_file_path + def _create_shortcut_file(self, target_name, target_path, shortcut_directory): """ Creates a Linux shortcut file. - Returns a tuple of (target_name, target_path, shortcut_file_path) + Returns shortcut_file_path """ shortcut_file_path = os.path.join(shortcut_directory, "launch_" + target_name + ".desktop") with open(shortcut_file_path, "w") as shortcut: diff --git a/shortcut/macos.py b/shortcut/macos.py index 82f97e8..7001f6a 100644 --- a/shortcut/macos.py +++ b/shortcut/macos.py @@ -4,8 +4,8 @@ from tempfile import NamedTemporaryFile import subprocess -class ShortCutterMacOS(ShortCutterLinux): +class ShortCutterMacOS(ShortCutterLinux): def _get_desktop_folder(self): return os.path.join(os.path.join(os.path.expanduser('~')), 'Desktop') @@ -16,7 +16,7 @@ def _create_shortcut_file(self, target_name, target_path, shortcut_directory): """ Creates a MacOS app which opens an executable via the terminal - Returns a the file path of the shortcut created + Returns the file path of the shortcut created """ shortcut_file_path = os.path.join(shortcut_directory, target_name + ".app") diff --git a/shortcut/windows.py b/shortcut/windows.py index 23e457e..ad139a4 100644 --- a/shortcut/windows.py +++ b/shortcut/windows.py @@ -18,28 +18,46 @@ raise e import winshell +from win32com.client import Dispatch import sys import os from .exception import * from .base import ShortCutter + class ShortCutterWindows(ShortCutter): - def __init__(self): + def __init__(self, err_file=None): self.executable_file_extensions = os.environ['PATHEXT'].split(os.pathsep) - super(ShortCutterWindows, self).__init__() + super(ShortCutterWindows, self).__init__(err_file) def _get_desktop_folder(self): - return winshell.folder("CSIDL_DESKTOPDIRECTORY") - + return winshell.desktop() + def _get_menu_folder(self): return winshell.folder("CSIDL_PROGRAMS") + def _create_shortcut_to_dir(self, target_name, target_path, shortcut_directory): + """ + Creates a Windows shortcut file for a directory. + TODO: This might be the same as _create_shortcut_file but it needs testing. + + Returns shortcut_file_path + """ + shell = Dispatch('WScript.Shell') + shortcut_file_path = os.path.join(shortcut_directory, target_name + '.lnk') + shortcut = shell.CreateShortCut(shortcut_file_path) + shortcut.Targetpath = target_path + shortcut.WorkingDirectory = target_path + shortcut.save() + + return shortcut_file_path + def _create_shortcut_file(self, target_name, target_path, shortcut_directory): """ Creates a Windows shortcut file. - Returns a tuple of (target_name, target_path, shortcut_file_path) + Returns shortcut_file_path """ shortcut_file_path = os.path.join(shortcut_directory, target_name + ".lnk") @@ -50,7 +68,7 @@ def _create_shortcut_file(self, target_name, target_path, shortcut_directory): Description = "Shortcut to" + target_name) return shortcut_file_path - + def _is_file_the_target(self, target, file_name, file_path): match = False # does the target have an extension? @@ -74,7 +92,7 @@ def _get_paths(self): Returns a list of paths. """ paths = super(ShortCutterWindows, self)._get_paths() - + # add the python scripts path python_scripts_path = self._get_python_scripts_path() if python_scripts_path not in paths: From fc830cfe33027eeeec9f6f8ed7a4b2637383739a Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Sun, 3 Jun 2018 01:49:08 +0700 Subject: [PATCH 09/16] for pycharm fixes --- shortcut/__init__.py | 5 ++--- shortcut/base.py | 8 +++++--- shortcut/exception.py | 2 ++ shortcut/linux.py | 12 +++++------- shortcut/macos.py | 5 +++-- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/shortcut/__init__.py b/shortcut/__init__.py index 8cc2bed..31668c2 100644 --- a/shortcut/__init__.py +++ b/shortcut/__init__.py @@ -1,3 +1,4 @@ +from .exception import * # get operating system import sys platform = sys.platform @@ -8,13 +9,12 @@ if platform == "win32": from .windows import ShortCutterWindows as ShortCutter elif platform == "linux": - from .linux import ShortCutterLinux as ShortCutter + from .linux import ShortCutterLinux as ShortCutter elif platform == "darwin": from .macos import ShortCutterMacOS as ShortCutter else: raise Exception("Error: '{}' platform is not supported.") -from .exception import * def main(): @@ -67,4 +67,3 @@ def main(): except ShortcutError as e: print("Shortcut failed: '{}'".format(e)) - diff --git a/shortcut/base.py b/shortcut/base.py index a17159c..997c292 100644 --- a/shortcut/base.py +++ b/shortcut/base.py @@ -1,6 +1,6 @@ import sys import os -import stat +# import stat from .exception import * import traceback @@ -119,6 +119,7 @@ def create_shortcut(self, target, shortcut_directory, target_name=None, virtual= print(''.join(traceback.format_exc()), file=self.err_file) # Create shortcut to the target_path: + # noinspection PyBroadException try: shortcut_file_path = self._create_shortcut_file(target_name, target_path, shortcut_directory) except: @@ -129,7 +130,7 @@ def create_shortcut(self, target, shortcut_directory, target_name=None, virtual= if clean: os.remove(target) - return (target_name, target_path, shortcut_file_path) + return target_name, target_path, shortcut_file_path def create_shortcut_to_dir(self, target_path, shortcut_directory, target_name=None): """ @@ -156,13 +157,14 @@ def create_shortcut_to_dir(self, target_path, shortcut_directory, target_name=No print(''.join(traceback.format_exc()), file=self.err_file) # Create shortcut to the target_path: + # noinspection PyBroadException try: shortcut_file_path = self._create_shortcut_to_dir(target_name, target_path, shortcut_directory) except: shortcut_file_path = None print(''.join(traceback.format_exc()), file=self.err_file) - return (target_name, target_path, shortcut_file_path) + return target_name, target_path, shortcut_file_path # should be overridden def _create_shortcut_to_dir(self, target_name, target_path, shortcut_directory): diff --git a/shortcut/exception.py b/shortcut/exception.py index f97307a..6a63834 100644 --- a/shortcut/exception.py +++ b/shortcut/exception.py @@ -1,8 +1,10 @@ class ShortcutError(Exception): pass + class ShortcutNoDesktopError(ShortcutError): pass + class ShortcutNoMenuError(ShortcutError): pass diff --git a/shortcut/linux.py b/shortcut/linux.py index 551dca6..6edc032 100644 --- a/shortcut/linux.py +++ b/shortcut/linux.py @@ -1,9 +1,7 @@ import os import stat -from .exception import * +# from .exception import * from .base import ShortCutter -import traceback -import sys class ShortCutterLinux(ShortCutter): @@ -23,10 +21,10 @@ def _create_shortcut_to_dir(self, target_name, target_path, shortcut_directory): Returns shortcut_file_path """ - shortcut_path = os.path.join(shortcut_directory, target_name) - if os.path.islink(shortcut_path): - os.remove(shortcut_path) - os.symlink(target_path, shortcut_path) + shortcut_file_path = os.path.join(shortcut_directory, target_name) + if os.path.islink(shortcut_file_path): + os.remove(shortcut_file_path) + os.symlink(target_path, shortcut_file_path) return shortcut_file_path def _create_shortcut_file(self, target_name, target_path, shortcut_directory): diff --git a/shortcut/macos.py b/shortcut/macos.py index 7001f6a..00cbd7c 100644 --- a/shortcut/macos.py +++ b/shortcut/macos.py @@ -21,7 +21,7 @@ def _create_shortcut_file(self, target_name, target_path, shortcut_directory): shortcut_file_path = os.path.join(shortcut_directory, target_name + ".app") # create the AppleScript script - sf = NamedTemporaryFile(mode = "w") + sf = NamedTemporaryFile(mode="w") sf.write('tell application "Terminal"\n') sf.write('activate\n') sf.write('do script "{}"\n'.format(target_path)) @@ -29,7 +29,8 @@ def _create_shortcut_file(self, target_name, target_path, shortcut_directory): sf.flush() # compile the script into an application - result = subprocess.run(["osacompile", "-o", shortcut_file_path, sf.name], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + result = subprocess.run(["osacompile", "-o", shortcut_file_path, sf.name], stdout=subprocess.PIPE, + stderr=subprocess.PIPE) if len(result.stderr): raise ShortcutError("Error occured creating app - {}".format(str(result.stderr))) From 223b63c5af7335dde441565bdfee7fdb994aa2b4 Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Sun, 3 Jun 2018 02:11:18 +0700 Subject: [PATCH 10/16] py2 compat fix --- shortcut/base.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/shortcut/base.py b/shortcut/base.py index 997c292..7569bb6 100644 --- a/shortcut/base.py +++ b/shortcut/base.py @@ -49,7 +49,7 @@ def create_desktop_shortcut(self, target, target_name=None, target_is_dir=False, Returns a tuple of (target_name, target_path, shortcut_file_path) or None """ if not os.path.isdir(self._desktop_folder): - print("Desktop folder '{}' not found.\n".format(self._desktop_folder), file=self.err_file) + self.err_file.write("Desktop folder '{}' not found.\n".format(self._desktop_folder)) else: if target_is_dir: return self.create_shortcut_to_dir(target, self._desktop_folder, target_name) @@ -75,7 +75,7 @@ def create_menu_shortcut(self, target, target_name=None, target_is_dir=False, vi Returns a tuple of (target_name, target_path, shortcut_file_path) or None """ if not os.path.isdir(self._menu_folder): - print("Menu folder '{}' not found.\n".format(self._menu_folder), file=self.err_file) + self.err_file.write("Menu folder '{}' not found.\n".format(self._menu_folder)) else: if target_is_dir: return self.create_shortcut_to_dir(target, self._menu_folder, target_name) @@ -116,7 +116,7 @@ def create_shortcut(self, target, shortcut_directory, target_name=None, virtual= target_path = self.find_target(target) clean = True except (OSError, IOError): - print(''.join(traceback.format_exc()), file=self.err_file) + self.err_file.write(''.join(traceback.format_exc())) # Create shortcut to the target_path: # noinspection PyBroadException @@ -124,7 +124,7 @@ def create_shortcut(self, target, shortcut_directory, target_name=None, virtual= shortcut_file_path = self._create_shortcut_file(target_name, target_path, shortcut_directory) except: shortcut_file_path = None - print(''.join(traceback.format_exc()), file=self.err_file) + self.err_file.write(''.join(traceback.format_exc())) # Delete temporal file: if clean: @@ -154,7 +154,7 @@ def create_shortcut_to_dir(self, target_path, shortcut_directory, target_name=No try: os.makedirs(target_path) except OSError: - print(''.join(traceback.format_exc()), file=self.err_file) + self.err_file.write(''.join(traceback.format_exc())) # Create shortcut to the target_path: # noinspection PyBroadException @@ -162,7 +162,7 @@ def create_shortcut_to_dir(self, target_path, shortcut_directory, target_name=No shortcut_file_path = self._create_shortcut_to_dir(target_name, target_path, shortcut_directory) except: shortcut_file_path = None - print(''.join(traceback.format_exc()), file=self.err_file) + self.err_file.write(''.join(traceback.format_exc())) return target_name, target_path, shortcut_file_path From 6eff1eac5a003abf2101679b7407411b2769440a Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Mon, 4 Jun 2018 01:15:07 +0700 Subject: [PATCH 11/16] fixed according to this https://github.com/martinohanlon/shortcut/pull/5 --- shortcut/__init__.py | 2 +- shortcut/base.py | 146 ++++++++++++++++++++++++++++++------------- shortcut/linux.py | 11 ++-- shortcut/macos.py | 2 +- shortcut/windows.py | 16 ++++- 5 files changed, 122 insertions(+), 55 deletions(-) diff --git a/shortcut/__init__.py b/shortcut/__init__.py index 31668c2..239b0ab 100644 --- a/shortcut/__init__.py +++ b/shortcut/__init__.py @@ -1,4 +1,4 @@ -from .exception import * +from .exception import ShortcutError, ShortcutNoDesktopError, ShortcutNoMenuError # get operating system import sys platform = sys.platform diff --git a/shortcut/base.py b/shortcut/base.py index 7569bb6..bfd1ed8 100644 --- a/shortcut/base.py +++ b/shortcut/base.py @@ -1,7 +1,7 @@ import sys import os # import stat -from .exception import * +from .exception import ShortcutError, ShortcutNoDesktopError, ShortcutNoMenuError import traceback @@ -17,8 +17,23 @@ class ShortCutter(object): s.create_menu_shortcut("python") """ - def __init__(self, err_file=None): - self.err_file = sys.stderr if (err_file is None) else err_file + def __init__(self, silent=False, err_file=None, virtual=False): + """ + Creates ShortCutter. + + :param bool silent: + Whether to use shortcut in a silent mode. + :param err_file: + File object where to write errors in a silent mode. Default is sys.stderr + :param bool virtual: + Whether to allow shortcuts to yet non-existing files/dirs + """ + self._silent = silent + if silent: + self._err_file = sys.stderr if (err_file is None) else err_file + else: + self._err_file = None + self._virtual = virtual self._desktop_folder = self._get_desktop_folder() self._menu_folder = self._get_menu_folder() @@ -30,7 +45,7 @@ def _get_desktop_folder(self): def _get_menu_folder(self): raise ShortcutError("_get_menu_folder needs overriding") - def create_desktop_shortcut(self, target, target_name=None, target_is_dir=False, virtual=True): + def create_desktop_shortcut(self, target, target_name=None, target_is_dir=False, virtual=None): """ Creates a desktop shortcut to a target. @@ -40,23 +55,30 @@ def create_desktop_shortcut(self, target, target_name=None, target_is_dir=False, `my_program`. :param str target_name: Name of the shortcut without extension (.lnk would be appended if needed). + If `None` uses the target filename. Defaults to `None`. :param bool target_is_dir: - whether it's a shortcut to a directory - :param bool virtual: - whether to allow shortcuts to yet non-existing files - (shortcuts to dirs always work on non-existing dirs) + Whether it's a shortcut to a directory + :param bool virtual: None | True | False + Whether to create shortcut to yet non-existing file/dir (creates dir) + Default is None - use defined in __init__ Returns a tuple of (target_name, target_path, shortcut_file_path) or None """ + virtual = virtual if (virtual is not None) else self._virtual + if not os.path.isdir(self._desktop_folder): - self.err_file.write("Desktop folder '{}' not found.\n".format(self._desktop_folder)) + msg = "Desktop folder '{}' not found.".format(self._desktop_folder) + if not self._silent: + raise ShortcutNoDesktopError(msg) + else: + self._err_file.write(msg + '\n') else: if target_is_dir: - return self.create_shortcut_to_dir(target, self._desktop_folder, target_name) + return self.create_shortcut_to_dir(target, self._desktop_folder, target_name, virtual) else: return self.create_shortcut(target, self._desktop_folder, target_name, virtual) - def create_menu_shortcut(self, target, target_name=None, target_is_dir=False, virtual=True): + def create_menu_shortcut(self, target, target_name=None, target_is_dir=False, virtual=None): """ Creates a menu shortcut to a target. @@ -66,23 +88,30 @@ def create_menu_shortcut(self, target, target_name=None, target_is_dir=False, vi `my_program`. :param str target_name: Name of the shortcut without extension (.lnk would be appended if needed). + If `None` uses the target filename. Defaults to `None`. :param bool target_is_dir: - whether it's a shortcut to a directory - :param bool virtual: - whether to allow shortcuts to yet non-existing files - (shortcuts to dirs always work on non-existing dirs) + Whether it's a shortcut to a directory + :param bool virtual: None | True | False + Whether to create shortcut to yet non-existing file/dir (creates dir) + Default is None - use defined in __init__ Returns a tuple of (target_name, target_path, shortcut_file_path) or None """ + virtual = virtual if (virtual is not None) else self._virtual + if not os.path.isdir(self._menu_folder): - self.err_file.write("Menu folder '{}' not found.\n".format(self._menu_folder)) + msg = "Menu folder '{}' not found.".format(self._menu_folder) + if not self._silent: + raise ShortcutNoMenuError(msg) + else: + self._err_file.write(msg + '\n') else: if target_is_dir: - return self.create_shortcut_to_dir(target, self._menu_folder, target_name) + return self.create_shortcut_to_dir(target, self._menu_folder, target_name, virtual) else: return self.create_shortcut(target, self._menu_folder, target_name, virtual) - def create_shortcut(self, target, shortcut_directory, target_name=None, virtual=True): + def create_shortcut(self, target, shortcut_directory, target_name=None, virtual=None): """ Creates a shortcut to a target. @@ -94,37 +123,52 @@ def create_shortcut(self, target, shortcut_directory, target_name=None, virtual= The directory path where the shortcut should be created. :param str target_name: Name of the shortcut without extension (.lnk would be appended if needed). - :param bool virtual: - whether to allow shortcuts to yet non-existing files + If `None` uses the target filename. Defaults to `None`. + :param bool virtual: None | True | False + Whether to create shortcut to yet non-existing file + Default is None - use defined in __init__ Returns a tuple of (target_name, target_path, shortcut_file_path) """ + virtual = virtual if (virtual is not None) else self._virtual + if target_name is None: # get the target name by getting the file name and removing the extension target_name = os.path.splitext(os.path.basename(target))[0] - # find for the target path + # find the target path: target_path = self.find_target(target) # Create temporal file in order to create shortcut in virtual mode: clean = False - if virtual and (target_path is None): - try: - if not os.path.isdir(os.path.dirname(target)): - os.makedirs(os.path.dirname(target)) - open(target, 'a').close() - target_path = self.find_target(target) + def create_temp_target(): + if not os.path.isdir(os.path.dirname(target)): + os.makedirs(os.path.dirname(target)) + open(target, 'a').close() + + if (target_path is None) and virtual: + if not self._silent: + create_temp_target() clean = True - except (OSError, IOError): - self.err_file.write(''.join(traceback.format_exc())) + else: + try: + create_temp_target() + clean = True + except (OSError, IOError): + self.err_file.write(''.join(traceback.format_exc())) + + target_path = self.find_target(target) # Create shortcut to the target_path: - # noinspection PyBroadException - try: + if not self._silent: shortcut_file_path = self._create_shortcut_file(target_name, target_path, shortcut_directory) - except: - shortcut_file_path = None - self.err_file.write(''.join(traceback.format_exc())) + else: + # noinspection PyBroadException + try: + shortcut_file_path = self._create_shortcut_file(target_name, target_path, shortcut_directory) + except: + shortcut_file_path = None + self.err_file.write(''.join(traceback.format_exc())) # Delete temporal file: if clean: @@ -132,7 +176,7 @@ def create_shortcut(self, target, shortcut_directory, target_name=None, virtual= return target_name, target_path, shortcut_file_path - def create_shortcut_to_dir(self, target_path, shortcut_directory, target_name=None): + def create_shortcut_to_dir(self, target_path, shortcut_directory, target_name=None, virtual=None): """ Creates a shortcut to a direcrory. @@ -142,27 +186,39 @@ def create_shortcut_to_dir(self, target_path, shortcut_directory, target_name=No The directory path where the shortcut should be created. :param str target_name: Name of the shortcut without extension (.lnk would be appended if needed). + If `None` uses the target filename. Defaults to `None`. + :param bool virtual: None | True | False + Whether to create shortcut to yet non-existing directory (creates dir) + Default is None - use defined in __init__ Returns a tuple of (target_name, target_path, shortcut_file_path) """ + virtual = virtual if (virtual is not None) else self._virtual + if target_name is None: # get the target_name by getting the target dir name target_name = os.path.basename(target_path) - # Create target_path if it doesn't exist: - if not os.path.isdir(target_path): - try: + # Create target_path if it doesn't exist in virtual mode: + if not os.path.isdir(target_path) and virtual: + if not self._silent: os.makedirs(target_path) - except OSError: - self.err_file.write(''.join(traceback.format_exc())) + else: + try: + os.makedirs(target_path) + except OSError: + self._err_file.write(''.join(traceback.format_exc())) # Create shortcut to the target_path: - # noinspection PyBroadException - try: + if not self._silent: shortcut_file_path = self._create_shortcut_to_dir(target_name, target_path, shortcut_directory) - except: - shortcut_file_path = None - self.err_file.write(''.join(traceback.format_exc())) + else: + # noinspection PyBroadException + try: + shortcut_file_path = self._create_shortcut_to_dir(target_name, target_path, shortcut_directory) + except: + shortcut_file_path = None + self.err_file.write(''.join(traceback.format_exc())) return target_name, target_path, shortcut_file_path diff --git a/shortcut/linux.py b/shortcut/linux.py index 6edc032..1a36d67 100644 --- a/shortcut/linux.py +++ b/shortcut/linux.py @@ -1,16 +1,17 @@ import os import stat -# from .exception import * +from .exception import ShortcutError, ShortcutNoDesktopError, ShortcutNoMenuError from .base import ShortCutter class ShortCutterLinux(ShortCutter): def _get_desktop_folder(self): import subprocess - return subprocess.check_output([ - 'xdg-user-dir', - 'DESKTOP' - ]).decode('utf-8').strip() + try: + return subprocess.check_output(['xdg-user-dir', + 'DESKTOP']).decode('utf-8').strip() + except CalledProcessError: + return os.path.join(os.path.join(os.path.expanduser('~')), 'Desktop') def _get_menu_folder(self): return os.path.join(os.path.join(os.path.expanduser('~')), '.local', 'share', 'applications') diff --git a/shortcut/macos.py b/shortcut/macos.py index 00cbd7c..35e0554 100644 --- a/shortcut/macos.py +++ b/shortcut/macos.py @@ -1,5 +1,5 @@ import os -from .exception import * +from .exception import ShortcutError, ShortcutNoDesktopError, ShortcutNoMenuError from .linux import ShortCutterLinux from tempfile import NamedTemporaryFile import subprocess diff --git a/shortcut/windows.py b/shortcut/windows.py index ad139a4..16f905d 100644 --- a/shortcut/windows.py +++ b/shortcut/windows.py @@ -21,15 +21,25 @@ from win32com.client import Dispatch import sys import os -from .exception import * +from .exception import ShortcutError, ShortcutNoDesktopError, ShortcutNoMenuError from .base import ShortCutter class ShortCutterWindows(ShortCutter): - def __init__(self, err_file=None): + def __init__(self, silent=False, err_file=None, virtual=False): + """ + Creates ShortCutter. + + :param bool silent: + Whether to use shortcut in a silent mode. + :param err_file: + File object where to write errors in a silent mode. Default is sys.stderr + :param bool virtual: + Whether to allow shortcuts to yet non-existing files/dirs + """ self.executable_file_extensions = os.environ['PATHEXT'].split(os.pathsep) - super(ShortCutterWindows, self).__init__(err_file) + super(ShortCutterWindows, self).__init__(silent, err_file, virtual) def _get_desktop_folder(self): return winshell.desktop() From 27f3c92105ebe95d0521c43bebfea2b53f90d501 Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Mon, 4 Jun 2018 01:36:47 +0700 Subject: [PATCH 12/16] bugfix --- shortcut/base.py | 7 ++++--- shortcut/linux.py | 4 ++-- shortcut/macos.py | 2 +- shortcut/windows.py | 22 +++++++++++++--------- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/shortcut/base.py b/shortcut/base.py index bfd1ed8..6c6f1a4 100644 --- a/shortcut/base.py +++ b/shortcut/base.py @@ -141,6 +141,7 @@ def create_shortcut(self, target, shortcut_directory, target_name=None, virtual= # Create temporal file in order to create shortcut in virtual mode: clean = False + def create_temp_target(): if not os.path.isdir(os.path.dirname(target)): os.makedirs(os.path.dirname(target)) @@ -155,7 +156,7 @@ def create_temp_target(): create_temp_target() clean = True except (OSError, IOError): - self.err_file.write(''.join(traceback.format_exc())) + self._err_file.write(''.join(traceback.format_exc())) target_path = self.find_target(target) @@ -168,7 +169,7 @@ def create_temp_target(): shortcut_file_path = self._create_shortcut_file(target_name, target_path, shortcut_directory) except: shortcut_file_path = None - self.err_file.write(''.join(traceback.format_exc())) + self._err_file.write(''.join(traceback.format_exc())) # Delete temporal file: if clean: @@ -218,7 +219,7 @@ def create_shortcut_to_dir(self, target_path, shortcut_directory, target_name=No shortcut_file_path = self._create_shortcut_to_dir(target_name, target_path, shortcut_directory) except: shortcut_file_path = None - self.err_file.write(''.join(traceback.format_exc())) + self._err_file.write(''.join(traceback.format_exc())) return target_name, target_path, shortcut_file_path diff --git a/shortcut/linux.py b/shortcut/linux.py index 1a36d67..3999521 100644 --- a/shortcut/linux.py +++ b/shortcut/linux.py @@ -1,6 +1,6 @@ import os import stat -from .exception import ShortcutError, ShortcutNoDesktopError, ShortcutNoMenuError +# from .exception import ShortcutError, ShortcutNoDesktopError, ShortcutNoMenuError from .base import ShortCutter @@ -10,7 +10,7 @@ def _get_desktop_folder(self): try: return subprocess.check_output(['xdg-user-dir', 'DESKTOP']).decode('utf-8').strip() - except CalledProcessError: + except subprocess.CalledProcessError: return os.path.join(os.path.join(os.path.expanduser('~')), 'Desktop') def _get_menu_folder(self): diff --git a/shortcut/macos.py b/shortcut/macos.py index 35e0554..d4a31b0 100644 --- a/shortcut/macos.py +++ b/shortcut/macos.py @@ -1,5 +1,5 @@ import os -from .exception import ShortcutError, ShortcutNoDesktopError, ShortcutNoMenuError +from .exception import ShortcutError # , ShortcutNoDesktopError, ShortcutNoMenuError from .linux import ShortCutterLinux from tempfile import NamedTemporaryFile import subprocess diff --git a/shortcut/windows.py b/shortcut/windows.py index 16f905d..aebd249 100644 --- a/shortcut/windows.py +++ b/shortcut/windows.py @@ -5,14 +5,16 @@ import win32com except ImportError as e: if "DLL load failed:" in str(e): - import os,sys - path = os.path.join(os.path.split(sys.executable)[0], "Lib","site-packages","pywin32_system32") + import os + import sys + path = os.path.join(os.path.split(sys.executable)[0], "Lib", "site-packages", "pywin32_system32") os.environ["PATH"] = os.environ["PATH"] + ";" + path try: import win32com except ImportError as ee: dll = os.listdir(path) - dll = [os.path.join(path,_) for _ in dll if "dll" in _] + dll = [os.path.join(path, _) for _ in dll if "dll" in _] + # TODO: Python version 2.7 does not support this syntax: raise ImportError("Failed to import win32com, due to missing DLL:\n" + "\n".join(dll)) from e else: raise e @@ -21,7 +23,7 @@ from win32com.client import Dispatch import sys import os -from .exception import ShortcutError, ShortcutNoDesktopError, ShortcutNoMenuError +# from .exception import ShortcutError, ShortcutNoDesktopError, ShortcutNoMenuError from .base import ShortCutter @@ -72,10 +74,10 @@ def _create_shortcut_file(self, target_name, target_path, shortcut_directory): shortcut_file_path = os.path.join(shortcut_directory, target_name + ".lnk") winshell.CreateShortcut( - Path = os.path.join(shortcut_file_path), - Target = target_path, - Icon = (target_path, 0), - Description = "Shortcut to" + target_name) + Path=os.path.join(shortcut_file_path), + Target=target_path, + Icon=(target_path, 0), + Description="Shortcut to" + target_name) return shortcut_file_path @@ -101,6 +103,7 @@ def _get_paths(self): Returns a list of paths. """ + # noinspection PyProtectedMember paths = super(ShortCutterWindows, self)._get_paths() # add the python scripts path @@ -110,6 +113,7 @@ def _get_paths(self): return paths + # noinspection PyMethodMayBeStatic def _get_python_scripts_path(self): """ Gets the Python Scripts path by examining the location of the @@ -125,7 +129,7 @@ def _get_python_scripts_path(self): current_path = python_path searched = False - while searched == False: + while not searched: path_to_test = os.path.join(current_path, "Scripts") if os.path.isdir(path_to_test): searched = True From 0416c5e702a4a403035064e049fbba76a1e37163 Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Mon, 4 Jun 2018 17:24:00 +0700 Subject: [PATCH 13/16] virtual/is_dir API change --- shortcut/base.py | 84 ++++++++++++++++++++++++++++----------------- shortcut/windows.py | 6 ++-- 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/shortcut/base.py b/shortcut/base.py index 6c6f1a4..7d783bc 100644 --- a/shortcut/base.py +++ b/shortcut/base.py @@ -17,7 +17,7 @@ class ShortCutter(object): s.create_menu_shortcut("python") """ - def __init__(self, silent=False, err_file=None, virtual=False): + def __init__(self, silent=False, err_file=None): """ Creates ShortCutter. @@ -25,15 +25,12 @@ def __init__(self, silent=False, err_file=None, virtual=False): Whether to use shortcut in a silent mode. :param err_file: File object where to write errors in a silent mode. Default is sys.stderr - :param bool virtual: - Whether to allow shortcuts to yet non-existing files/dirs """ self._silent = silent if silent: self._err_file = sys.stderr if (err_file is None) else err_file else: self._err_file = None - self._virtual = virtual self._desktop_folder = self._get_desktop_folder() self._menu_folder = self._get_menu_folder() @@ -45,7 +42,7 @@ def _get_desktop_folder(self): def _get_menu_folder(self): raise ShortcutError("_get_menu_folder needs overriding") - def create_desktop_shortcut(self, target, target_name=None, target_is_dir=False, virtual=None): + def create_desktop_shortcut(self, target, target_name=None, virtual_type=None): """ Creates a desktop shortcut to a target. @@ -56,15 +53,13 @@ def create_desktop_shortcut(self, target, target_name=None, target_is_dir=False, :param str target_name: Name of the shortcut without extension (.lnk would be appended if needed). If `None` uses the target filename. Defaults to `None`. - :param bool target_is_dir: - Whether it's a shortcut to a directory - :param bool virtual: None | True | False + :param str virtual_type: `None` | `'file'` | `'dir'` Whether to create shortcut to yet non-existing file/dir (creates dir) - Default is None - use defined in __init__ + Default is `None` - do not create - Returns a tuple of (target_name, target_path, shortcut_file_path) or None + Returns a tuple of (target_name, target_path, shortcut_file_path) """ - virtual = virtual if (virtual is not None) else self._virtual + isdir, virtual = self._isdir_virtual(target, virtual_type) if not os.path.isdir(self._desktop_folder): msg = "Desktop folder '{}' not found.".format(self._desktop_folder) @@ -73,12 +68,12 @@ def create_desktop_shortcut(self, target, target_name=None, target_is_dir=False, else: self._err_file.write(msg + '\n') else: - if target_is_dir: + if isdir: return self.create_shortcut_to_dir(target, self._desktop_folder, target_name, virtual) else: return self.create_shortcut(target, self._desktop_folder, target_name, virtual) - def create_menu_shortcut(self, target, target_name=None, target_is_dir=False, virtual=None): + def create_menu_shortcut(self, target, target_name=None, virtual_type=None): """ Creates a menu shortcut to a target. @@ -89,15 +84,13 @@ def create_menu_shortcut(self, target, target_name=None, target_is_dir=False, vi :param str target_name: Name of the shortcut without extension (.lnk would be appended if needed). If `None` uses the target filename. Defaults to `None`. - :param bool target_is_dir: - Whether it's a shortcut to a directory - :param bool virtual: None | True | False + :param str virtual_type: `None` | `'file'` | `'dir'` Whether to create shortcut to yet non-existing file/dir (creates dir) - Default is None - use defined in __init__ + Default is `None` - do not create - Returns a tuple of (target_name, target_path, shortcut_file_path) or None + Returns a tuple of (target_name, target_path, shortcut_file_path) """ - virtual = virtual if (virtual is not None) else self._virtual + isdir, virtual = self._isdir_virtual(target, virtual_type) if not os.path.isdir(self._menu_folder): msg = "Menu folder '{}' not found.".format(self._menu_folder) @@ -106,12 +99,12 @@ def create_menu_shortcut(self, target, target_name=None, target_is_dir=False, vi else: self._err_file.write(msg + '\n') else: - if target_is_dir: + if isdir: return self.create_shortcut_to_dir(target, self._menu_folder, target_name, virtual) else: return self.create_shortcut(target, self._menu_folder, target_name, virtual) - def create_shortcut(self, target, shortcut_directory, target_name=None, virtual=None): + def create_shortcut(self, target, shortcut_directory, target_name=None, virtual=False): """ Creates a shortcut to a target. @@ -124,14 +117,12 @@ def create_shortcut(self, target, shortcut_directory, target_name=None, virtual= :param str target_name: Name of the shortcut without extension (.lnk would be appended if needed). If `None` uses the target filename. Defaults to `None`. - :param bool virtual: None | True | False + :param bool virtual: Whether to create shortcut to yet non-existing file - Default is None - use defined in __init__ + Default is `False` - do not create Returns a tuple of (target_name, target_path, shortcut_file_path) """ - virtual = virtual if (virtual is not None) else self._virtual - if target_name is None: # get the target name by getting the file name and removing the extension target_name = os.path.splitext(os.path.basename(target))[0] @@ -177,7 +168,7 @@ def create_temp_target(): return target_name, target_path, shortcut_file_path - def create_shortcut_to_dir(self, target_path, shortcut_directory, target_name=None, virtual=None): + def create_shortcut_to_dir(self, target_path, shortcut_directory, target_name=None, virtual=False): """ Creates a shortcut to a direcrory. @@ -188,18 +179,19 @@ def create_shortcut_to_dir(self, target_path, shortcut_directory, target_name=No :param str target_name: Name of the shortcut without extension (.lnk would be appended if needed). If `None` uses the target filename. Defaults to `None`. - :param bool virtual: None | True | False - Whether to create shortcut to yet non-existing directory (creates dir) - Default is None - use defined in __init__ + :param bool virtual: + Whether to create shortcut to yet non-existing dir (creates dir) + Default is `False` - do not create Returns a tuple of (target_name, target_path, shortcut_file_path) """ - virtual = virtual if (virtual is not None) else self._virtual - if target_name is None: # get the target_name by getting the target dir name target_name = os.path.basename(target_path) + # Expand to abs path: + target_path = os.path.abspath(target_path) + # Create target_path if it doesn't exist in virtual mode: if not os.path.isdir(target_path) and virtual: if not self._silent: @@ -231,6 +223,36 @@ def _create_shortcut_to_dir(self, target_name, target_path, shortcut_directory): def _create_shortcut_file(self, target_name, target_path, shortcut_directory): raise ShortcutError("_create_shortcut_file needs overriding") + @staticmethod + def _isdir_virtual(target, virtual_type): + """ + Sets + isdir = True/False (whether it's a shortcut to a directory), + virtual = True/False (whether to create shortcut to yet non-existing file/dir), + depending on inputs. + + :param str target: + The target to create a shortcut for, it can be a fully qualified + file path `/path/to/my_program` or a simple application name + `my_program`. + :param str virtual_type: `None` | `'file'` | `'dir'` + Whether to create shortcut to yet non-existing file/dir (creates dir) + + returns (isdir, virtual) + """ + if virtual_type is None: + virtual = False + isdir = True if os.path.isdir(target) else False + elif virtual_type == 'file': + virtual = True + isdir = False + elif virtual_type == 'dir': + virtual = True + isdir = True + else: + raise ValueError("virtual_type kwarg can only be None, 'dir' or 'file'.") + return isdir, virtual + def find_target(self, target): """ Finds a file path for a target application. diff --git a/shortcut/windows.py b/shortcut/windows.py index aebd249..3aa9085 100644 --- a/shortcut/windows.py +++ b/shortcut/windows.py @@ -29,7 +29,7 @@ class ShortCutterWindows(ShortCutter): - def __init__(self, silent=False, err_file=None, virtual=False): + def __init__(self, silent=False, err_file=None): """ Creates ShortCutter. @@ -37,11 +37,9 @@ def __init__(self, silent=False, err_file=None, virtual=False): Whether to use shortcut in a silent mode. :param err_file: File object where to write errors in a silent mode. Default is sys.stderr - :param bool virtual: - Whether to allow shortcuts to yet non-existing files/dirs """ self.executable_file_extensions = os.environ['PATHEXT'].split(os.pathsep) - super(ShortCutterWindows, self).__init__(silent, err_file, virtual) + super(ShortCutterWindows, self).__init__(silent, err_file) def _get_desktop_folder(self): return winshell.desktop() From ac8c02fa7d1235e876382bbfc1d6bc169d242fd2 Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Tue, 5 Jun 2018 18:33:39 +0700 Subject: [PATCH 14/16] fixed everything except conda and venv support --- LICENSE | 1 + shortcut/base.py | 307 +++++++++++++++++++------------------------- shortcut/linux.py | 30 ++++- shortcut/macos.py | 6 +- shortcut/windows.py | 52 ++++---- 5 files changed, 179 insertions(+), 217 deletions(-) diff --git a/LICENSE b/LICENSE index d4402c5..364a47e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2018 Martin O'Hanlon + contributors: 2018 Peter Zagubisalo Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/shortcut/base.py b/shortcut/base.py index 7d783bc..d0af94d 100644 --- a/shortcut/base.py +++ b/shortcut/base.py @@ -15,24 +15,47 @@ class ShortCutter(object): s = ShortCutter() s.create_desktop_shortcut("python") s.create_menu_shortcut("python") + + Attributes: + ----------- + raise_errors : bool, default True + Whether to raise exceptions or skip errors and continue + error_log : object, default None + File object where to write errors when raise_errors=False. + Default is `None` - do not write errors. + Can also be `sys.stderr` or `io.StringIO()`. + desktop_folder : str + Directory used when creating desktop shortcuts + menu_folder : str + Directory used when creating menu shortcuts + bin_folder : str + `Scripts` or `bin` dir path (the one to where setup.py installs) + site_packages : str + Site packages dir path (the one to where setup.py installs) """ - def __init__(self, silent=False, err_file=None): + def __init__(self, raise_errors=True, error_log=None): """ Creates ShortCutter. - :param bool silent: - Whether to use shortcut in a silent mode. - :param err_file: - File object where to write errors in a silent mode. Default is sys.stderr + :param bool raise_errors: + Whether to raise exceptions or skip errors and continue. + :param error_log: + File object where to write errors when raise_errors=False. + Default is `None` - do not write errors. + Can also be `sys.stderr` or `io.StringIO()`. """ - self._silent = silent - if silent: - self._err_file = sys.stderr if (err_file is None) else err_file - else: - self._err_file = None - self._desktop_folder = self._get_desktop_folder() - self._menu_folder = self._get_menu_folder() + self.raise_errors = raise_errors + self.error_log = error_log + self.desktop_folder = self._get_desktop_folder() + self.menu_folder = self._get_menu_folder() + self.bin_folder = self._get_bin_folder() + self.site_packages = self._get_site_packages() + self._custom_init() + + # might be overridden if needed + def _custom_init(self): + pass # should be overridden def _get_desktop_folder(self): @@ -42,7 +65,15 @@ def _get_desktop_folder(self): def _get_menu_folder(self): raise ShortcutError("_get_menu_folder needs overriding") - def create_desktop_shortcut(self, target, target_name=None, virtual_type=None): + # should be overridden + def _get_bin_folder(self): + raise ShortcutError("_get_bin_folder needs overriding") + + # should be overridden + def _get_site_packages(self): + raise ShortcutError("_get_site_packages needs overriding") + + def create_desktop_shortcut(self, target, shortcut_name=None, entry_point=False): """ Creates a desktop shortcut to a target. @@ -50,30 +81,25 @@ def create_desktop_shortcut(self, target, target_name=None, virtual_type=None): The target to create a shortcut for, it can be a fully qualified file path `/path/to/my_program` or a simple application name `my_program`. - :param str target_name: + :param str shortcut_name: Name of the shortcut without extension (.lnk would be appended if needed). If `None` uses the target filename. Defaults to `None`. - :param str virtual_type: `None` | `'file'` | `'dir'` - Whether to create shortcut to yet non-existing file/dir (creates dir) - Default is `None` - do not create + :param bool entry_point: + Whether to create shortcut to executable created via `entry_points` > `'console_scripts'` in `setup.py` + If `create_shortcut` is run inside `setup.py` executable was not moved to it's place yet. - Returns a tuple of (target_name, target_path, shortcut_file_path) + Returns a tuple of (shortcut_name, target_path, shortcut_file_path) """ - isdir, virtual = self._isdir_virtual(target, virtual_type) - - if not os.path.isdir(self._desktop_folder): - msg = "Desktop folder '{}' not found.".format(self._desktop_folder) - if not self._silent: + if not os.path.isdir(self.desktop_folder): + msg = "Desktop folder '{}' not found.".format(self.desktop_folder) + if self.raise_errors: raise ShortcutNoDesktopError(msg) else: - self._err_file.write(msg + '\n') + self.error_log.write(msg + '\n') else: - if isdir: - return self.create_shortcut_to_dir(target, self._desktop_folder, target_name, virtual) - else: - return self.create_shortcut(target, self._desktop_folder, target_name, virtual) + return self.create_shortcut(target, self.desktop_folder, shortcut_name, entry_point) - def create_menu_shortcut(self, target, target_name=None, virtual_type=None): + def create_menu_shortcut(self, target, shortcut_name=None, entry_point=False): """ Creates a menu shortcut to a target. @@ -81,30 +107,25 @@ def create_menu_shortcut(self, target, target_name=None, virtual_type=None): The target to create a shortcut for, it can be a fully qualified file path `/path/to/my_program` or a simple application name `my_program`. - :param str target_name: + :param str shortcut_name: Name of the shortcut without extension (.lnk would be appended if needed). If `None` uses the target filename. Defaults to `None`. - :param str virtual_type: `None` | `'file'` | `'dir'` - Whether to create shortcut to yet non-existing file/dir (creates dir) - Default is `None` - do not create + :param bool entry_point: + Whether to create shortcut to executable created via `entry_points` > `'console_scripts'` in `setup.py` + If `create_shortcut` is run inside `setup.py` executable was not moved to it's place yet. - Returns a tuple of (target_name, target_path, shortcut_file_path) + Returns a tuple of (shortcut_name, target_path, shortcut_file_path) """ - isdir, virtual = self._isdir_virtual(target, virtual_type) - - if not os.path.isdir(self._menu_folder): - msg = "Menu folder '{}' not found.".format(self._menu_folder) - if not self._silent: + if not os.path.isdir(self.menu_folder): + msg = "Menu folder '{}' not found.".format(self.menu_folder) + if self.raise_errors: raise ShortcutNoMenuError(msg) else: - self._err_file.write(msg + '\n') + self.error_log.write(msg + '\n') else: - if isdir: - return self.create_shortcut_to_dir(target, self._menu_folder, target_name, virtual) - else: - return self.create_shortcut(target, self._menu_folder, target_name, virtual) + return self.create_shortcut(target, self.menu_folder, shortcut_name, entry_point) - def create_shortcut(self, target, shortcut_directory, target_name=None, virtual=False): + def create_shortcut(self, target, shortcut_directory, shortcut_name=None, entry_point=False): """ Creates a shortcut to a target. @@ -114,144 +135,96 @@ def create_shortcut(self, target, shortcut_directory, target_name=None, virtual= `my_program`. :param str shortcut_directory: The directory path where the shortcut should be created. - :param str target_name: + :param str shortcut_name: Name of the shortcut without extension (.lnk would be appended if needed). If `None` uses the target filename. Defaults to `None`. - :param bool virtual: - Whether to create shortcut to yet non-existing file - Default is `False` - do not create + :param bool entry_point: + Whether to create shortcut to executable created via `entry_points` > `'console_scripts'` in `setup.py` + If `create_shortcut` is run inside `setup.py` executable was not moved to it's place yet. - Returns a tuple of (target_name, target_path, shortcut_file_path) + Returns a tuple of (shortcut_name, target_path, shortcut_file_path) """ - if target_name is None: - # get the target name by getting the file name and removing the extension - target_name = os.path.splitext(os.path.basename(target))[0] + # Check entry_point input: + if entry_point and os.path.basename(target) != target: + raise ValueError('When entry_point=True target can be basename only.') - # find the target path: - target_path = self.find_target(target) - - # Create temporal file in order to create shortcut in virtual mode: - clean = False - - def create_temp_target(): - if not os.path.isdir(os.path.dirname(target)): - os.makedirs(os.path.dirname(target)) - open(target, 'a').close() + # Check if target is dir or file: + if entry_point: + isdir = False + elif (os.path.basename(target) == target) and (self.find_target(target) is not None): + isdir = False + else: + isdir = True if os.path.isdir(target) else False - if (target_path is None) and virtual: - if not self._silent: - create_temp_target() - clean = True + # Set shortcut name: + if shortcut_name is None: + if entry_point: + shortcut_name = target + elif isdir: + shortcut_name = os.path.basename(target) else: - try: - create_temp_target() - clean = True - except (OSError, IOError): - self._err_file.write(''.join(traceback.format_exc())) - - target_path = self.find_target(target) - - # Create shortcut to the target_path: - if not self._silent: - shortcut_file_path = self._create_shortcut_file(target_name, target_path, shortcut_directory) + # getting the file name and removing the extension: + shortcut_name = os.path.splitext(os.path.basename(target))[0] + + # Set the target path: + if entry_point: + target_path = os.path.join(self.bin_folder, + target + ('.exe' if (os.name == 'nt') else '')) + elif isdir: + target_path = os.path.abspath(target) else: - # noinspection PyBroadException - try: - shortcut_file_path = self._create_shortcut_file(target_name, target_path, shortcut_directory) - except: - shortcut_file_path = None - self._err_file.write(''.join(traceback.format_exc())) - - # Delete temporal file: - if clean: - os.remove(target) - - return target_name, target_path, shortcut_file_path - - def create_shortcut_to_dir(self, target_path, shortcut_directory, target_name=None, virtual=False): - """ - Creates a shortcut to a direcrory. + target_path = self.find_target(target) - :param str target_path: - The target directory path to create a shortcut for. - :param str shortcut_directory: - The directory path where the shortcut should be created. - :param str target_name: - Name of the shortcut without extension (.lnk would be appended if needed). - If `None` uses the target filename. Defaults to `None`. - :param bool virtual: - Whether to create shortcut to yet non-existing dir (creates dir) - Default is `False` - do not create - - Returns a tuple of (target_name, target_path, shortcut_file_path) - """ - if target_name is None: - # get the target_name by getting the target dir name - target_name = os.path.basename(target_path) - - # Expand to abs path: - target_path = os.path.abspath(target_path) - - # Create target_path if it doesn't exist in virtual mode: - if not os.path.isdir(target_path) and virtual: - if not self._silent: - os.makedirs(target_path) + # Create shortcut: + def create(): + if isdir: + return self._create_shortcut_to_dir(shortcut_name, target_path, shortcut_directory) else: - try: - os.makedirs(target_path) - except OSError: - self._err_file.write(''.join(traceback.format_exc())) - - # Create shortcut to the target_path: - if not self._silent: - shortcut_file_path = self._create_shortcut_to_dir(target_name, target_path, shortcut_directory) + return self._create_shortcut_file(shortcut_name, target_path, shortcut_directory) + + if self.raise_errors: + shortcut_file_path = create() else: # noinspection PyBroadException try: - shortcut_file_path = self._create_shortcut_to_dir(target_name, target_path, shortcut_directory) + shortcut_file_path = create() except: shortcut_file_path = None - self._err_file.write(''.join(traceback.format_exc())) + self.error_log.write(''.join(traceback.format_exc())) - return target_name, target_path, shortcut_file_path + return shortcut_name, target_path, shortcut_file_path # should be overridden - def _create_shortcut_to_dir(self, target_name, target_path, shortcut_directory): + def _create_shortcut_to_dir(self, shortcut_name, target_path, shortcut_directory): raise ShortcutError("_create_shortcut_to_dir needs overriding") # should be overridden - def _create_shortcut_file(self, target_name, target_path, shortcut_directory): + def _create_shortcut_file(self, shortcut_name, target_path, shortcut_directory): raise ShortcutError("_create_shortcut_file needs overriding") - @staticmethod - def _isdir_virtual(target, virtual_type): + def makedirs(self, *args): """ - Sets - isdir = True/False (whether it's a shortcut to a directory), - virtual = True/False (whether to create shortcut to yet non-existing file/dir), - depending on inputs. + Recursively creates dirs if they don't exist. + Utilizes self.raise_errors and self.error_log - :param str target: - The target to create a shortcut for, it can be a fully qualified - file path `/path/to/my_program` or a simple application name - `my_program`. - :param str virtual_type: `None` | `'file'` | `'dir'` - Whether to create shortcut to yet non-existing file/dir (creates dir) + :param args: + Multiple paths (str) for folders to create. - returns (isdir, virtual) + Returns True on success False of failure """ - if virtual_type is None: - virtual = False - isdir = True if os.path.isdir(target) else False - elif virtual_type == 'file': - virtual = True - isdir = False - elif virtual_type == 'dir': - virtual = True - isdir = True - else: - raise ValueError("virtual_type kwarg can only be None, 'dir' or 'file'.") - return isdir, virtual + ret = True + for path in args: + if not os.path.isdir(path): + if self.raise_errors: + os.makedirs(path) + else: + try: + os.makedirs(path) + except OSError: + if self.error_log is not None: + self.error_log.write(''.join(traceback.format_exc())) + ret = False + return ret def find_target(self, target): """ @@ -319,25 +292,3 @@ def _get_paths(self): paths = os.environ['PATH'].split(os.pathsep) return paths - - @property - def desktop_directory(self): - """ - Sets or returns the directory used when creating desktop shortcuts. - """ - return self._desktop_folder - - @desktop_directory.setter - def desktop_directory(self, value): - self._desktop_folder = value - - @property - def menu_directory(self): - """ - Sets or returns the directory used when creating menu shortcuts. - """ - return self._menu_folder - - @menu_directory.setter - def menu_directory(self, value): - self._menu_folder = value diff --git a/shortcut/linux.py b/shortcut/linux.py index 3999521..5256983 100644 --- a/shortcut/linux.py +++ b/shortcut/linux.py @@ -1,6 +1,7 @@ import os +import sys +import site import stat -# from .exception import ShortcutError, ShortcutNoDesktopError, ShortcutNoMenuError from .base import ShortCutter @@ -16,28 +17,45 @@ def _get_desktop_folder(self): def _get_menu_folder(self): return os.path.join(os.path.join(os.path.expanduser('~')), '.local', 'share', 'applications') - def _create_shortcut_to_dir(self, target_name, target_path, shortcut_directory): + def _get_site_packages(self): + """ + Returns site packages dir path + (the one to where setup.py installs if use `ShortCutter()` from setup.py). + Works (tested) only on Miniconda at Ubuntu. + """ + return site.getsitepackages()[0] + + def _get_bin_folder(self): + """ + Returns `bin` dir path + (the one to where setup.py installs if use `ShortCutter()` from setup.py). + Works (tested) only on Miniconda at Ubuntu. + TODO: add system python support + """ + return os.path.dirname(sys.executable) + + def _create_shortcut_to_dir(self, shortcut_name, target_path, shortcut_directory): """ Creates a Unix shortcut to a directory via symbolic link. Returns shortcut_file_path """ - shortcut_file_path = os.path.join(shortcut_directory, target_name) + shortcut_file_path = os.path.join(shortcut_directory, shortcut_name) if os.path.islink(shortcut_file_path): os.remove(shortcut_file_path) os.symlink(target_path, shortcut_file_path) return shortcut_file_path - def _create_shortcut_file(self, target_name, target_path, shortcut_directory): + def _create_shortcut_file(self, shortcut_name, target_path, shortcut_directory): """ Creates a Linux shortcut file. Returns shortcut_file_path """ - shortcut_file_path = os.path.join(shortcut_directory, "launch_" + target_name + ".desktop") + shortcut_file_path = os.path.join(shortcut_directory, "launch_" + shortcut_name + ".desktop") with open(shortcut_file_path, "w") as shortcut: shortcut.write("[Desktop Entry]\n") - shortcut.write("Name={}\n".format(target_name)) + shortcut.write("Name={}\n".format(shortcut_name)) shortcut.write("Exec={} %F\n".format(target_path)) shortcut.write("Terminal=true\n") shortcut.write("Type=Application\n") diff --git a/shortcut/macos.py b/shortcut/macos.py index d4a31b0..f771799 100644 --- a/shortcut/macos.py +++ b/shortcut/macos.py @@ -1,5 +1,5 @@ import os -from .exception import ShortcutError # , ShortcutNoDesktopError, ShortcutNoMenuError +from .exception import ShortcutError from .linux import ShortCutterLinux from tempfile import NamedTemporaryFile import subprocess @@ -12,13 +12,13 @@ def _get_desktop_folder(self): def _get_menu_folder(self): return os.path.join('/', 'Applications') - def _create_shortcut_file(self, target_name, target_path, shortcut_directory): + def _create_shortcut_file(self, shortcut_name, target_path, shortcut_directory): """ Creates a MacOS app which opens an executable via the terminal Returns the file path of the shortcut created """ - shortcut_file_path = os.path.join(shortcut_directory, target_name + ".app") + shortcut_file_path = os.path.join(shortcut_directory, shortcut_name + ".app") # create the AppleScript script sf = NamedTemporaryFile(mode="w") diff --git a/shortcut/windows.py b/shortcut/windows.py index 3aa9085..cb055b0 100644 --- a/shortcut/windows.py +++ b/shortcut/windows.py @@ -20,26 +20,14 @@ raise e import winshell -from win32com.client import Dispatch import sys import os -# from .exception import ShortcutError, ShortcutNoDesktopError, ShortcutNoMenuError from .base import ShortCutter class ShortCutterWindows(ShortCutter): - - def __init__(self, silent=False, err_file=None): - """ - Creates ShortCutter. - - :param bool silent: - Whether to use shortcut in a silent mode. - :param err_file: - File object where to write errors in a silent mode. Default is sys.stderr - """ + def _custom_init(self): self.executable_file_extensions = os.environ['PATHEXT'].split(os.pathsep) - super(ShortCutterWindows, self).__init__(silent, err_file) def _get_desktop_folder(self): return winshell.desktop() @@ -47,35 +35,39 @@ def _get_desktop_folder(self): def _get_menu_folder(self): return winshell.folder("CSIDL_PROGRAMS") - def _create_shortcut_to_dir(self, target_name, target_path, shortcut_directory): + def _get_site_packages(self): + """ + Returns site packages dir path + (the one to where setup.py installs if use `ShortCutter()` from setup.py). + Works (tested) only on Miniconda. """ - Creates a Windows shortcut file for a directory. - TODO: This might be the same as _create_shortcut_file but it needs testing. + return os.path.join(os.path.dirname(sys.executable), 'Lib', 'site-packages') - Returns shortcut_file_path + def _get_bin_folder(self): + """ + Returns `Scripts` dir path + (the one to where setup.py installs if use `ShortCutter()` from setup.py). + Works (tested) only on Miniconda. """ - shell = Dispatch('WScript.Shell') - shortcut_file_path = os.path.join(shortcut_directory, target_name + '.lnk') - shortcut = shell.CreateShortCut(shortcut_file_path) - shortcut.Targetpath = target_path - shortcut.WorkingDirectory = target_path - shortcut.save() + return os.path.join(os.path.dirname(sys.executable), "Scripts") - return shortcut_file_path + def _create_shortcut_to_dir(self, shortcut_name, target_path, shortcut_directory): + return self._create_shortcut_file(shortcut_name, target_path, shortcut_directory) - def _create_shortcut_file(self, target_name, target_path, shortcut_directory): + def _create_shortcut_file(self, shortcut_name, target_path, shortcut_directory): """ Creates a Windows shortcut file. Returns shortcut_file_path """ - shortcut_file_path = os.path.join(shortcut_directory, target_name + ".lnk") + shortcut_file_path = os.path.join(shortcut_directory, shortcut_name + ".lnk") winshell.CreateShortcut( - Path=os.path.join(shortcut_file_path), + Path=shortcut_file_path, Target=target_path, Icon=(target_path, 0), - Description="Shortcut to" + target_name) + Description="Shortcut to" + os.path.basename(target_path), + StartIn=target_path) return shortcut_file_path @@ -116,8 +108,8 @@ def _get_python_scripts_path(self): """ Gets the Python Scripts path by examining the location of the sys.executable and working backwards through the directory - structure. - + structure. + Returns `None` if it cant be found. """ python_exe_path = sys.executable From bc58a1ef9c9be53f249a20884a97e33de9cca6a4 Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Tue, 5 Jun 2018 18:44:40 +0700 Subject: [PATCH 15/16] docs upd --- shortcut/base.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/shortcut/base.py b/shortcut/base.py index d0af94d..e514814 100644 --- a/shortcut/base.py +++ b/shortcut/base.py @@ -29,9 +29,11 @@ class ShortCutter(object): menu_folder : str Directory used when creating menu shortcuts bin_folder : str - `Scripts` or `bin` dir path (the one to where setup.py installs) + `Scripts` or `bin` dir path + (the one to where setup.py installs if use `ShortCutter()` from setup.py) site_packages : str - Site packages dir path (the one to where setup.py installs) + Site packages dir path + (the one to where setup.py installs if use `ShortCutter()` from setup.py) """ def __init__(self, raise_errors=True, error_log=None): From d392625e8ce085cf98b3daeba835edcb3aa09f29 Mon Sep 17 00:00:00 2001 From: kiwi0fruit Date: Tue, 5 Jun 2018 18:47:59 +0700 Subject: [PATCH 16/16] docs upd --- shortcut/base.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shortcut/base.py b/shortcut/base.py index e514814..cc6e0ba 100644 --- a/shortcut/base.py +++ b/shortcut/base.py @@ -31,9 +31,11 @@ class ShortCutter(object): bin_folder : str `Scripts` or `bin` dir path (the one to where setup.py installs if use `ShortCutter()` from setup.py) + Works (tested) only on Miniconda. site_packages : str Site packages dir path (the one to where setup.py installs if use `ShortCutter()` from setup.py) + Works (tested) only on Miniconda. """ def __init__(self, raise_errors=True, error_log=None):