Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed everything except conda and venv support #17

Closed
wants to merge 16 commits into from
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#__requires__ = []
__extra_requires__ = {
':sys_platform == "win32"': [
'pypiwin32',
'pywin32',
'winshell',
],
}
Expand Down Expand Up @@ -58,4 +58,4 @@
'console_scripts': [
'shortcut = shortcut:main'
]},
zip_safe=False)
zip_safe=False)
5 changes: 2 additions & 3 deletions shortcut/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .exception import ShortcutError, ShortcutNoDesktopError, ShortcutNoMenuError
# get operating system
import sys
platform = sys.platform
Expand All @@ -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():

Expand Down Expand Up @@ -67,4 +67,3 @@ def main():

except ShortcutError as e:
print("Shortcut failed: '{}'".format(e))

233 changes: 177 additions & 56 deletions shortcut/base.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import sys
import os
import stat
from .exception import *
# import stat
from .exception import ShortcutError, ShortcutNoDesktopError, ShortcutNoMenuError
import traceback


class ShortCutter(object):
"""
Expand All @@ -13,79 +15,220 @@ 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 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):
self._desktop_folder = self._get_desktop_folder()
self._menu_folder = self._get_menu_folder()
def __init__(self, raise_errors=True, error_log=None):
"""
Creates ShortCutter.

: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.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):
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):
# 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.

: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`.

Returns a tuple of (target_name, target_path, shortcut_file_path)
: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 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 (shortcut_name, target_path, shortcut_file_path)
"""
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)
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.error_log.write(msg + '\n')
else:
return self.create_shortcut(target, self.desktop_folder, shortcut_name, entry_point)

def create_menu_shortcut(self, target):
def create_menu_shortcut(self, target, shortcut_name=None, entry_point=False):
"""
Creates a menu shortcut to a target.

: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`.

Returns a tuple of (target_name, target_path, shortcut_file_path)
: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 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 (shortcut_name, target_path, shortcut_file_path)
"""
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)
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.error_log.write(msg + '\n')
else:
return self.create_shortcut(target, self.menu_folder, shortcut_name, entry_point)

def create_shortcut(self, target, shortcut_directory):
def create_shortcut(self, target, shortcut_directory, shortcut_name=None, entry_point=False):
"""
Creates a shortcut to a target.

: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 shortcut_directory:
The directory path where the shortcut should be created.

Returns a tuple of (target_name, target_path, shortcut_file_path)
: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 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 (shortcut_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]
# Check entry_point input:
if entry_point and os.path.basename(target) != target:
raise ValueError('When entry_point=True target can be basename only.')

# 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

# Set shortcut name:
if shortcut_name is None:
if entry_point:
shortcut_name = target
elif isdir:
shortcut_name = os.path.basename(target)
else:
# 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:
target_path = self.find_target(target)

# find for the target path
target_path = self.find_target(target)
# Create shortcut:
def create():
if isdir:
return self._create_shortcut_to_dir(shortcut_name, target_path, shortcut_directory)
else:
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 = create()
except:
shortcut_file_path = None
self.error_log.write(''.join(traceback.format_exc()))

shortcut_file_path = self._create_shortcut_file(target_name, target_path, shortcut_directory)
return shortcut_name, target_path, shortcut_file_path

return (target_name, target_path, shortcut_file_path)
# should be overridden
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, shortcut_name, target_path, shortcut_directory):
raise ShortcutError("_create_shortcut_file needs overriding")

#needs overriding
def _create_shortcut_file(self, target_name, target_path, shortcut_directory):
raise ShortcutError("create_shortcut_file needs overriding")
def makedirs(self, *args):
"""
Recursively creates dirs if they don't exist.
Utilizes self.raise_errors and self.error_log

:param args:
Multiple paths (str) for folders to create.

Returns True on success False of failure
"""
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):
"""
Expand Down Expand Up @@ -151,27 +294,5 @@ def _get_paths(self):
"""
# get folders from PATH
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
return paths
2 changes: 2 additions & 0 deletions shortcut/exception.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
class ShortcutError(Exception):
pass


class ShortcutNoDesktopError(ShortcutError):
pass


class ShortcutNoMenuError(ShortcutError):
pass
Loading