Doa-doa's picture
Upload folder using huggingface_hub
72268ee
# Copyright (c) 2008-2011 by Enthought, Inc.
# Copyright (c) 2013-2017 Continuum Analytics, Inc.
# All rights reserved.
from __future__ import absolute_import, unicode_literals
import ctypes
import logging
import os
from os.path import isdir, join, exists, split
import sys
import locale
from .utils import rm_empty_dir, rm_rf
from .knownfolders import get_folder_path, FOLDERID
# KNOWNFOLDERID does provide a direct path to Quick Launch. No additional path necessary.
from .winshortcut import create_shortcut
# This allows debugging installer issues using DebugView from Microsoft.
OutputDebugString = ctypes.windll.kernel32.OutputDebugStringW
OutputDebugString.argtypes = [ctypes.c_wchar_p]
class DbgViewHandler(logging.Handler):
def emit(self, record):
OutputDebugString(self.format(record))
logger = logging.getLogger("menuinst_win32")
logger.setLevel(logging.DEBUG)
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.WARNING)
dbgview = DbgViewHandler()
dbgview.setLevel(logging.DEBUG)
logger.addHandler(dbgview)
logger.addHandler(stream_handler)
# When running as 'nt authority/system' as sometimes people do via SCCM,
# various folders do not exist, such as QuickLaunch. This doesn't matter
# as we'll use the "system" key finally and check for the "quicklaunch"
# subkey before adding any Quick Launch menu items.
# It can happen that some of the dirs[] entires refer to folders that do not
# exist, in which case, the 2nd entry of the value tuple is a sub-class of
# Exception.
dirs_src = {"system": { "desktop": get_folder_path(FOLDERID.PublicDesktop),
"start": get_folder_path(FOLDERID.CommonPrograms),
"documents": get_folder_path(FOLDERID.PublicDocuments),
"profile": get_folder_path(FOLDERID.Profile)},
"user": { "desktop": get_folder_path(FOLDERID.Desktop),
"start": get_folder_path(FOLDERID.Programs),
"quicklaunch": get_folder_path(FOLDERID.QuickLaunch),
"documents": get_folder_path(FOLDERID.Documents),
"profile": get_folder_path(FOLDERID.Profile)}}
def folder_path(preferred_mode, check_other_mode, key):
''' This function implements all heuristics and workarounds for messed up
KNOWNFOLDERID registry values. It's also verbose (OutputDebugStringW)
about whether fallbacks worked or whether they would have worked if
check_other_mode had been allowed.
'''
other_mode = 'system' if preferred_mode == 'user' else 'user'
path, exception = dirs_src[preferred_mode][key]
if not exception:
return path
logger.info("WARNING: menuinst key: '%s'\n"
" path: '%s'\n"
" .. excepted with: '%s' in knownfolders.py, implementing workarounds .."
% (key, path, type(exception).__name__))
# Since I have seen 'user', 'documents' set as '\\vmware-host\Shared Folders\Documents'
# when there's no such server, we check 'user', 'profile' + '\Documents' before maybe
# trying the other_mode (though I have chickened out on that idea).
if preferred_mode == 'user' and key == 'documents':
user_profile, exception = dirs_src['user']['profile']
if not exception:
path = join(user_profile, 'Documents')
if os.access(path, os.W_OK):
logger.info(" .. worked-around to: '%s'" % (path))
return path
path, exception = dirs_src[other_mode][key]
# Do not fall back to something we cannot write to.
if exception:
if check_other_mode:
logger.info(" .. despite 'check_other_mode'\n"
" and 'other_mode' 'path' of '%s'\n"
" it excepted with: '%s' in knownfolders.py" % (path,
type(exception).__name__))
else:
logger.info(" .. 'check_other_mode' is False,\n"
" and 'other_mode' 'path' is '%s'\n"
" but it excepted anyway with: '%s' in knownfolders.py" % (path, type(exception).__name__))
return None
if not check_other_mode:
logger.info(" .. due to lack of 'check_other_mode' not picking\n"
" non-excepting path of '%s'\n in knownfolders.py" % (path))
return None
return path
def quoted(s):
"""
quotes a string if necessary.
"""
# strip any existing quotes
s = s.strip(u'"')
# don't add quotes for minus or leading space
if s[0] in (u'-', u' '):
return s
if u' ' in s or u'/' in s:
return u'"%s"' % s
else:
return s
def ensure_pad(name, pad="_"):
"""
Examples:
>>> ensure_pad('conda')
'_conda_'
"""
if not name or name[0] == name[-1] == pad:
return name
else:
return "%s%s%s" % (pad, name, pad)
def to_unicode(var, codec=locale.getpreferredencoding()):
if sys.version_info[0] < 3 and isinstance(var, unicode):
return var
if not codec:
codec = "utf-8"
if hasattr(var, "decode"):
var = var.decode(codec)
return var
def to_bytes(var, codec=locale.getpreferredencoding()):
if isinstance(var, bytes):
return var
if not codec:
codec="utf-8"
if hasattr(var, "encode"):
var = var.encode(codec)
return var
unicode_root_prefix = to_unicode(sys.prefix)
if u'\\envs\\' in unicode_root_prefix:
logger.warn('menuinst called from non-root env %s', unicode_root_prefix)
def substitute_env_variables(text, dir):
# When conda is using Menuinst, only the root conda installation ever
# calls menuinst. Thus, these calls to sys refer to the root conda
# installation, NOT the child environment
py_major_ver = sys.version_info[0]
py_bitness = 8 * tuple.__itemsize__
env_prefix = to_unicode(dir['prefix'])
root_prefix = to_unicode(dir['root_prefix'])
text = to_unicode(text)
env_name = to_unicode(dir['env_name'])
for a, b in (
(u'${PREFIX}', env_prefix),
(u'${ROOT_PREFIX}', root_prefix),
(u'${DISTRIBUTION_NAME}', os.path.split(root_prefix)[-1].capitalize()),
(u'${PYTHON_SCRIPTS}',
os.path.normpath(join(env_prefix, u'Scripts')).replace(u"\\", u"/")),
(u'${MENU_DIR}', join(env_prefix, u'Menu')),
(u'${PERSONALDIR}', dir['documents']),
(u'${USERPROFILE}', dir['profile']),
(u'${ENV_NAME}', env_name),
(u'${PY_VER}', u'%d' % (py_major_ver)),
(u'${PLATFORM}', u"(%s-bit)" % py_bitness),
):
if b:
text = text.replace(a, b)
return text
class Menu(object):
def __init__(self, name, prefix=unicode_root_prefix, env_name=u"", mode=None, root_prefix=unicode_root_prefix):
"""
Prefix is the system prefix to be used -- this is needed since
there is the possibility of a different Python's packages being managed.
"""
# bytestrings passed in need to become unicode
self.prefix = to_unicode(prefix)
self.root_prefix = to_unicode(root_prefix)
used_mode = mode if mode else ('user' if exists(join(self.prefix, u'.nonadmin')) else 'system')
logger.debug("Menu: name: '%s', prefix: '%s', env_name: '%s', mode: '%s', used_mode: '%s', root_prefix: '%s'"
% (name, self.prefix, env_name, mode, used_mode, root_prefix))
try:
self.set_dir(name, self.prefix, env_name, used_mode, root_prefix)
except WindowsError:
# We get here if we aren't elevated. This is different from
# permissions: a user can have permission, but elevation is still
# required. If the process isn't elevated, we get the
# WindowsError
if 'user' in dirs_src and used_mode == 'system':
logger.warn("Insufficient permissions to write menu folder. "
"Falling back to user location")
try:
self.set_dir(name, self.prefix, env_name, 'user')
except:
pass
else:
logger.fatal("Unable to create AllUsers menu folder")
def set_dir(self, name, prefix, env_name, mode, root_prefix):
self.mode = mode
self.dir = dict()
# I have chickened out on allowing check_other_mode. Really there needs
# to be 3 distinct cases that 'menuinst' cares about:
# priv-user doing system install
# priv-user doing user-only install
# non-priv-user doing user-only install
# (priv-user only exists in an AllUsers installation).
check_other_mode = False
for k, v in dirs_src[mode].items():
# We may want to cache self.dir to some files, one for AllUsers
# (system) installs and one for each subsequent user install?
self.dir[k] = folder_path(mode, check_other_mode, k)
self.dir['prefix'] = prefix
self.dir['root_prefix'] = root_prefix
self.dir['env_name'] = env_name
folder_name = substitute_env_variables(name, self.dir)
self.path = join(self.dir["start"], folder_name)
self.create()
def create(self):
if not isdir(self.path):
os.mkdir(self.path)
def remove(self):
rm_empty_dir(self.path)
def extend_script_args(args, shortcut):
try:
args.append(shortcut['scriptargument'])
except KeyError:
pass
try:
args.extend(shortcut['scriptarguments'])
except KeyError:
pass
def quote_args(args):
# cmd.exe /K or /C expects a single string argument and requires
# doubled-up quotes when any sub-arguments have spaces:
# https://stackoverflow.com/a/6378038/3257826
if (len(args) > 2 and ("CMD.EXE" in args[0].upper() or "%COMSPEC%" in args[0].upper())
and (args[1].upper() == '/K' or args[1].upper() == '/C')
and any(' ' in arg for arg in args[2:])
):
args = [
ensure_pad(args[0], '"'), # cmd.exe
args[1], # /K or /C
'"%s"' % (' '.join(ensure_pad(arg, '"') for arg in args[2:])), # double-quoted
]
else:
args = [quoted(arg) for arg in args]
return args
class ShortCut(object):
def __init__(self, menu, shortcut):
self.menu = menu
self.shortcut = shortcut
def remove(self):
self.create(remove=True)
def create(self, remove=False):
# Substitute env variables early because we may need to escape spaces in the value.
args = []
fix_win_slashes = [0]
prefix = self.menu.prefix.replace('/', '\\')
unicode_root_prefix = self.menu.root_prefix.replace('/', '\\')
root_py = join(unicode_root_prefix, u"python.exe")
root_pyw = join(unicode_root_prefix, u"pythonw.exe")
env_py = join(prefix, u"python.exe")
env_pyw = join(prefix, u"pythonw.exe")
cwp_py = [root_py, join(unicode_root_prefix, u'cwp.py'), prefix, env_py]
cwp_pyw = [root_pyw, join(unicode_root_prefix, u'cwp.py'), prefix, env_pyw]
if "pywscript" in self.shortcut:
args = cwp_pyw
fix_win_slashes = [len(args)]
args += self.shortcut["pywscript"].split()
elif "pyscript" in self.shortcut:
args = cwp_py
fix_win_slashes = [len(args)]
args += self.shortcut["pyscript"].split()
elif "webbrowser" in self.shortcut:
args = [root_pyw, '-m', 'webbrowser', '-t', self.shortcut['webbrowser']]
elif "script" in self.shortcut:
# It is unclear whether running through cwp.py is what we want here. In
# the long term I would rather this was made an explicit choice.
args = [root_py, join(unicode_root_prefix, u'cwp.py'), prefix]
fix_win_slashes = [len(args)]
args += self.shortcut["script"].split()
extend_script_args(args, self.shortcut)
elif "system" in self.shortcut:
args = self.shortcut["system"].split()
extend_script_args(args, self.shortcut)
else:
raise Exception("Nothing to do: %r" % self.shortcut)
args = [substitute_env_variables(arg, self.menu.dir) for arg in args]
for fws in fix_win_slashes:
args[fws] = args[fws].replace('/', '\\')
args = quote_args(args)
cmd = args[0]
args = args[1:]
logger.debug('Shortcut cmd is %s, args are %s' % (cmd, args))
workdir = self.shortcut.get('workdir', '')
icon = self.shortcut.get('icon', '')
workdir = substitute_env_variables(workdir, self.menu.dir)
icon = substitute_env_variables(icon, self.menu.dir)
# Fix up the '/' to '\'
workdir = workdir.replace('/', '\\')
icon = icon.replace('/', '\\')
# Create the working directory if it doesn't exist
if workdir:
if not isdir(workdir):
os.makedirs(workdir)
else:
workdir = '%HOMEPATH%'
# Menu link
dst_dirs = [self.menu.path]
# Desktop link
if self.shortcut.get('desktop'):
dst_dirs.append(self.menu.dir['desktop'])
# Quicklaunch link
if self.shortcut.get('quicklaunch') and 'quicklaunch' in self.menu.dir:
dst_dirs.append(self.menu.dir['quicklaunch'])
name_suffix = " ({})".format(self.menu.dir['env_name']) if self.menu.dir['env_name'] else ""
for dst_dir in dst_dirs:
name = substitute_env_variables(self.shortcut['name'], self.menu.dir)
dst = join(dst_dir, name + name_suffix + '.lnk')
if remove:
rm_rf(dst)
else:
# The API for the call to 'create_shortcut' has 3
# required arguments (path, description and filename)
# and 4 optional ones (args, working_dir, icon_path and
# icon_index).
create_shortcut(
u'' + cmd,
u'' + name + name_suffix,
u'' + dst,
u' '.join(arg for arg in args),
u'' + workdir,
u'' + icon,
)