847 changed files with 217518 additions and 0 deletions
@ -0,0 +1,78 @@ |
|||||
|
# This file must be used with "source bin/activate" *from bash* |
||||
|
# you cannot run it directly |
||||
|
|
||||
|
deactivate () { |
||||
|
unset -f pydoc >/dev/null 2>&1 |
||||
|
|
||||
|
# reset old environment variables |
||||
|
# ! [ -z ${VAR+_} ] returns true if VAR is declared at all |
||||
|
if ! [ -z "${_OLD_VIRTUAL_PATH+_}" ] ; then |
||||
|
PATH="$_OLD_VIRTUAL_PATH" |
||||
|
export PATH |
||||
|
unset _OLD_VIRTUAL_PATH |
||||
|
fi |
||||
|
if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then |
||||
|
PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" |
||||
|
export PYTHONHOME |
||||
|
unset _OLD_VIRTUAL_PYTHONHOME |
||||
|
fi |
||||
|
|
||||
|
# This should detect bash and zsh, which have a hash command that must |
||||
|
# be called to get it to forget past commands. Without forgetting |
||||
|
# past commands the $PATH changes we made may not be respected |
||||
|
if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ] ; then |
||||
|
hash -r 2>/dev/null |
||||
|
fi |
||||
|
|
||||
|
if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then |
||||
|
PS1="$_OLD_VIRTUAL_PS1" |
||||
|
export PS1 |
||||
|
unset _OLD_VIRTUAL_PS1 |
||||
|
fi |
||||
|
|
||||
|
unset VIRTUAL_ENV |
||||
|
if [ ! "${1-}" = "nondestructive" ] ; then |
||||
|
# Self destruct! |
||||
|
unset -f deactivate |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
# unset irrelevant variables |
||||
|
deactivate nondestructive |
||||
|
|
||||
|
VIRTUAL_ENV="/home/wes/MGOAL/venv" |
||||
|
export VIRTUAL_ENV |
||||
|
|
||||
|
_OLD_VIRTUAL_PATH="$PATH" |
||||
|
PATH="$VIRTUAL_ENV/bin:$PATH" |
||||
|
export PATH |
||||
|
|
||||
|
# unset PYTHONHOME if set |
||||
|
if ! [ -z "${PYTHONHOME+_}" ] ; then |
||||
|
_OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" |
||||
|
unset PYTHONHOME |
||||
|
fi |
||||
|
|
||||
|
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then |
||||
|
_OLD_VIRTUAL_PS1="$PS1" |
||||
|
if [ "x" != x ] ; then |
||||
|
PS1="$PS1" |
||||
|
else |
||||
|
PS1="(`basename \"$VIRTUAL_ENV\"`) $PS1" |
||||
|
fi |
||||
|
export PS1 |
||||
|
fi |
||||
|
|
||||
|
# Make sure to unalias pydoc if it's already there |
||||
|
alias pydoc 2>/dev/null >/dev/null && unalias pydoc |
||||
|
|
||||
|
pydoc () { |
||||
|
python -m pydoc "$@" |
||||
|
} |
||||
|
|
||||
|
# This should detect bash and zsh, which have a hash command that must |
||||
|
# be called to get it to forget past commands. Without forgetting |
||||
|
# past commands the $PATH changes we made may not be respected |
||||
|
if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ] ; then |
||||
|
hash -r 2>/dev/null |
||||
|
fi |
@ -0,0 +1,36 @@ |
|||||
|
# This file must be used with "source bin/activate.csh" *from csh*. |
||||
|
# You cannot run it directly. |
||||
|
# Created by Davide Di Blasi <davidedb@gmail.com>. |
||||
|
|
||||
|
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc' |
||||
|
|
||||
|
# Unset irrelevant variables. |
||||
|
deactivate nondestructive |
||||
|
|
||||
|
setenv VIRTUAL_ENV "/home/wes/MGOAL/venv" |
||||
|
|
||||
|
set _OLD_VIRTUAL_PATH="$PATH" |
||||
|
setenv PATH "$VIRTUAL_ENV/bin:$PATH" |
||||
|
|
||||
|
|
||||
|
|
||||
|
if ("" != "") then |
||||
|
set env_name = "" |
||||
|
else |
||||
|
set env_name = `basename "$VIRTUAL_ENV"` |
||||
|
endif |
||||
|
|
||||
|
# Could be in a non-interactive environment, |
||||
|
# in which case, $prompt is undefined and we wouldn't |
||||
|
# care about the prompt anyway. |
||||
|
if ( $?prompt ) then |
||||
|
set _OLD_VIRTUAL_PROMPT="$prompt" |
||||
|
set prompt = "[$env_name] $prompt" |
||||
|
endif |
||||
|
|
||||
|
unset env_name |
||||
|
|
||||
|
alias pydoc python -m pydoc |
||||
|
|
||||
|
rehash |
||||
|
|
@ -0,0 +1,76 @@ |
|||||
|
# This file must be used using `. bin/activate.fish` *within a running fish ( http://fishshell.com ) session*. |
||||
|
# Do not run it directly. |
||||
|
|
||||
|
function deactivate -d 'Exit virtualenv mode and return to the normal environment.' |
||||
|
# reset old environment variables |
||||
|
if test -n "$_OLD_VIRTUAL_PATH" |
||||
|
set -gx PATH $_OLD_VIRTUAL_PATH |
||||
|
set -e _OLD_VIRTUAL_PATH |
||||
|
end |
||||
|
|
||||
|
if test -n "$_OLD_VIRTUAL_PYTHONHOME" |
||||
|
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME |
||||
|
set -e _OLD_VIRTUAL_PYTHONHOME |
||||
|
end |
||||
|
|
||||
|
if test -n "$_OLD_FISH_PROMPT_OVERRIDE" |
||||
|
# Set an empty local `$fish_function_path` to allow the removal of `fish_prompt` using `functions -e`. |
||||
|
set -l fish_function_path |
||||
|
|
||||
|
# Erase virtualenv's `fish_prompt` and restore the original. |
||||
|
functions -e fish_prompt |
||||
|
functions -c _old_fish_prompt fish_prompt |
||||
|
functions -e _old_fish_prompt |
||||
|
set -e _OLD_FISH_PROMPT_OVERRIDE |
||||
|
end |
||||
|
|
||||
|
set -e VIRTUAL_ENV |
||||
|
|
||||
|
if test "$argv[1]" != 'nondestructive' |
||||
|
# Self-destruct! |
||||
|
functions -e pydoc |
||||
|
functions -e deactivate |
||||
|
end |
||||
|
end |
||||
|
|
||||
|
# Unset irrelevant variables. |
||||
|
deactivate nondestructive |
||||
|
|
||||
|
set -gx VIRTUAL_ENV "/home/wes/MGOAL/venv" |
||||
|
|
||||
|
set -gx _OLD_VIRTUAL_PATH $PATH |
||||
|
set -gx PATH "$VIRTUAL_ENV/bin" $PATH |
||||
|
|
||||
|
# Unset `$PYTHONHOME` if set. |
||||
|
if set -q PYTHONHOME |
||||
|
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME |
||||
|
set -e PYTHONHOME |
||||
|
end |
||||
|
|
||||
|
function pydoc |
||||
|
python -m pydoc $argv |
||||
|
end |
||||
|
|
||||
|
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" |
||||
|
# Copy the current `fish_prompt` function as `_old_fish_prompt`. |
||||
|
functions -c fish_prompt _old_fish_prompt |
||||
|
|
||||
|
function fish_prompt |
||||
|
# Save the current $status, for fish_prompts that display it. |
||||
|
set -l old_status $status |
||||
|
|
||||
|
# Prompt override provided? |
||||
|
# If not, just prepend the environment name. |
||||
|
if test -n "" |
||||
|
printf '%s%s' "" (set_color normal) |
||||
|
else |
||||
|
printf '%s(%s) ' (set_color normal) (basename "$VIRTUAL_ENV") |
||||
|
end |
||||
|
|
||||
|
# Restore the original $status |
||||
|
echo "exit $old_status" | source |
||||
|
_old_fish_prompt |
||||
|
end |
||||
|
|
||||
|
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" |
||||
|
end |
@ -0,0 +1,34 @@ |
|||||
|
"""By using execfile(this_file, dict(__file__=this_file)) you will |
||||
|
activate this virtualenv environment. |
||||
|
|
||||
|
This can be used when you must use an existing Python interpreter, not |
||||
|
the virtualenv bin/python |
||||
|
""" |
||||
|
|
||||
|
try: |
||||
|
__file__ |
||||
|
except NameError: |
||||
|
raise AssertionError( |
||||
|
"You must run this like execfile('path/to/activate_this.py', dict(__file__='path/to/activate_this.py'))") |
||||
|
import sys |
||||
|
import os |
||||
|
|
||||
|
old_os_path = os.environ.get('PATH', '') |
||||
|
os.environ['PATH'] = os.path.dirname(os.path.abspath(__file__)) + os.pathsep + old_os_path |
||||
|
base = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
||||
|
if sys.platform == 'win32': |
||||
|
site_packages = os.path.join(base, 'Lib', 'site-packages') |
||||
|
else: |
||||
|
site_packages = os.path.join(base, 'lib', 'python%s' % sys.version[:3], 'site-packages') |
||||
|
prev_sys_path = list(sys.path) |
||||
|
import site |
||||
|
site.addsitedir(site_packages) |
||||
|
sys.real_prefix = sys.prefix |
||||
|
sys.prefix = base |
||||
|
# Move the added items to the front of the path: |
||||
|
new_sys_path = [] |
||||
|
for item in list(sys.path): |
||||
|
if item not in prev_sys_path: |
||||
|
new_sys_path.append(item) |
||||
|
sys.path.remove(item) |
||||
|
sys.path[:0] = new_sys_path |
@ -0,0 +1,11 @@ |
|||||
|
#!/home/wes/MGOAL/venv/bin/python2 |
||||
|
|
||||
|
# -*- coding: utf-8 -*- |
||||
|
import re |
||||
|
import sys |
||||
|
|
||||
|
from setuptools.command.easy_install import main |
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
||||
|
sys.exit(main()) |
@ -0,0 +1,11 @@ |
|||||
|
#!/home/wes/MGOAL/venv/bin/python2 |
||||
|
|
||||
|
# -*- coding: utf-8 -*- |
||||
|
import re |
||||
|
import sys |
||||
|
|
||||
|
from setuptools.command.easy_install import main |
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
||||
|
sys.exit(main()) |
@ -0,0 +1,11 @@ |
|||||
|
#!/home/wes/MGOAL/venv/bin/python2 |
||||
|
|
||||
|
# -*- coding: utf-8 -*- |
||||
|
import re |
||||
|
import sys |
||||
|
|
||||
|
from flask.cli import main |
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
||||
|
sys.exit(main()) |
@ -0,0 +1,11 @@ |
|||||
|
#!/home/wes/MGOAL/venv/bin/python2 |
||||
|
|
||||
|
# -*- coding: utf-8 -*- |
||||
|
import re |
||||
|
import sys |
||||
|
|
||||
|
from pip import main |
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
||||
|
sys.exit(main()) |
@ -0,0 +1,11 @@ |
|||||
|
#!/home/wes/MGOAL/venv/bin/python2 |
||||
|
|
||||
|
# -*- coding: utf-8 -*- |
||||
|
import re |
||||
|
import sys |
||||
|
|
||||
|
from pip import main |
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
||||
|
sys.exit(main()) |
@ -0,0 +1,11 @@ |
|||||
|
#!/home/wes/MGOAL/venv/bin/python2 |
||||
|
|
||||
|
# -*- coding: utf-8 -*- |
||||
|
import re |
||||
|
import sys |
||||
|
|
||||
|
from pip import main |
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
||||
|
sys.exit(main()) |
@ -0,0 +1 @@ |
|||||
|
python2 |
@ -0,0 +1,78 @@ |
|||||
|
#!/home/wes/MGOAL/venv/bin/python |
||||
|
|
||||
|
import sys |
||||
|
import getopt |
||||
|
import sysconfig |
||||
|
|
||||
|
valid_opts = ['prefix', 'exec-prefix', 'includes', 'libs', 'cflags', |
||||
|
'ldflags', 'help'] |
||||
|
|
||||
|
if sys.version_info >= (3, 2): |
||||
|
valid_opts.insert(-1, 'extension-suffix') |
||||
|
valid_opts.append('abiflags') |
||||
|
if sys.version_info >= (3, 3): |
||||
|
valid_opts.append('configdir') |
||||
|
|
||||
|
|
||||
|
def exit_with_usage(code=1): |
||||
|
sys.stderr.write("Usage: {0} [{1}]\n".format( |
||||
|
sys.argv[0], '|'.join('--'+opt for opt in valid_opts))) |
||||
|
sys.exit(code) |
||||
|
|
||||
|
try: |
||||
|
opts, args = getopt.getopt(sys.argv[1:], '', valid_opts) |
||||
|
except getopt.error: |
||||
|
exit_with_usage() |
||||
|
|
||||
|
if not opts: |
||||
|
exit_with_usage() |
||||
|
|
||||
|
pyver = sysconfig.get_config_var('VERSION') |
||||
|
getvar = sysconfig.get_config_var |
||||
|
|
||||
|
opt_flags = [flag for (flag, val) in opts] |
||||
|
|
||||
|
if '--help' in opt_flags: |
||||
|
exit_with_usage(code=0) |
||||
|
|
||||
|
for opt in opt_flags: |
||||
|
if opt == '--prefix': |
||||
|
print(sysconfig.get_config_var('prefix')) |
||||
|
|
||||
|
elif opt == '--exec-prefix': |
||||
|
print(sysconfig.get_config_var('exec_prefix')) |
||||
|
|
||||
|
elif opt in ('--includes', '--cflags'): |
||||
|
flags = ['-I' + sysconfig.get_path('include'), |
||||
|
'-I' + sysconfig.get_path('platinclude')] |
||||
|
if opt == '--cflags': |
||||
|
flags.extend(getvar('CFLAGS').split()) |
||||
|
print(' '.join(flags)) |
||||
|
|
||||
|
elif opt in ('--libs', '--ldflags'): |
||||
|
abiflags = getattr(sys, 'abiflags', '') |
||||
|
libs = ['-lpython' + pyver + abiflags] |
||||
|
libs += getvar('LIBS').split() |
||||
|
libs += getvar('SYSLIBS').split() |
||||
|
# add the prefix/lib/pythonX.Y/config dir, but only if there is no |
||||
|
# shared library in prefix/lib/. |
||||
|
if opt == '--ldflags': |
||||
|
if not getvar('Py_ENABLE_SHARED'): |
||||
|
libs.insert(0, '-L' + getvar('LIBPL')) |
||||
|
if not getvar('PYTHONFRAMEWORK'): |
||||
|
libs.extend(getvar('LINKFORSHARED').split()) |
||||
|
print(' '.join(libs)) |
||||
|
|
||||
|
elif opt == '--extension-suffix': |
||||
|
ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') |
||||
|
if ext_suffix is None: |
||||
|
ext_suffix = sysconfig.get_config_var('SO') |
||||
|
print(ext_suffix) |
||||
|
|
||||
|
elif opt == '--abiflags': |
||||
|
if not getattr(sys, 'abiflags', None): |
||||
|
exit_with_usage() |
||||
|
print(sys.abiflags) |
||||
|
|
||||
|
elif opt == '--configdir': |
||||
|
print(sysconfig.get_config_var('LIBPL')) |
Binary file not shown.
@ -0,0 +1 @@ |
|||||
|
python2 |
@ -0,0 +1,11 @@ |
|||||
|
#!/home/wes/MGOAL/venv/bin/python2 |
||||
|
|
||||
|
# -*- coding: utf-8 -*- |
||||
|
import re |
||||
|
import sys |
||||
|
|
||||
|
from wheel.tool import main |
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) |
||||
|
sys.exit(main()) |
@ -0,0 +1 @@ |
|||||
|
/usr/include/python2.7 |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/UserDict.py |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/_abcoll.py |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/_weakrefset.py |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/abc.py |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/codecs.py |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/config |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/copy_reg.py |
@ -0,0 +1,101 @@ |
|||||
|
import os |
||||
|
import sys |
||||
|
import warnings |
||||
|
import imp |
||||
|
import opcode # opcode is not a virtualenv module, so we can use it to find the stdlib |
||||
|
# Important! To work on pypy, this must be a module that resides in the |
||||
|
# lib-python/modified-x.y.z directory |
||||
|
|
||||
|
dirname = os.path.dirname |
||||
|
|
||||
|
distutils_path = os.path.join(os.path.dirname(opcode.__file__), 'distutils') |
||||
|
if os.path.normpath(distutils_path) == os.path.dirname(os.path.normpath(__file__)): |
||||
|
warnings.warn( |
||||
|
"The virtualenv distutils package at %s appears to be in the same location as the system distutils?") |
||||
|
else: |
||||
|
__path__.insert(0, distutils_path) |
||||
|
real_distutils = imp.load_module("_virtualenv_distutils", None, distutils_path, ('', '', imp.PKG_DIRECTORY)) |
||||
|
# Copy the relevant attributes |
||||
|
try: |
||||
|
__revision__ = real_distutils.__revision__ |
||||
|
except AttributeError: |
||||
|
pass |
||||
|
__version__ = real_distutils.__version__ |
||||
|
|
||||
|
from distutils import dist, sysconfig |
||||
|
|
||||
|
try: |
||||
|
basestring |
||||
|
except NameError: |
||||
|
basestring = str |
||||
|
|
||||
|
## patch build_ext (distutils doesn't know how to get the libs directory |
||||
|
## path on windows - it hardcodes the paths around the patched sys.prefix) |
||||
|
|
||||
|
if sys.platform == 'win32': |
||||
|
from distutils.command.build_ext import build_ext as old_build_ext |
||||
|
class build_ext(old_build_ext): |
||||
|
def finalize_options (self): |
||||
|
if self.library_dirs is None: |
||||
|
self.library_dirs = [] |
||||
|
elif isinstance(self.library_dirs, basestring): |
||||
|
self.library_dirs = self.library_dirs.split(os.pathsep) |
||||
|
|
||||
|
self.library_dirs.insert(0, os.path.join(sys.real_prefix, "Libs")) |
||||
|
old_build_ext.finalize_options(self) |
||||
|
|
||||
|
from distutils.command import build_ext as build_ext_module |
||||
|
build_ext_module.build_ext = build_ext |
||||
|
|
||||
|
## distutils.dist patches: |
||||
|
|
||||
|
old_find_config_files = dist.Distribution.find_config_files |
||||
|
def find_config_files(self): |
||||
|
found = old_find_config_files(self) |
||||
|
system_distutils = os.path.join(distutils_path, 'distutils.cfg') |
||||
|
#if os.path.exists(system_distutils): |
||||
|
# found.insert(0, system_distutils) |
||||
|
# What to call the per-user config file |
||||
|
if os.name == 'posix': |
||||
|
user_filename = ".pydistutils.cfg" |
||||
|
else: |
||||
|
user_filename = "pydistutils.cfg" |
||||
|
user_filename = os.path.join(sys.prefix, user_filename) |
||||
|
if os.path.isfile(user_filename): |
||||
|
for item in list(found): |
||||
|
if item.endswith('pydistutils.cfg'): |
||||
|
found.remove(item) |
||||
|
found.append(user_filename) |
||||
|
return found |
||||
|
dist.Distribution.find_config_files = find_config_files |
||||
|
|
||||
|
## distutils.sysconfig patches: |
||||
|
|
||||
|
old_get_python_inc = sysconfig.get_python_inc |
||||
|
def sysconfig_get_python_inc(plat_specific=0, prefix=None): |
||||
|
if prefix is None: |
||||
|
prefix = sys.real_prefix |
||||
|
return old_get_python_inc(plat_specific, prefix) |
||||
|
sysconfig_get_python_inc.__doc__ = old_get_python_inc.__doc__ |
||||
|
sysconfig.get_python_inc = sysconfig_get_python_inc |
||||
|
|
||||
|
old_get_python_lib = sysconfig.get_python_lib |
||||
|
def sysconfig_get_python_lib(plat_specific=0, standard_lib=0, prefix=None): |
||||
|
if standard_lib and prefix is None: |
||||
|
prefix = sys.real_prefix |
||||
|
return old_get_python_lib(plat_specific, standard_lib, prefix) |
||||
|
sysconfig_get_python_lib.__doc__ = old_get_python_lib.__doc__ |
||||
|
sysconfig.get_python_lib = sysconfig_get_python_lib |
||||
|
|
||||
|
old_get_config_vars = sysconfig.get_config_vars |
||||
|
def sysconfig_get_config_vars(*args): |
||||
|
real_vars = old_get_config_vars(*args) |
||||
|
if sys.platform == 'win32': |
||||
|
lib_dir = os.path.join(sys.real_prefix, "libs") |
||||
|
if isinstance(real_vars, dict) and 'LIBDIR' not in real_vars: |
||||
|
real_vars['LIBDIR'] = lib_dir # asked for all |
||||
|
elif isinstance(real_vars, list) and 'LIBDIR' in args: |
||||
|
real_vars = real_vars + [lib_dir] # asked for list |
||||
|
return real_vars |
||||
|
sysconfig_get_config_vars.__doc__ = old_get_config_vars.__doc__ |
||||
|
sysconfig.get_config_vars = sysconfig_get_config_vars |
@ -0,0 +1,6 @@ |
|||||
|
# This is a config file local to this virtualenv installation |
||||
|
# You may include options that will be used by all distutils commands, |
||||
|
# and by easy_install. For instance: |
||||
|
# |
||||
|
# [easy_install] |
||||
|
# find_links = http://mylocalsite |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/encodings |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/fnmatch.py |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/genericpath.py |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/lib-dynload |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/linecache.py |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/locale.py |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/ntpath.py |
@ -0,0 +1 @@ |
|||||
|
/usr |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/os.py |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/posixpath.py |
@ -0,0 +1 @@ |
|||||
|
/usr/lib/python2.7/re.py |
@ -0,0 +1,44 @@ |
|||||
|
Flask |
||||
|
----- |
||||
|
|
||||
|
Flask is a microframework for Python based on Werkzeug, Jinja 2 and good |
||||
|
intentions. And before you ask: It's BSD licensed! |
||||
|
|
||||
|
Flask is Fun |
||||
|
```````````` |
||||
|
|
||||
|
Save in a hello.py: |
||||
|
|
||||
|
.. code:: python |
||||
|
|
||||
|
from flask import Flask |
||||
|
app = Flask(__name__) |
||||
|
|
||||
|
@app.route("/") |
||||
|
def hello(): |
||||
|
return "Hello World!" |
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
app.run() |
||||
|
|
||||
|
And Easy to Setup |
||||
|
````````````````` |
||||
|
|
||||
|
And run it: |
||||
|
|
||||
|
.. code:: bash |
||||
|
|
||||
|
$ pip install Flask |
||||
|
$ python hello.py |
||||
|
* Running on http://localhost:5000/ |
||||
|
|
||||
|
Links |
||||
|
````` |
||||
|
|
||||
|
* `website <http://flask.pocoo.org/>`_ |
||||
|
* `documentation <http://flask.pocoo.org/docs/>`_ |
||||
|
* `development version |
||||
|
<http://github.com/pallets/flask/zipball/master#egg=Flask-dev>`_ |
||||
|
|
||||
|
|
||||
|
|
@ -0,0 +1 @@ |
|||||
|
pip |
@ -0,0 +1,73 @@ |
|||||
|
Metadata-Version: 2.0 |
||||
|
Name: Flask |
||||
|
Version: 0.11.1 |
||||
|
Summary: A microframework based on Werkzeug, Jinja2 and good intentions |
||||
|
Home-page: http://github.com/pallets/flask/ |
||||
|
Author: Armin Ronacher |
||||
|
Author-email: armin.ronacher@active-4.com |
||||
|
License: BSD |
||||
|
Platform: any |
||||
|
Classifier: Development Status :: 4 - Beta |
||||
|
Classifier: Environment :: Web Environment |
||||
|
Classifier: Intended Audience :: Developers |
||||
|
Classifier: License :: OSI Approved :: BSD License |
||||
|
Classifier: Operating System :: OS Independent |
||||
|
Classifier: Programming Language :: Python |
||||
|
Classifier: Programming Language :: Python :: 2 |
||||
|
Classifier: Programming Language :: Python :: 2.6 |
||||
|
Classifier: Programming Language :: Python :: 2.7 |
||||
|
Classifier: Programming Language :: Python :: 3 |
||||
|
Classifier: Programming Language :: Python :: 3.3 |
||||
|
Classifier: Programming Language :: Python :: 3.4 |
||||
|
Classifier: Programming Language :: Python :: 3.5 |
||||
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content |
||||
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules |
||||
|
Requires-Dist: Jinja2 (>=2.4) |
||||
|
Requires-Dist: Werkzeug (>=0.7) |
||||
|
Requires-Dist: click (>=2.0) |
||||
|
Requires-Dist: itsdangerous (>=0.21) |
||||
|
|
||||
|
Flask |
||||
|
----- |
||||
|
|
||||
|
Flask is a microframework for Python based on Werkzeug, Jinja 2 and good |
||||
|
intentions. And before you ask: It's BSD licensed! |
||||
|
|
||||
|
Flask is Fun |
||||
|
```````````` |
||||
|
|
||||
|
Save in a hello.py: |
||||
|
|
||||
|
.. code:: python |
||||
|
|
||||
|
from flask import Flask |
||||
|
app = Flask(__name__) |
||||
|
|
||||
|
@app.route("/") |
||||
|
def hello(): |
||||
|
return "Hello World!" |
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
app.run() |
||||
|
|
||||
|
And Easy to Setup |
||||
|
````````````````` |
||||
|
|
||||
|
And run it: |
||||
|
|
||||
|
.. code:: bash |
||||
|
|
||||
|
$ pip install Flask |
||||
|
$ python hello.py |
||||
|
* Running on http://localhost:5000/ |
||||
|
|
||||
|
Links |
||||
|
````` |
||||
|
|
||||
|
* `website <http://flask.pocoo.org/>`_ |
||||
|
* `documentation <http://flask.pocoo.org/docs/>`_ |
||||
|
* `development version |
||||
|
<http://github.com/pallets/flask/zipball/master#egg=Flask-dev>`_ |
||||
|
|
||||
|
|
||||
|
|
@ -0,0 +1,51 @@ |
|||||
|
Flask-0.11.1.dist-info/DESCRIPTION.rst,sha256=d3yS7Rb_P-vHUNF_euyueS52NGfpmuqg-C4FVuyGL1w,726 |
||||
|
Flask-0.11.1.dist-info/METADATA,sha256=JYsqnubNfoj5zUbNv3Ip0Oim6ubPsjTRmdgDj0vsQOQ,1864 |
||||
|
Flask-0.11.1.dist-info/RECORD,, |
||||
|
Flask-0.11.1.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 |
||||
|
Flask-0.11.1.dist-info/entry_points.txt,sha256=jzk2Wy2h30uEcqqzd4CVnlzsMXB-vaD5GXjuPMXmTmI,60 |
||||
|
Flask-0.11.1.dist-info/metadata.json,sha256=mZ1xu9uLOritQIZ0KTAMKnzcA1PHcyFP6e7pgi2Uh64,1363 |
||||
|
Flask-0.11.1.dist-info/top_level.txt,sha256=dvi65F6AeGWVU0TBpYiC04yM60-FX1gJFkK31IKQr5c,6 |
||||
|
flask/__init__.py,sha256=nPn1VVakZyNaD7j1f3k8I4y81ch8U_aKTUhGWE0mcTY,1673 |
||||
|
flask/__main__.py,sha256=cldbNi5zpjE68XzIWI8uYHNWwBHHVJmwtlXWk6P4CO4,291 |
||||
|
flask/_compat.py,sha256=vtZmNzjdZ5slXhSrjOVIX52C_FIY03xipZjJdrWUv1o,2490 |
||||
|
flask/app.py,sha256=Fmtl5tQjFcz-x6jSFpRDX7AmLSu-qPP4jvpx2G7d2EE,83370 |
||||
|
flask/blueprints.py,sha256=6HVasMcPcaq7tk36kCrgX4bnhTkky4G5WIWCyyJL8HY,16872 |
||||
|
flask/cli.py,sha256=mUcrLCj_e_GjTxM5YOF4f_qCB3kPphTFwDHPuStrL4s,16941 |
||||
|
flask/config.py,sha256=94IXmQjOxy4L-EsW5R5cljc9uzzCdYNFVwZjg4uJ2Xg,9602 |
||||
|
flask/ctx.py,sha256=UPA0YwoIlHP0txOGanC9lQLSGv6eCqV5Fmw2cVJRmgQ,14739 |
||||
|
flask/debughelpers.py,sha256=z-uQavKIymOZl0WQDLXsnacA00ERIlCx3S3Tnb_OYsE,6024 |
||||
|
flask/exthook.py,sha256=BHC5mNVqvmMpktMxTckJKiw2wyWiKQkV8eiiK1ZXTSQ,5748 |
||||
|
flask/globals.py,sha256=I3m_4RssLhWW1R11zuEI8oFryHUHX3NQwjMkGXOZzg8,1645 |
||||
|
flask/helpers.py,sha256=VTVl7C1GRyp3y-SkX1fYGJhG-TlzfAWphglayGR6Zog,37384 |
||||
|
flask/json.py,sha256=Dht7yQuPBpp0YH3gETBJDo5hdeSSzHCVEEOxnSslgpg,9261 |
||||
|
flask/logging.py,sha256=NwB0-gTqg2htzI88_BxWcmiMWVKb0NgY2RgZTzEZQmA,2683 |
||||
|
flask/sessions.py,sha256=iZBE63y-1SA8PmpIIngYrjJZvc3ZY3wDZt0Q2DTFrdc,14333 |
||||
|
flask/signals.py,sha256=ufhmyiteedO7_nOOcsMheQwGpjPkEP93dh_I7lZlu_c,2209 |
||||
|
flask/templating.py,sha256=u7FbN6j56H_q6CrdJJyJ6gZtqaMa0vh1_GP12gEHRQQ,4912 |
||||
|
flask/testing.py,sha256=t8dNl0b8Qq8A9dUXgYp1HmLOq-FGa90gTr-gLh9WTgA,5101 |
||||
|
flask/views.py,sha256=1QCDSf2w9cmrrKPAmresZiBbvYsDbPnEr1-KmBkQFGY,5632 |
||||
|
flask/wrappers.py,sha256=avZTGHogskE_pJ1iHhULhXFPkaYoeha6vT20JskH_fc,7538 |
||||
|
flask/ext/__init__.py,sha256=UEezCApsG4ZJWqwUnX9YmWcNN4OVENgph_9L05n0eOM,842 |
||||
|
../../../bin/flask,sha256=TCnOAaDSCS_Yn-mcXLKUOrE9i1L2r4Ukgzy0gHRnUgU,227 |
||||
|
Flask-0.11.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 |
||||
|
flask/config.pyc,, |
||||
|
flask/testing.pyc,, |
||||
|
flask/wrappers.pyc,, |
||||
|
flask/views.pyc,, |
||||
|
flask/logging.pyc,, |
||||
|
flask/json.pyc,, |
||||
|
flask/helpers.pyc,, |
||||
|
flask/__main__.pyc,, |
||||
|
flask/blueprints.pyc,, |
||||
|
flask/signals.pyc,, |
||||
|
flask/exthook.pyc,, |
||||
|
flask/__init__.pyc,, |
||||
|
flask/debughelpers.pyc,, |
||||
|
flask/templating.pyc,, |
||||
|
flask/ctx.pyc,, |
||||
|
flask/cli.pyc,, |
||||
|
flask/_compat.pyc,, |
||||
|
flask/app.pyc,, |
||||
|
flask/globals.pyc,, |
||||
|
flask/ext/__init__.pyc,, |
||||
|
flask/sessions.pyc,, |
@ -0,0 +1,6 @@ |
|||||
|
Wheel-Version: 1.0 |
||||
|
Generator: bdist_wheel (0.29.0) |
||||
|
Root-Is-Purelib: true |
||||
|
Tag: py2-none-any |
||||
|
Tag: py3-none-any |
||||
|
|
@ -0,0 +1,4 @@ |
|||||
|
|
||||
|
[console_scripts] |
||||
|
flask=flask.cli:main |
||||
|
|
@ -0,0 +1 @@ |
|||||
|
{"classifiers": ["Development Status :: 4 - Beta", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules"], "extensions": {"python.commands": {"wrap_console": {"flask": "flask.cli:main"}}, "python.details": {"contacts": [{"email": "armin.ronacher@active-4.com", "name": "Armin Ronacher", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/pallets/flask/"}}, "python.exports": {"console_scripts": {"flask": "flask.cli:main"}}}, "extras": [], "generator": "bdist_wheel (0.29.0)", "license": "BSD", "metadata_version": "2.0", "name": "Flask", "platform": "any", "run_requires": [{"requires": ["Jinja2 (>=2.4)", "Werkzeug (>=0.7)", "click (>=2.0)", "itsdangerous (>=0.21)"]}], "summary": "A microframework based on Werkzeug, Jinja2 and good intentions", "version": "0.11.1"} |
@ -0,0 +1 @@ |
|||||
|
flask |
@ -0,0 +1,36 @@ |
|||||
|
Jinja2 |
||||
|
~~~~~~ |
||||
|
|
||||
|
Jinja2 is a template engine written in pure Python. It provides a |
||||
|
`Django`_ inspired non-XML syntax but supports inline expressions and |
||||
|
an optional `sandboxed`_ environment. |
||||
|
|
||||
|
Nutshell |
||||
|
-------- |
||||
|
|
||||
|
Here a small example of a Jinja template:: |
||||
|
|
||||
|
{% extends 'base.html' %} |
||||
|
{% block title %}Memberlist{% endblock %} |
||||
|
{% block content %} |
||||
|
<ul> |
||||
|
{% for user in users %} |
||||
|
<li><a href="{{ user.url }}">{{ user.username }}</a></li> |
||||
|
{% endfor %} |
||||
|
</ul> |
||||
|
{% endblock %} |
||||
|
|
||||
|
Philosophy |
||||
|
---------- |
||||
|
|
||||
|
Application logic is for the controller but don't try to make the life |
||||
|
for the template designer too hard by giving him too few functionality. |
||||
|
|
||||
|
For more informations visit the new `Jinja2 webpage`_ and `documentation`_. |
||||
|
|
||||
|
.. _sandboxed: http://en.wikipedia.org/wiki/Sandbox_(computer_security) |
||||
|
.. _Django: http://www.djangoproject.com/ |
||||
|
.. _Jinja2 webpage: http://jinja.pocoo.org/ |
||||
|
.. _documentation: http://jinja.pocoo.org/2/documentation/ |
||||
|
|
||||
|
|
@ -0,0 +1 @@ |
|||||
|
pip |
@ -0,0 +1,63 @@ |
|||||
|
Metadata-Version: 2.0 |
||||
|
Name: Jinja2 |
||||
|
Version: 2.8 |
||||
|
Summary: A small but fast and easy to use stand-alone template engine written in pure python. |
||||
|
Home-page: http://jinja.pocoo.org/ |
||||
|
Author: Armin Ronacher |
||||
|
Author-email: armin.ronacher@active-4.com |
||||
|
License: BSD |
||||
|
Platform: UNKNOWN |
||||
|
Classifier: Development Status :: 5 - Production/Stable |
||||
|
Classifier: Environment :: Web Environment |
||||
|
Classifier: Intended Audience :: Developers |
||||
|
Classifier: License :: OSI Approved :: BSD License |
||||
|
Classifier: Operating System :: OS Independent |
||||
|
Classifier: Programming Language :: Python |
||||
|
Classifier: Programming Language :: Python :: 2 |
||||
|
Classifier: Programming Language :: Python :: 2.6 |
||||
|
Classifier: Programming Language :: Python :: 2.7 |
||||
|
Classifier: Programming Language :: Python :: 3 |
||||
|
Classifier: Programming Language :: Python :: 3.3 |
||||
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content |
||||
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules |
||||
|
Classifier: Topic :: Text Processing :: Markup :: HTML |
||||
|
Requires-Dist: MarkupSafe |
||||
|
Provides-Extra: i18n |
||||
|
Requires-Dist: Babel (>=0.8); extra == 'i18n' |
||||
|
|
||||
|
Jinja2 |
||||
|
~~~~~~ |
||||
|
|
||||
|
Jinja2 is a template engine written in pure Python. It provides a |
||||
|
`Django`_ inspired non-XML syntax but supports inline expressions and |
||||
|
an optional `sandboxed`_ environment. |
||||
|
|
||||
|
Nutshell |
||||
|
-------- |
||||
|
|
||||
|
Here a small example of a Jinja template:: |
||||
|
|
||||
|
{% extends 'base.html' %} |
||||
|
{% block title %}Memberlist{% endblock %} |
||||
|
{% block content %} |
||||
|
<ul> |
||||
|
{% for user in users %} |
||||
|
<li><a href="{{ user.url }}">{{ user.username }}</a></li> |
||||
|
{% endfor %} |
||||
|
</ul> |
||||
|
{% endblock %} |
||||
|
|
||||
|
Philosophy |
||||
|
---------- |
||||
|
|
||||
|
Application logic is for the controller but don't try to make the life |
||||
|
for the template designer too hard by giving him too few functionality. |
||||
|
|
||||
|
For more informations visit the new `Jinja2 webpage`_ and `documentation`_. |
||||
|
|
||||
|
.. _sandboxed: http://en.wikipedia.org/wiki/Sandbox_(computer_security) |
||||
|
.. _Django: http://www.djangoproject.com/ |
||||
|
.. _Jinja2 webpage: http://jinja.pocoo.org/ |
||||
|
.. _documentation: http://jinja.pocoo.org/2/documentation/ |
||||
|
|
||||
|
|
@ -0,0 +1,54 @@ |
|||||
|
jinja2/__init__.py,sha256=c59bnaAFo63I7lYUZlO2UKHj8LPG3JACKnCrwWgvjGY,2326 |
||||
|
jinja2/_compat.py,sha256=O4FnYOMi4HRBfoCKkX137tt3sR6HvpnQNcwqg8ARYog,3109 |
||||
|
jinja2/_stringdefs.py,sha256=SFObWX5vSMeGNc_aSO3_B2EEmScCstFWtjS4K0YFBXk,404291 |
||||
|
jinja2/bccache.py,sha256=EMN9fsvOpwK3DfxQ9F1lmWoxU2Qlo6AnNhPXTsMrw84,12793 |
||||
|
jinja2/compiler.py,sha256=nQmoS6HpGwgDIC8UXkSdjPYiAjbVqZ-Gf4odO-SAR6E,63846 |
||||
|
jinja2/constants.py,sha256=DCr-oKC2xQO-fkOQO3kXRJW7rEYgmcsMRNpPnM66YSU,1626 |
||||
|
jinja2/debug.py,sha256=GEGHM8vFsNFF-kGc0_fwyj1ftMtuyaH4r0nyG-XA9Z8,11553 |
||||
|
jinja2/defaults.py,sha256=eLMOE7JC52QwZBu5Gz4TPZqzoJy9IgV5EynL_pW7MUw,1057 |
||||
|
jinja2/environment.py,sha256=jzJmujSFtxb1HvITO4TdUCOOA-hSZx0gHrxeDZ2VE-M,48120 |
||||
|
jinja2/exceptions.py,sha256=Q9yZOUif-lhVj5BRw0ELjfBvEdBsB7xZobgOvC2qGy4,4428 |
||||
|
jinja2/ext.py,sha256=X-1zCiut1cuxIteKPkJr3jb6odlVE1jciO8RnrMniPE,25072 |
||||
|
jinja2/filters.py,sha256=R4x2flPfyzIjrtItzpGpK4LzBvx-NOlEXH9wD-ZBWtU,30115 |
||||
|
jinja2/lexer.py,sha256=QyiQwAQVEE2YREZJLcA04F3yqv0XOwBbSlWaFW4xJ20,28425 |
||||
|
jinja2/loaders.py,sha256=BgDCvmiB0gH_zPMf-6TMemqtJdrck3IyJ8g0kWUvFa0,17380 |
||||
|
jinja2/meta.py,sha256=cxAOtMuSWWSQX2H8zhYsAtjNwRcNB8Zvs06Y-JlWnbk,4198 |
||||
|
jinja2/nodes.py,sha256=YN6hfFa0WlfToG2r-Q-yhUkAUp0O9l8KulK53mOAVUo,28954 |
||||
|
jinja2/optimizer.py,sha256=bNNKbo5SC5FBUm9dvP-I3GkiXZYBYIER7_g9hK77ZVI,2302 |
||||
|
jinja2/parser.py,sha256=pjLfkZDg2IKJKt_ixNosV-RzwAja5GWYuVeBQumIRns,35442 |
||||
|
jinja2/runtime.py,sha256=Ct36Q9-gVmKer45syS4j3thQ15T_DnLDh6CqvTcnPwQ,22530 |
||||
|
jinja2/sandbox.py,sha256=qgH4CoBsF5NwGj0krqsCOw8sg2mXmfpZKnvmZEE-da4,13327 |
||||
|
jinja2/tests.py,sha256=znB0L_k6wdKp_lQJvxboXwUXDy1HhFe5SSA888tHt_w,4131 |
||||
|
jinja2/utils.py,sha256=pjbOhQJ5NYexu2MbjA66nBibudUkYcQRZbxvbYE0tFk,16560 |
||||
|
jinja2/visitor.py,sha256=3hEAYD26xS_JiJBf4RfcqYPpiuR6efOH8Hh6om59eU8,3316 |
||||
|
Jinja2-2.8.dist-info/DESCRIPTION.rst,sha256=CXIS1UnPSk5_lZBS6Lb8ko-3lqGfjsiUwNBLXCTj2lc,975 |
||||
|
Jinja2-2.8.dist-info/entry_points.txt,sha256=NdzVcOrqyNyKDxD09aERj__3bFx2paZhizFDsKmVhiA,72 |
||||
|
Jinja2-2.8.dist-info/METADATA,sha256=Vio5F8qaEVcGzaCV1rl8tIWEKsHUFSSSAfL0u9oMmGk,2061 |
||||
|
Jinja2-2.8.dist-info/metadata.json,sha256=4TsqsSBwGwy0C2xF_uRZHYsRn2W5Lv4NUMBjTnXPldM,1275 |
||||
|
Jinja2-2.8.dist-info/RECORD,, |
||||
|
Jinja2-2.8.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7 |
||||
|
Jinja2-2.8.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110 |
||||
|
Jinja2-2.8.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 |
||||
|
jinja2/_compat.pyc,, |
||||
|
jinja2/defaults.pyc,, |
||||
|
jinja2/sandbox.pyc,, |
||||
|
jinja2/_stringdefs.pyc,, |
||||
|
jinja2/environment.pyc,, |
||||
|
jinja2/bccache.pyc,, |
||||
|
jinja2/runtime.pyc,, |
||||
|
jinja2/utils.pyc,, |
||||
|
jinja2/parser.pyc,, |
||||
|
jinja2/debug.pyc,, |
||||
|
jinja2/visitor.pyc,, |
||||
|
jinja2/ext.pyc,, |
||||
|
jinja2/lexer.pyc,, |
||||
|
jinja2/nodes.pyc,, |
||||
|
jinja2/meta.pyc,, |
||||
|
jinja2/compiler.pyc,, |
||||
|
jinja2/exceptions.pyc,, |
||||
|
jinja2/__init__.pyc,, |
||||
|
jinja2/optimizer.pyc,, |
||||
|
jinja2/constants.pyc,, |
||||
|
jinja2/tests.pyc,, |
||||
|
jinja2/loaders.pyc,, |
||||
|
jinja2/filters.pyc,, |
@ -0,0 +1,6 @@ |
|||||
|
Wheel-Version: 1.0 |
||||
|
Generator: bdist_wheel (0.24.0) |
||||
|
Root-Is-Purelib: true |
||||
|
Tag: py2-none-any |
||||
|
Tag: py3-none-any |
||||
|
|
@ -0,0 +1,4 @@ |
|||||
|
|
||||
|
[babel.extractors] |
||||
|
jinja2 = jinja2.ext:babel_extract[i18n] |
||||
|
|
@ -0,0 +1 @@ |
|||||
|
{"license": "BSD", "name": "Jinja2", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "summary": "A small but fast and easy to use stand-alone template engine written in pure python.", "run_requires": [{"requires": ["Babel (>=0.8)"], "extra": "i18n"}, {"requires": ["MarkupSafe"]}], "version": "2.8", "extensions": {"python.details": {"project_urls": {"Home": "http://jinja.pocoo.org/"}, "document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"role": "author", "email": "armin.ronacher@active-4.com", "name": "Armin Ronacher"}]}, "python.exports": {"babel.extractors": {"jinja2": "jinja2.ext:babel_extract [i18n]"}}}, "classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Text Processing :: Markup :: HTML"], "extras": ["i18n"]} |
@ -0,0 +1 @@ |
|||||
|
jinja2 |
@ -0,0 +1,101 @@ |
|||||
|
MarkupSafe |
||||
|
========== |
||||
|
|
||||
|
Implements a unicode subclass that supports HTML strings: |
||||
|
|
||||
|
>>> from markupsafe import Markup, escape |
||||
|
>>> escape("<script>alert(document.cookie);</script>") |
||||
|
Markup(u'<script>alert(document.cookie);</script>') |
||||
|
>>> tmpl = Markup("<em>%s</em>") |
||||
|
>>> tmpl % "Peter > Lustig" |
||||
|
Markup(u'<em>Peter > Lustig</em>') |
||||
|
|
||||
|
If you want to make an object unicode that is not yet unicode |
||||
|
but don't want to lose the taint information, you can use the |
||||
|
`soft_unicode` function. (On Python 3 you can also use `soft_str` which |
||||
|
is a different name for the same function). |
||||
|
|
||||
|
>>> from markupsafe import soft_unicode |
||||
|
>>> soft_unicode(42) |
||||
|
u'42' |
||||
|
>>> soft_unicode(Markup('foo')) |
||||
|
Markup(u'foo') |
||||
|
|
||||
|
HTML Representations |
||||
|
-------------------- |
||||
|
|
||||
|
Objects can customize their HTML markup equivalent by overriding |
||||
|
the `__html__` function: |
||||
|
|
||||
|
>>> class Foo(object): |
||||
|
... def __html__(self): |
||||
|
... return '<strong>Nice</strong>' |
||||
|
... |
||||
|
>>> escape(Foo()) |
||||
|
Markup(u'<strong>Nice</strong>') |
||||
|
>>> Markup(Foo()) |
||||
|
Markup(u'<strong>Nice</strong>') |
||||
|
|
||||
|
Silent Escapes |
||||
|
-------------- |
||||
|
|
||||
|
Since MarkupSafe 0.10 there is now also a separate escape function |
||||
|
called `escape_silent` that returns an empty string for `None` for |
||||
|
consistency with other systems that return empty strings for `None` |
||||
|
when escaping (for instance Pylons' webhelpers). |
||||
|
|
||||
|
If you also want to use this for the escape method of the Markup |
||||
|
object, you can create your own subclass that does that:: |
||||
|
|
||||
|
from markupsafe import Markup, escape_silent as escape |
||||
|
|
||||
|
class SilentMarkup(Markup): |
||||
|
__slots__ = () |
||||
|
|
||||
|
@classmethod |
||||
|
def escape(cls, s): |
||||
|
return cls(escape(s)) |
||||
|
|
||||
|
New-Style String Formatting |
||||
|
--------------------------- |
||||
|
|
||||
|
Starting with MarkupSafe 0.21 new style string formats from Python 2.6 and |
||||
|
3.x are now fully supported. Previously the escape behavior of those |
||||
|
functions was spotty at best. The new implementations operates under the |
||||
|
following algorithm: |
||||
|
|
||||
|
1. if an object has an ``__html_format__`` method it is called as |
||||
|
replacement for ``__format__`` with the format specifier. It either |
||||
|
has to return a string or markup object. |
||||
|
2. if an object has an ``__html__`` method it is called. |
||||
|
3. otherwise the default format system of Python kicks in and the result |
||||
|
is HTML escaped. |
||||
|
|
||||
|
Here is how you can implement your own formatting:: |
||||
|
|
||||
|
class User(object): |
||||
|
|
||||
|
def __init__(self, id, username): |
||||
|
self.id = id |
||||
|
self.username = username |
||||
|
|
||||
|
def __html_format__(self, format_spec): |
||||
|
if format_spec == 'link': |
||||
|
return Markup('<a href="/user/{0}">{1}</a>').format( |
||||
|
self.id, |
||||
|
self.__html__(), |
||||
|
) |
||||
|
elif format_spec: |
||||
|
raise ValueError('Invalid format spec') |
||||
|
return self.__html__() |
||||
|
|
||||
|
def __html__(self): |
||||
|
return Markup('<span class=user>{0}</span>').format(self.username) |
||||
|
|
||||
|
And to format that user: |
||||
|
|
||||
|
>>> user = User(1, 'foo') |
||||
|
>>> Markup('<p>User: {0:link}').format(user) |
||||
|
Markup(u'<p>User: <a href="/user/1"><span class=user>foo</span></a>') |
||||
|
|
||||
|
|
@ -0,0 +1 @@ |
|||||
|
pip |
@ -0,0 +1,121 @@ |
|||||
|
Metadata-Version: 2.0 |
||||
|
Name: MarkupSafe |
||||
|
Version: 0.23 |
||||
|
Summary: Implements a XML/HTML/XHTML Markup safe string for Python |
||||
|
Home-page: http://github.com/mitsuhiko/markupsafe |
||||
|
Author: Armin Ronacher |
||||
|
Author-email: armin.ronacher@active-4.com |
||||
|
License: BSD |
||||
|
Platform: UNKNOWN |
||||
|
Classifier: Development Status :: 5 - Production/Stable |
||||
|
Classifier: Environment :: Web Environment |
||||
|
Classifier: Intended Audience :: Developers |
||||
|
Classifier: License :: OSI Approved :: BSD License |
||||
|
Classifier: Operating System :: OS Independent |
||||
|
Classifier: Programming Language :: Python |
||||
|
Classifier: Programming Language :: Python :: 3 |
||||
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content |
||||
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules |
||||
|
Classifier: Topic :: Text Processing :: Markup :: HTML |
||||
|
|
||||
|
MarkupSafe |
||||
|
========== |
||||
|
|
||||
|
Implements a unicode subclass that supports HTML strings: |
||||
|
|
||||
|
>>> from markupsafe import Markup, escape |
||||
|
>>> escape("<script>alert(document.cookie);</script>") |
||||
|
Markup(u'<script>alert(document.cookie);</script>') |
||||
|
>>> tmpl = Markup("<em>%s</em>") |
||||
|
>>> tmpl % "Peter > Lustig" |
||||
|
Markup(u'<em>Peter > Lustig</em>') |
||||
|
|
||||
|
If you want to make an object unicode that is not yet unicode |
||||
|
but don't want to lose the taint information, you can use the |
||||
|
`soft_unicode` function. (On Python 3 you can also use `soft_str` which |
||||
|
is a different name for the same function). |
||||
|
|
||||
|
>>> from markupsafe import soft_unicode |
||||
|
>>> soft_unicode(42) |
||||
|
u'42' |
||||
|
>>> soft_unicode(Markup('foo')) |
||||
|
Markup(u'foo') |
||||
|
|
||||
|
HTML Representations |
||||
|
-------------------- |
||||
|
|
||||
|
Objects can customize their HTML markup equivalent by overriding |
||||
|
the `__html__` function: |
||||
|
|
||||
|
>>> class Foo(object): |
||||
|
... def __html__(self): |
||||
|
... return '<strong>Nice</strong>' |
||||
|
... |
||||
|
>>> escape(Foo()) |
||||
|
Markup(u'<strong>Nice</strong>') |
||||
|
>>> Markup(Foo()) |
||||
|
Markup(u'<strong>Nice</strong>') |
||||
|
|
||||
|
Silent Escapes |
||||
|
-------------- |
||||
|
|
||||
|
Since MarkupSafe 0.10 there is now also a separate escape function |
||||
|
called `escape_silent` that returns an empty string for `None` for |
||||
|
consistency with other systems that return empty strings for `None` |
||||
|
when escaping (for instance Pylons' webhelpers). |
||||
|
|
||||
|
If you also want to use this for the escape method of the Markup |
||||
|
object, you can create your own subclass that does that:: |
||||
|
|
||||
|
from markupsafe import Markup, escape_silent as escape |
||||
|
|
||||
|
class SilentMarkup(Markup): |
||||
|
__slots__ = () |
||||
|
|
||||
|
@classmethod |
||||
|
def escape(cls, s): |
||||
|
return cls(escape(s)) |
||||
|
|
||||
|
New-Style String Formatting |
||||
|
--------------------------- |
||||
|
|
||||
|
Starting with MarkupSafe 0.21 new style string formats from Python 2.6 and |
||||
|
3.x are now fully supported. Previously the escape behavior of those |
||||
|
functions was spotty at best. The new implementations operates under the |
||||
|
following algorithm: |
||||
|
|
||||
|
1. if an object has an ``__html_format__`` method it is called as |
||||
|
replacement for ``__format__`` with the format specifier. It either |
||||
|
has to return a string or markup object. |
||||
|
2. if an object has an ``__html__`` method it is called. |
||||
|
3. otherwise the default format system of Python kicks in and the result |
||||
|
is HTML escaped. |
||||
|
|
||||
|
Here is how you can implement your own formatting:: |
||||
|
|
||||
|
class User(object): |
||||
|
|
||||
|
def __init__(self, id, username): |
||||
|
self.id = id |
||||
|
self.username = username |
||||
|
|
||||
|
def __html_format__(self, format_spec): |
||||
|
if format_spec == 'link': |
||||
|
return Markup('<a href="/user/{0}">{1}</a>').format( |
||||
|
self.id, |
||||
|
self.__html__(), |
||||
|
) |
||||
|
elif format_spec: |
||||
|
raise ValueError('Invalid format spec') |
||||
|
return self.__html__() |
||||
|
|
||||
|
def __html__(self): |
||||
|
return Markup('<span class=user>{0}</span>').format(self.username) |
||||
|
|
||||
|
And to format that user: |
||||
|
|
||||
|
>>> user = User(1, 'foo') |
||||
|
>>> Markup('<p>User: {0:link}').format(user) |
||||
|
Markup(u'<p>User: <a href="/user/1"><span class=user>foo</span></a>') |
||||
|
|
||||
|
|
@ -0,0 +1,19 @@ |
|||||
|
MarkupSafe-0.23.dist-info/DESCRIPTION.rst,sha256=VnEbwPneiOkqh-nzxb0DUiGlcVGHuaDQjsNBLi-yNYw,3091 |
||||
|
MarkupSafe-0.23.dist-info/METADATA,sha256=g-KikeSr9J7vagkJoCt0ViT2ORy9O4NYV7XtRu1Pni8,3879 |
||||
|
MarkupSafe-0.23.dist-info/RECORD,, |
||||
|
MarkupSafe-0.23.dist-info/WHEEL,sha256=LgENoDL_AkiRWbJ7HCJLnTXVvHl9ZtXZ7UZmjmcCeN0,105 |
||||
|
MarkupSafe-0.23.dist-info/metadata.json,sha256=y9yFyMJYU3UIRc5KfHIY6A3Z6nvJ2lXva5-7Ts2lsvY,901 |
||||
|
MarkupSafe-0.23.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 |
||||
|
markupsafe/__init__.py,sha256=zFQpANILi3mCCALiPd6ZJdlW6ibu_hTKzikMXKXVtaM,10338 |
||||
|
markupsafe/_compat.py,sha256=r1HE0CpcAZeb-AiTV9wITR91PeLHn0CzZ_XHkYoozpI,565 |
||||
|
markupsafe/_constants.py,sha256=U_xybFQsyXKCgHSfranJnFzo-z9nn9fuBeSk243sE5Q,4795 |
||||
|
markupsafe/_native.py,sha256=E2Un1ysOf-w45d18YCj8UelT5UP7Vt__IuFPYJ7YRIs,1187 |
||||
|
markupsafe/_speedups.c,sha256=gZwPEM_0zcbAzJjPuPYXk97R67QR1uUGtDvOPsvirCA,5939 |
||||
|
markupsafe/_speedups.so,sha256=fQbdBe1y1gzgoPajRdqAcU39EvIJQ_j_ZrgO3v2zIQk,13344 |
||||
|
markupsafe/tests.py,sha256=RLI4eYI0ICNZwkoN638VHXf_fDu4d_jnvbGr22j58Ng,6107 |
||||
|
MarkupSafe-0.23.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 |
||||
|
markupsafe/_constants.pyc,, |
||||
|
markupsafe/__init__.pyc,, |
||||
|
markupsafe/_native.pyc,, |
||||
|
markupsafe/_compat.pyc,, |
||||
|
markupsafe/tests.pyc,, |
@ -0,0 +1,5 @@ |
|||||
|
Wheel-Version: 1.0 |
||||
|
Generator: bdist_wheel (0.29.0) |
||||
|
Root-Is-Purelib: false |
||||
|
Tag: cp27-cp27mu-linux_x86_64 |
||||
|
|
@ -0,0 +1 @@ |
|||||
|
{"classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Text Processing :: Markup :: HTML"], "extensions": {"python.details": {"contacts": [{"email": "armin.ronacher@active-4.com", "name": "Armin Ronacher", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/mitsuhiko/markupsafe"}}}, "generator": "bdist_wheel (0.29.0)", "license": "BSD", "metadata_version": "2.0", "name": "MarkupSafe", "summary": "Implements a XML/HTML/XHTML Markup safe string for Python", "version": "0.23"} |
@ -0,0 +1 @@ |
|||||
|
markupsafe |
@ -0,0 +1,54 @@ |
|||||
|
Werkzeug |
||||
|
======== |
||||
|
|
||||
|
Werkzeug started as simple collection of various utilities for WSGI |
||||
|
applications and has become one of the most advanced WSGI utility |
||||
|
modules. It includes a powerful debugger, full featured request and |
||||
|
response objects, HTTP utilities to handle entity tags, cache control |
||||
|
headers, HTTP dates, cookie handling, file uploads, a powerful URL |
||||
|
routing system and a bunch of community contributed addon modules. |
||||
|
|
||||
|
Werkzeug is unicode aware and doesn't enforce a specific template |
||||
|
engine, database adapter or anything else. It doesn't even enforce |
||||
|
a specific way of handling requests and leaves all that up to the |
||||
|
developer. It's most useful for end user applications which should work |
||||
|
on as many server environments as possible (such as blogs, wikis, |
||||
|
bulletin boards, etc.). |
||||
|
|
||||
|
Details and example applications are available on the |
||||
|
`Werkzeug website <http://werkzeug.pocoo.org/>`_. |
||||
|
|
||||
|
|
||||
|
Features |
||||
|
-------- |
||||
|
|
||||
|
- unicode awareness |
||||
|
|
||||
|
- request and response objects |
||||
|
|
||||
|
- various utility functions for dealing with HTTP headers such as |
||||
|
`Accept` and `Cache-Control` headers. |
||||
|
|
||||
|
- thread local objects with proper cleanup at request end |
||||
|
|
||||
|
- an interactive debugger |
||||
|
|
||||
|
- A simple WSGI server with support for threading and forking |
||||
|
with an automatic reloader. |
||||
|
|
||||
|
- a flexible URL routing system with REST support. |
||||
|
|
||||
|
- fully WSGI compatible |
||||
|
|
||||
|
|
||||
|
Development Version |
||||
|
------------------- |
||||
|
|
||||
|
The Werkzeug development version can be installed by cloning the git |
||||
|
repository from `github`_:: |
||||
|
|
||||
|
git clone git@github.com:mitsuhiko/werkzeug.git |
||||
|
|
||||
|
.. _github: http://github.com/mitsuhiko/werkzeug |
||||
|
|
||||
|
|
@ -0,0 +1 @@ |
|||||
|
pip |
@ -0,0 +1,79 @@ |
|||||
|
Metadata-Version: 2.0 |
||||
|
Name: Werkzeug |
||||
|
Version: 0.11.10 |
||||
|
Summary: The Swiss Army knife of Python web development |
||||
|
Home-page: http://werkzeug.pocoo.org/ |
||||
|
Author: Armin Ronacher |
||||
|
Author-email: armin.ronacher@active-4.com |
||||
|
License: BSD |
||||
|
Platform: any |
||||
|
Classifier: Development Status :: 5 - Production/Stable |
||||
|
Classifier: Environment :: Web Environment |
||||
|
Classifier: Intended Audience :: Developers |
||||
|
Classifier: License :: OSI Approved :: BSD License |
||||
|
Classifier: Operating System :: OS Independent |
||||
|
Classifier: Programming Language :: Python |
||||
|
Classifier: Programming Language :: Python :: 2 |
||||
|
Classifier: Programming Language :: Python :: 2.6 |
||||
|
Classifier: Programming Language :: Python :: 2.7 |
||||
|
Classifier: Programming Language :: Python :: 3 |
||||
|
Classifier: Programming Language :: Python :: 3.3 |
||||
|
Classifier: Programming Language :: Python :: 3.4 |
||||
|
Classifier: Programming Language :: Python :: 3.5 |
||||
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content |
||||
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules |
||||
|
|
||||
|
Werkzeug |
||||
|
======== |
||||
|
|
||||
|
Werkzeug started as simple collection of various utilities for WSGI |
||||
|
applications and has become one of the most advanced WSGI utility |
||||
|
modules. It includes a powerful debugger, full featured request and |
||||
|
response objects, HTTP utilities to handle entity tags, cache control |
||||
|
headers, HTTP dates, cookie handling, file uploads, a powerful URL |
||||
|
routing system and a bunch of community contributed addon modules. |
||||
|
|
||||
|
Werkzeug is unicode aware and doesn't enforce a specific template |
||||
|
engine, database adapter or anything else. It doesn't even enforce |
||||
|
a specific way of handling requests and leaves all that up to the |
||||
|
developer. It's most useful for end user applications which should work |
||||
|
on as many server environments as possible (such as blogs, wikis, |
||||
|
bulletin boards, etc.). |
||||
|
|
||||
|
Details and example applications are available on the |
||||
|
`Werkzeug website <http://werkzeug.pocoo.org/>`_. |
||||
|
|
||||
|
|
||||
|
Features |
||||
|
-------- |
||||
|
|
||||
|
- unicode awareness |
||||
|
|
||||
|
- request and response objects |
||||
|
|
||||
|
- various utility functions for dealing with HTTP headers such as |
||||
|
`Accept` and `Cache-Control` headers. |
||||
|
|
||||
|
- thread local objects with proper cleanup at request end |
||||
|
|
||||
|
- an interactive debugger |
||||
|
|
||||
|
- A simple WSGI server with support for threading and forking |
||||
|
with an automatic reloader. |
||||
|
|
||||
|
- a flexible URL routing system with REST support. |
||||
|
|
||||
|
- fully WSGI compatible |
||||
|
|
||||
|
|
||||
|
Development Version |
||||
|
------------------- |
||||
|
|
||||
|
The Werkzeug development version can be installed by cloning the git |
||||
|
repository from `github`_:: |
||||
|
|
||||
|
git clone git@github.com:mitsuhiko/werkzeug.git |
||||
|
|
||||
|
.. _github: http://github.com/mitsuhiko/werkzeug |
||||
|
|
||||
|
|
@ -0,0 +1,94 @@ |
|||||
|
Werkzeug-0.11.10.dist-info/DESCRIPTION.rst,sha256=5sTwZ_Sj5aeEN8mlcOdNJ_ng40HiGazGmILLyTMX8o0,1595 |
||||
|
Werkzeug-0.11.10.dist-info/METADATA,sha256=vweH07PHdEwMIeOU7fvKXKi_6zY5e6d_dfVCgqT--Aw,2600 |
||||
|
Werkzeug-0.11.10.dist-info/RECORD,, |
||||
|
Werkzeug-0.11.10.dist-info/WHEEL,sha256=GrqQvamwgBV4nLoJe0vhYRSWzWsx7xjlt74FT0SWYfE,110 |
||||
|
Werkzeug-0.11.10.dist-info/metadata.json,sha256=PC5ATsCVx4EUlD8uEGHEhx-aTnCr9GA8vfWXT3CLwQw,1096 |
||||
|
Werkzeug-0.11.10.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9 |
||||
|
werkzeug/__init__.py,sha256=BDx17HnxLmB4L8OSE0Bkj3DjDTO6xLu6_nNck8QiRaw,6920 |
||||
|
werkzeug/_compat.py,sha256=8c4U9o6A_TR9nKCcTbpZNxpqCXcXDVIbFawwKM2s92c,6311 |
||||
|
werkzeug/_internal.py,sha256=IEScSoFtQ8KqFH_2ubdfscNAdQ2RIysyVupI5BR9W2U,13709 |
||||
|
werkzeug/_reloader.py,sha256=YQykMSQW7AlojJQ7qOlgNaXw5_CNjf9yzxplwzVdL7Q,8336 |
||||
|
werkzeug/datastructures.py,sha256=5CLVMLROGMoB0dSdQ7aBabma6IC_vxIx77VnMoFFVyc,87447 |
||||
|
werkzeug/exceptions.py,sha256=c-3fKHItsPvC52X_NwBNLcmGXR30h0WP5ynPSwCqPiw,18733 |
||||
|
werkzeug/filesystem.py,sha256=hHWeWo_gqLMzTRfYt8-7n2wWcWUNTnDyudQDLOBEICE,2175 |
||||
|
werkzeug/formparser.py,sha256=90D5Urp8Ghrzw32kAs090G0nXPYlU73NeAzPlQFMVrY,21296 |
||||
|
werkzeug/http.py,sha256=sqNaMmLBvi16cPoVRiBviwnVOb1bAQ1lGTrvfCdaQrY,35264 |
||||
|
werkzeug/local.py,sha256=4Q5gwHQJhfhZFqTR8iQDs2VHohpR1OEsP4YTwn7rt7w,14275 |
||||
|
werkzeug/posixemulation.py,sha256=xEF2Bxc-vUCPkiu4IbfWVd3LW7DROYAT-ExW6THqyzw,3519 |
||||
|
werkzeug/routing.py,sha256=TqiZD5HkwdLBnKBUjC5PlytzXmpczQC5dz54VfQzMOw,66350 |
||||
|
werkzeug/script.py,sha256=DwaVDcXdaOTffdNvlBdLitxWXjKaRVT32VbhDtljFPY,11365 |
||||
|
werkzeug/security.py,sha256=tuVc22OqoHV5K-TrYJmynCJJa12aUt9BQ3wR_vEPQ34,8971 |
||||
|
werkzeug/serving.py,sha256=uRUqXuA-Dw2MRA-d232cK_034-taldoj66fEFrtin7k,27736 |
||||
|
werkzeug/test.py,sha256=nan0aDi3g5hyUzWCtaN3XL9HrbIsNNNgMNjwpfM6qMc,34152 |
||||
|
werkzeug/testapp.py,sha256=3HQRW1sHZKXuAjCvFMet4KXtQG3loYTFnvn6LWt-4zI,9396 |
||||
|
werkzeug/urls.py,sha256=fSbI4Gb29_p02Zk21VAZQRN1QdOVY9CNTgpb2rbajNQ,36710 |
||||
|
werkzeug/useragents.py,sha256=uqpgPcJ5BfcCVh9nPIIl2r3duIrIuENmrbRqbAMmPDk,5418 |
||||
|
werkzeug/utils.py,sha256=lkybtv_mq35zV1qhelvEcILTzrMUwZ9yon6E8XwapJE,22972 |
||||
|
werkzeug/wrappers.py,sha256=lKYevpKD1-quk9Cop7bsFxt1eWJxU3h33HCnOI_YzSU,77011 |
||||
|
werkzeug/wsgi.py,sha256=S8R3pBGPlBK67s-d6Wa93nhzG27WjfcHs_ZBGIAQCxM,39573 |
||||
|
werkzeug/contrib/__init__.py,sha256=f7PfttZhbrImqpr5Ezre8CXgwvcGUJK7zWNpO34WWrw,623 |
||||
|
werkzeug/contrib/atom.py,sha256=rvijBrphjMzVObfuCR6ddu6aLwI_SiNiudu64OSTh4Q,15588 |
||||
|
werkzeug/contrib/cache.py,sha256=4W2WCT9Hw6HEU8yME9GuU4Xf8e50r2K84ASMxhLb6tY,27983 |
||||
|
werkzeug/contrib/fixers.py,sha256=MtN_YmENxoTsGvXGGERmtbQ62LaeFc5I2d1YifXNENA,10183 |
||||
|
werkzeug/contrib/iterio.py,sha256=pTX36rYCKO_9IEoB5sIN5cFSYszI9zdx6YhquWovcPY,10814 |
||||
|
werkzeug/contrib/jsrouting.py,sha256=QTmgeDoKXvNK02KzXgx9lr3cAH6fAzpwF5bBdPNvJPs,8564 |
||||
|
werkzeug/contrib/limiter.py,sha256=iS8-ahPZ-JLRnmfIBzxpm7O_s3lPsiDMVWv7llAIDCI,1334 |
||||
|
werkzeug/contrib/lint.py,sha256=XDKYx0ELn9k18xRn4SiAsCgltCuN4yLjzxnCN8tG_eM,12490 |
||||
|
werkzeug/contrib/profiler.py,sha256=ISwCWvwVyGpDLRBRpLjo_qUWma6GXYBrTAco4PEQSHY,5151 |
||||
|
werkzeug/contrib/securecookie.py,sha256=X-Ao_0NRDveW6K1Fhe4U42hHWBW8esCpA3VcBDpzWIk,12206 |
||||
|
werkzeug/contrib/sessions.py,sha256=uAPcnyxaxEla-bUA13gKc3KK4mwSagdzbCZzyKl3PeE,12577 |
||||
|
werkzeug/contrib/testtools.py,sha256=G9xN-qeihJlhExrIZMCahvQOIDxdL9NiX874jiiHFMs,2453 |
||||
|
werkzeug/contrib/wrappers.py,sha256=Uv5FRO5OqKwOsNgkW2-FRcw0vUDe3uiaivjPNYWNfAk,10337 |
||||
|
werkzeug/debug/__init__.py,sha256=qQT5YnOv9Eov9Jt5eLtP6MOqwpmo-tORJ6HcQmmnvro,17271 |
||||
|
werkzeug/debug/console.py,sha256=B7uAu9Rk60siDnGlEt-A_q1ZR4zCtmxx5itg3X-BOxo,5599 |
||||
|
werkzeug/debug/repr.py,sha256=NaoB89aHb0vuvdSWels-GWdeGDZp76uE4uSNZPX1jAM,9354 |
||||
|
werkzeug/debug/tbtools.py,sha256=L5P5TkGEHc_Bc5duNosP6D4CNe7ieTo1oiPX8nKQdek,18402 |
||||
|
werkzeug/debug/shared/FONT_LICENSE,sha256=LwAVEI1oYnvXiNMT9SnCH_TaLCxCpeHziDrMg0gPkAI,4673 |
||||
|
werkzeug/debug/shared/console.png,sha256=bxax6RXXlvOij_KeqvSNX0ojJf83YbnZ7my-3Gx9w2A,507 |
||||
|
werkzeug/debug/shared/debugger.js,sha256=PEMBoNuD6fUaNou8Km_ZvVmFcIA3z3k3jSEMWLW-cA0,6187 |
||||
|
werkzeug/debug/shared/jquery.js,sha256=7LkWEzqTdpEfELxcZZlS6wAx5Ff13zZ83lYO2_ujj7g,95957 |
||||
|
werkzeug/debug/shared/less.png,sha256=-4-kNRaXJSONVLahrQKUxMwXGm9R4OnZ9SxDGpHlIR4,191 |
||||
|
werkzeug/debug/shared/more.png,sha256=GngN7CioHQoV58rH6ojnkYi8c_qED2Aka5FO5UXrReY,200 |
||||
|
werkzeug/debug/shared/source.png,sha256=RoGcBTE4CyCB85GBuDGTFlAnUqxwTBiIfDqW15EpnUQ,818 |
||||
|
werkzeug/debug/shared/style.css,sha256=7x1s8olZO1XHalqD4M9MWn9vRqQkA635S9_6zRoe220,6231 |
||||
|
werkzeug/debug/shared/ubuntu.ttf,sha256=1eaHFyepmy4FyDvjLVzpITrGEBu_CZYY94jE0nED1c0,70220 |
||||
|
Werkzeug-0.11.10.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 |
||||
|
werkzeug/_reloader.pyc,, |
||||
|
werkzeug/filesystem.pyc,, |
||||
|
werkzeug/contrib/testtools.pyc,, |
||||
|
werkzeug/formparser.pyc,, |
||||
|
werkzeug/_compat.pyc,, |
||||
|
werkzeug/posixemulation.pyc,, |
||||
|
werkzeug/wsgi.pyc,, |
||||
|
werkzeug/serving.pyc,, |
||||
|
werkzeug/contrib/__init__.pyc,, |
||||
|
werkzeug/contrib/iterio.pyc,, |
||||
|
werkzeug/test.pyc,, |
||||
|
werkzeug/__init__.pyc,, |
||||
|
werkzeug/contrib/limiter.pyc,, |
||||
|
werkzeug/debug/tbtools.pyc,, |
||||
|
werkzeug/contrib/sessions.pyc,, |
||||
|
werkzeug/contrib/securecookie.pyc,, |
||||
|
werkzeug/local.pyc,, |
||||
|
werkzeug/utils.pyc,, |
||||
|
werkzeug/_internal.pyc,, |
||||
|
werkzeug/security.pyc,, |
||||
|
werkzeug/contrib/cache.pyc,, |
||||
|
werkzeug/script.pyc,, |
||||
|
werkzeug/routing.pyc,, |
||||
|
werkzeug/wrappers.pyc,, |
||||
|
werkzeug/contrib/jsrouting.pyc,, |
||||
|
werkzeug/contrib/fixers.pyc,, |
||||
|
werkzeug/contrib/profiler.pyc,, |
||||
|
werkzeug/debug/console.pyc,, |
||||
|
werkzeug/debug/__init__.pyc,, |
||||
|
werkzeug/datastructures.pyc,, |
||||
|
werkzeug/http.pyc,, |
||||
|
werkzeug/urls.pyc,, |
||||
|
werkzeug/contrib/lint.pyc,, |
||||
|
werkzeug/contrib/wrappers.pyc,, |
||||
|
werkzeug/exceptions.pyc,, |
||||
|
werkzeug/contrib/atom.pyc,, |
||||
|
werkzeug/testapp.pyc,, |
||||
|
werkzeug/debug/repr.pyc,, |
||||
|
werkzeug/useragents.pyc,, |
@ -0,0 +1,6 @@ |
|||||
|
Wheel-Version: 1.0 |
||||
|
Generator: bdist_wheel (0.26.0) |
||||
|
Root-Is-Purelib: true |
||||
|
Tag: py2-none-any |
||||
|
Tag: py3-none-any |
||||
|
|
@ -0,0 +1 @@ |
|||||
|
{"generator": "bdist_wheel (0.26.0)", "summary": "The Swiss Army knife of Python web development", "classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules"], "extensions": {"python.details": {"project_urls": {"Home": "http://werkzeug.pocoo.org/"}, "contacts": [{"email": "armin.ronacher@active-4.com", "name": "Armin Ronacher", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}}}, "license": "BSD", "metadata_version": "2.0", "name": "Werkzeug", "platform": "any", "version": "0.11.10"} |
@ -0,0 +1 @@ |
|||||
|
werkzeug |
@ -0,0 +1,3 @@ |
|||||
|
UNKNOWN |
||||
|
|
||||
|
|
@ -0,0 +1 @@ |
|||||
|
pip |
@ -0,0 +1,16 @@ |
|||||
|
Metadata-Version: 2.0 |
||||
|
Name: click |
||||
|
Version: 6.6 |
||||
|
Summary: A simple wrapper around optparse for powerful command line utilities. |
||||
|
Home-page: http://github.com/mitsuhiko/click |
||||
|
Author: Armin Ronacher |
||||
|
Author-email: armin.ronacher@active-4.com |
||||
|
License: UNKNOWN |
||||
|
Platform: UNKNOWN |
||||
|
Classifier: License :: OSI Approved :: BSD License |
||||
|
Classifier: Programming Language :: Python |
||||
|
Classifier: Programming Language :: Python :: 3 |
||||
|
|
||||
|
UNKNOWN |
||||
|
|
||||
|
|
@ -0,0 +1,41 @@ |
|||||
|
click/__init__.py,sha256=-BDiJ4G3FXy1nn7Lt9UTIYdKb7SZTHnTm9yv2WQbHm0,2858 |
||||
|
click/_bashcomplete.py,sha256=J8L3TvE23YVicEf5LQdiN5pCS3aAquP8D8897Jp_PvE,2423 |
||||
|
click/_compat.py,sha256=xP-n7Tkuof1I6aEzcR6A88vusa-Lu6gpkPxnO-KY0pA,20706 |
||||
|
click/_termui_impl.py,sha256=BbBPaw2nDEmNYgjBl_JlxUOSAzFexO0jL3z0OY-Z6BM,16377 |
||||
|
click/_textwrap.py,sha256=gwS4m7bdQiJnzaDG8osFcRb-5vn4t4l2qSCy-5csCEc,1198 |
||||
|
click/_unicodefun.py,sha256=vK1_Y_d29nxYhE7_QF8y8-DLjG_fYDhiT-LlcmiHVeE,4265 |
||||
|
click/_winconsole.py,sha256=MzG46DEYPoRyx4SO7EIhFuFZHESgooAfJLIukbB6p5c,7790 |
||||
|
click/core.py,sha256=PFAK6hjAiM8fcXxdHNwaDKINSoHYCYfLV8n9TdodYS4,70085 |
||||
|
click/decorators.py,sha256=y7CX2needh8iRWafj-QS_hGQFsN24eyXAhx5Y2ATwas,10941 |
||||
|
click/exceptions.py,sha256=rOa0pP3PbSy0_AAPOW9irBEM8AJ3BySN-4z2VUwFVo4,6788 |
||||
|
click/formatting.py,sha256=eh-cypTUAhpI3HD-K4ZpR3vCiURIO62xXvKkR3tNUTM,8889 |
||||
|
click/globals.py,sha256=PAgnKvGxq4YuEIldw3lgYOGBLYwsyxnm1IByBX3BFXo,1515 |
||||
|
click/parser.py,sha256=i01xgYuIA6AwQWEXjshwHSwnTR3gUep4FxJIfyW4ta4,15510 |
||||
|
click/termui.py,sha256=Bp99MSWQtyoWe1_7HggDmA77n--3KLxu7NsZMFMaCUo,21008 |
||||
|
click/testing.py,sha256=LjWcVtxA5vMdWx5ev7KSjU4vcTlxZiSDrP7tOV1nCxk,11004 |
||||
|
click/types.py,sha256=ZGb2lmFs5Vwd9loTRIMbGcqhPVOql8mGoBhWBRT6V4E,18864 |
||||
|
click/utils.py,sha256=1jalPlkUU28JReTEQeeSFtbJd-SirYWBNfjtELBKzT4,14916 |
||||
|
click-6.6.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 |
||||
|
click-6.6.dist-info/METADATA,sha256=gYQxaGWBuXfQy0NGVARhka10cSm9b_6XF6WeD7k3_Rg,424 |
||||
|
click-6.6.dist-info/RECORD,, |
||||
|
click-6.6.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 |
||||
|
click-6.6.dist-info/metadata.json,sha256=6QQvh7-Y7ZLMEiYZVIYzVKGeKPmzdxcgSIioWfhp9DE,568 |
||||
|
click-6.6.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 |
||||
|
click-6.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 |
||||
|
click/_unicodefun.pyc,, |
||||
|
click/_compat.pyc,, |
||||
|
click/_termui_impl.pyc,, |
||||
|
click/_textwrap.pyc,, |
||||
|
click/formatting.pyc,, |
||||
|
click/exceptions.pyc,, |
||||
|
click/decorators.pyc,, |
||||
|
click/parser.pyc,, |
||||
|
click/testing.pyc,, |
||||
|
click/utils.pyc,, |
||||
|
click/_bashcomplete.pyc,, |
||||
|
click/types.pyc,, |
||||
|
click/termui.pyc,, |
||||
|
click/core.pyc,, |
||||
|
click/_winconsole.pyc,, |
||||
|
click/__init__.pyc,, |
||||
|
click/globals.pyc,, |
@ -0,0 +1,6 @@ |
|||||
|
Wheel-Version: 1.0 |
||||
|
Generator: bdist_wheel (0.29.0) |
||||
|
Root-Is-Purelib: true |
||||
|
Tag: py2-none-any |
||||
|
Tag: py3-none-any |
||||
|
|
@ -0,0 +1 @@ |
|||||
|
{"classifiers": ["License :: OSI Approved :: BSD License", "Programming Language :: Python", "Programming Language :: Python :: 3"], "extensions": {"python.details": {"contacts": [{"email": "armin.ronacher@active-4.com", "name": "Armin Ronacher", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/mitsuhiko/click"}}}, "generator": "bdist_wheel (0.29.0)", "metadata_version": "2.0", "name": "click", "summary": "A simple wrapper around optparse for powerful command line utilities.", "version": "6.6"} |
@ -0,0 +1 @@ |
|||||
|
click |
@ -0,0 +1,98 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
""" |
||||
|
click |
||||
|
~~~~~ |
||||
|
|
||||
|
Click is a simple Python module that wraps the stdlib's optparse to make |
||||
|
writing command line scripts fun. Unlike other modules, it's based around |
||||
|
a simple API that does not come with too much magic and is composable. |
||||
|
|
||||
|
In case optparse ever gets removed from the stdlib, it will be shipped by |
||||
|
this module. |
||||
|
|
||||
|
:copyright: (c) 2014 by Armin Ronacher. |
||||
|
:license: BSD, see LICENSE for more details. |
||||
|
""" |
||||
|
|
||||
|
# Core classes |
||||
|
from .core import Context, BaseCommand, Command, MultiCommand, Group, \ |
||||
|
CommandCollection, Parameter, Option, Argument |
||||
|
|
||||
|
# Globals |
||||
|
from .globals import get_current_context |
||||
|
|
||||
|
# Decorators |
||||
|
from .decorators import pass_context, pass_obj, make_pass_decorator, \ |
||||
|
command, group, argument, option, confirmation_option, \ |
||||
|
password_option, version_option, help_option |
||||
|
|
||||
|
# Types |
||||
|
from .types import ParamType, File, Path, Choice, IntRange, Tuple, \ |
||||
|
STRING, INT, FLOAT, BOOL, UUID, UNPROCESSED |
||||
|
|
||||
|
# Utilities |
||||
|
from .utils import echo, get_binary_stream, get_text_stream, open_file, \ |
||||
|
format_filename, get_app_dir, get_os_args |
||||
|
|
||||
|
# Terminal functions |
||||
|
from .termui import prompt, confirm, get_terminal_size, echo_via_pager, \ |
||||
|
progressbar, clear, style, unstyle, secho, edit, launch, getchar, \ |
||||
|
pause |
||||
|
|
||||
|
# Exceptions |
||||
|
from .exceptions import ClickException, UsageError, BadParameter, \ |
||||
|
FileError, Abort, NoSuchOption, BadOptionUsage, BadArgumentUsage, \ |
||||
|
MissingParameter |
||||
|
|
||||
|
# Formatting |
||||
|
from .formatting import HelpFormatter, wrap_text |
||||
|
|
||||
|
# Parsing |
||||
|
from .parser import OptionParser |
||||
|
|
||||
|
|
||||
|
__all__ = [ |
||||
|
# Core classes |
||||
|
'Context', 'BaseCommand', 'Command', 'MultiCommand', 'Group', |
||||
|
'CommandCollection', 'Parameter', 'Option', 'Argument', |
||||
|
|
||||
|
# Globals |
||||
|
'get_current_context', |
||||
|
|
||||
|
# Decorators |
||||
|
'pass_context', 'pass_obj', 'make_pass_decorator', 'command', 'group', |
||||
|
'argument', 'option', 'confirmation_option', 'password_option', |
||||
|
'version_option', 'help_option', |
||||
|
|
||||
|
# Types |
||||
|
'ParamType', 'File', 'Path', 'Choice', 'IntRange', 'Tuple', 'STRING', |
||||
|
'INT', 'FLOAT', 'BOOL', 'UUID', 'UNPROCESSED', |
||||
|
|
||||
|
# Utilities |
||||
|
'echo', 'get_binary_stream', 'get_text_stream', 'open_file', |
||||
|
'format_filename', 'get_app_dir', 'get_os_args', |
||||
|
|
||||
|
# Terminal functions |
||||
|
'prompt', 'confirm', 'get_terminal_size', 'echo_via_pager', |
||||
|
'progressbar', 'clear', 'style', 'unstyle', 'secho', 'edit', 'launch', |
||||
|
'getchar', 'pause', |
||||
|
|
||||
|
# Exceptions |
||||
|
'ClickException', 'UsageError', 'BadParameter', 'FileError', |
||||
|
'Abort', 'NoSuchOption', 'BadOptionUsage', 'BadArgumentUsage', |
||||
|
'MissingParameter', |
||||
|
|
||||
|
# Formatting |
||||
|
'HelpFormatter', 'wrap_text', |
||||
|
|
||||
|
# Parsing |
||||
|
'OptionParser', |
||||
|
] |
||||
|
|
||||
|
|
||||
|
# Controls if click should emit the warning about the use of unicode |
||||
|
# literals. |
||||
|
disable_unicode_literals_warning = False |
||||
|
|
||||
|
|
||||
|
__version__ = '6.6' |
@ -0,0 +1,83 @@ |
|||||
|
import os |
||||
|
import re |
||||
|
from .utils import echo |
||||
|
from .parser import split_arg_string |
||||
|
from .core import MultiCommand, Option |
||||
|
|
||||
|
|
||||
|
COMPLETION_SCRIPT = ''' |
||||
|
%(complete_func)s() { |
||||
|
COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \\ |
||||
|
COMP_CWORD=$COMP_CWORD \\ |
||||
|
%(autocomplete_var)s=complete $1 ) ) |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
complete -F %(complete_func)s -o default %(script_names)s |
||||
|
''' |
||||
|
|
||||
|
_invalid_ident_char_re = re.compile(r'[^a-zA-Z0-9_]') |
||||
|
|
||||
|
|
||||
|
def get_completion_script(prog_name, complete_var): |
||||
|
cf_name = _invalid_ident_char_re.sub('', prog_name.replace('-', '_')) |
||||
|
return (COMPLETION_SCRIPT % { |
||||
|
'complete_func': '_%s_completion' % cf_name, |
||||
|
'script_names': prog_name, |
||||
|
'autocomplete_var': complete_var, |
||||
|
}).strip() + ';' |
||||
|
|
||||
|
|
||||
|
def resolve_ctx(cli, prog_name, args): |
||||
|
ctx = cli.make_context(prog_name, args, resilient_parsing=True) |
||||
|
while ctx.args + ctx.protected_args and isinstance(ctx.command, MultiCommand): |
||||
|
a = ctx.args + ctx.protected_args |
||||
|
cmd = ctx.command.get_command(ctx, a[0]) |
||||
|
if cmd is None: |
||||
|
return None |
||||
|
ctx = cmd.make_context(a[0], a[1:], parent=ctx, resilient_parsing=True) |
||||
|
return ctx |
||||
|
|
||||
|
|
||||
|
def get_choices(cli, prog_name, args, incomplete): |
||||
|
ctx = resolve_ctx(cli, prog_name, args) |
||||
|
if ctx is None: |
||||
|
return |
||||
|
|
||||
|
choices = [] |
||||
|
if incomplete and not incomplete[:1].isalnum(): |
||||
|
for param in ctx.command.params: |
||||
|
if not isinstance(param, Option): |
||||
|
continue |
||||
|
choices.extend(param.opts) |
||||
|
choices.extend(param.secondary_opts) |
||||
|
elif isinstance(ctx.command, MultiCommand): |
||||
|
choices.extend(ctx.command.list_commands(ctx)) |
||||
|
|
||||
|
for item in choices: |
||||
|
if item.startswith(incomplete): |
||||
|
yield item |
||||
|
|
||||
|
|
||||
|
def do_complete(cli, prog_name): |
||||
|
cwords = split_arg_string(os.environ['COMP_WORDS']) |
||||
|
cword = int(os.environ['COMP_CWORD']) |
||||
|
args = cwords[1:cword] |
||||
|
try: |
||||
|
incomplete = cwords[cword] |
||||
|
except IndexError: |
||||
|
incomplete = '' |
||||
|
|
||||
|
for item in get_choices(cli, prog_name, args, incomplete): |
||||
|
echo(item) |
||||
|
|
||||
|
return True |
||||
|
|
||||
|
|
||||
|
def bashcomplete(cli, prog_name, complete_var, complete_instr): |
||||
|
if complete_instr == 'source': |
||||
|
echo(get_completion_script(prog_name, complete_var)) |
||||
|
return True |
||||
|
elif complete_instr == 'complete': |
||||
|
return do_complete(cli, prog_name) |
||||
|
return False |
@ -0,0 +1,642 @@ |
|||||
|
import re |
||||
|
import io |
||||
|
import os |
||||
|
import sys |
||||
|
import codecs |
||||
|
from weakref import WeakKeyDictionary |
||||
|
|
||||
|
|
||||
|
PY2 = sys.version_info[0] == 2 |
||||
|
WIN = sys.platform.startswith('win') |
||||
|
DEFAULT_COLUMNS = 80 |
||||
|
|
||||
|
|
||||
|
_ansi_re = re.compile('\033\[((?:\d|;)*)([a-zA-Z])') |
||||
|
|
||||
|
|
||||
|
def get_filesystem_encoding(): |
||||
|
return sys.getfilesystemencoding() or sys.getdefaultencoding() |
||||
|
|
||||
|
|
||||
|
def _make_text_stream(stream, encoding, errors): |
||||
|
if encoding is None: |
||||
|
encoding = get_best_encoding(stream) |
||||
|
if errors is None: |
||||
|
errors = 'replace' |
||||
|
return _NonClosingTextIOWrapper(stream, encoding, errors, |
||||
|
line_buffering=True) |
||||
|
|
||||
|
|
||||
|
def is_ascii_encoding(encoding): |
||||
|
"""Checks if a given encoding is ascii.""" |
||||
|
try: |
||||
|
return codecs.lookup(encoding).name == 'ascii' |
||||
|
except LookupError: |
||||
|
return False |
||||
|
|
||||
|
|
||||
|
def get_best_encoding(stream): |
||||
|
"""Returns the default stream encoding if not found.""" |
||||
|
rv = getattr(stream, 'encoding', None) or sys.getdefaultencoding() |
||||
|
if is_ascii_encoding(rv): |
||||
|
return 'utf-8' |
||||
|
return rv |
||||
|
|
||||
|
|
||||
|
class _NonClosingTextIOWrapper(io.TextIOWrapper): |
||||
|
|
||||
|
def __init__(self, stream, encoding, errors, **extra): |
||||
|
self._stream = stream = _FixupStream(stream) |
||||
|
io.TextIOWrapper.__init__(self, stream, encoding, errors, **extra) |
||||
|
|
||||
|
# The io module is a place where the Python 3 text behavior |
||||
|
# was forced upon Python 2, so we need to unbreak |
||||
|
# it to look like Python 2. |
||||
|
if PY2: |
||||
|
def write(self, x): |
||||
|
if isinstance(x, str) or is_bytes(x): |
||||
|
try: |
||||
|
self.flush() |
||||
|
except Exception: |
||||
|
pass |
||||
|
return self.buffer.write(str(x)) |
||||
|
return io.TextIOWrapper.write(self, x) |
||||
|
|
||||
|
def writelines(self, lines): |
||||
|
for line in lines: |
||||
|
self.write(line) |
||||
|
|
||||
|
def __del__(self): |
||||
|
try: |
||||
|
self.detach() |
||||
|
except Exception: |
||||
|
pass |
||||
|
|
||||
|
def isatty(self): |
||||
|
# https://bitbucket.org/pypy/pypy/issue/1803 |
||||
|
return self._stream.isatty() |
||||
|
|
||||
|
|
||||
|
class _FixupStream(object): |
||||
|
"""The new io interface needs more from streams than streams |
||||
|
traditionally implement. As such, this fix-up code is necessary in |
||||
|
some circumstances. |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, stream): |
||||
|
self._stream = stream |
||||
|
|
||||
|
def __getattr__(self, name): |
||||
|
return getattr(self._stream, name) |
||||
|
|
||||
|
def read1(self, size): |
||||
|
f = getattr(self._stream, 'read1', None) |
||||
|
if f is not None: |
||||
|
return f(size) |
||||
|
# We only dispatch to readline instead of read in Python 2 as we |
||||
|
# do not want cause problems with the different implementation |
||||
|
# of line buffering. |
||||
|
if PY2: |
||||
|
return self._stream.readline(size) |
||||
|
return self._stream.read(size) |
||||
|
|
||||
|
def readable(self): |
||||
|
x = getattr(self._stream, 'readable', None) |
||||
|
if x is not None: |
||||
|
return x() |
||||
|
try: |
||||
|
self._stream.read(0) |
||||
|
except Exception: |
||||
|
return False |
||||
|
return True |
||||
|
|
||||
|
def writable(self): |
||||
|
x = getattr(self._stream, 'writable', None) |
||||
|
if x is not None: |
||||
|
return x() |
||||
|
try: |
||||
|
self._stream.write('') |
||||
|
except Exception: |
||||
|
try: |
||||
|
self._stream.write(b'') |
||||
|
except Exception: |
||||
|
return False |
||||
|
return True |
||||
|
|
||||
|
def seekable(self): |
||||
|
x = getattr(self._stream, 'seekable', None) |
||||
|
if x is not None: |
||||
|
return x() |
||||
|
try: |
||||
|
self._stream.seek(self._stream.tell()) |
||||
|
except Exception: |
||||
|
return False |
||||
|
return True |
||||
|
|
||||
|
|
||||
|
if PY2: |
||||
|
text_type = unicode |
||||
|
bytes = str |
||||
|
raw_input = raw_input |
||||
|
string_types = (str, unicode) |
||||
|
iteritems = lambda x: x.iteritems() |
||||
|
range_type = xrange |
||||
|
|
||||
|
def is_bytes(x): |
||||
|
return isinstance(x, (buffer, bytearray)) |
||||
|
|
||||
|
_identifier_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$') |
||||
|
|
||||
|
# For Windows, we need to force stdout/stdin/stderr to binary if it's |
||||
|
# fetched for that. This obviously is not the most correct way to do |
||||
|
# it as it changes global state. Unfortunately, there does not seem to |
||||
|
# be a clear better way to do it as just reopening the file in binary |
||||
|
# mode does not change anything. |
||||
|
# |
||||
|
# An option would be to do what Python 3 does and to open the file as |
||||
|
# binary only, patch it back to the system, and then use a wrapper |
||||
|
# stream that converts newlines. It's not quite clear what's the |
||||
|
# correct option here. |
||||
|
# |
||||
|
# This code also lives in _winconsole for the fallback to the console |
||||
|
# emulation stream. |
||||
|
if WIN: |
||||
|
import msvcrt |
||||
|
def set_binary_mode(f): |
||||
|
try: |
||||
|
fileno = f.fileno() |
||||
|
except Exception: |
||||
|
pass |
||||
|
else: |
||||
|
msvcrt.setmode(fileno, os.O_BINARY) |
||||
|
return f |
||||
|
else: |
||||
|
set_binary_mode = lambda x: x |
||||
|
|
||||
|
def isidentifier(x): |
||||
|
return _identifier_re.search(x) is not None |
||||
|
|
||||
|
def get_binary_stdin(): |
||||
|
return set_binary_mode(sys.stdin) |
||||
|
|
||||
|
def get_binary_stdout(): |
||||
|
return set_binary_mode(sys.stdout) |
||||
|
|
||||
|
def get_binary_stderr(): |
||||
|
return set_binary_mode(sys.stderr) |
||||
|
|
||||
|
def get_text_stdin(encoding=None, errors=None): |
||||
|
rv = _get_windows_console_stream(sys.stdin, encoding, errors) |
||||
|
if rv is not None: |
||||
|
return rv |
||||
|
return _make_text_stream(sys.stdin, encoding, errors) |
||||
|
|
||||
|
def get_text_stdout(encoding=None, errors=None): |
||||
|
rv = _get_windows_console_stream(sys.stdout, encoding, errors) |
||||
|
if rv is not None: |
||||
|
return rv |
||||
|
return _make_text_stream(sys.stdout, encoding, errors) |
||||
|
|
||||
|
def get_text_stderr(encoding=None, errors=None): |
||||
|
rv = _get_windows_console_stream(sys.stderr, encoding, errors) |
||||
|
if rv is not None: |
||||
|
return rv |
||||
|
return _make_text_stream(sys.stderr, encoding, errors) |
||||
|
|
||||
|
def filename_to_ui(value): |
||||
|
if isinstance(value, bytes): |
||||
|
value = value.decode(get_filesystem_encoding(), 'replace') |
||||
|
return value |
||||
|
else: |
||||
|
import io |
||||
|
text_type = str |
||||
|
raw_input = input |
||||
|
string_types = (str,) |
||||
|
range_type = range |
||||
|
isidentifier = lambda x: x.isidentifier() |
||||
|
iteritems = lambda x: iter(x.items()) |
||||
|
|
||||
|
def is_bytes(x): |
||||
|
return isinstance(x, (bytes, memoryview, bytearray)) |
||||
|
|
||||
|
def _is_binary_reader(stream, default=False): |
||||
|
try: |
||||
|
return isinstance(stream.read(0), bytes) |
||||
|
except Exception: |
||||
|
return default |
||||
|
# This happens in some cases where the stream was already |
||||
|
# closed. In this case, we assume the default. |
||||
|
|
||||
|
def _is_binary_writer(stream, default=False): |
||||
|
try: |
||||
|
stream.write(b'') |
||||
|
except Exception: |
||||
|
try: |
||||
|
stream.write('') |
||||
|
return False |
||||
|
except Exception: |
||||
|
pass |
||||
|
return default |
||||
|
return True |
||||
|
|
||||
|
def _find_binary_reader(stream): |
||||
|
# We need to figure out if the given stream is already binary. |
||||
|
# This can happen because the official docs recommend detaching |
||||
|
# the streams to get binary streams. Some code might do this, so |
||||
|
# we need to deal with this case explicitly. |
||||
|
if _is_binary_reader(stream, False): |
||||
|
return stream |
||||
|
|
||||
|
buf = getattr(stream, 'buffer', None) |
||||
|
|
||||
|
# Same situation here; this time we assume that the buffer is |
||||
|
# actually binary in case it's closed. |
||||
|
if buf is not None and _is_binary_reader(buf, True): |
||||
|
return buf |
||||
|
|
||||
|
def _find_binary_writer(stream): |
||||
|
# We need to figure out if the given stream is already binary. |
||||
|
# This can happen because the official docs recommend detatching |
||||
|
# the streams to get binary streams. Some code might do this, so |
||||
|
# we need to deal with this case explicitly. |
||||
|
if _is_binary_writer(stream, False): |
||||
|
return stream |
||||
|
|
||||
|
buf = getattr(stream, 'buffer', None) |
||||
|
|
||||
|
# Same situation here; this time we assume that the buffer is |
||||
|
# actually binary in case it's closed. |
||||
|
if buf is not None and _is_binary_writer(buf, True): |
||||
|
return buf |
||||
|
|
||||
|
def _stream_is_misconfigured(stream): |
||||
|
"""A stream is misconfigured if its encoding is ASCII.""" |
||||
|
# If the stream does not have an encoding set, we assume it's set |
||||
|
# to ASCII. This appears to happen in certain unittest |
||||
|
# environments. It's not quite clear what the correct behavior is |
||||
|
# but this at least will force Click to recover somehow. |
||||
|
return is_ascii_encoding(getattr(stream, 'encoding', None) or 'ascii') |
||||
|
|
||||
|
def _is_compatible_text_stream(stream, encoding, errors): |
||||
|
stream_encoding = getattr(stream, 'encoding', None) |
||||
|
stream_errors = getattr(stream, 'errors', None) |
||||
|
|
||||
|
# Perfect match. |
||||
|
if stream_encoding == encoding and stream_errors == errors: |
||||
|
return True |
||||
|
|
||||
|
# Otherwise, it's only a compatible stream if we did not ask for |
||||
|
# an encoding. |
||||
|
if encoding is None: |
||||
|
return stream_encoding is not None |
||||
|
|
||||
|
return False |
||||
|
|
||||
|
def _force_correct_text_reader(text_reader, encoding, errors): |
||||
|
if _is_binary_reader(text_reader, False): |
||||
|
binary_reader = text_reader |
||||
|
else: |
||||
|
# If there is no target encoding set, we need to verify that the |
||||
|
# reader is not actually misconfigured. |
||||
|
if encoding is None and not _stream_is_misconfigured(text_reader): |
||||
|
return text_reader |
||||
|
|
||||
|
if _is_compatible_text_stream(text_reader, encoding, errors): |
||||
|
return text_reader |
||||
|
|
||||
|
# If the reader has no encoding, we try to find the underlying |
||||
|
# binary reader for it. If that fails because the environment is |
||||
|
# misconfigured, we silently go with the same reader because this |
||||
|
# is too common to happen. In that case, mojibake is better than |
||||
|
# exceptions. |
||||
|
binary_reader = _find_binary_reader(text_reader) |
||||
|
if binary_reader is None: |
||||
|
return text_reader |
||||
|
|
||||
|
# At this point, we default the errors to replace instead of strict |
||||
|
# because nobody handles those errors anyways and at this point |
||||
|
# we're so fundamentally fucked that nothing can repair it. |
||||
|
if errors is None: |
||||
|
errors = 'replace' |
||||
|
return _make_text_stream(binary_reader, encoding, errors) |
||||
|
|
||||
|
def _force_correct_text_writer(text_writer, encoding, errors): |
||||
|
if _is_binary_writer(text_writer, False): |
||||
|
binary_writer = text_writer |
||||
|
else: |
||||
|
# If there is no target encoding set, we need to verify that the |
||||
|
# writer is not actually misconfigured. |
||||
|
if encoding is None and not _stream_is_misconfigured(text_writer): |
||||
|
return text_writer |
||||
|
|
||||
|
if _is_compatible_text_stream(text_writer, encoding, errors): |
||||
|
return text_writer |
||||
|
|
||||
|
# If the writer has no encoding, we try to find the underlying |
||||
|
# binary writer for it. If that fails because the environment is |
||||
|
# misconfigured, we silently go with the same writer because this |
||||
|
# is too common to happen. In that case, mojibake is better than |
||||
|
# exceptions. |
||||
|
binary_writer = _find_binary_writer(text_writer) |
||||
|
if binary_writer is None: |
||||
|
return text_writer |
||||
|
|
||||
|
# At this point, we default the errors to replace instead of strict |
||||
|
# because nobody handles those errors anyways and at this point |
||||
|
# we're so fundamentally fucked that nothing can repair it. |
||||
|
if errors is None: |
||||
|
errors = 'replace' |
||||
|
return _make_text_stream(binary_writer, encoding, errors) |
||||
|
|
||||
|
def get_binary_stdin(): |
||||
|
reader = _find_binary_reader(sys.stdin) |
||||
|
if reader is None: |
||||
|
raise RuntimeError('Was not able to determine binary ' |
||||
|
'stream for sys.stdin.') |
||||
|
return reader |
||||
|
|
||||
|
def get_binary_stdout(): |
||||
|
writer = _find_binary_writer(sys.stdout) |
||||
|
if writer is None: |
||||
|
raise RuntimeError('Was not able to determine binary ' |
||||
|
'stream for sys.stdout.') |
||||
|
return writer |
||||
|
|
||||
|
def get_binary_stderr(): |
||||
|
writer = _find_binary_writer(sys.stderr) |
||||
|
if writer is None: |
||||
|
raise RuntimeError('Was not able to determine binary ' |
||||
|
'stream for sys.stderr.') |
||||
|
return writer |
||||
|
|
||||
|
def get_text_stdin(encoding=None, errors=None): |
||||
|
rv = _get_windows_console_stream(sys.stdin, encoding, errors) |
||||
|
if rv is not None: |
||||
|
return rv |
||||
|
return _force_correct_text_reader(sys.stdin, encoding, errors) |
||||
|
|
||||
|
def get_text_stdout(encoding=None, errors=None): |
||||
|
rv = _get_windows_console_stream(sys.stdout, encoding, errors) |
||||
|
if rv is not None: |
||||
|
return rv |
||||
|
return _force_correct_text_writer(sys.stdout, encoding, errors) |
||||
|
|
||||
|
def get_text_stderr(encoding=None, errors=None): |
||||
|
rv = _get_windows_console_stream(sys.stderr, encoding, errors) |
||||
|
if rv is not None: |
||||
|
return rv |
||||
|
return _force_correct_text_writer(sys.stderr, encoding, errors) |
||||
|
|
||||
|
def filename_to_ui(value): |
||||
|
if isinstance(value, bytes): |
||||
|
value = value.decode(get_filesystem_encoding(), 'replace') |
||||
|
else: |
||||
|
value = value.encode('utf-8', 'surrogateescape') \ |
||||
|
.decode('utf-8', 'replace') |
||||
|
return value |
||||
|
|
||||
|
|
||||
|
def get_streerror(e, default=None): |
||||
|
if hasattr(e, 'strerror'): |
||||
|
msg = e.strerror |
||||
|
else: |
||||
|
if default is not None: |
||||
|
msg = default |
||||
|
else: |
||||
|
msg = str(e) |
||||
|
if isinstance(msg, bytes): |
||||
|
msg = msg.decode('utf-8', 'replace') |
||||
|
return msg |
||||
|
|
||||
|
|
||||
|
def open_stream(filename, mode='r', encoding=None, errors='strict', |
||||
|
atomic=False): |
||||
|
# Standard streams first. These are simple because they don't need |
||||
|
# special handling for the atomic flag. It's entirely ignored. |
||||
|
if filename == '-': |
||||
|
if 'w' in mode: |
||||
|
if 'b' in mode: |
||||
|
return get_binary_stdout(), False |
||||
|
return get_text_stdout(encoding=encoding, errors=errors), False |
||||
|
if 'b' in mode: |
||||
|
return get_binary_stdin(), False |
||||
|
return get_text_stdin(encoding=encoding, errors=errors), False |
||||
|
|
||||
|
# Non-atomic writes directly go out through the regular open functions. |
||||
|
if not atomic: |
||||
|
if encoding is None: |
||||
|
return open(filename, mode), True |
||||
|
return io.open(filename, mode, encoding=encoding, errors=errors), True |
||||
|
|
||||
|
# Some usability stuff for atomic writes |
||||
|
if 'a' in mode: |
||||
|
raise ValueError( |
||||
|
'Appending to an existing file is not supported, because that ' |
||||
|
'would involve an expensive `copy`-operation to a temporary ' |
||||
|
'file. Open the file in normal `w`-mode and copy explicitly ' |
||||
|
'if that\'s what you\'re after.' |
||||
|
) |
||||
|
if 'x' in mode: |
||||
|
raise ValueError('Use the `overwrite`-parameter instead.') |
||||
|
if 'w' not in mode: |
||||
|
raise ValueError('Atomic writes only make sense with `w`-mode.') |
||||
|
|
||||
|
# Atomic writes are more complicated. They work by opening a file |
||||
|
# as a proxy in the same folder and then using the fdopen |
||||
|
# functionality to wrap it in a Python file. Then we wrap it in an |
||||
|
# atomic file that moves the file over on close. |
||||
|
import tempfile |
||||
|
fd, tmp_filename = tempfile.mkstemp(dir=os.path.dirname(filename), |
||||
|
prefix='.__atomic-write') |
||||
|
|
||||
|
if encoding is not None: |
||||
|
f = io.open(fd, mode, encoding=encoding, errors=errors) |
||||
|
else: |
||||
|
f = os.fdopen(fd, mode) |
||||
|
|
||||
|
return _AtomicFile(f, tmp_filename, filename), True |
||||
|
|
||||
|
|
||||
|
# Used in a destructor call, needs extra protection from interpreter cleanup. |
||||
|
if hasattr(os, 'replace'): |
||||
|
_replace = os.replace |
||||
|
_can_replace = True |
||||
|
else: |
||||
|
_replace = os.rename |
||||
|
_can_replace = not WIN |
||||
|
|
||||
|
|
||||
|
class _AtomicFile(object): |
||||
|
|
||||
|
def __init__(self, f, tmp_filename, real_filename): |
||||
|
self._f = f |
||||
|
self._tmp_filename = tmp_filename |
||||
|
self._real_filename = real_filename |
||||
|
self.closed = False |
||||
|
|
||||
|
@property |
||||
|
def name(self): |
||||
|
return self._real_filename |
||||
|
|
||||
|
def close(self, delete=False): |
||||
|
if self.closed: |
||||
|
return |
||||
|
self._f.close() |
||||
|
if not _can_replace: |
||||
|
try: |
||||
|
os.remove(self._real_filename) |
||||
|
except OSError: |
||||
|
pass |
||||
|
_replace(self._tmp_filename, self._real_filename) |
||||
|
self.closed = True |
||||
|
|
||||
|
def __getattr__(self, name): |
||||
|
return getattr(self._f, name) |
||||
|
|
||||
|
def __enter__(self): |
||||
|
return self |
||||
|
|
||||
|
def __exit__(self, exc_type, exc_value, tb): |
||||
|
self.close(delete=exc_type is not None) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return repr(self._f) |
||||
|
|
||||
|
|
||||
|
auto_wrap_for_ansi = None |
||||
|
colorama = None |
||||
|
get_winterm_size = None |
||||
|
|
||||
|
|
||||
|
def strip_ansi(value): |
||||
|
return _ansi_re.sub('', value) |
||||
|
|
||||
|
|
||||
|
def should_strip_ansi(stream=None, color=None): |
||||
|
if color is None: |
||||
|
if stream is None: |
||||
|
stream = sys.stdin |
||||
|
return not isatty(stream) |
||||
|
return not color |
||||
|
|
||||
|
|
||||
|
# If we're on Windows, we provide transparent integration through |
||||
|
# colorama. This will make ANSI colors through the echo function |
||||
|
# work automatically. |
||||
|
if WIN: |
||||
|
# Windows has a smaller terminal |
||||
|
DEFAULT_COLUMNS = 79 |
||||
|
|
||||
|
from ._winconsole import _get_windows_console_stream |
||||
|
|
||||
|
def _get_argv_encoding(): |
||||
|
import locale |
||||
|
return locale.getpreferredencoding() |
||||
|
|
||||
|
if PY2: |
||||
|
def raw_input(prompt=''): |
||||
|
sys.stderr.flush() |
||||
|
if prompt: |
||||
|
stdout = _default_text_stdout() |
||||
|
stdout.write(prompt) |
||||
|
stdin = _default_text_stdin() |
||||
|
return stdin.readline().rstrip('\r\n') |
||||
|
|
||||
|
try: |
||||
|
import colorama |
||||
|
except ImportError: |
||||
|
pass |
||||
|
else: |
||||
|
_ansi_stream_wrappers = WeakKeyDictionary() |
||||
|
|
||||
|
def auto_wrap_for_ansi(stream, color=None): |
||||
|
"""This function wraps a stream so that calls through colorama |
||||
|
are issued to the win32 console API to recolor on demand. It |
||||
|
also ensures to reset the colors if a write call is interrupted |
||||
|
to not destroy the console afterwards. |
||||
|
""" |
||||
|
try: |
||||
|
cached = _ansi_stream_wrappers.get(stream) |
||||
|
except Exception: |
||||
|
cached = None |
||||
|
if cached is not None: |
||||
|
return cached |
||||
|
strip = should_strip_ansi(stream, color) |
||||
|
ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) |
||||
|
rv = ansi_wrapper.stream |
||||
|
_write = rv.write |
||||
|
|
||||
|
def _safe_write(s): |
||||
|
try: |
||||
|
return _write(s) |
||||
|
except: |
||||
|
ansi_wrapper.reset_all() |
||||
|
raise |
||||
|
|
||||
|
rv.write = _safe_write |
||||
|
try: |
||||
|
_ansi_stream_wrappers[stream] = rv |
||||
|
except Exception: |
||||
|
pass |
||||
|
return rv |
||||
|
|
||||
|
def get_winterm_size(): |
||||
|
win = colorama.win32.GetConsoleScreenBufferInfo( |
||||
|
colorama.win32.STDOUT).srWindow |
||||
|
return win.Right - win.Left, win.Bottom - win.Top |
||||
|
else: |
||||
|
def _get_argv_encoding(): |
||||
|
return getattr(sys.stdin, 'encoding', None) or get_filesystem_encoding() |
||||
|
|
||||
|
_get_windows_console_stream = lambda *x: None |
||||
|
|
||||
|
|
||||
|
def term_len(x): |
||||
|
return len(strip_ansi(x)) |
||||
|
|
||||
|
|
||||
|
def isatty(stream): |
||||
|
try: |
||||
|
return stream.isatty() |
||||
|
except Exception: |
||||
|
return False |
||||
|
|
||||
|
|
||||
|
def _make_cached_stream_func(src_func, wrapper_func): |
||||
|
cache = WeakKeyDictionary() |
||||
|
def func(): |
||||
|
stream = src_func() |
||||
|
try: |
||||
|
rv = cache.get(stream) |
||||
|
except Exception: |
||||
|
rv = None |
||||
|
if rv is not None: |
||||
|
return rv |
||||
|
rv = wrapper_func() |
||||
|
try: |
||||
|
cache[stream] = rv |
||||
|
except Exception: |
||||
|
pass |
||||
|
return rv |
||||
|
return func |
||||
|
|
||||
|
|
||||
|
_default_text_stdin = _make_cached_stream_func( |
||||
|
lambda: sys.stdin, get_text_stdin) |
||||
|
_default_text_stdout = _make_cached_stream_func( |
||||
|
lambda: sys.stdout, get_text_stdout) |
||||
|
_default_text_stderr = _make_cached_stream_func( |
||||
|
lambda: sys.stderr, get_text_stderr) |
||||
|
|
||||
|
|
||||
|
binary_streams = { |
||||
|
'stdin': get_binary_stdin, |
||||
|
'stdout': get_binary_stdout, |
||||
|
'stderr': get_binary_stderr, |
||||
|
} |
||||
|
|
||||
|
text_streams = { |
||||
|
'stdin': get_text_stdin, |
||||
|
'stdout': get_text_stdout, |
||||
|
'stderr': get_text_stderr, |
||||
|
} |
@ -0,0 +1,547 @@ |
|||||
|
""" |
||||
|
click._termui_impl |
||||
|
~~~~~~~~~~~~~~~~~~ |
||||
|
|
||||
|
This module contains implementations for the termui module. To keep the |
||||
|
import time of Click down, some infrequently used functionality is placed |
||||
|
in this module and only imported as needed. |
||||
|
|
||||
|
:copyright: (c) 2014 by Armin Ronacher. |
||||
|
:license: BSD, see LICENSE for more details. |
||||
|
""" |
||||
|
import os |
||||
|
import sys |
||||
|
import time |
||||
|
import math |
||||
|
from ._compat import _default_text_stdout, range_type, PY2, isatty, \ |
||||
|
open_stream, strip_ansi, term_len, get_best_encoding, WIN |
||||
|
from .utils import echo |
||||
|
from .exceptions import ClickException |
||||
|
|
||||
|
|
||||
|
if os.name == 'nt': |
||||
|
BEFORE_BAR = '\r' |
||||
|
AFTER_BAR = '\n' |
||||
|
else: |
||||
|
BEFORE_BAR = '\r\033[?25l' |
||||
|
AFTER_BAR = '\033[?25h\n' |
||||
|
|
||||
|
|
||||
|
def _length_hint(obj): |
||||
|
"""Returns the length hint of an object.""" |
||||
|
try: |
||||
|
return len(obj) |
||||
|
except TypeError: |
||||
|
try: |
||||
|
get_hint = type(obj).__length_hint__ |
||||
|
except AttributeError: |
||||
|
return None |
||||
|
try: |
||||
|
hint = get_hint(obj) |
||||
|
except TypeError: |
||||
|
return None |
||||
|
if hint is NotImplemented or \ |
||||
|
not isinstance(hint, (int, long)) or \ |
||||
|
hint < 0: |
||||
|
return None |
||||
|
return hint |
||||
|
|
||||
|
|
||||
|
class ProgressBar(object): |
||||
|
|
||||
|
def __init__(self, iterable, length=None, fill_char='#', empty_char=' ', |
||||
|
bar_template='%(bar)s', info_sep=' ', show_eta=True, |
||||
|
show_percent=None, show_pos=False, item_show_func=None, |
||||
|
label=None, file=None, color=None, width=30): |
||||
|
self.fill_char = fill_char |
||||
|
self.empty_char = empty_char |
||||
|
self.bar_template = bar_template |
||||
|
self.info_sep = info_sep |
||||
|
self.show_eta = show_eta |
||||
|
self.show_percent = show_percent |
||||
|
self.show_pos = show_pos |
||||
|
self.item_show_func = item_show_func |
||||
|
self.label = label or '' |
||||
|
if file is None: |
||||
|
file = _default_text_stdout() |
||||
|
self.file = file |
||||
|
self.color = color |
||||
|
self.width = width |
||||
|
self.autowidth = width == 0 |
||||
|
|
||||
|
if length is None: |
||||
|
length = _length_hint(iterable) |
||||
|
if iterable is None: |
||||
|
if length is None: |
||||
|
raise TypeError('iterable or length is required') |
||||
|
iterable = range_type(length) |
||||
|
self.iter = iter(iterable) |
||||
|
self.length = length |
||||
|
self.length_known = length is not None |
||||
|
self.pos = 0 |
||||
|
self.avg = [] |
||||
|
self.start = self.last_eta = time.time() |
||||
|
self.eta_known = False |
||||
|
self.finished = False |
||||
|
self.max_width = None |
||||
|
self.entered = False |
||||
|
self.current_item = None |
||||
|
self.is_hidden = not isatty(self.file) |
||||
|
self._last_line = None |
||||
|
|
||||
|
def __enter__(self): |
||||
|
self.entered = True |
||||
|
self.render_progress() |
||||
|
return self |
||||
|
|
||||
|
def __exit__(self, exc_type, exc_value, tb): |
||||
|
self.render_finish() |
||||
|
|
||||
|
def __iter__(self): |
||||
|
if not self.entered: |
||||
|
raise RuntimeError('You need to use progress bars in a with block.') |
||||
|
self.render_progress() |
||||
|
return self |
||||
|
|
||||
|
def render_finish(self): |
||||
|
if self.is_hidden: |
||||
|
return |
||||
|
self.file.write(AFTER_BAR) |
||||
|
self.file.flush() |
||||
|
|
||||
|
@property |
||||
|
def pct(self): |
||||
|
if self.finished: |
||||
|
return 1.0 |
||||
|
return min(self.pos / (float(self.length) or 1), 1.0) |
||||
|
|
||||
|
@property |
||||
|
def time_per_iteration(self): |
||||
|
if not self.avg: |
||||
|
return 0.0 |
||||
|
return sum(self.avg) / float(len(self.avg)) |
||||
|
|
||||
|
@property |
||||
|
def eta(self): |
||||
|
if self.length_known and not self.finished: |
||||
|
return self.time_per_iteration * (self.length - self.pos) |
||||
|
return 0.0 |
||||
|
|
||||
|
def format_eta(self): |
||||
|
if self.eta_known: |
||||
|
t = self.eta + 1 |
||||
|
seconds = t % 60 |
||||
|
t /= 60 |
||||
|
minutes = t % 60 |
||||
|
t /= 60 |
||||
|
hours = t % 24 |
||||
|
t /= 24 |
||||
|
if t > 0: |
||||
|
days = t |
||||
|
return '%dd %02d:%02d:%02d' % (days, hours, minutes, seconds) |
||||
|
else: |
||||
|
return '%02d:%02d:%02d' % (hours, minutes, seconds) |
||||
|
return '' |
||||
|
|
||||
|
def format_pos(self): |
||||
|
pos = str(self.pos) |
||||
|
if self.length_known: |
||||
|
pos += '/%s' % self.length |
||||
|
return pos |
||||
|
|
||||
|
def format_pct(self): |
||||
|
return ('% 4d%%' % int(self.pct * 100))[1:] |
||||
|
|
||||
|
def format_progress_line(self): |
||||
|
show_percent = self.show_percent |
||||
|
|
||||
|
info_bits = [] |
||||
|
if self.length_known: |
||||
|
bar_length = int(self.pct * self.width) |
||||
|
bar = self.fill_char * bar_length |
||||
|
bar += self.empty_char * (self.width - bar_length) |
||||
|
if show_percent is None: |
||||
|
show_percent = not self.show_pos |
||||
|
else: |
||||
|
if self.finished: |
||||
|
bar = self.fill_char * self.width |
||||
|
else: |
||||
|
bar = list(self.empty_char * (self.width or 1)) |
||||
|
if self.time_per_iteration != 0: |
||||
|
bar[int((math.cos(self.pos * self.time_per_iteration) |
||||
|
/ 2.0 + 0.5) * self.width)] = self.fill_char |
||||
|
bar = ''.join(bar) |
||||
|
|
||||
|
if self.show_pos: |
||||
|
info_bits.append(self.format_pos()) |
||||
|
if show_percent: |
||||
|
info_bits.append(self.format_pct()) |
||||
|
if self.show_eta and self.eta_known and not self.finished: |
||||
|
info_bits.append(self.format_eta()) |
||||
|
if self.item_show_func is not None: |
||||
|
item_info = self.item_show_func(self.current_item) |
||||
|
if item_info is not None: |
||||
|
info_bits.append(item_info) |
||||
|
|
||||
|
return (self.bar_template % { |
||||
|
'label': self.label, |
||||
|
'bar': bar, |
||||
|
'info': self.info_sep.join(info_bits) |
||||
|
}).rstrip() |
||||
|
|
||||
|
def render_progress(self): |
||||
|
from .termui import get_terminal_size |
||||
|
nl = False |
||||
|
|
||||
|
if self.is_hidden: |
||||
|
buf = [self.label] |
||||
|
nl = True |
||||
|
else: |
||||
|
buf = [] |
||||
|
# Update width in case the terminal has been resized |
||||
|
if self.autowidth: |
||||
|
old_width = self.width |
||||
|
self.width = 0 |
||||
|
clutter_length = term_len(self.format_progress_line()) |
||||
|
new_width = max(0, get_terminal_size()[0] - clutter_length) |
||||
|
if new_width < old_width: |
||||
|
buf.append(BEFORE_BAR) |
||||
|
buf.append(' ' * self.max_width) |
||||
|
self.max_width = new_width |
||||
|
self.width = new_width |
||||
|
|
||||
|
clear_width = self.width |
||||
|
if self.max_width is not None: |
||||
|
clear_width = self.max_width |
||||
|
|
||||
|
buf.append(BEFORE_BAR) |
||||
|
line = self.format_progress_line() |
||||
|
line_len = term_len(line) |
||||
|
if self.max_width is None or self.max_width < line_len: |
||||
|
self.max_width = line_len |
||||
|
buf.append(line) |
||||
|
|
||||
|
buf.append(' ' * (clear_width - line_len)) |
||||
|
line = ''.join(buf) |
||||
|
|
||||
|
# Render the line only if it changed. |
||||
|
if line != self._last_line: |
||||
|
self._last_line = line |
||||
|
echo(line, file=self.file, color=self.color, nl=nl) |
||||
|
self.file.flush() |
||||
|
|
||||
|
def make_step(self, n_steps): |
||||
|
self.pos += n_steps |
||||
|
if self.length_known and self.pos >= self.length: |
||||
|
self.finished = True |
||||
|
|
||||
|
if (time.time() - self.last_eta) < 1.0: |
||||
|
return |
||||
|
|
||||
|
self.last_eta = time.time() |
||||
|
self.avg = self.avg[-6:] + [-(self.start - time.time()) / (self.pos)] |
||||
|
|
||||
|
self.eta_known = self.length_known |
||||
|
|
||||
|
def update(self, n_steps): |
||||
|
self.make_step(n_steps) |
||||
|
self.render_progress() |
||||
|
|
||||
|
def finish(self): |
||||
|
self.eta_known = 0 |
||||
|
self.current_item = None |
||||
|
self.finished = True |
||||
|
|
||||
|
def next(self): |
||||
|
if self.is_hidden: |
||||
|
return next(self.iter) |
||||
|
try: |
||||
|
rv = next(self.iter) |
||||
|
self.current_item = rv |
||||
|
except StopIteration: |
||||
|
self.finish() |
||||
|
self.render_progress() |
||||
|
raise StopIteration() |
||||
|
else: |
||||
|
self.update(1) |
||||
|
return rv |
||||
|
|
||||
|
if not PY2: |
||||
|
__next__ = next |
||||
|
del next |
||||
|
|
||||
|
|
||||
|
def pager(text, color=None): |
||||
|
"""Decide what method to use for paging through text.""" |
||||
|
stdout = _default_text_stdout() |
||||
|
if not isatty(sys.stdin) or not isatty(stdout): |
||||
|
return _nullpager(stdout, text, color) |
||||
|
pager_cmd = (os.environ.get('PAGER', None) or '').strip() |
||||
|
if pager_cmd: |
||||
|
if WIN: |
||||
|
return _tempfilepager(text, pager_cmd, color) |
||||
|
return _pipepager(text, pager_cmd, color) |
||||
|
if os.environ.get('TERM') in ('dumb', 'emacs'): |
||||
|
return _nullpager(stdout, text, color) |
||||
|
if WIN or sys.platform.startswith('os2'): |
||||
|
return _tempfilepager(text, 'more <', color) |
||||
|
if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0: |
||||
|
return _pipepager(text, 'less', color) |
||||
|
|
||||
|
import tempfile |
||||
|
fd, filename = tempfile.mkstemp() |
||||
|
os.close(fd) |
||||
|
try: |
||||
|
if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0: |
||||
|
return _pipepager(text, 'more', color) |
||||
|
return _nullpager(stdout, text, color) |
||||
|
finally: |
||||
|
os.unlink(filename) |
||||
|
|
||||
|
|
||||
|
def _pipepager(text, cmd, color): |
||||
|
"""Page through text by feeding it to another program. Invoking a |
||||
|
pager through this might support colors. |
||||
|
""" |
||||
|
import subprocess |
||||
|
env = dict(os.environ) |
||||
|
|
||||
|
# If we're piping to less we might support colors under the |
||||
|
# condition that |
||||
|
cmd_detail = cmd.rsplit('/', 1)[-1].split() |
||||
|
if color is None and cmd_detail[0] == 'less': |
||||
|
less_flags = os.environ.get('LESS', '') + ' '.join(cmd_detail[1:]) |
||||
|
if not less_flags: |
||||
|
env['LESS'] = '-R' |
||||
|
color = True |
||||
|
elif 'r' in less_flags or 'R' in less_flags: |
||||
|
color = True |
||||
|
|
||||
|
if not color: |
||||
|
text = strip_ansi(text) |
||||
|
|
||||
|
c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, |
||||
|
env=env) |
||||
|
encoding = get_best_encoding(c.stdin) |
||||
|
try: |
||||
|
c.stdin.write(text.encode(encoding, 'replace')) |
||||
|
c.stdin.close() |
||||
|
except (IOError, KeyboardInterrupt): |
||||
|
pass |
||||
|
|
||||
|
# Less doesn't respect ^C, but catches it for its own UI purposes (aborting |
||||
|
# search or other commands inside less). |
||||
|
# |
||||
|
# That means when the user hits ^C, the parent process (click) terminates, |
||||
|
# but less is still alive, paging the output and messing up the terminal. |
||||
|
# |
||||
|
# If the user wants to make the pager exit on ^C, they should set |
||||
|
# `LESS='-K'`. It's not our decision to make. |
||||
|
while True: |
||||
|
try: |
||||
|
c.wait() |
||||
|
except KeyboardInterrupt: |
||||
|
pass |
||||
|
else: |
||||
|
break |
||||
|
|
||||
|
|
||||
|
def _tempfilepager(text, cmd, color): |
||||
|
"""Page through text by invoking a program on a temporary file.""" |
||||
|
import tempfile |
||||
|
filename = tempfile.mktemp() |
||||
|
if not color: |
||||
|
text = strip_ansi(text) |
||||
|
encoding = get_best_encoding(sys.stdout) |
||||
|
with open_stream(filename, 'wb')[0] as f: |
||||
|
f.write(text.encode(encoding)) |
||||
|
try: |
||||
|
os.system(cmd + ' "' + filename + '"') |
||||
|
finally: |
||||
|
os.unlink(filename) |
||||
|
|
||||
|
|
||||
|
def _nullpager(stream, text, color): |
||||
|
"""Simply print unformatted text. This is the ultimate fallback.""" |
||||
|
if not color: |
||||
|
text = strip_ansi(text) |
||||
|
stream.write(text) |
||||
|
|
||||
|
|
||||
|
class Editor(object): |
||||
|
|
||||
|
def __init__(self, editor=None, env=None, require_save=True, |
||||
|
extension='.txt'): |
||||
|
self.editor = editor |
||||
|
self.env = env |
||||
|
self.require_save = require_save |
||||
|
self.extension = extension |
||||
|
|
||||
|
def get_editor(self): |
||||
|
if self.editor is not None: |
||||
|
return self.editor |
||||
|
for key in 'VISUAL', 'EDITOR': |
||||
|
rv = os.environ.get(key) |
||||
|
if rv: |
||||
|
return rv |
||||
|
if WIN: |
||||
|
return 'notepad' |
||||
|
for editor in 'vim', 'nano': |
||||
|
if os.system('which %s >/dev/null 2>&1' % editor) == 0: |
||||
|
return editor |
||||
|
return 'vi' |
||||
|
|
||||
|
def edit_file(self, filename): |
||||
|
import subprocess |
||||
|
editor = self.get_editor() |
||||
|
if self.env: |
||||
|
environ = os.environ.copy() |
||||
|
environ.update(self.env) |
||||
|
else: |
||||
|
environ = None |
||||
|
try: |
||||
|
c = subprocess.Popen('%s "%s"' % (editor, filename), |
||||
|
env=environ, shell=True) |
||||
|
exit_code = c.wait() |
||||
|
if exit_code != 0: |
||||
|
raise ClickException('%s: Editing failed!' % editor) |
||||
|
except OSError as e: |
||||
|
raise ClickException('%s: Editing failed: %s' % (editor, e)) |
||||
|
|
||||
|
def edit(self, text): |
||||
|
import tempfile |
||||
|
|
||||
|
text = text or '' |
||||
|
if text and not text.endswith('\n'): |
||||
|
text += '\n' |
||||
|
|
||||
|
fd, name = tempfile.mkstemp(prefix='editor-', suffix=self.extension) |
||||
|
try: |
||||
|
if WIN: |
||||
|
encoding = 'utf-8-sig' |
||||
|
text = text.replace('\n', '\r\n') |
||||
|
else: |
||||
|
encoding = 'utf-8' |
||||
|
text = text.encode(encoding) |
||||
|
|
||||
|
f = os.fdopen(fd, 'wb') |
||||
|
f.write(text) |
||||
|
f.close() |
||||
|
timestamp = os.path.getmtime(name) |
||||
|
|
||||
|
self.edit_file(name) |
||||
|
|
||||
|
if self.require_save \ |
||||
|
and os.path.getmtime(name) == timestamp: |
||||
|
return None |
||||
|
|
||||
|
f = open(name, 'rb') |
||||
|
try: |
||||
|
rv = f.read() |
||||
|
finally: |
||||
|
f.close() |
||||
|
return rv.decode('utf-8-sig').replace('\r\n', '\n') |
||||
|
finally: |
||||
|
os.unlink(name) |
||||
|
|
||||
|
|
||||
|
def open_url(url, wait=False, locate=False): |
||||
|
import subprocess |
||||
|
|
||||
|
def _unquote_file(url): |
||||
|
try: |
||||
|
import urllib |
||||
|
except ImportError: |
||||
|
import urllib |
||||
|
if url.startswith('file://'): |
||||
|
url = urllib.unquote(url[7:]) |
||||
|
return url |
||||
|
|
||||
|
if sys.platform == 'darwin': |
||||
|
args = ['open'] |
||||
|
if wait: |
||||
|
args.append('-W') |
||||
|
if locate: |
||||
|
args.append('-R') |
||||
|
args.append(_unquote_file(url)) |
||||
|
null = open('/dev/null', 'w') |
||||
|
try: |
||||
|
return subprocess.Popen(args, stderr=null).wait() |
||||
|
finally: |
||||
|
null.close() |
||||
|
elif WIN: |
||||
|
if locate: |
||||
|
url = _unquote_file(url) |
||||
|
args = 'explorer /select,"%s"' % _unquote_file( |
||||
|
url.replace('"', '')) |
||||
|
else: |
||||
|
args = 'start %s "" "%s"' % ( |
||||
|
wait and '/WAIT' or '', url.replace('"', '')) |
||||
|
return os.system(args) |
||||
|
|
||||
|
try: |
||||
|
if locate: |
||||
|
url = os.path.dirname(_unquote_file(url)) or '.' |
||||
|
else: |
||||
|
url = _unquote_file(url) |
||||
|
c = subprocess.Popen(['xdg-open', url]) |
||||
|
if wait: |
||||
|
return c.wait() |
||||
|
return 0 |
||||
|
except OSError: |
||||
|
if url.startswith(('http://', 'https://')) and not locate and not wait: |
||||
|
import webbrowser |
||||
|
webbrowser.open(url) |
||||
|
return 0 |
||||
|
return 1 |
||||
|
|
||||
|
|
||||
|
def _translate_ch_to_exc(ch): |
||||
|
if ch == '\x03': |
||||
|
raise KeyboardInterrupt() |
||||
|
if ch == '\x04': |
||||
|
raise EOFError() |
||||
|
|
||||
|
|
||||
|
if WIN: |
||||
|
import msvcrt |
||||
|
|
||||
|
def getchar(echo): |
||||
|
rv = msvcrt.getch() |
||||
|
if echo: |
||||
|
msvcrt.putchar(rv) |
||||
|
_translate_ch_to_exc(rv) |
||||
|
if PY2: |
||||
|
enc = getattr(sys.stdin, 'encoding', None) |
||||
|
if enc is not None: |
||||
|
rv = rv.decode(enc, 'replace') |
||||
|
else: |
||||
|
rv = rv.decode('cp1252', 'replace') |
||||
|
return rv |
||||
|
else: |
||||
|
import tty |
||||
|
import termios |
||||
|
|
||||
|
def getchar(echo): |
||||
|
if not isatty(sys.stdin): |
||||
|
f = open('/dev/tty') |
||||
|
fd = f.fileno() |
||||
|
else: |
||||
|
fd = sys.stdin.fileno() |
||||
|
f = None |
||||
|
try: |
||||
|
old_settings = termios.tcgetattr(fd) |
||||
|
try: |
||||
|
tty.setraw(fd) |
||||
|
ch = os.read(fd, 32) |
||||
|
if echo and isatty(sys.stdout): |
||||
|
sys.stdout.write(ch) |
||||
|
finally: |
||||
|
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) |
||||
|
sys.stdout.flush() |
||||
|
if f is not None: |
||||
|
f.close() |
||||
|
except termios.error: |
||||
|
pass |
||||
|
_translate_ch_to_exc(ch) |
||||
|
return ch.decode(get_best_encoding(sys.stdin), 'replace') |
@ -0,0 +1,38 @@ |
|||||
|
import textwrap |
||||
|
from contextlib import contextmanager |
||||
|
|
||||
|
|
||||
|
class TextWrapper(textwrap.TextWrapper): |
||||
|
|
||||
|
def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): |
||||
|
space_left = max(width - cur_len, 1) |
||||
|
|
||||
|
if self.break_long_words: |
||||
|
last = reversed_chunks[-1] |
||||
|
cut = last[:space_left] |
||||
|
res = last[space_left:] |
||||
|
cur_line.append(cut) |
||||
|
reversed_chunks[-1] = res |
||||
|
elif not cur_line: |
||||
|
cur_line.append(reversed_chunks.pop()) |
||||
|
|
||||
|
@contextmanager |
||||
|
def extra_indent(self, indent): |
||||
|
old_initial_indent = self.initial_indent |
||||
|
old_subsequent_indent = self.subsequent_indent |
||||
|
self.initial_indent += indent |
||||
|
self.subsequent_indent += indent |
||||
|
try: |
||||
|
yield |
||||
|
finally: |
||||
|
self.initial_indent = old_initial_indent |
||||
|
self.subsequent_indent = old_subsequent_indent |
||||
|
|
||||
|
def indent_only(self, text): |
||||
|
rv = [] |
||||
|
for idx, line in enumerate(text.splitlines()): |
||||
|
indent = self.initial_indent |
||||
|
if idx > 0: |
||||
|
indent = self.subsequent_indent |
||||
|
rv.append(indent + line) |
||||
|
return '\n'.join(rv) |
@ -0,0 +1,119 @@ |
|||||
|
import os |
||||
|
import sys |
||||
|
import codecs |
||||
|
|
||||
|
from ._compat import PY2 |
||||
|
|
||||
|
|
||||
|
# If someone wants to vendor click, we want to ensure the |
||||
|
# correct package is discovered. Ideally we could use a |
||||
|
# relative import here but unfortunately Python does not |
||||
|
# support that. |
||||
|
click = sys.modules[__name__.rsplit('.', 1)[0]] |
||||
|
|
||||
|
|
||||
|
def _find_unicode_literals_frame(): |
||||
|
import __future__ |
||||
|
frm = sys._getframe(1) |
||||
|
idx = 1 |
||||
|
while frm is not None: |
||||
|
if frm.f_globals.get('__name__', '').startswith('click.'): |
||||
|
frm = frm.f_back |
||||
|
idx += 1 |
||||
|
elif frm.f_code.co_flags & __future__.unicode_literals.compiler_flag: |
||||
|
return idx |
||||
|
else: |
||||
|
break |
||||
|
return 0 |
||||
|
|
||||
|
|
||||
|
def _check_for_unicode_literals(): |
||||
|
if not __debug__: |
||||
|
return |
||||
|
if not PY2 or click.disable_unicode_literals_warning: |
||||
|
return |
||||
|
bad_frame = _find_unicode_literals_frame() |
||||
|
if bad_frame <= 0: |
||||
|
return |
||||
|
from warnings import warn |
||||
|
warn(Warning('Click detected the use of the unicode_literals ' |
||||
|
'__future__ import. This is heavily discouraged ' |
||||
|
'because it can introduce subtle bugs in your ' |
||||
|
'code. You should instead use explicit u"" literals ' |
||||
|
'for your unicode strings. For more information see ' |
||||
|
'http://click.pocoo.org/python3/'), |
||||
|
stacklevel=bad_frame) |
||||
|
|
||||
|
|
||||
|
def _verify_python3_env(): |
||||
|
"""Ensures that the environment is good for unicode on Python 3.""" |
||||
|
if PY2: |
||||
|
return |
||||
|
try: |
||||
|
import locale |
||||
|
fs_enc = codecs.lookup(locale.getpreferredencoding()).name |
||||
|
except Exception: |
||||
|
fs_enc = 'ascii' |
||||
|
if fs_enc != 'ascii': |
||||
|
return |
||||
|
|
||||
|
extra = '' |
||||
|
if os.name == 'posix': |
||||
|
import subprocess |
||||
|
rv = subprocess.Popen(['locale', '-a'], stdout=subprocess.PIPE, |
||||
|
stderr=subprocess.PIPE).communicate()[0] |
||||
|
good_locales = set() |
||||
|
has_c_utf8 = False |
||||
|
|
||||
|
# Make sure we're operating on text here. |
||||
|
if isinstance(rv, bytes): |
||||
|
rv = rv.decode('ascii', 'replace') |
||||
|
|
||||
|
for line in rv.splitlines(): |
||||
|
locale = line.strip() |
||||
|
if locale.lower().endswith(('.utf-8', '.utf8')): |
||||
|
good_locales.add(locale) |
||||
|
if locale.lower() in ('c.utf8', 'c.utf-8'): |
||||
|
has_c_utf8 = True |
||||
|
|
||||
|
extra += '\n\n' |
||||
|
if not good_locales: |
||||
|
extra += ( |
||||
|
'Additional information: on this system no suitable UTF-8\n' |
||||
|
'locales were discovered. This most likely requires resolving\n' |
||||
|
'by reconfiguring the locale system.' |
||||
|
) |
||||
|
elif has_c_utf8: |
||||
|
extra += ( |
||||
|
'This system supports the C.UTF-8 locale which is recommended.\n' |
||||
|
'You might be able to resolve your issue by exporting the\n' |
||||
|
'following environment variables:\n\n' |
||||
|
' export LC_ALL=C.UTF-8\n' |
||||
|
' export LANG=C.UTF-8' |
||||
|
) |
||||
|
else: |
||||
|
extra += ( |
||||
|
'This system lists a couple of UTF-8 supporting locales that\n' |
||||
|
'you can pick from. The following suitable locales where\n' |
||||
|
'discovered: %s' |
||||
|
) % ', '.join(sorted(good_locales)) |
||||
|
|
||||
|
bad_locale = None |
||||
|
for locale in os.environ.get('LC_ALL'), os.environ.get('LANG'): |
||||
|
if locale and locale.lower().endswith(('.utf-8', '.utf8')): |
||||
|
bad_locale = locale |
||||
|
if locale is not None: |
||||
|
break |
||||
|
if bad_locale is not None: |
||||
|
extra += ( |
||||
|
'\n\nClick discovered that you exported a UTF-8 locale\n' |
||||
|
'but the locale system could not pick up from it because\n' |
||||
|
'it does not exist. The exported locale is "%s" but it\n' |
||||
|
'is not supported' |
||||
|
) % bad_locale |
||||
|
|
||||
|
raise RuntimeError('Click will abort further execution because Python 3 ' |
||||
|
'was configured to use ASCII as encoding for the ' |
||||
|
'environment. Either run this under Python 2 or ' |
||||
|
'consult http://click.pocoo.org/python3/ for ' |
||||
|
'mitigation steps.' + extra) |
@ -0,0 +1,273 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# This module is based on the excellent work by Adam Bartoš who |
||||
|
# provided a lot of what went into the implementation here in |
||||
|
# the discussion to issue1602 in the Python bug tracker. |
||||
|
# |
||||
|
# There are some general differences in regards to how this works |
||||
|
# compared to the original patches as we do not need to patch |
||||
|
# the entire interpreter but just work in our little world of |
||||
|
# echo and prmopt. |
||||
|
|
||||
|
import io |
||||
|
import os |
||||
|
import sys |
||||
|
import zlib |
||||
|
import time |
||||
|
import ctypes |
||||
|
import msvcrt |
||||
|
from click._compat import _NonClosingTextIOWrapper, text_type, PY2 |
||||
|
from ctypes import byref, POINTER, c_int, c_char, c_char_p, \ |
||||
|
c_void_p, py_object, c_ssize_t, c_ulong, windll, WINFUNCTYPE |
||||
|
try: |
||||
|
from ctypes import pythonapi |
||||
|
PyObject_GetBuffer = pythonapi.PyObject_GetBuffer |
||||
|
PyBuffer_Release = pythonapi.PyBuffer_Release |
||||
|
except ImportError: |
||||
|
pythonapi = None |
||||
|
from ctypes.wintypes import LPWSTR, LPCWSTR |
||||
|
|
||||
|
|
||||
|
c_ssize_p = POINTER(c_ssize_t) |
||||
|
|
||||
|
kernel32 = windll.kernel32 |
||||
|
GetStdHandle = kernel32.GetStdHandle |
||||
|
ReadConsoleW = kernel32.ReadConsoleW |
||||
|
WriteConsoleW = kernel32.WriteConsoleW |
||||
|
GetLastError = kernel32.GetLastError |
||||
|
GetCommandLineW = WINFUNCTYPE(LPWSTR)( |
||||
|
('GetCommandLineW', windll.kernel32)) |
||||
|
CommandLineToArgvW = WINFUNCTYPE( |
||||
|
POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( |
||||
|
('CommandLineToArgvW', windll.shell32)) |
||||
|
|
||||
|
|
||||
|
STDIN_HANDLE = GetStdHandle(-10) |
||||
|
STDOUT_HANDLE = GetStdHandle(-11) |
||||
|
STDERR_HANDLE = GetStdHandle(-12) |
||||
|
|
||||
|
|
||||
|
PyBUF_SIMPLE = 0 |
||||
|
PyBUF_WRITABLE = 1 |
||||
|
|
||||
|
ERROR_SUCCESS = 0 |
||||
|
ERROR_NOT_ENOUGH_MEMORY = 8 |
||||
|
ERROR_OPERATION_ABORTED = 995 |
||||
|
|
||||
|
STDIN_FILENO = 0 |
||||
|
STDOUT_FILENO = 1 |
||||
|
STDERR_FILENO = 2 |
||||
|
|
||||
|
EOF = b'\x1a' |
||||
|
MAX_BYTES_WRITTEN = 32767 |
||||
|
|
||||
|
|
||||
|
class Py_buffer(ctypes.Structure): |
||||
|
_fields_ = [ |
||||
|
('buf', c_void_p), |
||||
|
('obj', py_object), |
||||
|
('len', c_ssize_t), |
||||
|
('itemsize', c_ssize_t), |
||||
|
('readonly', c_int), |
||||
|
('ndim', c_int), |
||||
|
('format', c_char_p), |
||||
|
('shape', c_ssize_p), |
||||
|
('strides', c_ssize_p), |
||||
|
('suboffsets', c_ssize_p), |
||||
|
('internal', c_void_p) |
||||
|
] |
||||
|
|
||||
|
if PY2: |
||||
|
_fields_.insert(-1, ('smalltable', c_ssize_t * 2)) |
||||
|
|
||||
|
|
||||
|
# On PyPy we cannot get buffers so our ability to operate here is |
||||
|
# serverly limited. |
||||
|
if pythonapi is None: |
||||
|
get_buffer = None |
||||
|
else: |
||||
|
def get_buffer(obj, writable=False): |
||||
|
buf = Py_buffer() |
||||
|
flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE |
||||
|
PyObject_GetBuffer(py_object(obj), byref(buf), flags) |
||||
|
try: |
||||
|
buffer_type = c_char * buf.len |
||||
|
return buffer_type.from_address(buf.buf) |
||||
|
finally: |
||||
|
PyBuffer_Release(byref(buf)) |
||||
|
|
||||
|
|
||||
|
class _WindowsConsoleRawIOBase(io.RawIOBase): |
||||
|
|
||||
|
def __init__(self, handle): |
||||
|
self.handle = handle |
||||
|
|
||||
|
def isatty(self): |
||||
|
io.RawIOBase.isatty(self) |
||||
|
return True |
||||
|
|
||||
|
|
||||
|
class _WindowsConsoleReader(_WindowsConsoleRawIOBase): |
||||
|
|
||||
|
def readable(self): |
||||
|
return True |
||||
|
|
||||
|
def readinto(self, b): |
||||
|
bytes_to_be_read = len(b) |
||||
|
if not bytes_to_be_read: |
||||
|
return 0 |
||||
|
elif bytes_to_be_read % 2: |
||||
|
raise ValueError('cannot read odd number of bytes from ' |
||||
|
'UTF-16-LE encoded console') |
||||
|
|
||||
|
buffer = get_buffer(b, writable=True) |
||||
|
code_units_to_be_read = bytes_to_be_read // 2 |
||||
|
code_units_read = c_ulong() |
||||
|
|
||||
|
rv = ReadConsoleW(self.handle, buffer, code_units_to_be_read, |
||||
|
byref(code_units_read), None) |
||||
|
if GetLastError() == ERROR_OPERATION_ABORTED: |
||||
|
# wait for KeyboardInterrupt |
||||
|
time.sleep(0.1) |
||||
|
if not rv: |
||||
|
raise OSError('Windows error: %s' % GetLastError()) |
||||
|
|
||||
|
if buffer[0] == EOF: |
||||
|
return 0 |
||||
|
return 2 * code_units_read.value |
||||
|
|
||||
|
|
||||
|
class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): |
||||
|
|
||||
|
def writable(self): |
||||
|
return True |
||||
|
|
||||
|
@staticmethod |
||||
|
def _get_error_message(errno): |
||||
|
if errno == ERROR_SUCCESS: |
||||
|
return 'ERROR_SUCCESS' |
||||
|
elif errno == ERROR_NOT_ENOUGH_MEMORY: |
||||
|
return 'ERROR_NOT_ENOUGH_MEMORY' |
||||
|
return 'Windows error %s' % errno |
||||
|
|
||||
|
def write(self, b): |
||||
|
bytes_to_be_written = len(b) |
||||
|
buf = get_buffer(b) |
||||
|
code_units_to_be_written = min(bytes_to_be_written, |
||||
|
MAX_BYTES_WRITTEN) // 2 |
||||
|
code_units_written = c_ulong() |
||||
|
|
||||
|
WriteConsoleW(self.handle, buf, code_units_to_be_written, |
||||
|
byref(code_units_written), None) |
||||
|
bytes_written = 2 * code_units_written.value |
||||
|
|
||||
|
if bytes_written == 0 and bytes_to_be_written > 0: |
||||
|
raise OSError(self._get_error_message(GetLastError())) |
||||
|
return bytes_written |
||||
|
|
||||
|
|
||||
|
class ConsoleStream(object): |
||||
|
|
||||
|
def __init__(self, text_stream, byte_stream): |
||||
|
self._text_stream = text_stream |
||||
|
self.buffer = byte_stream |
||||
|
|
||||
|
@property |
||||
|
def name(self): |
||||
|
return self.buffer.name |
||||
|
|
||||
|
def write(self, x): |
||||
|
if isinstance(x, text_type): |
||||
|
return self._text_stream.write(x) |
||||
|
try: |
||||
|
self.flush() |
||||
|
except Exception: |
||||
|
pass |
||||
|
return self.buffer.write(x) |
||||
|
|
||||
|
def writelines(self, lines): |
||||
|
for line in lines: |
||||
|
self.write(line) |
||||
|
|
||||
|
def __getattr__(self, name): |
||||
|
return getattr(self._text_stream, name) |
||||
|
|
||||
|
def isatty(self): |
||||
|
return self.buffer.isatty() |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return '<ConsoleStream name=%r encoding=%r>' % ( |
||||
|
self.name, |
||||
|
self.encoding, |
||||
|
) |
||||
|
|
||||
|
|
||||
|
def _get_text_stdin(buffer_stream): |
||||
|
text_stream = _NonClosingTextIOWrapper( |
||||
|
io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), |
||||
|
'utf-16-le', 'strict', line_buffering=True) |
||||
|
return ConsoleStream(text_stream, buffer_stream) |
||||
|
|
||||
|
|
||||
|
def _get_text_stdout(buffer_stream): |
||||
|
text_stream = _NonClosingTextIOWrapper( |
||||
|
_WindowsConsoleWriter(STDOUT_HANDLE), |
||||
|
'utf-16-le', 'strict', line_buffering=True) |
||||
|
return ConsoleStream(text_stream, buffer_stream) |
||||
|
|
||||
|
|
||||
|
def _get_text_stderr(buffer_stream): |
||||
|
text_stream = _NonClosingTextIOWrapper( |
||||
|
_WindowsConsoleWriter(STDERR_HANDLE), |
||||
|
'utf-16-le', 'strict', line_buffering=True) |
||||
|
return ConsoleStream(text_stream, buffer_stream) |
||||
|
|
||||
|
|
||||
|
if PY2: |
||||
|
def _hash_py_argv(): |
||||
|
return zlib.crc32('\x00'.join(sys.argv[1:])) |
||||
|
|
||||
|
_initial_argv_hash = _hash_py_argv() |
||||
|
|
||||
|
def _get_windows_argv(): |
||||
|
argc = c_int(0) |
||||
|
argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc)) |
||||
|
argv = [argv_unicode[i] for i in range(0, argc.value)] |
||||
|
|
||||
|
if not hasattr(sys, 'frozen'): |
||||
|
argv = argv[1:] |
||||
|
while len(argv) > 0: |
||||
|
arg = argv[0] |
||||
|
if not arg.startswith('-') or arg == '-': |
||||
|
break |
||||
|
argv = argv[1:] |
||||
|
if arg.startswith(('-c', '-m')): |
||||
|
break |
||||
|
|
||||
|
return argv[1:] |
||||
|
|
||||
|
|
||||
|
_stream_factories = { |
||||
|
0: _get_text_stdin, |
||||
|
1: _get_text_stdout, |
||||
|
2: _get_text_stderr, |
||||
|
} |
||||
|
|
||||
|
|
||||
|
def _get_windows_console_stream(f, encoding, errors): |
||||
|
if get_buffer is not None and \ |
||||
|
encoding in ('utf-16-le', None) \ |
||||
|
and errors in ('strict', None) and \ |
||||
|
hasattr(f, 'isatty') and f.isatty(): |
||||
|
func = _stream_factories.get(f.fileno()) |
||||
|
if func is not None: |
||||
|
if not PY2: |
||||
|
f = getattr(f, 'buffer') |
||||
|
if f is None: |
||||
|
return None |
||||
|
else: |
||||
|
# If we are on Python 2 we need to set the stream that we |
||||
|
# deal with to binary mode as otherwise the exercise if a |
||||
|
# bit moot. The same problems apply as for |
||||
|
# get_binary_stdin and friends from _compat. |
||||
|
msvcrt.setmode(f.fileno(), os.O_BINARY) |
||||
|
return func(f) |
File diff suppressed because it is too large
@ -0,0 +1,304 @@ |
|||||
|
import sys |
||||
|
import inspect |
||||
|
|
||||
|
from functools import update_wrapper |
||||
|
|
||||
|
from ._compat import iteritems |
||||
|
from ._unicodefun import _check_for_unicode_literals |
||||
|
from .utils import echo |
||||
|
from .globals import get_current_context |
||||
|
|
||||
|
|
||||
|
def pass_context(f): |
||||
|
"""Marks a callback as wanting to receive the current context |
||||
|
object as first argument. |
||||
|
""" |
||||
|
def new_func(*args, **kwargs): |
||||
|
return f(get_current_context(), *args, **kwargs) |
||||
|
return update_wrapper(new_func, f) |
||||
|
|
||||
|
|
||||
|
def pass_obj(f): |
||||
|
"""Similar to :func:`pass_context`, but only pass the object on the |
||||
|
context onwards (:attr:`Context.obj`). This is useful if that object |
||||
|
represents the state of a nested system. |
||||
|
""" |
||||
|
def new_func(*args, **kwargs): |
||||
|
return f(get_current_context().obj, *args, **kwargs) |
||||
|
return update_wrapper(new_func, f) |
||||
|
|
||||
|
|
||||
|
def make_pass_decorator(object_type, ensure=False): |
||||
|
"""Given an object type this creates a decorator that will work |
||||
|
similar to :func:`pass_obj` but instead of passing the object of the |
||||
|
current context, it will find the innermost context of type |
||||
|
:func:`object_type`. |
||||
|
|
||||
|
This generates a decorator that works roughly like this:: |
||||
|
|
||||
|
from functools import update_wrapper |
||||
|
|
||||
|
def decorator(f): |
||||
|
@pass_context |
||||
|
def new_func(ctx, *args, **kwargs): |
||||
|
obj = ctx.find_object(object_type) |
||||
|
return ctx.invoke(f, obj, *args, **kwargs) |
||||
|
return update_wrapper(new_func, f) |
||||
|
return decorator |
||||
|
|
||||
|
:param object_type: the type of the object to pass. |
||||
|
:param ensure: if set to `True`, a new object will be created and |
||||
|
remembered on the context if it's not there yet. |
||||
|
""" |
||||
|
def decorator(f): |
||||
|
def new_func(*args, **kwargs): |
||||
|
ctx = get_current_context() |
||||
|
if ensure: |
||||
|
obj = ctx.ensure_object(object_type) |
||||
|
else: |
||||
|
obj = ctx.find_object(object_type) |
||||
|
if obj is None: |
||||
|
raise RuntimeError('Managed to invoke callback without a ' |
||||
|
'context object of type %r existing' |
||||
|
% object_type.__name__) |
||||
|
return ctx.invoke(f, obj, *args[1:], **kwargs) |
||||
|
return update_wrapper(new_func, f) |
||||
|
return decorator |
||||
|
|
||||
|
|
||||
|
def _make_command(f, name, attrs, cls): |
||||
|
if isinstance(f, Command): |
||||
|
raise TypeError('Attempted to convert a callback into a ' |
||||
|
'command twice.') |
||||
|
try: |
||||
|
params = f.__click_params__ |
||||
|
params.reverse() |
||||
|
del f.__click_params__ |
||||
|
except AttributeError: |
||||
|
params = [] |
||||
|
help = attrs.get('help') |
||||
|
if help is None: |
||||
|
help = inspect.getdoc(f) |
||||
|
if isinstance(help, bytes): |
||||
|
help = help.decode('utf-8') |
||||
|
else: |
||||
|
help = inspect.cleandoc(help) |
||||
|
attrs['help'] = help |
||||
|
_check_for_unicode_literals() |
||||
|
return cls(name=name or f.__name__.lower(), |
||||
|
callback=f, params=params, **attrs) |
||||
|
|
||||
|
|
||||
|
def command(name=None, cls=None, **attrs): |
||||
|
"""Creates a new :class:`Command` and uses the decorated function as |
||||
|
callback. This will also automatically attach all decorated |
||||
|
:func:`option`\s and :func:`argument`\s as parameters to the command. |
||||
|
|
||||
|
The name of the command defaults to the name of the function. If you |
||||
|
want to change that, you can pass the intended name as the first |
||||
|
argument. |
||||
|
|
||||
|
All keyword arguments are forwarded to the underlying command class. |
||||
|
|
||||
|
Once decorated the function turns into a :class:`Command` instance |
||||
|
that can be invoked as a command line utility or be attached to a |
||||
|
command :class:`Group`. |
||||
|
|
||||
|
:param name: the name of the command. This defaults to the function |
||||
|
name. |
||||
|
:param cls: the command class to instantiate. This defaults to |
||||
|
:class:`Command`. |
||||
|
""" |
||||
|
if cls is None: |
||||
|
cls = Command |
||||
|
def decorator(f): |
||||
|
cmd = _make_command(f, name, attrs, cls) |
||||
|
cmd.__doc__ = f.__doc__ |
||||
|
return cmd |
||||
|
return decorator |
||||
|
|
||||
|
|
||||
|
def group(name=None, **attrs): |
||||
|
"""Creates a new :class:`Group` with a function as callback. This |
||||
|
works otherwise the same as :func:`command` just that the `cls` |
||||
|
parameter is set to :class:`Group`. |
||||
|
""" |
||||
|
attrs.setdefault('cls', Group) |
||||
|
return command(name, **attrs) |
||||
|
|
||||
|
|
||||
|
def _param_memo(f, param): |
||||
|
if isinstance(f, Command): |
||||
|
f.params.append(param) |
||||
|
else: |
||||
|
if not hasattr(f, '__click_params__'): |
||||
|
f.__click_params__ = [] |
||||
|
f.__click_params__.append(param) |
||||
|
|
||||
|
|
||||
|
def argument(*param_decls, **attrs): |
||||
|
"""Attaches an argument to the command. All positional arguments are |
||||
|
passed as parameter declarations to :class:`Argument`; all keyword |
||||
|
arguments are forwarded unchanged (except ``cls``). |
||||
|
This is equivalent to creating an :class:`Argument` instance manually |
||||
|
and attaching it to the :attr:`Command.params` list. |
||||
|
|
||||
|
:param cls: the argument class to instantiate. This defaults to |
||||
|
:class:`Argument`. |
||||
|
""" |
||||
|
def decorator(f): |
||||
|
ArgumentClass = attrs.pop('cls', Argument) |
||||
|
_param_memo(f, ArgumentClass(param_decls, **attrs)) |
||||
|
return f |
||||
|
return decorator |
||||
|
|
||||
|
|
||||
|
def option(*param_decls, **attrs): |
||||
|
"""Attaches an option to the command. All positional arguments are |
||||
|
passed as parameter declarations to :class:`Option`; all keyword |
||||
|
arguments are forwarded unchanged (except ``cls``). |
||||
|
This is equivalent to creating an :class:`Option` instance manually |
||||
|
and attaching it to the :attr:`Command.params` list. |
||||
|
|
||||
|
:param cls: the option class to instantiate. This defaults to |
||||
|
:class:`Option`. |
||||
|
""" |
||||
|
def decorator(f): |
||||
|
if 'help' in attrs: |
||||
|
attrs['help'] = inspect.cleandoc(attrs['help']) |
||||
|
OptionClass = attrs.pop('cls', Option) |
||||
|
_param_memo(f, OptionClass(param_decls, **attrs)) |
||||
|
return f |
||||
|
return decorator |
||||
|
|
||||
|
|
||||
|
def confirmation_option(*param_decls, **attrs): |
||||
|
"""Shortcut for confirmation prompts that can be ignored by passing |
||||
|
``--yes`` as parameter. |
||||
|
|
||||
|
This is equivalent to decorating a function with :func:`option` with |
||||
|
the following parameters:: |
||||
|
|
||||
|
def callback(ctx, param, value): |
||||
|
if not value: |
||||
|
ctx.abort() |
||||
|
|
||||
|
@click.command() |
||||
|
@click.option('--yes', is_flag=True, callback=callback, |
||||
|
expose_value=False, prompt='Do you want to continue?') |
||||
|
def dropdb(): |
||||
|
pass |
||||
|
""" |
||||
|
def decorator(f): |
||||
|
def callback(ctx, param, value): |
||||
|
if not value: |
||||
|
ctx.abort() |
||||
|
attrs.setdefault('is_flag', True) |
||||
|
attrs.setdefault('callback', callback) |
||||
|
attrs.setdefault('expose_value', False) |
||||
|
attrs.setdefault('prompt', 'Do you want to continue?') |
||||
|
attrs.setdefault('help', 'Confirm the action without prompting.') |
||||
|
return option(*(param_decls or ('--yes',)), **attrs)(f) |
||||
|
return decorator |
||||
|
|
||||
|
|
||||
|
def password_option(*param_decls, **attrs): |
||||
|
"""Shortcut for password prompts. |
||||
|
|
||||
|
This is equivalent to decorating a function with :func:`option` with |
||||
|
the following parameters:: |
||||
|
|
||||
|
@click.command() |
||||
|
@click.option('--password', prompt=True, confirmation_prompt=True, |
||||
|
hide_input=True) |
||||
|
def changeadmin(password): |
||||
|
pass |
||||
|
""" |
||||
|
def decorator(f): |
||||
|
attrs.setdefault('prompt', True) |
||||
|
attrs.setdefault('confirmation_prompt', True) |
||||
|
attrs.setdefault('hide_input', True) |
||||
|
return option(*(param_decls or ('--password',)), **attrs)(f) |
||||
|
return decorator |
||||
|
|
||||
|
|
||||
|
def version_option(version=None, *param_decls, **attrs): |
||||
|
"""Adds a ``--version`` option which immediately ends the program |
||||
|
printing out the version number. This is implemented as an eager |
||||
|
option that prints the version and exits the program in the callback. |
||||
|
|
||||
|
:param version: the version number to show. If not provided Click |
||||
|
attempts an auto discovery via setuptools. |
||||
|
:param prog_name: the name of the program (defaults to autodetection) |
||||
|
:param message: custom message to show instead of the default |
||||
|
(``'%(prog)s, version %(version)s'``) |
||||
|
:param others: everything else is forwarded to :func:`option`. |
||||
|
""" |
||||
|
if version is None: |
||||
|
module = sys._getframe(1).f_globals.get('__name__') |
||||
|
def decorator(f): |
||||
|
prog_name = attrs.pop('prog_name', None) |
||||
|
message = attrs.pop('message', '%(prog)s, version %(version)s') |
||||
|
|
||||
|
def callback(ctx, param, value): |
||||
|
if not value or ctx.resilient_parsing: |
||||
|
return |
||||
|
prog = prog_name |
||||
|
if prog is None: |
||||
|
prog = ctx.find_root().info_name |
||||
|
ver = version |
||||
|
if ver is None: |
||||
|
try: |
||||
|
import pkg_resources |
||||
|
except ImportError: |
||||
|
pass |
||||
|
else: |
||||
|
for dist in pkg_resources.working_set: |
||||
|
scripts = dist.get_entry_map().get('console_scripts') or {} |
||||
|
for script_name, entry_point in iteritems(scripts): |
||||
|
if entry_point.module_name == module: |
||||
|
ver = dist.version |
||||
|
break |
||||
|
if ver is None: |
||||
|
raise RuntimeError('Could not determine version') |
||||
|
echo(message % { |
||||
|
'prog': prog, |
||||
|
'version': ver, |
||||
|
}, color=ctx.color) |
||||
|
ctx.exit() |
||||
|
|
||||
|
attrs.setdefault('is_flag', True) |
||||
|
attrs.setdefault('expose_value', False) |
||||
|
attrs.setdefault('is_eager', True) |
||||
|
attrs.setdefault('help', 'Show the version and exit.') |
||||
|
attrs['callback'] = callback |
||||
|
return option(*(param_decls or ('--version',)), **attrs)(f) |
||||
|
return decorator |
||||
|
|
||||
|
|
||||
|
def help_option(*param_decls, **attrs): |
||||
|
"""Adds a ``--help`` option which immediately ends the program |
||||
|
printing out the help page. This is usually unnecessary to add as |
||||
|
this is added by default to all commands unless suppressed. |
||||
|
|
||||
|
Like :func:`version_option`, this is implemented as eager option that |
||||
|
prints in the callback and exits. |
||||
|
|
||||
|
All arguments are forwarded to :func:`option`. |
||||
|
""" |
||||
|
def decorator(f): |
||||
|
def callback(ctx, param, value): |
||||
|
if value and not ctx.resilient_parsing: |
||||
|
echo(ctx.get_help(), color=ctx.color) |
||||
|
ctx.exit() |
||||
|
attrs.setdefault('is_flag', True) |
||||
|
attrs.setdefault('expose_value', False) |
||||
|
attrs.setdefault('help', 'Show this message and exit.') |
||||
|
attrs.setdefault('is_eager', True) |
||||
|
attrs['callback'] = callback |
||||
|
return option(*(param_decls or ('--help',)), **attrs)(f) |
||||
|
return decorator |
||||
|
|
||||
|
|
||||
|
# Circular dependencies between core and decorators |
||||
|
from .core import Command, Group, Argument, Option |
@ -0,0 +1,201 @@ |
|||||
|
from ._compat import PY2, filename_to_ui, get_text_stderr |
||||
|
from .utils import echo |
||||
|
|
||||
|
|
||||
|
class ClickException(Exception): |
||||
|
"""An exception that Click can handle and show to the user.""" |
||||
|
|
||||
|
#: The exit code for this exception |
||||
|
exit_code = 1 |
||||
|
|
||||
|
def __init__(self, message): |
||||
|
if PY2: |
||||
|
if message is not None: |
||||
|
message = message.encode('utf-8') |
||||
|
Exception.__init__(self, message) |
||||
|
self.message = message |
||||
|
|
||||
|
def format_message(self): |
||||
|
return self.message |
||||
|
|
||||
|
def show(self, file=None): |
||||
|
if file is None: |
||||
|
file = get_text_stderr() |
||||
|
echo('Error: %s' % self.format_message(), file=file) |
||||
|
|
||||
|
|
||||
|
class UsageError(ClickException): |
||||
|
"""An internal exception that signals a usage error. This typically |
||||
|
aborts any further handling. |
||||
|
|
||||
|
:param message: the error message to display. |
||||
|
:param ctx: optionally the context that caused this error. Click will |
||||
|
fill in the context automatically in some situations. |
||||
|
""" |
||||
|
exit_code = 2 |
||||
|
|
||||
|
def __init__(self, message, ctx=None): |
||||
|
ClickException.__init__(self, message) |
||||
|
self.ctx = ctx |
||||
|
|
||||
|
def show(self, file=None): |
||||
|
if file is None: |
||||
|
file = get_text_stderr() |
||||
|
color = None |
||||
|
if self.ctx is not None: |
||||
|
color = self.ctx.color |
||||
|
echo(self.ctx.get_usage() + '\n', file=file, color=color) |
||||
|
echo('Error: %s' % self.format_message(), file=file, color=color) |
||||
|
|
||||
|
|
||||
|
class BadParameter(UsageError): |
||||
|
"""An exception that formats out a standardized error message for a |
||||
|
bad parameter. This is useful when thrown from a callback or type as |
||||
|
Click will attach contextual information to it (for instance, which |
||||
|
parameter it is). |
||||
|
|
||||
|
.. versionadded:: 2.0 |
||||
|
|
||||
|
:param param: the parameter object that caused this error. This can |
||||
|
be left out, and Click will attach this info itself |
||||
|
if possible. |
||||
|
:param param_hint: a string that shows up as parameter name. This |
||||
|
can be used as alternative to `param` in cases |
||||
|
where custom validation should happen. If it is |
||||
|
a string it's used as such, if it's a list then |
||||
|
each item is quoted and separated. |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, message, ctx=None, param=None, |
||||
|
param_hint=None): |
||||
|
UsageError.__init__(self, message, ctx) |
||||
|
self.param = param |
||||
|
self.param_hint = param_hint |
||||
|
|
||||
|
def format_message(self): |
||||
|
if self.param_hint is not None: |
||||
|
param_hint = self.param_hint |
||||
|
elif self.param is not None: |
||||
|
param_hint = self.param.opts or [self.param.human_readable_name] |
||||
|
else: |
||||
|
return 'Invalid value: %s' % self.message |
||||
|
if isinstance(param_hint, (tuple, list)): |
||||
|
param_hint = ' / '.join('"%s"' % x for x in param_hint) |
||||
|
return 'Invalid value for %s: %s' % (param_hint, self.message) |
||||
|
|
||||
|
|
||||
|
class MissingParameter(BadParameter): |
||||
|
"""Raised if click required an option or argument but it was not |
||||
|
provided when invoking the script. |
||||
|
|
||||
|
.. versionadded:: 4.0 |
||||
|
|
||||
|
:param param_type: a string that indicates the type of the parameter. |
||||
|
The default is to inherit the parameter type from |
||||
|
the given `param`. Valid values are ``'parameter'``, |
||||
|
``'option'`` or ``'argument'``. |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, message=None, ctx=None, param=None, |
||||
|
param_hint=None, param_type=None): |
||||
|
BadParameter.__init__(self, message, ctx, param, param_hint) |
||||
|
self.param_type = param_type |
||||
|
|
||||
|
def format_message(self): |
||||
|
if self.param_hint is not None: |
||||
|
param_hint = self.param_hint |
||||
|
elif self.param is not None: |
||||
|
param_hint = self.param.opts or [self.param.human_readable_name] |
||||
|
else: |
||||
|
param_hint = None |
||||
|
if isinstance(param_hint, (tuple, list)): |
||||
|
param_hint = ' / '.join('"%s"' % x for x in param_hint) |
||||
|
|
||||
|
param_type = self.param_type |
||||
|
if param_type is None and self.param is not None: |
||||
|
param_type = self.param.param_type_name |
||||
|
|
||||
|
msg = self.message |
||||
|
if self.param is not None: |
||||
|
msg_extra = self.param.type.get_missing_message(self.param) |
||||
|
if msg_extra: |
||||
|
if msg: |
||||
|
msg += '. ' + msg_extra |
||||
|
else: |
||||
|
msg = msg_extra |
||||
|
|
||||
|
return 'Missing %s%s%s%s' % ( |
||||
|
param_type, |
||||
|
param_hint and ' %s' % param_hint or '', |
||||
|
msg and '. ' or '.', |
||||
|
msg or '', |
||||
|
) |
||||
|
|
||||
|
|
||||
|
class NoSuchOption(UsageError): |
||||
|
"""Raised if click attempted to handle an option that does not |
||||
|
exist. |
||||
|
|
||||
|
.. versionadded:: 4.0 |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, option_name, message=None, possibilities=None, |
||||
|
ctx=None): |
||||
|
if message is None: |
||||
|
message = 'no such option: %s' % option_name |
||||
|
UsageError.__init__(self, message, ctx) |
||||
|
self.option_name = option_name |
||||
|
self.possibilities = possibilities |
||||
|
|
||||
|
def format_message(self): |
||||
|
bits = [self.message] |
||||
|
if self.possibilities: |
||||
|
if len(self.possibilities) == 1: |
||||
|
bits.append('Did you mean %s?' % self.possibilities[0]) |
||||
|
else: |
||||
|
possibilities = sorted(self.possibilities) |
||||
|
bits.append('(Possible options: %s)' % ', '.join(possibilities)) |
||||
|
return ' '.join(bits) |
||||
|
|
||||
|
|
||||
|
class BadOptionUsage(UsageError): |
||||
|
"""Raised if an option is generally supplied but the use of the option |
||||
|
was incorrect. This is for instance raised if the number of arguments |
||||
|
for an option is not correct. |
||||
|
|
||||
|
.. versionadded:: 4.0 |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, message, ctx=None): |
||||
|
UsageError.__init__(self, message, ctx) |
||||
|
|
||||
|
|
||||
|
class BadArgumentUsage(UsageError): |
||||
|
"""Raised if an argument is generally supplied but the use of the argument |
||||
|
was incorrect. This is for instance raised if the number of values |
||||
|
for an argument is not correct. |
||||
|
|
||||
|
.. versionadded:: 6.0 |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, message, ctx=None): |
||||
|
UsageError.__init__(self, message, ctx) |
||||
|
|
||||
|
|
||||
|
class FileError(ClickException): |
||||
|
"""Raised if a file cannot be opened.""" |
||||
|
|
||||
|
def __init__(self, filename, hint=None): |
||||
|
ui_filename = filename_to_ui(filename) |
||||
|
if hint is None: |
||||
|
hint = 'unknown error' |
||||
|
ClickException.__init__(self, hint) |
||||
|
self.ui_filename = ui_filename |
||||
|
self.filename = filename |
||||
|
|
||||
|
def format_message(self): |
||||
|
return 'Could not open file %s: %s' % (self.ui_filename, self.message) |
||||
|
|
||||
|
|
||||
|
class Abort(RuntimeError): |
||||
|
"""An internal signalling exception that signals Click to abort.""" |
@ -0,0 +1,256 @@ |
|||||
|
from contextlib import contextmanager |
||||
|
from .termui import get_terminal_size |
||||
|
from .parser import split_opt |
||||
|
from ._compat import term_len |
||||
|
|
||||
|
|
||||
|
# Can force a width. This is used by the test system |
||||
|
FORCED_WIDTH = None |
||||
|
|
||||
|
|
||||
|
def measure_table(rows): |
||||
|
widths = {} |
||||
|
for row in rows: |
||||
|
for idx, col in enumerate(row): |
||||
|
widths[idx] = max(widths.get(idx, 0), term_len(col)) |
||||
|
return tuple(y for x, y in sorted(widths.items())) |
||||
|
|
||||
|
|
||||
|
def iter_rows(rows, col_count): |
||||
|
for row in rows: |
||||
|
row = tuple(row) |
||||
|
yield row + ('',) * (col_count - len(row)) |
||||
|
|
||||
|
|
||||
|
def wrap_text(text, width=78, initial_indent='', subsequent_indent='', |
||||
|
preserve_paragraphs=False): |
||||
|
"""A helper function that intelligently wraps text. By default, it |
||||
|
assumes that it operates on a single paragraph of text but if the |
||||
|
`preserve_paragraphs` parameter is provided it will intelligently |
||||
|
handle paragraphs (defined by two empty lines). |
||||
|
|
||||
|
If paragraphs are handled, a paragraph can be prefixed with an empty |
||||
|
line containing the ``\\b`` character (``\\x08``) to indicate that |
||||
|
no rewrapping should happen in that block. |
||||
|
|
||||
|
:param text: the text that should be rewrapped. |
||||
|
:param width: the maximum width for the text. |
||||
|
:param initial_indent: the initial indent that should be placed on the |
||||
|
first line as a string. |
||||
|
:param subsequent_indent: the indent string that should be placed on |
||||
|
each consecutive line. |
||||
|
:param preserve_paragraphs: if this flag is set then the wrapping will |
||||
|
intelligently handle paragraphs. |
||||
|
""" |
||||
|
from ._textwrap import TextWrapper |
||||
|
text = text.expandtabs() |
||||
|
wrapper = TextWrapper(width, initial_indent=initial_indent, |
||||
|
subsequent_indent=subsequent_indent, |
||||
|
replace_whitespace=False) |
||||
|
if not preserve_paragraphs: |
||||
|
return wrapper.fill(text) |
||||
|
|
||||
|
p = [] |
||||
|
buf = [] |
||||
|
indent = None |
||||
|
|
||||
|
def _flush_par(): |
||||
|
if not buf: |
||||
|
return |
||||
|
if buf[0].strip() == '\b': |
||||
|
p.append((indent or 0, True, '\n'.join(buf[1:]))) |
||||
|
else: |
||||
|
p.append((indent or 0, False, ' '.join(buf))) |
||||
|
del buf[:] |
||||
|
|
||||
|
for line in text.splitlines(): |
||||
|
if not line: |
||||
|
_flush_par() |
||||
|
indent = None |
||||
|
else: |
||||
|
if indent is None: |
||||
|
orig_len = term_len(line) |
||||
|
line = line.lstrip() |
||||
|
indent = orig_len - term_len(line) |
||||
|
buf.append(line) |
||||
|
_flush_par() |
||||
|
|
||||
|
rv = [] |
||||
|
for indent, raw, text in p: |
||||
|
with wrapper.extra_indent(' ' * indent): |
||||
|
if raw: |
||||
|
rv.append(wrapper.indent_only(text)) |
||||
|
else: |
||||
|
rv.append(wrapper.fill(text)) |
||||
|
|
||||
|
return '\n\n'.join(rv) |
||||
|
|
||||
|
|
||||
|
class HelpFormatter(object): |
||||
|
"""This class helps with formatting text-based help pages. It's |
||||
|
usually just needed for very special internal cases, but it's also |
||||
|
exposed so that developers can write their own fancy outputs. |
||||
|
|
||||
|
At present, it always writes into memory. |
||||
|
|
||||
|
:param indent_increment: the additional increment for each level. |
||||
|
:param width: the width for the text. This defaults to the terminal |
||||
|
width clamped to a maximum of 78. |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, indent_increment=2, width=None, max_width=None): |
||||
|
self.indent_increment = indent_increment |
||||
|
if max_width is None: |
||||
|
max_width = 80 |
||||
|
if width is None: |
||||
|
width = FORCED_WIDTH |
||||
|
if width is None: |
||||
|
width = max(min(get_terminal_size()[0], max_width) - 2, 50) |
||||
|
self.width = width |
||||
|
self.current_indent = 0 |
||||
|
self.buffer = [] |
||||
|
|
||||
|
def write(self, string): |
||||
|
"""Writes a unicode string into the internal buffer.""" |
||||
|
self.buffer.append(string) |
||||
|
|
||||
|
def indent(self): |
||||
|
"""Increases the indentation.""" |
||||
|
self.current_indent += self.indent_increment |
||||
|
|
||||
|
def dedent(self): |
||||
|
"""Decreases the indentation.""" |
||||
|
self.current_indent -= self.indent_increment |
||||
|
|
||||
|
def write_usage(self, prog, args='', prefix='Usage: '): |
||||
|
"""Writes a usage line into the buffer. |
||||
|
|
||||
|
:param prog: the program name. |
||||
|
:param args: whitespace separated list of arguments. |
||||
|
:param prefix: the prefix for the first line. |
||||
|
""" |
||||
|
usage_prefix = '%*s%s ' % (self.current_indent, prefix, prog) |
||||
|
text_width = self.width - self.current_indent |
||||
|
|
||||
|
if text_width >= (term_len(usage_prefix) + 20): |
||||
|
# The arguments will fit to the right of the prefix. |
||||
|
indent = ' ' * term_len(usage_prefix) |
||||
|
self.write(wrap_text(args, text_width, |
||||
|
initial_indent=usage_prefix, |
||||
|
subsequent_indent=indent)) |
||||
|
else: |
||||
|
# The prefix is too long, put the arguments on the next line. |
||||
|
self.write(usage_prefix) |
||||
|
self.write('\n') |
||||
|
indent = ' ' * (max(self.current_indent, term_len(prefix)) + 4) |
||||
|
self.write(wrap_text(args, text_width, |
||||
|
initial_indent=indent, |
||||
|
subsequent_indent=indent)) |
||||
|
|
||||
|
self.write('\n') |
||||
|
|
||||
|
def write_heading(self, heading): |
||||
|
"""Writes a heading into the buffer.""" |
||||
|
self.write('%*s%s:\n' % (self.current_indent, '', heading)) |
||||
|
|
||||
|
def write_paragraph(self): |
||||
|
"""Writes a paragraph into the buffer.""" |
||||
|
if self.buffer: |
||||
|
self.write('\n') |
||||
|
|
||||
|
def write_text(self, text): |
||||
|
"""Writes re-indented text into the buffer. This rewraps and |
||||
|
preserves paragraphs. |
||||
|
""" |
||||
|
text_width = max(self.width - self.current_indent, 11) |
||||
|
indent = ' ' * self.current_indent |
||||
|
self.write(wrap_text(text, text_width, |
||||
|
initial_indent=indent, |
||||
|
subsequent_indent=indent, |
||||
|
preserve_paragraphs=True)) |
||||
|
self.write('\n') |
||||
|
|
||||
|
def write_dl(self, rows, col_max=30, col_spacing=2): |
||||
|
"""Writes a definition list into the buffer. This is how options |
||||
|
and commands are usually formatted. |
||||
|
|
||||
|
:param rows: a list of two item tuples for the terms and values. |
||||
|
:param col_max: the maximum width of the first column. |
||||
|
:param col_spacing: the number of spaces between the first and |
||||
|
second column. |
||||
|
""" |
||||
|
rows = list(rows) |
||||
|
widths = measure_table(rows) |
||||
|
if len(widths) != 2: |
||||
|
raise TypeError('Expected two columns for definition list') |
||||
|
|
||||
|
first_col = min(widths[0], col_max) + col_spacing |
||||
|
|
||||
|
for first, second in iter_rows(rows, len(widths)): |
||||
|
self.write('%*s%s' % (self.current_indent, '', first)) |
||||
|
if not second: |
||||
|
self.write('\n') |
||||
|
continue |
||||
|
if term_len(first) <= first_col - col_spacing: |
||||
|
self.write(' ' * (first_col - term_len(first))) |
||||
|
else: |
||||
|
self.write('\n') |
||||
|
self.write(' ' * (first_col + self.current_indent)) |
||||
|
|
||||
|
text_width = max(self.width - first_col - 2, 10) |
||||
|
lines = iter(wrap_text(second, text_width).splitlines()) |
||||
|
if lines: |
||||
|
self.write(next(lines) + '\n') |
||||
|
for line in lines: |
||||
|
self.write('%*s%s\n' % ( |
||||
|
first_col + self.current_indent, '', line)) |
||||
|
else: |
||||
|
self.write('\n') |
||||
|
|
||||
|
@contextmanager |
||||
|
def section(self, name): |
||||
|
"""Helpful context manager that writes a paragraph, a heading, |
||||
|
and the indents. |
||||
|
|
||||
|
:param name: the section name that is written as heading. |
||||
|
""" |
||||
|
self.write_paragraph() |
||||
|
self.write_heading(name) |
||||
|
self.indent() |
||||
|
try: |
||||
|
yield |
||||
|
finally: |
||||
|
self.dedent() |
||||
|
|
||||
|
@contextmanager |
||||
|
def indentation(self): |
||||
|
"""A context manager that increases the indentation.""" |
||||
|
self.indent() |
||||
|
try: |
||||
|
yield |
||||
|
finally: |
||||
|
self.dedent() |
||||
|
|
||||
|
def getvalue(self): |
||||
|
"""Returns the buffer contents.""" |
||||
|
return ''.join(self.buffer) |
||||
|
|
||||
|
|
||||
|
def join_options(options): |
||||
|
"""Given a list of option strings this joins them in the most appropriate |
||||
|
way and returns them in the form ``(formatted_string, |
||||
|
any_prefix_is_slash)`` where the second item in the tuple is a flag that |
||||
|
indicates if any of the option prefixes was a slash. |
||||
|
""" |
||||
|
rv = [] |
||||
|
any_prefix_is_slash = False |
||||
|
for opt in options: |
||||
|
prefix = split_opt(opt)[0] |
||||
|
if prefix == '/': |
||||
|
any_prefix_is_slash = True |
||||
|
rv.append((len(prefix), opt)) |
||||
|
|
||||
|
rv.sort(key=lambda x: x[0]) |
||||
|
|
||||
|
rv = ', '.join(x[1] for x in rv) |
||||
|
return rv, any_prefix_is_slash |
@ -0,0 +1,48 @@ |
|||||
|
from threading import local |
||||
|
|
||||
|
|
||||
|
_local = local() |
||||
|
|
||||
|
|
||||
|
def get_current_context(silent=False): |
||||
|
"""Returns the current click context. This can be used as a way to |
||||
|
access the current context object from anywhere. This is a more implicit |
||||
|
alternative to the :func:`pass_context` decorator. This function is |
||||
|
primarily useful for helpers such as :func:`echo` which might be |
||||
|
interested in changing it's behavior based on the current context. |
||||
|
|
||||
|
To push the current context, :meth:`Context.scope` can be used. |
||||
|
|
||||
|
.. versionadded:: 5.0 |
||||
|
|
||||
|
:param silent: is set to `True` the return value is `None` if no context |
||||
|
is available. The default behavior is to raise a |
||||
|
:exc:`RuntimeError`. |
||||
|
""" |
||||
|
try: |
||||
|
return getattr(_local, 'stack')[-1] |
||||
|
except (AttributeError, IndexError): |
||||
|
if not silent: |
||||
|
raise RuntimeError('There is no active click context.') |
||||
|
|
||||
|
|
||||
|
def push_context(ctx): |
||||
|
"""Pushes a new context to the current stack.""" |
||||
|
_local.__dict__.setdefault('stack', []).append(ctx) |
||||
|
|
||||
|
|
||||
|
def pop_context(): |
||||
|
"""Removes the top level from the stack.""" |
||||
|
_local.stack.pop() |
||||
|
|
||||
|
|
||||
|
def resolve_color_default(color=None): |
||||
|
""""Internal helper to get the default value of the color flag. If a |
||||
|
value is passed it's returned unchanged, otherwise it's looked up from |
||||
|
the current context. |
||||
|
""" |
||||
|
if color is not None: |
||||
|
return color |
||||
|
ctx = get_current_context(silent=True) |
||||
|
if ctx is not None: |
||||
|
return ctx.color |
@ -0,0 +1,426 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
""" |
||||
|
click.parser |
||||
|
~~~~~~~~~~~~ |
||||
|
|
||||
|
This module started out as largely a copy paste from the stdlib's |
||||
|
optparse module with the features removed that we do not need from |
||||
|
optparse because we implement them in Click on a higher level (for |
||||
|
instance type handling, help formatting and a lot more). |
||||
|
|
||||
|
The plan is to remove more and more from here over time. |
||||
|
|
||||
|
The reason this is a different module and not optparse from the stdlib |
||||
|
is that there are differences in 2.x and 3.x about the error messages |
||||
|
generated and optparse in the stdlib uses gettext for no good reason |
||||
|
and might cause us issues. |
||||
|
""" |
||||
|
import re |
||||
|
from collections import deque |
||||
|
from .exceptions import UsageError, NoSuchOption, BadOptionUsage, \ |
||||
|
BadArgumentUsage |
||||
|
|
||||
|
|
||||
|
def _unpack_args(args, nargs_spec): |
||||
|
"""Given an iterable of arguments and an iterable of nargs specifications, |
||||
|
it returns a tuple with all the unpacked arguments at the first index |
||||
|
and all remaining arguments as the second. |
||||
|
|
||||
|
The nargs specification is the number of arguments that should be consumed |
||||
|
or `-1` to indicate that this position should eat up all the remainders. |
||||
|
|
||||
|
Missing items are filled with `None`. |
||||
|
""" |
||||
|
args = deque(args) |
||||
|
nargs_spec = deque(nargs_spec) |
||||
|
rv = [] |
||||
|
spos = None |
||||
|
|
||||
|
def _fetch(c): |
||||
|
try: |
||||
|
if spos is None: |
||||
|
return c.popleft() |
||||
|
else: |
||||
|
return c.pop() |
||||
|
except IndexError: |
||||
|
return None |
||||
|
|
||||
|
while nargs_spec: |
||||
|
nargs = _fetch(nargs_spec) |
||||
|
if nargs == 1: |
||||
|
rv.append(_fetch(args)) |
||||
|
elif nargs > 1: |
||||
|
x = [_fetch(args) for _ in range(nargs)] |
||||
|
# If we're reversed, we're pulling in the arguments in reverse, |
||||
|
# so we need to turn them around. |
||||
|
if spos is not None: |
||||
|
x.reverse() |
||||
|
rv.append(tuple(x)) |
||||
|
elif nargs < 0: |
||||
|
if spos is not None: |
||||
|
raise TypeError('Cannot have two nargs < 0') |
||||
|
spos = len(rv) |
||||
|
rv.append(None) |
||||
|
|
||||
|
# spos is the position of the wildcard (star). If it's not `None`, |
||||
|
# we fill it with the remainder. |
||||
|
if spos is not None: |
||||
|
rv[spos] = tuple(args) |
||||
|
args = [] |
||||
|
rv[spos + 1:] = reversed(rv[spos + 1:]) |
||||
|
|
||||
|
return tuple(rv), list(args) |
||||
|
|
||||
|
|
||||
|
def _error_opt_args(nargs, opt): |
||||
|
if nargs == 1: |
||||
|
raise BadOptionUsage('%s option requires an argument' % opt) |
||||
|
raise BadOptionUsage('%s option requires %d arguments' % (opt, nargs)) |
||||
|
|
||||
|
|
||||
|
def split_opt(opt): |
||||
|
first = opt[:1] |
||||
|
if first.isalnum(): |
||||
|
return '', opt |
||||
|
if opt[1:2] == first: |
||||
|
return opt[:2], opt[2:] |
||||
|
return first, opt[1:] |
||||
|
|
||||
|
|
||||
|
def normalize_opt(opt, ctx): |
||||
|
if ctx is None or ctx.token_normalize_func is None: |
||||
|
return opt |
||||
|
prefix, opt = split_opt(opt) |
||||
|
return prefix + ctx.token_normalize_func(opt) |
||||
|
|
||||
|
|
||||
|
def split_arg_string(string): |
||||
|
"""Given an argument string this attempts to split it into small parts.""" |
||||
|
rv = [] |
||||
|
for match in re.finditer(r"('([^'\\]*(?:\\.[^'\\]*)*)'" |
||||
|
r'|"([^"\\]*(?:\\.[^"\\]*)*)"' |
||||
|
r'|\S+)\s*', string, re.S): |
||||
|
arg = match.group().strip() |
||||
|
if arg[:1] == arg[-1:] and arg[:1] in '"\'': |
||||
|
arg = arg[1:-1].encode('ascii', 'backslashreplace') \ |
||||
|
.decode('unicode-escape') |
||||
|
try: |
||||
|
arg = type(string)(arg) |
||||
|
except UnicodeError: |
||||
|
pass |
||||
|
rv.append(arg) |
||||
|
return rv |
||||
|
|
||||
|
|
||||
|
class Option(object): |
||||
|
|
||||
|
def __init__(self, opts, dest, action=None, nargs=1, const=None, obj=None): |
||||
|
self._short_opts = [] |
||||
|
self._long_opts = [] |
||||
|
self.prefixes = set() |
||||
|
|
||||
|
for opt in opts: |
||||
|
prefix, value = split_opt(opt) |
||||
|
if not prefix: |
||||
|
raise ValueError('Invalid start character for option (%s)' |
||||
|
% opt) |
||||
|
self.prefixes.add(prefix[0]) |
||||
|
if len(prefix) == 1 and len(value) == 1: |
||||
|
self._short_opts.append(opt) |
||||
|
else: |
||||
|
self._long_opts.append(opt) |
||||
|
self.prefixes.add(prefix) |
||||
|
|
||||
|
if action is None: |
||||
|
action = 'store' |
||||
|
|
||||
|
self.dest = dest |
||||
|
self.action = action |
||||
|
self.nargs = nargs |
||||
|
self.const = const |
||||
|
self.obj = obj |
||||
|
|
||||
|
@property |
||||
|
def takes_value(self): |
||||
|
return self.action in ('store', 'append') |
||||
|
|
||||
|
def process(self, value, state): |
||||
|
if self.action == 'store': |
||||
|
state.opts[self.dest] = value |
||||
|
elif self.action == 'store_const': |
||||
|
state.opts[self.dest] = self.const |
||||
|
elif self.action == 'append': |
||||
|
state.opts.setdefault(self.dest, []).append(value) |
||||
|
elif self.action == 'append_const': |
||||
|
state.opts.setdefault(self.dest, []).append(self.const) |
||||
|
elif self.action == 'count': |
||||
|
state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 |
||||
|
else: |
||||
|
raise ValueError('unknown action %r' % self.action) |
||||
|
state.order.append(self.obj) |
||||
|
|
||||
|
|
||||
|
class Argument(object): |
||||
|
|
||||
|
def __init__(self, dest, nargs=1, obj=None): |
||||
|
self.dest = dest |
||||
|
self.nargs = nargs |
||||
|
self.obj = obj |
||||
|
|
||||
|
def process(self, value, state): |
||||
|
if self.nargs > 1: |
||||
|
holes = sum(1 for x in value if x is None) |
||||
|
if holes == len(value): |
||||
|
value = None |
||||
|
elif holes != 0: |
||||
|
raise BadArgumentUsage('argument %s takes %d values' |
||||
|
% (self.dest, self.nargs)) |
||||
|
state.opts[self.dest] = value |
||||
|
state.order.append(self.obj) |
||||
|
|
||||
|
|
||||
|
class ParsingState(object): |
||||
|
|
||||
|
def __init__(self, rargs): |
||||
|
self.opts = {} |
||||
|
self.largs = [] |
||||
|
self.rargs = rargs |
||||
|
self.order = [] |
||||
|
|
||||
|
|
||||
|
class OptionParser(object): |
||||
|
"""The option parser is an internal class that is ultimately used to |
||||
|
parse options and arguments. It's modelled after optparse and brings |
||||
|
a similar but vastly simplified API. It should generally not be used |
||||
|
directly as the high level Click classes wrap it for you. |
||||
|
|
||||
|
It's not nearly as extensible as optparse or argparse as it does not |
||||
|
implement features that are implemented on a higher level (such as |
||||
|
types or defaults). |
||||
|
|
||||
|
:param ctx: optionally the :class:`~click.Context` where this parser |
||||
|
should go with. |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, ctx=None): |
||||
|
#: The :class:`~click.Context` for this parser. This might be |
||||
|
#: `None` for some advanced use cases. |
||||
|
self.ctx = ctx |
||||
|
#: This controls how the parser deals with interspersed arguments. |
||||
|
#: If this is set to `False`, the parser will stop on the first |
||||
|
#: non-option. Click uses this to implement nested subcommands |
||||
|
#: safely. |
||||
|
self.allow_interspersed_args = True |
||||
|
#: This tells the parser how to deal with unknown options. By |
||||
|
#: default it will error out (which is sensible), but there is a |
||||
|
#: second mode where it will ignore it and continue processing |
||||
|
#: after shifting all the unknown options into the resulting args. |
||||
|
self.ignore_unknown_options = False |
||||
|
if ctx is not None: |
||||
|
self.allow_interspersed_args = ctx.allow_interspersed_args |
||||
|
self.ignore_unknown_options = ctx.ignore_unknown_options |
||||
|
self._short_opt = {} |
||||
|
self._long_opt = {} |
||||
|
self._opt_prefixes = set(['-', '--']) |
||||
|
self._args = [] |
||||
|
|
||||
|
def add_option(self, opts, dest, action=None, nargs=1, const=None, |
||||
|
obj=None): |
||||
|
"""Adds a new option named `dest` to the parser. The destination |
||||
|
is not inferred (unlike with optparse) and needs to be explicitly |
||||
|
provided. Action can be any of ``store``, ``store_const``, |
||||
|
``append``, ``appnd_const`` or ``count``. |
||||
|
|
||||
|
The `obj` can be used to identify the option in the order list |
||||
|
that is returned from the parser. |
||||
|
""" |
||||
|
if obj is None: |
||||
|
obj = dest |
||||
|
opts = [normalize_opt(opt, self.ctx) for opt in opts] |
||||
|
option = Option(opts, dest, action=action, nargs=nargs, |
||||
|
const=const, obj=obj) |
||||
|
self._opt_prefixes.update(option.prefixes) |
||||
|
for opt in option._short_opts: |
||||
|
self._short_opt[opt] = option |
||||
|
for opt in option._long_opts: |
||||
|
self._long_opt[opt] = option |
||||
|
|
||||
|
def add_argument(self, dest, nargs=1, obj=None): |
||||
|
"""Adds a positional argument named `dest` to the parser. |
||||
|
|
||||
|
The `obj` can be used to identify the option in the order list |
||||
|
that is returned from the parser. |
||||
|
""" |
||||
|
if obj is None: |
||||
|
obj = dest |
||||
|
self._args.append(Argument(dest=dest, nargs=nargs, obj=obj)) |
||||
|
|
||||
|
def parse_args(self, args): |
||||
|
"""Parses positional arguments and returns ``(values, args, order)`` |
||||
|
for the parsed options and arguments as well as the leftover |
||||
|
arguments if there are any. The order is a list of objects as they |
||||
|
appear on the command line. If arguments appear multiple times they |
||||
|
will be memorized multiple times as well. |
||||
|
""" |
||||
|
state = ParsingState(args) |
||||
|
try: |
||||
|
self._process_args_for_options(state) |
||||
|
self._process_args_for_args(state) |
||||
|
except UsageError: |
||||
|
if self.ctx is None or not self.ctx.resilient_parsing: |
||||
|
raise |
||||
|
return state.opts, state.largs, state.order |
||||
|
|
||||
|
def _process_args_for_args(self, state): |
||||
|
pargs, args = _unpack_args(state.largs + state.rargs, |
||||
|
[x.nargs for x in self._args]) |
||||
|
|
||||
|
for idx, arg in enumerate(self._args): |
||||
|
arg.process(pargs[idx], state) |
||||
|
|
||||
|
state.largs = args |
||||
|
state.rargs = [] |
||||
|
|
||||
|
def _process_args_for_options(self, state): |
||||
|
while state.rargs: |
||||
|
arg = state.rargs.pop(0) |
||||
|
arglen = len(arg) |
||||
|
# Double dashes always handled explicitly regardless of what |
||||
|
# prefixes are valid. |
||||
|
if arg == '--': |
||||
|
return |
||||
|
elif arg[:1] in self._opt_prefixes and arglen > 1: |
||||
|
self._process_opts(arg, state) |
||||
|
elif self.allow_interspersed_args: |
||||
|
state.largs.append(arg) |
||||
|
else: |
||||
|
state.rargs.insert(0, arg) |
||||
|
return |
||||
|
|
||||
|
# Say this is the original argument list: |
||||
|
# [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] |
||||
|
# ^ |
||||
|
# (we are about to process arg(i)). |
||||
|
# |
||||
|
# Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of |
||||
|
# [arg0, ..., arg(i-1)] (any options and their arguments will have |
||||
|
# been removed from largs). |
||||
|
# |
||||
|
# The while loop will usually consume 1 or more arguments per pass. |
||||
|
# If it consumes 1 (eg. arg is an option that takes no arguments), |
||||
|
# then after _process_arg() is done the situation is: |
||||
|
# |
||||
|
# largs = subset of [arg0, ..., arg(i)] |
||||
|
# rargs = [arg(i+1), ..., arg(N-1)] |
||||
|
# |
||||
|
# If allow_interspersed_args is false, largs will always be |
||||
|
# *empty* -- still a subset of [arg0, ..., arg(i-1)], but |
||||
|
# not a very interesting subset! |
||||
|
|
||||
|
def _match_long_opt(self, opt, explicit_value, state): |
||||
|
if opt not in self._long_opt: |
||||
|
possibilities = [word for word in self._long_opt |
||||
|
if word.startswith(opt)] |
||||
|
raise NoSuchOption(opt, possibilities=possibilities) |
||||
|
|
||||
|
option = self._long_opt[opt] |
||||
|
if option.takes_value: |
||||
|
# At this point it's safe to modify rargs by injecting the |
||||
|
# explicit value, because no exception is raised in this |
||||
|
# branch. This means that the inserted value will be fully |
||||
|
# consumed. |
||||
|
if explicit_value is not None: |
||||
|
state.rargs.insert(0, explicit_value) |
||||
|
|
||||
|
nargs = option.nargs |
||||
|
if len(state.rargs) < nargs: |
||||
|
_error_opt_args(nargs, opt) |
||||
|
elif nargs == 1: |
||||
|
value = state.rargs.pop(0) |
||||
|
else: |
||||
|
value = tuple(state.rargs[:nargs]) |
||||
|
del state.rargs[:nargs] |
||||
|
|
||||
|
elif explicit_value is not None: |
||||
|
raise BadOptionUsage('%s option does not take a value' % opt) |
||||
|
|
||||
|
else: |
||||
|
value = None |
||||
|
|
||||
|
option.process(value, state) |
||||
|
|
||||
|
def _match_short_opt(self, arg, state): |
||||
|
stop = False |
||||
|
i = 1 |
||||
|
prefix = arg[0] |
||||
|
unknown_options = [] |
||||
|
|
||||
|
for ch in arg[1:]: |
||||
|
opt = normalize_opt(prefix + ch, self.ctx) |
||||
|
option = self._short_opt.get(opt) |
||||
|
i += 1 |
||||
|
|
||||
|
if not option: |
||||
|
if self.ignore_unknown_options: |
||||
|
unknown_options.append(ch) |
||||
|
continue |
||||
|
raise NoSuchOption(opt) |
||||
|
if option.takes_value: |
||||
|
# Any characters left in arg? Pretend they're the |
||||
|
# next arg, and stop consuming characters of arg. |
||||
|
if i < len(arg): |
||||
|
state.rargs.insert(0, arg[i:]) |
||||
|
stop = True |
||||
|
|
||||
|
nargs = option.nargs |
||||
|
if len(state.rargs) < nargs: |
||||
|
_error_opt_args(nargs, opt) |
||||
|
elif nargs == 1: |
||||
|
value = state.rargs.pop(0) |
||||
|
else: |
||||
|
value = tuple(state.rargs[:nargs]) |
||||
|
del state.rargs[:nargs] |
||||
|
|
||||
|
else: |
||||
|
value = None |
||||
|
|
||||
|
option.process(value, state) |
||||
|
|
||||
|
if stop: |
||||
|
break |
||||
|
|
||||
|
# If we got any unknown options we re-combinate the string of the |
||||
|
# remaining options and re-attach the prefix, then report that |
||||
|
# to the state as new larg. This way there is basic combinatorics |
||||
|
# that can be achieved while still ignoring unknown arguments. |
||||
|
if self.ignore_unknown_options and unknown_options: |
||||
|
state.largs.append(prefix + ''.join(unknown_options)) |
||||
|
|
||||
|
def _process_opts(self, arg, state): |
||||
|
explicit_value = None |
||||
|
# Long option handling happens in two parts. The first part is |
||||
|
# supporting explicitly attached values. In any case, we will try |
||||
|
# to long match the option first. |
||||
|
if '=' in arg: |
||||
|
long_opt, explicit_value = arg.split('=', 1) |
||||
|
else: |
||||
|
long_opt = arg |
||||
|
norm_long_opt = normalize_opt(long_opt, self.ctx) |
||||
|
|
||||
|
# At this point we will match the (assumed) long option through |
||||
|
# the long option matching code. Note that this allows options |
||||
|
# like "-foo" to be matched as long options. |
||||
|
try: |
||||
|
self._match_long_opt(norm_long_opt, explicit_value, state) |
||||
|
except NoSuchOption: |
||||
|
# At this point the long option matching failed, and we need |
||||
|
# to try with short options. However there is a special rule |
||||
|
# which says, that if we have a two character options prefix |
||||
|
# (applies to "--foo" for instance), we do not dispatch to the |
||||
|
# short option code and will instead raise the no option |
||||
|
# error. |
||||
|
if arg[:2] not in self._opt_prefixes: |
||||
|
return self._match_short_opt(arg, state) |
||||
|
if not self.ignore_unknown_options: |
||||
|
raise |
||||
|
state.largs.append(arg) |
@ -0,0 +1,539 @@ |
|||||
|
import os |
||||
|
import sys |
||||
|
import struct |
||||
|
|
||||
|
from ._compat import raw_input, text_type, string_types, \ |
||||
|
isatty, strip_ansi, get_winterm_size, DEFAULT_COLUMNS, WIN |
||||
|
from .utils import echo |
||||
|
from .exceptions import Abort, UsageError |
||||
|
from .types import convert_type |
||||
|
from .globals import resolve_color_default |
||||
|
|
||||
|
|
||||
|
# The prompt functions to use. The doc tools currently override these |
||||
|
# functions to customize how they work. |
||||
|
visible_prompt_func = raw_input |
||||
|
|
||||
|
_ansi_colors = ('black', 'red', 'green', 'yellow', 'blue', 'magenta', |
||||
|
'cyan', 'white', 'reset') |
||||
|
_ansi_reset_all = '\033[0m' |
||||
|
|
||||
|
|
||||
|
def hidden_prompt_func(prompt): |
||||
|
import getpass |
||||
|
return getpass.getpass(prompt) |
||||
|
|
||||
|
|
||||
|
def _build_prompt(text, suffix, show_default=False, default=None): |
||||
|
prompt = text |
||||
|
if default is not None and show_default: |
||||
|
prompt = '%s [%s]' % (prompt, default) |
||||
|
return prompt + suffix |
||||
|
|
||||
|
|
||||
|
def prompt(text, default=None, hide_input=False, |
||||
|
confirmation_prompt=False, type=None, |
||||
|
value_proc=None, prompt_suffix=': ', |
||||
|
show_default=True, err=False): |
||||
|
"""Prompts a user for input. This is a convenience function that can |
||||
|
be used to prompt a user for input later. |
||||
|
|
||||
|
If the user aborts the input by sending a interrupt signal, this |
||||
|
function will catch it and raise a :exc:`Abort` exception. |
||||
|
|
||||
|
.. versionadded:: 6.0 |
||||
|
Added unicode support for cmd.exe on Windows. |
||||
|
|
||||
|
.. versionadded:: 4.0 |
||||
|
Added the `err` parameter. |
||||
|
|
||||
|
:param text: the text to show for the prompt. |
||||
|
:param default: the default value to use if no input happens. If this |
||||
|
is not given it will prompt until it's aborted. |
||||
|
:param hide_input: if this is set to true then the input value will |
||||
|
be hidden. |
||||
|
:param confirmation_prompt: asks for confirmation for the value. |
||||
|
:param type: the type to use to check the value against. |
||||
|
:param value_proc: if this parameter is provided it's a function that |
||||
|
is invoked instead of the type conversion to |
||||
|
convert a value. |
||||
|
:param prompt_suffix: a suffix that should be added to the prompt. |
||||
|
:param show_default: shows or hides the default value in the prompt. |
||||
|
:param err: if set to true the file defaults to ``stderr`` instead of |
||||
|
``stdout``, the same as with echo. |
||||
|
""" |
||||
|
result = None |
||||
|
|
||||
|
def prompt_func(text): |
||||
|
f = hide_input and hidden_prompt_func or visible_prompt_func |
||||
|
try: |
||||
|
# Write the prompt separately so that we get nice |
||||
|
# coloring through colorama on Windows |
||||
|
echo(text, nl=False, err=err) |
||||
|
return f('') |
||||
|
except (KeyboardInterrupt, EOFError): |
||||
|
# getpass doesn't print a newline if the user aborts input with ^C. |
||||
|
# Allegedly this behavior is inherited from getpass(3). |
||||
|
# A doc bug has been filed at https://bugs.python.org/issue24711 |
||||
|
if hide_input: |
||||
|
echo(None, err=err) |
||||
|
raise Abort() |
||||
|
|
||||
|
if value_proc is None: |
||||
|
value_proc = convert_type(type, default) |
||||
|
|
||||
|
prompt = _build_prompt(text, prompt_suffix, show_default, default) |
||||
|
|
||||
|
while 1: |
||||
|
while 1: |
||||
|
value = prompt_func(prompt) |
||||
|
if value: |
||||
|
break |
||||
|
# If a default is set and used, then the confirmation |
||||
|
# prompt is always skipped because that's the only thing |
||||
|
# that really makes sense. |
||||
|
elif default is not None: |
||||
|
return default |
||||
|
try: |
||||
|
result = value_proc(value) |
||||
|
except UsageError as e: |
||||
|
echo('Error: %s' % e.message, err=err) |
||||
|
continue |
||||
|
if not confirmation_prompt: |
||||
|
return result |
||||
|
while 1: |
||||
|
value2 = prompt_func('Repeat for confirmation: ') |
||||
|
if value2: |
||||
|
break |
||||
|
if value == value2: |
||||
|
return result |
||||
|
echo('Error: the two entered values do not match', err=err) |
||||
|
|
||||
|
|
||||
|
def confirm(text, default=False, abort=False, prompt_suffix=': ', |
||||
|
show_default=True, err=False): |
||||
|
"""Prompts for confirmation (yes/no question). |
||||
|
|
||||
|
If the user aborts the input by sending a interrupt signal this |
||||
|
function will catch it and raise a :exc:`Abort` exception. |
||||
|
|
||||
|
.. versionadded:: 4.0 |
||||
|
Added the `err` parameter. |
||||
|
|
||||
|
:param text: the question to ask. |
||||
|
:param default: the default for the prompt. |
||||
|
:param abort: if this is set to `True` a negative answer aborts the |
||||
|
exception by raising :exc:`Abort`. |
||||
|
:param prompt_suffix: a suffix that should be added to the prompt. |
||||
|
:param show_default: shows or hides the default value in the prompt. |
||||
|
:param err: if set to true the file defaults to ``stderr`` instead of |
||||
|
``stdout``, the same as with echo. |
||||
|
""" |
||||
|
prompt = _build_prompt(text, prompt_suffix, show_default, |
||||
|
default and 'Y/n' or 'y/N') |
||||
|
while 1: |
||||
|
try: |
||||
|
# Write the prompt separately so that we get nice |
||||
|
# coloring through colorama on Windows |
||||
|
echo(prompt, nl=False, err=err) |
||||
|
value = visible_prompt_func('').lower().strip() |
||||
|
except (KeyboardInterrupt, EOFError): |
||||
|
raise Abort() |
||||
|
if value in ('y', 'yes'): |
||||
|
rv = True |
||||
|
elif value in ('n', 'no'): |
||||
|
rv = False |
||||
|
elif value == '': |
||||
|
rv = default |
||||
|
else: |
||||
|
echo('Error: invalid input', err=err) |
||||
|
continue |
||||
|
break |
||||
|
if abort and not rv: |
||||
|
raise Abort() |
||||
|
return rv |
||||
|
|
||||
|
|
||||
|
def get_terminal_size(): |
||||
|
"""Returns the current size of the terminal as tuple in the form |
||||
|
``(width, height)`` in columns and rows. |
||||
|
""" |
||||
|
# If shutil has get_terminal_size() (Python 3.3 and later) use that |
||||
|
if sys.version_info >= (3, 3): |
||||
|
import shutil |
||||
|
shutil_get_terminal_size = getattr(shutil, 'get_terminal_size', None) |
||||
|
if shutil_get_terminal_size: |
||||
|
sz = shutil_get_terminal_size() |
||||
|
return sz.columns, sz.lines |
||||
|
|
||||
|
if get_winterm_size is not None: |
||||
|
return get_winterm_size() |
||||
|
|
||||
|
def ioctl_gwinsz(fd): |
||||
|
try: |
||||
|
import fcntl |
||||
|
import termios |
||||
|
cr = struct.unpack( |
||||
|
'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) |
||||
|
except Exception: |
||||
|
return |
||||
|
return cr |
||||
|
|
||||
|
cr = ioctl_gwinsz(0) or ioctl_gwinsz(1) or ioctl_gwinsz(2) |
||||
|
if not cr: |
||||
|
try: |
||||
|
fd = os.open(os.ctermid(), os.O_RDONLY) |
||||
|
try: |
||||
|
cr = ioctl_gwinsz(fd) |
||||
|
finally: |
||||
|
os.close(fd) |
||||
|
except Exception: |
||||
|
pass |
||||
|
if not cr or not cr[0] or not cr[1]: |
||||
|
cr = (os.environ.get('LINES', 25), |
||||
|
os.environ.get('COLUMNS', DEFAULT_COLUMNS)) |
||||
|
return int(cr[1]), int(cr[0]) |
||||
|
|
||||
|
|
||||
|
def echo_via_pager(text, color=None): |
||||
|
"""This function takes a text and shows it via an environment specific |
||||
|
pager on stdout. |
||||
|
|
||||
|
.. versionchanged:: 3.0 |
||||
|
Added the `color` flag. |
||||
|
|
||||
|
:param text: the text to page. |
||||
|
:param color: controls if the pager supports ANSI colors or not. The |
||||
|
default is autodetection. |
||||
|
""" |
||||
|
color = resolve_color_default(color) |
||||
|
if not isinstance(text, string_types): |
||||
|
text = text_type(text) |
||||
|
from ._termui_impl import pager |
||||
|
return pager(text + '\n', color) |
||||
|
|
||||
|
|
||||
|
def progressbar(iterable=None, length=None, label=None, show_eta=True, |
||||
|
show_percent=None, show_pos=False, |
||||
|
item_show_func=None, fill_char='#', empty_char='-', |
||||
|
bar_template='%(label)s [%(bar)s] %(info)s', |
||||
|
info_sep=' ', width=36, file=None, color=None): |
||||
|
"""This function creates an iterable context manager that can be used |
||||
|
to iterate over something while showing a progress bar. It will |
||||
|
either iterate over the `iterable` or `length` items (that are counted |
||||
|
up). While iteration happens, this function will print a rendered |
||||
|
progress bar to the given `file` (defaults to stdout) and will attempt |
||||
|
to calculate remaining time and more. By default, this progress bar |
||||
|
will not be rendered if the file is not a terminal. |
||||
|
|
||||
|
The context manager creates the progress bar. When the context |
||||
|
manager is entered the progress bar is already displayed. With every |
||||
|
iteration over the progress bar, the iterable passed to the bar is |
||||
|
advanced and the bar is updated. When the context manager exits, |
||||
|
a newline is printed and the progress bar is finalized on screen. |
||||
|
|
||||
|
No printing must happen or the progress bar will be unintentionally |
||||
|
destroyed. |
||||
|
|
||||
|
Example usage:: |
||||
|
|
||||
|
with progressbar(items) as bar: |
||||
|
for item in bar: |
||||
|
do_something_with(item) |
||||
|
|
||||
|
Alternatively, if no iterable is specified, one can manually update the |
||||
|
progress bar through the `update()` method instead of directly |
||||
|
iterating over the progress bar. The update method accepts the number |
||||
|
of steps to increment the bar with:: |
||||
|
|
||||
|
with progressbar(length=chunks.total_bytes) as bar: |
||||
|
for chunk in chunks: |
||||
|
process_chunk(chunk) |
||||
|
bar.update(chunks.bytes) |
||||
|
|
||||
|
.. versionadded:: 2.0 |
||||
|
|
||||
|
.. versionadded:: 4.0 |
||||
|
Added the `color` parameter. Added a `update` method to the |
||||
|
progressbar object. |
||||
|
|
||||
|
:param iterable: an iterable to iterate over. If not provided the length |
||||
|
is required. |
||||
|
:param length: the number of items to iterate over. By default the |
||||
|
progressbar will attempt to ask the iterator about its |
||||
|
length, which might or might not work. If an iterable is |
||||
|
also provided this parameter can be used to override the |
||||
|
length. If an iterable is not provided the progress bar |
||||
|
will iterate over a range of that length. |
||||
|
:param label: the label to show next to the progress bar. |
||||
|
:param show_eta: enables or disables the estimated time display. This is |
||||
|
automatically disabled if the length cannot be |
||||
|
determined. |
||||
|
:param show_percent: enables or disables the percentage display. The |
||||
|
default is `True` if the iterable has a length or |
||||
|
`False` if not. |
||||
|
:param show_pos: enables or disables the absolute position display. The |
||||
|
default is `False`. |
||||
|
:param item_show_func: a function called with the current item which |
||||
|
can return a string to show the current item |
||||
|
next to the progress bar. Note that the current |
||||
|
item can be `None`! |
||||
|
:param fill_char: the character to use to show the filled part of the |
||||
|
progress bar. |
||||
|
:param empty_char: the character to use to show the non-filled part of |
||||
|
the progress bar. |
||||
|
:param bar_template: the format string to use as template for the bar. |
||||
|
The parameters in it are ``label`` for the label, |
||||
|
``bar`` for the progress bar and ``info`` for the |
||||
|
info section. |
||||
|
:param info_sep: the separator between multiple info items (eta etc.) |
||||
|
:param width: the width of the progress bar in characters, 0 means full |
||||
|
terminal width |
||||
|
:param file: the file to write to. If this is not a terminal then |
||||
|
only the label is printed. |
||||
|
:param color: controls if the terminal supports ANSI colors or not. The |
||||
|
default is autodetection. This is only needed if ANSI |
||||
|
codes are included anywhere in the progress bar output |
||||
|
which is not the case by default. |
||||
|
""" |
||||
|
from ._termui_impl import ProgressBar |
||||
|
color = resolve_color_default(color) |
||||
|
return ProgressBar(iterable=iterable, length=length, show_eta=show_eta, |
||||
|
show_percent=show_percent, show_pos=show_pos, |
||||
|
item_show_func=item_show_func, fill_char=fill_char, |
||||
|
empty_char=empty_char, bar_template=bar_template, |
||||
|
info_sep=info_sep, file=file, label=label, |
||||
|
width=width, color=color) |
||||
|
|
||||
|
|
||||
|
def clear(): |
||||
|
"""Clears the terminal screen. This will have the effect of clearing |
||||
|
the whole visible space of the terminal and moving the cursor to the |
||||
|
top left. This does not do anything if not connected to a terminal. |
||||
|
|
||||
|
.. versionadded:: 2.0 |
||||
|
""" |
||||
|
if not isatty(sys.stdout): |
||||
|
return |
||||
|
# If we're on Windows and we don't have colorama available, then we |
||||
|
# clear the screen by shelling out. Otherwise we can use an escape |
||||
|
# sequence. |
||||
|
if WIN: |
||||
|
os.system('cls') |
||||
|
else: |
||||
|
sys.stdout.write('\033[2J\033[1;1H') |
||||
|
|
||||
|
|
||||
|
def style(text, fg=None, bg=None, bold=None, dim=None, underline=None, |
||||
|
blink=None, reverse=None, reset=True): |
||||
|
"""Styles a text with ANSI styles and returns the new string. By |
||||
|
default the styling is self contained which means that at the end |
||||
|
of the string a reset code is issued. This can be prevented by |
||||
|
passing ``reset=False``. |
||||
|
|
||||
|
Examples:: |
||||
|
|
||||
|
click.echo(click.style('Hello World!', fg='green')) |
||||
|
click.echo(click.style('ATTENTION!', blink=True)) |
||||
|
click.echo(click.style('Some things', reverse=True, fg='cyan')) |
||||
|
|
||||
|
Supported color names: |
||||
|
|
||||
|
* ``black`` (might be a gray) |
||||
|
* ``red`` |
||||
|
* ``green`` |
||||
|
* ``yellow`` (might be an orange) |
||||
|
* ``blue`` |
||||
|
* ``magenta`` |
||||
|
* ``cyan`` |
||||
|
* ``white`` (might be light gray) |
||||
|
* ``reset`` (reset the color code only) |
||||
|
|
||||
|
.. versionadded:: 2.0 |
||||
|
|
||||
|
:param text: the string to style with ansi codes. |
||||
|
:param fg: if provided this will become the foreground color. |
||||
|
:param bg: if provided this will become the background color. |
||||
|
:param bold: if provided this will enable or disable bold mode. |
||||
|
:param dim: if provided this will enable or disable dim mode. This is |
||||
|
badly supported. |
||||
|
:param underline: if provided this will enable or disable underline. |
||||
|
:param blink: if provided this will enable or disable blinking. |
||||
|
:param reverse: if provided this will enable or disable inverse |
||||
|
rendering (foreground becomes background and the |
||||
|
other way round). |
||||
|
:param reset: by default a reset-all code is added at the end of the |
||||
|
string which means that styles do not carry over. This |
||||
|
can be disabled to compose styles. |
||||
|
""" |
||||
|
bits = [] |
||||
|
if fg: |
||||
|
try: |
||||
|
bits.append('\033[%dm' % (_ansi_colors.index(fg) + 30)) |
||||
|
except ValueError: |
||||
|
raise TypeError('Unknown color %r' % fg) |
||||
|
if bg: |
||||
|
try: |
||||
|
bits.append('\033[%dm' % (_ansi_colors.index(bg) + 40)) |
||||
|
except ValueError: |
||||
|
raise TypeError('Unknown color %r' % bg) |
||||
|
if bold is not None: |
||||
|
bits.append('\033[%dm' % (1 if bold else 22)) |
||||
|
if dim is not None: |
||||
|
bits.append('\033[%dm' % (2 if dim else 22)) |
||||
|
if underline is not None: |
||||
|
bits.append('\033[%dm' % (4 if underline else 24)) |
||||
|
if blink is not None: |
||||
|
bits.append('\033[%dm' % (5 if blink else 25)) |
||||
|
if reverse is not None: |
||||
|
bits.append('\033[%dm' % (7 if reverse else 27)) |
||||
|
bits.append(text) |
||||
|
if reset: |
||||
|
bits.append(_ansi_reset_all) |
||||
|
return ''.join(bits) |
||||
|
|
||||
|
|
||||
|
def unstyle(text): |
||||
|
"""Removes ANSI styling information from a string. Usually it's not |
||||
|
necessary to use this function as Click's echo function will |
||||
|
automatically remove styling if necessary. |
||||
|
|
||||
|
.. versionadded:: 2.0 |
||||
|
|
||||
|
:param text: the text to remove style information from. |
||||
|
""" |
||||
|
return strip_ansi(text) |
||||
|
|
||||
|
|
||||
|
def secho(text, file=None, nl=True, err=False, color=None, **styles): |
||||
|
"""This function combines :func:`echo` and :func:`style` into one |
||||
|
call. As such the following two calls are the same:: |
||||
|
|
||||
|
click.secho('Hello World!', fg='green') |
||||
|
click.echo(click.style('Hello World!', fg='green')) |
||||
|
|
||||
|
All keyword arguments are forwarded to the underlying functions |
||||
|
depending on which one they go with. |
||||
|
|
||||
|
.. versionadded:: 2.0 |
||||
|
""" |
||||
|
return echo(style(text, **styles), file=file, nl=nl, err=err, color=color) |
||||
|
|
||||
|
|
||||
|
def edit(text=None, editor=None, env=None, require_save=True, |
||||
|
extension='.txt', filename=None): |
||||
|
r"""Edits the given text in the defined editor. If an editor is given |
||||
|
(should be the full path to the executable but the regular operating |
||||
|
system search path is used for finding the executable) it overrides |
||||
|
the detected editor. Optionally, some environment variables can be |
||||
|
used. If the editor is closed without changes, `None` is returned. In |
||||
|
case a file is edited directly the return value is always `None` and |
||||
|
`require_save` and `extension` are ignored. |
||||
|
|
||||
|
If the editor cannot be opened a :exc:`UsageError` is raised. |
||||
|
|
||||
|
Note for Windows: to simplify cross-platform usage, the newlines are |
||||
|
automatically converted from POSIX to Windows and vice versa. As such, |
||||
|
the message here will have ``\n`` as newline markers. |
||||
|
|
||||
|
:param text: the text to edit. |
||||
|
:param editor: optionally the editor to use. Defaults to automatic |
||||
|
detection. |
||||
|
:param env: environment variables to forward to the editor. |
||||
|
:param require_save: if this is true, then not saving in the editor |
||||
|
will make the return value become `None`. |
||||
|
:param extension: the extension to tell the editor about. This defaults |
||||
|
to `.txt` but changing this might change syntax |
||||
|
highlighting. |
||||
|
:param filename: if provided it will edit this file instead of the |
||||
|
provided text contents. It will not use a temporary |
||||
|
file as an indirection in that case. |
||||
|
""" |
||||
|
from ._termui_impl import Editor |
||||
|
editor = Editor(editor=editor, env=env, require_save=require_save, |
||||
|
extension=extension) |
||||
|
if filename is None: |
||||
|
return editor.edit(text) |
||||
|
editor.edit_file(filename) |
||||
|
|
||||
|
|
||||
|
def launch(url, wait=False, locate=False): |
||||
|
"""This function launches the given URL (or filename) in the default |
||||
|
viewer application for this file type. If this is an executable, it |
||||
|
might launch the executable in a new session. The return value is |
||||
|
the exit code of the launched application. Usually, ``0`` indicates |
||||
|
success. |
||||
|
|
||||
|
Examples:: |
||||
|
|
||||
|
click.launch('http://click.pocoo.org/') |
||||
|
click.launch('/my/downloaded/file', locate=True) |
||||
|
|
||||
|
.. versionadded:: 2.0 |
||||
|
|
||||
|
:param url: URL or filename of the thing to launch. |
||||
|
:param wait: waits for the program to stop. |
||||
|
:param locate: if this is set to `True` then instead of launching the |
||||
|
application associated with the URL it will attempt to |
||||
|
launch a file manager with the file located. This |
||||
|
might have weird effects if the URL does not point to |
||||
|
the filesystem. |
||||
|
""" |
||||
|
from ._termui_impl import open_url |
||||
|
return open_url(url, wait=wait, locate=locate) |
||||
|
|
||||
|
|
||||
|
# If this is provided, getchar() calls into this instead. This is used |
||||
|
# for unittesting purposes. |
||||
|
_getchar = None |
||||
|
|
||||
|
|
||||
|
def getchar(echo=False): |
||||
|
"""Fetches a single character from the terminal and returns it. This |
||||
|
will always return a unicode character and under certain rare |
||||
|
circumstances this might return more than one character. The |
||||
|
situations which more than one character is returned is when for |
||||
|
whatever reason multiple characters end up in the terminal buffer or |
||||
|
standard input was not actually a terminal. |
||||
|
|
||||
|
Note that this will always read from the terminal, even if something |
||||
|
is piped into the standard input. |
||||
|
|
||||
|
.. versionadded:: 2.0 |
||||
|
|
||||
|
:param echo: if set to `True`, the character read will also show up on |
||||
|
the terminal. The default is to not show it. |
||||
|
""" |
||||
|
f = _getchar |
||||
|
if f is None: |
||||
|
from ._termui_impl import getchar as f |
||||
|
return f(echo) |
||||
|
|
||||
|
|
||||
|
def pause(info='Press any key to continue ...', err=False): |
||||
|
"""This command stops execution and waits for the user to press any |
||||
|
key to continue. This is similar to the Windows batch "pause" |
||||
|
command. If the program is not run through a terminal, this command |
||||
|
will instead do nothing. |
||||
|
|
||||
|
.. versionadded:: 2.0 |
||||
|
|
||||
|
.. versionadded:: 4.0 |
||||
|
Added the `err` parameter. |
||||
|
|
||||
|
:param info: the info string to print before pausing. |
||||
|
:param err: if set to message goes to ``stderr`` instead of |
||||
|
``stdout``, the same as with echo. |
||||
|
""" |
||||
|
if not isatty(sys.stdin) or not isatty(sys.stdout): |
||||
|
return |
||||
|
try: |
||||
|
if info: |
||||
|
echo(info, nl=False, err=err) |
||||
|
try: |
||||
|
getchar() |
||||
|
except (KeyboardInterrupt, EOFError): |
||||
|
pass |
||||
|
finally: |
||||
|
if info: |
||||
|
echo(err=err) |
@ -0,0 +1,322 @@ |
|||||
|
import os |
||||
|
import sys |
||||
|
import shutil |
||||
|
import tempfile |
||||
|
import contextlib |
||||
|
|
||||
|
from ._compat import iteritems, PY2 |
||||
|
|
||||
|
|
||||
|
# If someone wants to vendor click, we want to ensure the |
||||
|
# correct package is discovered. Ideally we could use a |
||||
|
# relative import here but unfortunately Python does not |
||||
|
# support that. |
||||
|
clickpkg = sys.modules[__name__.rsplit('.', 1)[0]] |
||||
|
|
||||
|
|
||||
|
if PY2: |
||||
|
from cStringIO import StringIO |
||||
|
else: |
||||
|
import io |
||||
|
from ._compat import _find_binary_reader |
||||
|
|
||||
|
|
||||
|
class EchoingStdin(object): |
||||
|
|
||||
|
def __init__(self, input, output): |
||||
|
self._input = input |
||||
|
self._output = output |
||||
|
|
||||
|
def __getattr__(self, x): |
||||
|
return getattr(self._input, x) |
||||
|
|
||||
|
def _echo(self, rv): |
||||
|
self._output.write(rv) |
||||
|
return rv |
||||
|
|
||||
|
def read(self, n=-1): |
||||
|
return self._echo(self._input.read(n)) |
||||
|
|
||||
|
def readline(self, n=-1): |
||||
|
return self._echo(self._input.readline(n)) |
||||
|
|
||||
|
def readlines(self): |
||||
|
return [self._echo(x) for x in self._input.readlines()] |
||||
|
|
||||
|
def __iter__(self): |
||||
|
return iter(self._echo(x) for x in self._input) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return repr(self._input) |
||||
|
|
||||
|
|
||||
|
def make_input_stream(input, charset): |
||||
|
# Is already an input stream. |
||||
|
if hasattr(input, 'read'): |
||||
|
if PY2: |
||||
|
return input |
||||
|
rv = _find_binary_reader(input) |
||||
|
if rv is not None: |
||||
|
return rv |
||||
|
raise TypeError('Could not find binary reader for input stream.') |
||||
|
|
||||
|
if input is None: |
||||
|
input = b'' |
||||
|
elif not isinstance(input, bytes): |
||||
|
input = input.encode(charset) |
||||
|
if PY2: |
||||
|
return StringIO(input) |
||||
|
return io.BytesIO(input) |
||||
|
|
||||
|
|
||||
|
class Result(object): |
||||
|
"""Holds the captured result of an invoked CLI script.""" |
||||
|
|
||||
|
def __init__(self, runner, output_bytes, exit_code, exception, |
||||
|
exc_info=None): |
||||
|
#: The runner that created the result |
||||
|
self.runner = runner |
||||
|
#: The output as bytes. |
||||
|
self.output_bytes = output_bytes |
||||
|
#: The exit code as integer. |
||||
|
self.exit_code = exit_code |
||||
|
#: The exception that happend if one did. |
||||
|
self.exception = exception |
||||
|
#: The traceback |
||||
|
self.exc_info = exc_info |
||||
|
|
||||
|
@property |
||||
|
def output(self): |
||||
|
"""The output as unicode string.""" |
||||
|
return self.output_bytes.decode(self.runner.charset, 'replace') \ |
||||
|
.replace('\r\n', '\n') |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return '<Result %s>' % ( |
||||
|
self.exception and repr(self.exception) or 'okay', |
||||
|
) |
||||
|
|
||||
|
|
||||
|
class CliRunner(object): |
||||
|
"""The CLI runner provides functionality to invoke a Click command line |
||||
|
script for unittesting purposes in a isolated environment. This only |
||||
|
works in single-threaded systems without any concurrency as it changes the |
||||
|
global interpreter state. |
||||
|
|
||||
|
:param charset: the character set for the input and output data. This is |
||||
|
UTF-8 by default and should not be changed currently as |
||||
|
the reporting to Click only works in Python 2 properly. |
||||
|
:param env: a dictionary with environment variables for overriding. |
||||
|
:param echo_stdin: if this is set to `True`, then reading from stdin writes |
||||
|
to stdout. This is useful for showing examples in |
||||
|
some circumstances. Note that regular prompts |
||||
|
will automatically echo the input. |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, charset=None, env=None, echo_stdin=False): |
||||
|
if charset is None: |
||||
|
charset = 'utf-8' |
||||
|
self.charset = charset |
||||
|
self.env = env or {} |
||||
|
self.echo_stdin = echo_stdin |
||||
|
|
||||
|
def get_default_prog_name(self, cli): |
||||
|
"""Given a command object it will return the default program name |
||||
|
for it. The default is the `name` attribute or ``"root"`` if not |
||||
|
set. |
||||
|
""" |
||||
|
return cli.name or 'root' |
||||
|
|
||||
|
def make_env(self, overrides=None): |
||||
|
"""Returns the environment overrides for invoking a script.""" |
||||
|
rv = dict(self.env) |
||||
|
if overrides: |
||||
|
rv.update(overrides) |
||||
|
return rv |
||||
|
|
||||
|
@contextlib.contextmanager |
||||
|
def isolation(self, input=None, env=None, color=False): |
||||
|
"""A context manager that sets up the isolation for invoking of a |
||||
|
command line tool. This sets up stdin with the given input data |
||||
|
and `os.environ` with the overrides from the given dictionary. |
||||
|
This also rebinds some internals in Click to be mocked (like the |
||||
|
prompt functionality). |
||||
|
|
||||
|
This is automatically done in the :meth:`invoke` method. |
||||
|
|
||||
|
.. versionadded:: 4.0 |
||||
|
The ``color`` parameter was added. |
||||
|
|
||||
|
:param input: the input stream to put into sys.stdin. |
||||
|
:param env: the environment overrides as dictionary. |
||||
|
:param color: whether the output should contain color codes. The |
||||
|
application can still override this explicitly. |
||||
|
""" |
||||
|
input = make_input_stream(input, self.charset) |
||||
|
|
||||
|
old_stdin = sys.stdin |
||||
|
old_stdout = sys.stdout |
||||
|
old_stderr = sys.stderr |
||||
|
old_forced_width = clickpkg.formatting.FORCED_WIDTH |
||||
|
clickpkg.formatting.FORCED_WIDTH = 80 |
||||
|
|
||||
|
env = self.make_env(env) |
||||
|
|
||||
|
if PY2: |
||||
|
sys.stdout = sys.stderr = bytes_output = StringIO() |
||||
|
if self.echo_stdin: |
||||
|
input = EchoingStdin(input, bytes_output) |
||||
|
else: |
||||
|
bytes_output = io.BytesIO() |
||||
|
if self.echo_stdin: |
||||
|
input = EchoingStdin(input, bytes_output) |
||||
|
input = io.TextIOWrapper(input, encoding=self.charset) |
||||
|
sys.stdout = sys.stderr = io.TextIOWrapper( |
||||
|
bytes_output, encoding=self.charset) |
||||
|
|
||||
|
sys.stdin = input |
||||
|
|
||||
|
def visible_input(prompt=None): |
||||
|
sys.stdout.write(prompt or '') |
||||
|
val = input.readline().rstrip('\r\n') |
||||
|
sys.stdout.write(val + '\n') |
||||
|
sys.stdout.flush() |
||||
|
return val |
||||
|
|
||||
|
def hidden_input(prompt=None): |
||||
|
sys.stdout.write((prompt or '') + '\n') |
||||
|
sys.stdout.flush() |
||||
|
return input.readline().rstrip('\r\n') |
||||
|
|
||||
|
def _getchar(echo): |
||||
|
char = sys.stdin.read(1) |
||||
|
if echo: |
||||
|
sys.stdout.write(char) |
||||
|
sys.stdout.flush() |
||||
|
return char |
||||
|
|
||||
|
default_color = color |
||||
|
def should_strip_ansi(stream=None, color=None): |
||||
|
if color is None: |
||||
|
return not default_color |
||||
|
return not color |
||||
|
|
||||
|
old_visible_prompt_func = clickpkg.termui.visible_prompt_func |
||||
|
old_hidden_prompt_func = clickpkg.termui.hidden_prompt_func |
||||
|
old__getchar_func = clickpkg.termui._getchar |
||||
|
old_should_strip_ansi = clickpkg.utils.should_strip_ansi |
||||
|
clickpkg.termui.visible_prompt_func = visible_input |
||||
|
clickpkg.termui.hidden_prompt_func = hidden_input |
||||
|
clickpkg.termui._getchar = _getchar |
||||
|
clickpkg.utils.should_strip_ansi = should_strip_ansi |
||||
|
|
||||
|
old_env = {} |
||||
|
try: |
||||
|
for key, value in iteritems(env): |
||||
|
old_env[key] = os.environ.get(value) |
||||
|
if value is None: |
||||
|
try: |
||||
|
del os.environ[key] |
||||
|
except Exception: |
||||
|
pass |
||||
|
else: |
||||
|
os.environ[key] = value |
||||
|
yield bytes_output |
||||
|
finally: |
||||
|
for key, value in iteritems(old_env): |
||||
|
if value is None: |
||||
|
try: |
||||
|
del os.environ[key] |
||||
|
except Exception: |
||||
|
pass |
||||
|
else: |
||||
|
os.environ[key] = value |
||||
|
sys.stdout = old_stdout |
||||
|
sys.stderr = old_stderr |
||||
|
sys.stdin = old_stdin |
||||
|
clickpkg.termui.visible_prompt_func = old_visible_prompt_func |
||||
|
clickpkg.termui.hidden_prompt_func = old_hidden_prompt_func |
||||
|
clickpkg.termui._getchar = old__getchar_func |
||||
|
clickpkg.utils.should_strip_ansi = old_should_strip_ansi |
||||
|
clickpkg.formatting.FORCED_WIDTH = old_forced_width |
||||
|
|
||||
|
def invoke(self, cli, args=None, input=None, env=None, |
||||
|
catch_exceptions=True, color=False, **extra): |
||||
|
"""Invokes a command in an isolated environment. The arguments are |
||||
|
forwarded directly to the command line script, the `extra` keyword |
||||
|
arguments are passed to the :meth:`~clickpkg.Command.main` function of |
||||
|
the command. |
||||
|
|
||||
|
This returns a :class:`Result` object. |
||||
|
|
||||
|
.. versionadded:: 3.0 |
||||
|
The ``catch_exceptions`` parameter was added. |
||||
|
|
||||
|
.. versionchanged:: 3.0 |
||||
|
The result object now has an `exc_info` attribute with the |
||||
|
traceback if available. |
||||
|
|
||||
|
.. versionadded:: 4.0 |
||||
|
The ``color`` parameter was added. |
||||
|
|
||||
|
:param cli: the command to invoke |
||||
|
:param args: the arguments to invoke |
||||
|
:param input: the input data for `sys.stdin`. |
||||
|
:param env: the environment overrides. |
||||
|
:param catch_exceptions: Whether to catch any other exceptions than |
||||
|
``SystemExit``. |
||||
|
:param extra: the keyword arguments to pass to :meth:`main`. |
||||
|
:param color: whether the output should contain color codes. The |
||||
|
application can still override this explicitly. |
||||
|
""" |
||||
|
exc_info = None |
||||
|
with self.isolation(input=input, env=env, color=color) as out: |
||||
|
exception = None |
||||
|
exit_code = 0 |
||||
|
|
||||
|
try: |
||||
|
cli.main(args=args or (), |
||||
|
prog_name=self.get_default_prog_name(cli), **extra) |
||||
|
except SystemExit as e: |
||||
|
if e.code != 0: |
||||
|
exception = e |
||||
|
|
||||
|
exc_info = sys.exc_info() |
||||
|
|
||||
|
exit_code = e.code |
||||
|
if not isinstance(exit_code, int): |
||||
|
sys.stdout.write(str(exit_code)) |
||||
|
sys.stdout.write('\n') |
||||
|
exit_code = 1 |
||||
|
except Exception as e: |
||||
|
if not catch_exceptions: |
||||
|
raise |
||||
|
exception = e |
||||
|
exit_code = -1 |
||||
|
exc_info = sys.exc_info() |
||||
|
finally: |
||||
|
sys.stdout.flush() |
||||
|
output = out.getvalue() |
||||
|
|
||||
|
return Result(runner=self, |
||||
|
output_bytes=output, |
||||
|
exit_code=exit_code, |
||||
|
exception=exception, |
||||
|
exc_info=exc_info) |
||||
|
|
||||
|
@contextlib.contextmanager |
||||
|
def isolated_filesystem(self): |
||||
|
"""A context manager that creates a temporary folder and changes |
||||
|
the current working directory to it for isolated filesystem tests. |
||||
|
""" |
||||
|
cwd = os.getcwd() |
||||
|
t = tempfile.mkdtemp() |
||||
|
os.chdir(t) |
||||
|
try: |
||||
|
yield t |
||||
|
finally: |
||||
|
os.chdir(cwd) |
||||
|
try: |
||||
|
shutil.rmtree(t) |
||||
|
except (OSError, IOError): |
||||
|
pass |
@ -0,0 +1,550 @@ |
|||||
|
import os |
||||
|
import stat |
||||
|
|
||||
|
from ._compat import open_stream, text_type, filename_to_ui, \ |
||||
|
get_filesystem_encoding, get_streerror, _get_argv_encoding, PY2 |
||||
|
from .exceptions import BadParameter |
||||
|
from .utils import safecall, LazyFile |
||||
|
|
||||
|
|
||||
|
class ParamType(object): |
||||
|
"""Helper for converting values through types. The following is |
||||
|
necessary for a valid type: |
||||
|
|
||||
|
* it needs a name |
||||
|
* it needs to pass through None unchanged |
||||
|
* it needs to convert from a string |
||||
|
* it needs to convert its result type through unchanged |
||||
|
(eg: needs to be idempotent) |
||||
|
* it needs to be able to deal with param and context being `None`. |
||||
|
This can be the case when the object is used with prompt |
||||
|
inputs. |
||||
|
""" |
||||
|
is_composite = False |
||||
|
|
||||
|
#: the descriptive name of this type |
||||
|
name = None |
||||
|
|
||||
|
#: if a list of this type is expected and the value is pulled from a |
||||
|
#: string environment variable, this is what splits it up. `None` |
||||
|
#: means any whitespace. For all parameters the general rule is that |
||||
|
#: whitespace splits them up. The exception are paths and files which |
||||
|
#: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on |
||||
|
#: Windows). |
||||
|
envvar_list_splitter = None |
||||
|
|
||||
|
def __call__(self, value, param=None, ctx=None): |
||||
|
if value is not None: |
||||
|
return self.convert(value, param, ctx) |
||||
|
|
||||
|
def get_metavar(self, param): |
||||
|
"""Returns the metavar default for this param if it provides one.""" |
||||
|
|
||||
|
def get_missing_message(self, param): |
||||
|
"""Optionally might return extra information about a missing |
||||
|
parameter. |
||||
|
|
||||
|
.. versionadded:: 2.0 |
||||
|
""" |
||||
|
|
||||
|
def convert(self, value, param, ctx): |
||||
|
"""Converts the value. This is not invoked for values that are |
||||
|
`None` (the missing value). |
||||
|
""" |
||||
|
return value |
||||
|
|
||||
|
def split_envvar_value(self, rv): |
||||
|
"""Given a value from an environment variable this splits it up |
||||
|
into small chunks depending on the defined envvar list splitter. |
||||
|
|
||||
|
If the splitter is set to `None`, which means that whitespace splits, |
||||
|
then leading and trailing whitespace is ignored. Otherwise, leading |
||||
|
and trailing splitters usually lead to empty items being included. |
||||
|
""" |
||||
|
return (rv or '').split(self.envvar_list_splitter) |
||||
|
|
||||
|
def fail(self, message, param=None, ctx=None): |
||||
|
"""Helper method to fail with an invalid value message.""" |
||||
|
raise BadParameter(message, ctx=ctx, param=param) |
||||
|
|
||||
|
|
||||
|
class CompositeParamType(ParamType): |
||||
|
is_composite = True |
||||
|
|
||||
|
@property |
||||
|
def arity(self): |
||||
|
raise NotImplementedError() |
||||
|
|
||||
|
|
||||
|
class FuncParamType(ParamType): |
||||
|
|
||||
|
def __init__(self, func): |
||||
|
self.name = func.__name__ |
||||
|
self.func = func |
||||
|
|
||||
|
def convert(self, value, param, ctx): |
||||
|
try: |
||||
|
return self.func(value) |
||||
|
except ValueError: |
||||
|
try: |
||||
|
value = text_type(value) |
||||
|
except UnicodeError: |
||||
|
value = str(value).decode('utf-8', 'replace') |
||||
|
self.fail(value, param, ctx) |
||||
|
|
||||
|
|
||||
|
class UnprocessedParamType(ParamType): |
||||
|
name = 'text' |
||||
|
|
||||
|
def convert(self, value, param, ctx): |
||||
|
return value |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return 'UNPROCESSED' |
||||
|
|
||||
|
|
||||
|
class StringParamType(ParamType): |
||||
|
name = 'text' |
||||
|
|
||||
|
def convert(self, value, param, ctx): |
||||
|
if isinstance(value, bytes): |
||||
|
enc = _get_argv_encoding() |
||||
|
try: |
||||
|
value = value.decode(enc) |
||||
|
except UnicodeError: |
||||
|
fs_enc = get_filesystem_encoding() |
||||
|
if fs_enc != enc: |
||||
|
try: |
||||
|
value = value.decode(fs_enc) |
||||
|
except UnicodeError: |
||||
|
value = value.decode('utf-8', 'replace') |
||||
|
return value |
||||
|
return value |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return 'STRING' |
||||
|
|
||||
|
|
||||
|
class Choice(ParamType): |
||||
|
"""The choice type allows a value to be checked against a fixed set of |
||||
|
supported values. All of these values have to be strings. |
||||
|
|
||||
|
See :ref:`choice-opts` for an example. |
||||
|
""" |
||||
|
name = 'choice' |
||||
|
|
||||
|
def __init__(self, choices): |
||||
|
self.choices = choices |
||||
|
|
||||
|
def get_metavar(self, param): |
||||
|
return '[%s]' % '|'.join(self.choices) |
||||
|
|
||||
|
def get_missing_message(self, param): |
||||
|
return 'Choose from %s.' % ', '.join(self.choices) |
||||
|
|
||||
|
def convert(self, value, param, ctx): |
||||
|
# Exact match |
||||
|
if value in self.choices: |
||||
|
return value |
||||
|
|
||||
|
# Match through normalization |
||||
|
if ctx is not None and \ |
||||
|
ctx.token_normalize_func is not None: |
||||
|
value = ctx.token_normalize_func(value) |
||||
|
for choice in self.choices: |
||||
|
if ctx.token_normalize_func(choice) == value: |
||||
|
return choice |
||||
|
|
||||
|
self.fail('invalid choice: %s. (choose from %s)' % |
||||
|
(value, ', '.join(self.choices)), param, ctx) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return 'Choice(%r)' % list(self.choices) |
||||
|
|
||||
|
|
||||
|
class IntParamType(ParamType): |
||||
|
name = 'integer' |
||||
|
|
||||
|
def convert(self, value, param, ctx): |
||||
|
try: |
||||
|
return int(value) |
||||
|
except (ValueError, UnicodeError): |
||||
|
self.fail('%s is not a valid integer' % value, param, ctx) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return 'INT' |
||||
|
|
||||
|
|
||||
|
class IntRange(IntParamType): |
||||
|
"""A parameter that works similar to :data:`click.INT` but restricts |
||||
|
the value to fit into a range. The default behavior is to fail if the |
||||
|
value falls outside the range, but it can also be silently clamped |
||||
|
between the two edges. |
||||
|
|
||||
|
See :ref:`ranges` for an example. |
||||
|
""" |
||||
|
name = 'integer range' |
||||
|
|
||||
|
def __init__(self, min=None, max=None, clamp=False): |
||||
|
self.min = min |
||||
|
self.max = max |
||||
|
self.clamp = clamp |
||||
|
|
||||
|
def convert(self, value, param, ctx): |
||||
|
rv = IntParamType.convert(self, value, param, ctx) |
||||
|
if self.clamp: |
||||
|
if self.min is not None and rv < self.min: |
||||
|
return self.min |
||||
|
if self.max is not None and rv > self.max: |
||||
|
return self.max |
||||
|
if self.min is not None and rv < self.min or \ |
||||
|
self.max is not None and rv > self.max: |
||||
|
if self.min is None: |
||||
|
self.fail('%s is bigger than the maximum valid value ' |
||||
|
'%s.' % (rv, self.max), param, ctx) |
||||
|
elif self.max is None: |
||||
|
self.fail('%s is smaller than the minimum valid value ' |
||||
|
'%s.' % (rv, self.min), param, ctx) |
||||
|
else: |
||||
|
self.fail('%s is not in the valid range of %s to %s.' |
||||
|
% (rv, self.min, self.max), param, ctx) |
||||
|
return rv |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return 'IntRange(%r, %r)' % (self.min, self.max) |
||||
|
|
||||
|
|
||||
|
class BoolParamType(ParamType): |
||||
|
name = 'boolean' |
||||
|
|
||||
|
def convert(self, value, param, ctx): |
||||
|
if isinstance(value, bool): |
||||
|
return bool(value) |
||||
|
value = value.lower() |
||||
|
if value in ('true', '1', 'yes', 'y'): |
||||
|
return True |
||||
|
elif value in ('false', '0', 'no', 'n'): |
||||
|
return False |
||||
|
self.fail('%s is not a valid boolean' % value, param, ctx) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return 'BOOL' |
||||
|
|
||||
|
|
||||
|
class FloatParamType(ParamType): |
||||
|
name = 'float' |
||||
|
|
||||
|
def convert(self, value, param, ctx): |
||||
|
try: |
||||
|
return float(value) |
||||
|
except (UnicodeError, ValueError): |
||||
|
self.fail('%s is not a valid floating point value' % |
||||
|
value, param, ctx) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return 'FLOAT' |
||||
|
|
||||
|
|
||||
|
class UUIDParameterType(ParamType): |
||||
|
name = 'uuid' |
||||
|
|
||||
|
def convert(self, value, param, ctx): |
||||
|
import uuid |
||||
|
try: |
||||
|
if PY2 and isinstance(value, text_type): |
||||
|
value = value.encode('ascii') |
||||
|
return uuid.UUID(value) |
||||
|
except (UnicodeError, ValueError): |
||||
|
self.fail('%s is not a valid UUID value' % value, param, ctx) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return 'UUID' |
||||
|
|
||||
|
|
||||
|
class File(ParamType): |
||||
|
"""Declares a parameter to be a file for reading or writing. The file |
||||
|
is automatically closed once the context tears down (after the command |
||||
|
finished working). |
||||
|
|
||||
|
Files can be opened for reading or writing. The special value ``-`` |
||||
|
indicates stdin or stdout depending on the mode. |
||||
|
|
||||
|
By default, the file is opened for reading text data, but it can also be |
||||
|
opened in binary mode or for writing. The encoding parameter can be used |
||||
|
to force a specific encoding. |
||||
|
|
||||
|
The `lazy` flag controls if the file should be opened immediately or |
||||
|
upon first IO. The default is to be non lazy for standard input and |
||||
|
output streams as well as files opened for reading, lazy otherwise. |
||||
|
|
||||
|
Starting with Click 2.0, files can also be opened atomically in which |
||||
|
case all writes go into a separate file in the same folder and upon |
||||
|
completion the file will be moved over to the original location. This |
||||
|
is useful if a file regularly read by other users is modified. |
||||
|
|
||||
|
See :ref:`file-args` for more information. |
||||
|
""" |
||||
|
name = 'filename' |
||||
|
envvar_list_splitter = os.path.pathsep |
||||
|
|
||||
|
def __init__(self, mode='r', encoding=None, errors='strict', lazy=None, |
||||
|
atomic=False): |
||||
|
self.mode = mode |
||||
|
self.encoding = encoding |
||||
|
self.errors = errors |
||||
|
self.lazy = lazy |
||||
|
self.atomic = atomic |
||||
|
|
||||
|
def resolve_lazy_flag(self, value): |
||||
|
if self.lazy is not None: |
||||
|
return self.lazy |
||||
|
if value == '-': |
||||
|
return False |
||||
|
elif 'w' in self.mode: |
||||
|
return True |
||||
|
return False |
||||
|
|
||||
|
def convert(self, value, param, ctx): |
||||
|
try: |
||||
|
if hasattr(value, 'read') or hasattr(value, 'write'): |
||||
|
return value |
||||
|
|
||||
|
lazy = self.resolve_lazy_flag(value) |
||||
|
|
||||
|
if lazy: |
||||
|
f = LazyFile(value, self.mode, self.encoding, self.errors, |
||||
|
atomic=self.atomic) |
||||
|
if ctx is not None: |
||||
|
ctx.call_on_close(f.close_intelligently) |
||||
|
return f |
||||
|
|
||||
|
f, should_close = open_stream(value, self.mode, |
||||
|
self.encoding, self.errors, |
||||
|
atomic=self.atomic) |
||||
|
# If a context is provided, we automatically close the file |
||||
|
# at the end of the context execution (or flush out). If a |
||||
|
# context does not exist, it's the caller's responsibility to |
||||
|
# properly close the file. This for instance happens when the |
||||
|
# type is used with prompts. |
||||
|
if ctx is not None: |
||||
|
if should_close: |
||||
|
ctx.call_on_close(safecall(f.close)) |
||||
|
else: |
||||
|
ctx.call_on_close(safecall(f.flush)) |
||||
|
return f |
||||
|
except (IOError, OSError) as e: |
||||
|
self.fail('Could not open file: %s: %s' % ( |
||||
|
filename_to_ui(value), |
||||
|
get_streerror(e), |
||||
|
), param, ctx) |
||||
|
|
||||
|
|
||||
|
class Path(ParamType): |
||||
|
"""The path type is similar to the :class:`File` type but it performs |
||||
|
different checks. First of all, instead of returning an open file |
||||
|
handle it returns just the filename. Secondly, it can perform various |
||||
|
basic checks about what the file or directory should be. |
||||
|
|
||||
|
.. versionchanged:: 6.0 |
||||
|
`allow_dash` was added. |
||||
|
|
||||
|
:param exists: if set to true, the file or directory needs to exist for |
||||
|
this value to be valid. If this is not required and a |
||||
|
file does indeed not exist, then all further checks are |
||||
|
silently skipped. |
||||
|
:param file_okay: controls if a file is a possible value. |
||||
|
:param dir_okay: controls if a directory is a possible value. |
||||
|
:param writable: if true, a writable check is performed. |
||||
|
:param readable: if true, a readable check is performed. |
||||
|
:param resolve_path: if this is true, then the path is fully resolved |
||||
|
before the value is passed onwards. This means |
||||
|
that it's absolute and symlinks are resolved. |
||||
|
:param allow_dash: If this is set to `True`, a single dash to indicate |
||||
|
standard streams is permitted. |
||||
|
:param type: optionally a string type that should be used to |
||||
|
represent the path. The default is `None` which |
||||
|
means the return value will be either bytes or |
||||
|
unicode depending on what makes most sense given the |
||||
|
input data Click deals with. |
||||
|
""" |
||||
|
envvar_list_splitter = os.path.pathsep |
||||
|
|
||||
|
def __init__(self, exists=False, file_okay=True, dir_okay=True, |
||||
|
writable=False, readable=True, resolve_path=False, |
||||
|
allow_dash=False, path_type=None): |
||||
|
self.exists = exists |
||||
|
self.file_okay = file_okay |
||||
|
self.dir_okay = dir_okay |
||||
|
self.writable = writable |
||||
|
self.readable = readable |
||||
|
self.resolve_path = resolve_path |
||||
|
self.allow_dash = allow_dash |
||||
|
self.type = path_type |
||||
|
|
||||
|
if self.file_okay and not self.dir_okay: |
||||
|
self.name = 'file' |
||||
|
self.path_type = 'File' |
||||
|
if self.dir_okay and not self.file_okay: |
||||
|
self.name = 'directory' |
||||
|
self.path_type = 'Directory' |
||||
|
else: |
||||
|
self.name = 'path' |
||||
|
self.path_type = 'Path' |
||||
|
|
||||
|
def coerce_path_result(self, rv): |
||||
|
if self.type is not None and not isinstance(rv, self.type): |
||||
|
if self.type is text_type: |
||||
|
rv = rv.decode(get_filesystem_encoding()) |
||||
|
else: |
||||
|
rv = rv.encode(get_filesystem_encoding()) |
||||
|
return rv |
||||
|
|
||||
|
def convert(self, value, param, ctx): |
||||
|
rv = value |
||||
|
|
||||
|
is_dash = self.file_okay and self.allow_dash and rv in (b'-', '-') |
||||
|
|
||||
|
if not is_dash: |
||||
|
if self.resolve_path: |
||||
|
rv = os.path.realpath(rv) |
||||
|
|
||||
|
try: |
||||
|
st = os.stat(rv) |
||||
|
except OSError: |
||||
|
if not self.exists: |
||||
|
return self.coerce_path_result(rv) |
||||
|
self.fail('%s "%s" does not exist.' % ( |
||||
|
self.path_type, |
||||
|
filename_to_ui(value) |
||||
|
), param, ctx) |
||||
|
|
||||
|
if not self.file_okay and stat.S_ISREG(st.st_mode): |
||||
|
self.fail('%s "%s" is a file.' % ( |
||||
|
self.path_type, |
||||
|
filename_to_ui(value) |
||||
|
), param, ctx) |
||||
|
if not self.dir_okay and stat.S_ISDIR(st.st_mode): |
||||
|
self.fail('%s "%s" is a directory.' % ( |
||||
|
self.path_type, |
||||
|
filename_to_ui(value) |
||||
|
), param, ctx) |
||||
|
if self.writable and not os.access(value, os.W_OK): |
||||
|
self.fail('%s "%s" is not writable.' % ( |
||||
|
self.path_type, |
||||
|
filename_to_ui(value) |
||||
|
), param, ctx) |
||||
|
if self.readable and not os.access(value, os.R_OK): |
||||
|
self.fail('%s "%s" is not readable.' % ( |
||||
|
self.path_type, |
||||
|
filename_to_ui(value) |
||||
|
), param, ctx) |
||||
|
|
||||
|
return self.coerce_path_result(rv) |
||||
|
|
||||
|
|
||||
|
class Tuple(CompositeParamType): |
||||
|
"""The default behavior of Click is to apply a type on a value directly. |
||||
|
This works well in most cases, except for when `nargs` is set to a fixed |
||||
|
count and different types should be used for different items. In this |
||||
|
case the :class:`Tuple` type can be used. This type can only be used |
||||
|
if `nargs` is set to a fixed number. |
||||
|
|
||||
|
For more information see :ref:`tuple-type`. |
||||
|
|
||||
|
This can be selected by using a Python tuple literal as a type. |
||||
|
|
||||
|
:param types: a list of types that should be used for the tuple items. |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, types): |
||||
|
self.types = [convert_type(ty) for ty in types] |
||||
|
|
||||
|
@property |
||||
|
def name(self): |
||||
|
return "<" + " ".join(ty.name for ty in self.types) + ">" |
||||
|
|
||||
|
@property |
||||
|
def arity(self): |
||||
|
return len(self.types) |
||||
|
|
||||
|
def convert(self, value, param, ctx): |
||||
|
if len(value) != len(self.types): |
||||
|
raise TypeError('It would appear that nargs is set to conflict ' |
||||
|
'with the composite type arity.') |
||||
|
return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) |
||||
|
|
||||
|
|
||||
|
def convert_type(ty, default=None): |
||||
|
"""Converts a callable or python ty into the most appropriate param |
||||
|
ty. |
||||
|
""" |
||||
|
guessed_type = False |
||||
|
if ty is None and default is not None: |
||||
|
if isinstance(default, tuple): |
||||
|
ty = tuple(map(type, default)) |
||||
|
else: |
||||
|
ty = type(default) |
||||
|
guessed_type = True |
||||
|
|
||||
|
if isinstance(ty, tuple): |
||||
|
return Tuple(ty) |
||||
|
if isinstance(ty, ParamType): |
||||
|
return ty |
||||
|
if ty is text_type or ty is str or ty is None: |
||||
|
return STRING |
||||
|
if ty is int: |
||||
|
return INT |
||||
|
# Booleans are only okay if not guessed. This is done because for |
||||
|
# flags the default value is actually a bit of a lie in that it |
||||
|
# indicates which of the flags is the one we want. See get_default() |
||||
|
# for more information. |
||||
|
if ty is bool and not guessed_type: |
||||
|
return BOOL |
||||
|
if ty is float: |
||||
|
return FLOAT |
||||
|
if guessed_type: |
||||
|
return STRING |
||||
|
|
||||
|
# Catch a common mistake |
||||
|
if __debug__: |
||||
|
try: |
||||
|
if issubclass(ty, ParamType): |
||||
|
raise AssertionError('Attempted to use an uninstantiated ' |
||||
|
'parameter type (%s).' % ty) |
||||
|
except TypeError: |
||||
|
pass |
||||
|
return FuncParamType(ty) |
||||
|
|
||||
|
|
||||
|
#: A dummy parameter type that just does nothing. From a user's |
||||
|
#: perspective this appears to just be the same as `STRING` but internally |
||||
|
#: no string conversion takes place. This is necessary to achieve the |
||||
|
#: same bytes/unicode behavior on Python 2/3 in situations where you want |
||||
|
#: to not convert argument types. This is usually useful when working |
||||
|
#: with file paths as they can appear in bytes and unicode. |
||||
|
#: |
||||
|
#: For path related uses the :class:`Path` type is a better choice but |
||||
|
#: there are situations where an unprocessed type is useful which is why |
||||
|
#: it is is provided. |
||||
|
#: |
||||
|
#: .. versionadded:: 4.0 |
||||
|
UNPROCESSED = UnprocessedParamType() |
||||
|
|
||||
|
#: A unicode string parameter type which is the implicit default. This |
||||
|
#: can also be selected by using ``str`` as type. |
||||
|
STRING = StringParamType() |
||||
|
|
||||
|
#: An integer parameter. This can also be selected by using ``int`` as |
||||
|
#: type. |
||||
|
INT = IntParamType() |
||||
|
|
||||
|
#: A floating point value parameter. This can also be selected by using |
||||
|
#: ``float`` as type. |
||||
|
FLOAT = FloatParamType() |
||||
|
|
||||
|
#: A boolean parameter. This is the default for boolean flags. This can |
||||
|
#: also be selected by using ``bool`` as a type. |
||||
|
BOOL = BoolParamType() |
||||
|
|
||||
|
#: A UUID parameter. |
||||
|
UUID = UUIDParameterType() |
@ -0,0 +1,415 @@ |
|||||
|
import os |
||||
|
import sys |
||||
|
|
||||
|
from .globals import resolve_color_default |
||||
|
|
||||
|
from ._compat import text_type, open_stream, get_filesystem_encoding, \ |
||||
|
get_streerror, string_types, PY2, binary_streams, text_streams, \ |
||||
|
filename_to_ui, auto_wrap_for_ansi, strip_ansi, should_strip_ansi, \ |
||||
|
_default_text_stdout, _default_text_stderr, is_bytes, WIN |
||||
|
|
||||
|
if not PY2: |
||||
|
from ._compat import _find_binary_writer |
||||
|
elif WIN: |
||||
|
from ._winconsole import _get_windows_argv, \ |
||||
|
_hash_py_argv, _initial_argv_hash |
||||
|
|
||||
|
|
||||
|
echo_native_types = string_types + (bytes, bytearray) |
||||
|
|
||||
|
|
||||
|
def _posixify(name): |
||||
|
return '-'.join(name.split()).lower() |
||||
|
|
||||
|
|
||||
|
def safecall(func): |
||||
|
"""Wraps a function so that it swallows exceptions.""" |
||||
|
def wrapper(*args, **kwargs): |
||||
|
try: |
||||
|
return func(*args, **kwargs) |
||||
|
except Exception: |
||||
|
pass |
||||
|
return wrapper |
||||
|
|
||||
|
|
||||
|
def make_str(value): |
||||
|
"""Converts a value into a valid string.""" |
||||
|
if isinstance(value, bytes): |
||||
|
try: |
||||
|
return value.decode(get_filesystem_encoding()) |
||||
|
except UnicodeError: |
||||
|
return value.decode('utf-8', 'replace') |
||||
|
return text_type(value) |
||||
|
|
||||
|
|
||||
|
def make_default_short_help(help, max_length=45): |
||||
|
words = help.split() |
||||
|
total_length = 0 |
||||
|
result = [] |
||||
|
done = False |
||||
|
|
||||
|
for word in words: |
||||
|
if word[-1:] == '.': |
||||
|
done = True |
||||
|
new_length = result and 1 + len(word) or len(word) |
||||
|
if total_length + new_length > max_length: |
||||
|
result.append('...') |
||||
|
done = True |
||||
|
else: |
||||
|
if result: |
||||
|
result.append(' ') |
||||
|
result.append(word) |
||||
|
if done: |
||||
|
break |
||||
|
total_length += new_length |
||||
|
|
||||
|
return ''.join(result) |
||||
|
|
||||
|
|
||||
|
class LazyFile(object): |
||||
|
"""A lazy file works like a regular file but it does not fully open |
||||
|
the file but it does perform some basic checks early to see if the |
||||
|
filename parameter does make sense. This is useful for safely opening |
||||
|
files for writing. |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, filename, mode='r', encoding=None, errors='strict', |
||||
|
atomic=False): |
||||
|
self.name = filename |
||||
|
self.mode = mode |
||||
|
self.encoding = encoding |
||||
|
self.errors = errors |
||||
|
self.atomic = atomic |
||||
|
|
||||
|
if filename == '-': |
||||
|
self._f, self.should_close = open_stream(filename, mode, |
||||
|
encoding, errors) |
||||
|
else: |
||||
|
if 'r' in mode: |
||||
|
# Open and close the file in case we're opening it for |
||||
|
# reading so that we can catch at least some errors in |
||||
|
# some cases early. |
||||
|
open(filename, mode).close() |
||||
|
self._f = None |
||||
|
self.should_close = True |
||||
|
|
||||
|
def __getattr__(self, name): |
||||
|
return getattr(self.open(), name) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
if self._f is not None: |
||||
|
return repr(self._f) |
||||
|
return '<unopened file %r %s>' % (self.name, self.mode) |
||||
|
|
||||
|
def open(self): |
||||
|
"""Opens the file if it's not yet open. This call might fail with |
||||
|
a :exc:`FileError`. Not handling this error will produce an error |
||||
|
that Click shows. |
||||
|
""" |
||||
|
if self._f is not None: |
||||
|
return self._f |
||||
|
try: |
||||
|
rv, self.should_close = open_stream(self.name, self.mode, |
||||
|
self.encoding, |
||||
|
self.errors, |
||||
|
atomic=self.atomic) |
||||
|
except (IOError, OSError) as e: |
||||
|
from .exceptions import FileError |
||||
|
raise FileError(self.name, hint=get_streerror(e)) |
||||
|
self._f = rv |
||||
|
return rv |
||||
|
|
||||
|
def close(self): |
||||
|
"""Closes the underlying file, no matter what.""" |
||||
|
if self._f is not None: |
||||
|
self._f.close() |
||||
|
|
||||
|
def close_intelligently(self): |
||||
|
"""This function only closes the file if it was opened by the lazy |
||||
|
file wrapper. For instance this will never close stdin. |
||||
|
""" |
||||
|
if self.should_close: |
||||
|
self.close() |
||||
|
|
||||
|
def __enter__(self): |
||||
|
return self |
||||
|
|
||||
|
def __exit__(self, exc_type, exc_value, tb): |
||||
|
self.close_intelligently() |
||||
|
|
||||
|
def __iter__(self): |
||||
|
self.open() |
||||
|
return iter(self._f) |
||||
|
|
||||
|
|
||||
|
class KeepOpenFile(object): |
||||
|
|
||||
|
def __init__(self, file): |
||||
|
self._file = file |
||||
|
|
||||
|
def __getattr__(self, name): |
||||
|
return getattr(self._file, name) |
||||
|
|
||||
|
def __enter__(self): |
||||
|
return self |
||||
|
|
||||
|
def __exit__(self, exc_type, exc_value, tb): |
||||
|
pass |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return repr(self._file) |
||||
|
|
||||
|
def __iter__(self): |
||||
|
return iter(self._file) |
||||
|
|
||||
|
|
||||
|
def echo(message=None, file=None, nl=True, err=False, color=None): |
||||
|
"""Prints a message plus a newline to the given file or stdout. On |
||||
|
first sight, this looks like the print function, but it has improved |
||||
|
support for handling Unicode and binary data that does not fail no |
||||
|
matter how badly configured the system is. |
||||
|
|
||||
|
Primarily it means that you can print binary data as well as Unicode |
||||
|
data on both 2.x and 3.x to the given file in the most appropriate way |
||||
|
possible. This is a very carefree function as in that it will try its |
||||
|
best to not fail. As of Click 6.0 this includes support for unicode |
||||
|
output on the Windows console. |
||||
|
|
||||
|
In addition to that, if `colorama`_ is installed, the echo function will |
||||
|
also support clever handling of ANSI codes. Essentially it will then |
||||
|
do the following: |
||||
|
|
||||
|
- add transparent handling of ANSI color codes on Windows. |
||||
|
- hide ANSI codes automatically if the destination file is not a |
||||
|
terminal. |
||||
|
|
||||
|
.. _colorama: http://pypi.python.org/pypi/colorama |
||||
|
|
||||
|
.. versionchanged:: 6.0 |
||||
|
As of Click 6.0 the echo function will properly support unicode |
||||
|
output on the windows console. Not that click does not modify |
||||
|
the interpreter in any way which means that `sys.stdout` or the |
||||
|
print statement or function will still not provide unicode support. |
||||
|
|
||||
|
.. versionchanged:: 2.0 |
||||
|
Starting with version 2.0 of Click, the echo function will work |
||||
|
with colorama if it's installed. |
||||
|
|
||||
|
.. versionadded:: 3.0 |
||||
|
The `err` parameter was added. |
||||
|
|
||||
|
.. versionchanged:: 4.0 |
||||
|
Added the `color` flag. |
||||
|
|
||||
|
:param message: the message to print |
||||
|
:param file: the file to write to (defaults to ``stdout``) |
||||
|
:param err: if set to true the file defaults to ``stderr`` instead of |
||||
|
``stdout``. This is faster and easier than calling |
||||
|
:func:`get_text_stderr` yourself. |
||||
|
:param nl: if set to `True` (the default) a newline is printed afterwards. |
||||
|
:param color: controls if the terminal supports ANSI colors or not. The |
||||
|
default is autodetection. |
||||
|
""" |
||||
|
if file is None: |
||||
|
if err: |
||||
|
file = _default_text_stderr() |
||||
|
else: |
||||
|
file = _default_text_stdout() |
||||
|
|
||||
|
# Convert non bytes/text into the native string type. |
||||
|
if message is not None and not isinstance(message, echo_native_types): |
||||
|
message = text_type(message) |
||||
|
|
||||
|
if nl: |
||||
|
message = message or u'' |
||||
|
if isinstance(message, text_type): |
||||
|
message += u'\n' |
||||
|
else: |
||||
|
message += b'\n' |
||||
|
|
||||
|
# If there is a message, and we're in Python 3, and the value looks |
||||
|
# like bytes, we manually need to find the binary stream and write the |
||||
|
# message in there. This is done separately so that most stream |
||||
|
# types will work as you would expect. Eg: you can write to StringIO |
||||
|
# for other cases. |
||||
|
if message and not PY2 and is_bytes(message): |
||||
|
binary_file = _find_binary_writer(file) |
||||
|
if binary_file is not None: |
||||
|
file.flush() |
||||
|
binary_file.write(message) |
||||
|
binary_file.flush() |
||||
|
return |
||||
|
|
||||
|
# ANSI-style support. If there is no message or we are dealing with |
||||
|
# bytes nothing is happening. If we are connected to a file we want |
||||
|
# to strip colors. If we are on windows we either wrap the stream |
||||
|
# to strip the color or we use the colorama support to translate the |
||||
|
# ansi codes to API calls. |
||||
|
if message and not is_bytes(message): |
||||
|
color = resolve_color_default(color) |
||||
|
if should_strip_ansi(file, color): |
||||
|
message = strip_ansi(message) |
||||
|
elif WIN: |
||||
|
if auto_wrap_for_ansi is not None: |
||||
|
file = auto_wrap_for_ansi(file) |
||||
|
elif not color: |
||||
|
message = strip_ansi(message) |
||||
|
|
||||
|
if message: |
||||
|
file.write(message) |
||||
|
file.flush() |
||||
|
|
||||
|
|
||||
|
def get_binary_stream(name): |
||||
|
"""Returns a system stream for byte processing. This essentially |
||||
|
returns the stream from the sys module with the given name but it |
||||
|
solves some compatibility issues between different Python versions. |
||||
|
Primarily this function is necessary for getting binary streams on |
||||
|
Python 3. |
||||
|
|
||||
|
:param name: the name of the stream to open. Valid names are ``'stdin'``, |
||||
|
``'stdout'`` and ``'stderr'`` |
||||
|
""" |
||||
|
opener = binary_streams.get(name) |
||||
|
if opener is None: |
||||
|
raise TypeError('Unknown standard stream %r' % name) |
||||
|
return opener() |
||||
|
|
||||
|
|
||||
|
def get_text_stream(name, encoding=None, errors='strict'): |
||||
|
"""Returns a system stream for text processing. This usually returns |
||||
|
a wrapped stream around a binary stream returned from |
||||
|
:func:`get_binary_stream` but it also can take shortcuts on Python 3 |
||||
|
for already correctly configured streams. |
||||
|
|
||||
|
:param name: the name of the stream to open. Valid names are ``'stdin'``, |
||||
|
``'stdout'`` and ``'stderr'`` |
||||
|
:param encoding: overrides the detected default encoding. |
||||
|
:param errors: overrides the default error mode. |
||||
|
""" |
||||
|
opener = text_streams.get(name) |
||||
|
if opener is None: |
||||
|
raise TypeError('Unknown standard stream %r' % name) |
||||
|
return opener(encoding, errors) |
||||
|
|
||||
|
|
||||
|
def open_file(filename, mode='r', encoding=None, errors='strict', |
||||
|
lazy=False, atomic=False): |
||||
|
"""This is similar to how the :class:`File` works but for manual |
||||
|
usage. Files are opened non lazy by default. This can open regular |
||||
|
files as well as stdin/stdout if ``'-'`` is passed. |
||||
|
|
||||
|
If stdin/stdout is returned the stream is wrapped so that the context |
||||
|
manager will not close the stream accidentally. This makes it possible |
||||
|
to always use the function like this without having to worry to |
||||
|
accidentally close a standard stream:: |
||||
|
|
||||
|
with open_file(filename) as f: |
||||
|
... |
||||
|
|
||||
|
.. versionadded:: 3.0 |
||||
|
|
||||
|
:param filename: the name of the file to open (or ``'-'`` for stdin/stdout). |
||||
|
:param mode: the mode in which to open the file. |
||||
|
:param encoding: the encoding to use. |
||||
|
:param errors: the error handling for this file. |
||||
|
:param lazy: can be flipped to true to open the file lazily. |
||||
|
:param atomic: in atomic mode writes go into a temporary file and it's |
||||
|
moved on close. |
||||
|
""" |
||||
|
if lazy: |
||||
|
return LazyFile(filename, mode, encoding, errors, atomic=atomic) |
||||
|
f, should_close = open_stream(filename, mode, encoding, errors, |
||||
|
atomic=atomic) |
||||
|
if not should_close: |
||||
|
f = KeepOpenFile(f) |
||||
|
return f |
||||
|
|
||||
|
|
||||
|
def get_os_args(): |
||||
|
"""This returns the argument part of sys.argv in the most appropriate |
||||
|
form for processing. What this means is that this return value is in |
||||
|
a format that works for Click to process but does not necessarily |
||||
|
correspond well to what's actually standard for the interpreter. |
||||
|
|
||||
|
On most environments the return value is ``sys.argv[:1]`` unchanged. |
||||
|
However if you are on Windows and running Python 2 the return value |
||||
|
will actually be a list of unicode strings instead because the |
||||
|
default behavior on that platform otherwise will not be able to |
||||
|
carry all possible values that sys.argv can have. |
||||
|
|
||||
|
.. versionadded:: 6.0 |
||||
|
""" |
||||
|
# We can only extract the unicode argv if sys.argv has not been |
||||
|
# changed since the startup of the application. |
||||
|
if PY2 and WIN and _initial_argv_hash == _hash_py_argv(): |
||||
|
return _get_windows_argv() |
||||
|
return sys.argv[1:] |
||||
|
|
||||
|
|
||||
|
def format_filename(filename, shorten=False): |
||||
|
"""Formats a filename for user display. The main purpose of this |
||||
|
function is to ensure that the filename can be displayed at all. This |
||||
|
will decode the filename to unicode if necessary in a way that it will |
||||
|
not fail. Optionally, it can shorten the filename to not include the |
||||
|
full path to the filename. |
||||
|
|
||||
|
:param filename: formats a filename for UI display. This will also convert |
||||
|
the filename into unicode without failing. |
||||
|
:param shorten: this optionally shortens the filename to strip of the |
||||
|
path that leads up to it. |
||||
|
""" |
||||
|
if shorten: |
||||
|
filename = os.path.basename(filename) |
||||
|
return filename_to_ui(filename) |
||||
|
|
||||
|
|
||||
|
def get_app_dir(app_name, roaming=True, force_posix=False): |
||||
|
r"""Returns the config folder for the application. The default behavior |
||||
|
is to return whatever is most appropriate for the operating system. |
||||
|
|
||||
|
To give you an idea, for an app called ``"Foo Bar"``, something like |
||||
|
the following folders could be returned: |
||||
|
|
||||
|
Mac OS X: |
||||
|
``~/Library/Application Support/Foo Bar`` |
||||
|
Mac OS X (POSIX): |
||||
|
``~/.foo-bar`` |
||||
|
Unix: |
||||
|
``~/.config/foo-bar`` |
||||
|
Unix (POSIX): |
||||
|
``~/.foo-bar`` |
||||
|
Win XP (roaming): |
||||
|
``C:\Documents and Settings\<user>\Local Settings\Application Data\Foo Bar`` |
||||
|
Win XP (not roaming): |
||||
|
``C:\Documents and Settings\<user>\Application Data\Foo Bar`` |
||||
|
Win 7 (roaming): |
||||
|
``C:\Users\<user>\AppData\Roaming\Foo Bar`` |
||||
|
Win 7 (not roaming): |
||||
|
``C:\Users\<user>\AppData\Local\Foo Bar`` |
||||
|
|
||||
|
.. versionadded:: 2.0 |
||||
|
|
||||
|
:param app_name: the application name. This should be properly capitalized |
||||
|
and can contain whitespace. |
||||
|
:param roaming: controls if the folder should be roaming or not on Windows. |
||||
|
Has no affect otherwise. |
||||
|
:param force_posix: if this is set to `True` then on any POSIX system the |
||||
|
folder will be stored in the home folder with a leading |
||||
|
dot instead of the XDG config home or darwin's |
||||
|
application support folder. |
||||
|
""" |
||||
|
if WIN: |
||||
|
key = roaming and 'APPDATA' or 'LOCALAPPDATA' |
||||
|
folder = os.environ.get(key) |
||||
|
if folder is None: |
||||
|
folder = os.path.expanduser('~') |
||||
|
return os.path.join(folder, app_name) |
||||
|
if force_posix: |
||||
|
return os.path.join(os.path.expanduser('~/.' + _posixify(app_name))) |
||||
|
if sys.platform == 'darwin': |
||||
|
return os.path.join(os.path.expanduser( |
||||
|
'~/Library/Application Support'), app_name) |
||||
|
return os.path.join( |
||||
|
os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), |
||||
|
_posixify(app_name)) |
@ -0,0 +1,2 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
__version__ = "2.5.3" |
@ -0,0 +1,89 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
""" |
||||
|
This module offers a generic easter computing method for any given year, using |
||||
|
Western, Orthodox or Julian algorithms. |
||||
|
""" |
||||
|
|
||||
|
import datetime |
||||
|
|
||||
|
__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"] |
||||
|
|
||||
|
EASTER_JULIAN = 1 |
||||
|
EASTER_ORTHODOX = 2 |
||||
|
EASTER_WESTERN = 3 |
||||
|
|
||||
|
|
||||
|
def easter(year, method=EASTER_WESTERN): |
||||
|
""" |
||||
|
This method was ported from the work done by GM Arts, |
||||
|
on top of the algorithm by Claus Tondering, which was |
||||
|
based in part on the algorithm of Ouding (1940), as |
||||
|
quoted in "Explanatory Supplement to the Astronomical |
||||
|
Almanac", P. Kenneth Seidelmann, editor. |
||||
|
|
||||
|
This algorithm implements three different easter |
||||
|
calculation methods: |
||||
|
|
||||
|
1 - Original calculation in Julian calendar, valid in |
||||
|
dates after 326 AD |
||||
|
2 - Original method, with date converted to Gregorian |
||||
|
calendar, valid in years 1583 to 4099 |
||||
|
3 - Revised method, in Gregorian calendar, valid in |
||||
|
years 1583 to 4099 as well |
||||
|
|
||||
|
These methods are represented by the constants: |
||||
|
|
||||
|
EASTER_JULIAN = 1 |
||||
|
EASTER_ORTHODOX = 2 |
||||
|
EASTER_WESTERN = 3 |
||||
|
|
||||
|
The default method is method 3. |
||||
|
|
||||
|
More about the algorithm may be found at: |
||||
|
|
||||
|
http://users.chariot.net.au/~gmarts/eastalg.htm |
||||
|
|
||||
|
and |
||||
|
|
||||
|
http://www.tondering.dk/claus/calendar.html |
||||
|
|
||||
|
""" |
||||
|
|
||||
|
if not (1 <= method <= 3): |
||||
|
raise ValueError("invalid method") |
||||
|
|
||||
|
# g - Golden year - 1 |
||||
|
# c - Century |
||||
|
# h - (23 - Epact) mod 30 |
||||
|
# i - Number of days from March 21 to Paschal Full Moon |
||||
|
# j - Weekday for PFM (0=Sunday, etc) |
||||
|
# p - Number of days from March 21 to Sunday on or before PFM |
||||
|
# (-6 to 28 methods 1 & 3, to 56 for method 2) |
||||
|
# e - Extra days to add for method 2 (converting Julian |
||||
|
# date to Gregorian date) |
||||
|
|
||||
|
y = year |
||||
|
g = y % 19 |
||||
|
e = 0 |
||||
|
if method < 3: |
||||
|
# Old method |
||||
|
i = (19*g + 15) % 30 |
||||
|
j = (y + y//4 + i) % 7 |
||||
|
if method == 2: |
||||
|
# Extra dates to convert Julian to Gregorian date |
||||
|
e = 10 |
||||
|
if y > 1600: |
||||
|
e = e + y//100 - 16 - (y//100 - 16)//4 |
||||
|
else: |
||||
|
# New method |
||||
|
c = y//100 |
||||
|
h = (c - c//4 - (8*c + 13)//25 + 19*g + 15) % 30 |
||||
|
i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11)) |
||||
|
j = (y + y//4 + i + 2 - c + c//4) % 7 |
||||
|
|
||||
|
# p can be from -6 to 56 corresponding to dates 22 March to 23 May |
||||
|
# (later dates apply to method 2, although 23 May never actually occurs) |
||||
|
p = i - j + e |
||||
|
d = 1 + (p + 27 + (p + 6)//40) % 31 |
||||
|
m = 3 + (p + 26)//30 |
||||
|
return datetime.date(int(y), int(m), int(d)) |
File diff suppressed because it is too large
@ -0,0 +1,530 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
import datetime |
||||
|
import calendar |
||||
|
|
||||
|
import operator |
||||
|
from math import copysign |
||||
|
|
||||
|
from six import integer_types |
||||
|
from warnings import warn |
||||
|
|
||||
|
__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] |
||||
|
|
||||
|
|
||||
|
class weekday(object): |
||||
|
__slots__ = ["weekday", "n"] |
||||
|
|
||||
|
def __init__(self, weekday, n=None): |
||||
|
self.weekday = weekday |
||||
|
self.n = n |
||||
|
|
||||
|
def __call__(self, n): |
||||
|
if n == self.n: |
||||
|
return self |
||||
|
else: |
||||
|
return self.__class__(self.weekday, n) |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
try: |
||||
|
if self.weekday != other.weekday or self.n != other.n: |
||||
|
return False |
||||
|
except AttributeError: |
||||
|
return False |
||||
|
return True |
||||
|
|
||||
|
def __repr__(self): |
||||
|
s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday] |
||||
|
if not self.n: |
||||
|
return s |
||||
|
else: |
||||
|
return "%s(%+d)" % (s, self.n) |
||||
|
|
||||
|
MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([weekday(x) for x in range(7)]) |
||||
|
|
||||
|
|
||||
|
class relativedelta(object): |
||||
|
""" |
||||
|
The relativedelta type is based on the specification of the excellent |
||||
|
work done by M.-A. Lemburg in his |
||||
|
`mx.DateTime <http://www.egenix.com/files/python/mxDateTime.html>`_ extension. |
||||
|
However, notice that this type does *NOT* implement the same algorithm as |
||||
|
his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. |
||||
|
|
||||
|
There are two different ways to build a relativedelta instance. The |
||||
|
first one is passing it two date/datetime classes:: |
||||
|
|
||||
|
relativedelta(datetime1, datetime2) |
||||
|
|
||||
|
The second one is passing it any number of the following keyword arguments:: |
||||
|
|
||||
|
relativedelta(arg1=x,arg2=y,arg3=z...) |
||||
|
|
||||
|
year, month, day, hour, minute, second, microsecond: |
||||
|
Absolute information (argument is singular); adding or subtracting a |
||||
|
relativedelta with absolute information does not perform an aritmetic |
||||
|
operation, but rather REPLACES the corresponding value in the |
||||
|
original datetime with the value(s) in relativedelta. |
||||
|
|
||||
|
years, months, weeks, days, hours, minutes, seconds, microseconds: |
||||
|
Relative information, may be negative (argument is plural); adding |
||||
|
or subtracting a relativedelta with relative information performs |
||||
|
the corresponding aritmetic operation on the original datetime value |
||||
|
with the information in the relativedelta. |
||||
|
|
||||
|
weekday: |
||||
|
One of the weekday instances (MO, TU, etc). These instances may |
||||
|
receive a parameter N, specifying the Nth weekday, which could |
||||
|
be positive or negative (like MO(+1) or MO(-2). Not specifying |
||||
|
it is the same as specifying +1. You can also use an integer, |
||||
|
where 0=MO. |
||||
|
|
||||
|
leapdays: |
||||
|
Will add given days to the date found, if year is a leap |
||||
|
year, and the date found is post 28 of february. |
||||
|
|
||||
|
yearday, nlyearday: |
||||
|
Set the yearday or the non-leap year day (jump leap days). |
||||
|
These are converted to day/month/leapdays information. |
||||
|
|
||||
|
Here is the behavior of operations with relativedelta: |
||||
|
|
||||
|
1. Calculate the absolute year, using the 'year' argument, or the |
||||
|
original datetime year, if the argument is not present. |
||||
|
|
||||
|
2. Add the relative 'years' argument to the absolute year. |
||||
|
|
||||
|
3. Do steps 1 and 2 for month/months. |
||||
|
|
||||
|
4. Calculate the absolute day, using the 'day' argument, or the |
||||
|
original datetime day, if the argument is not present. Then, |
||||
|
subtract from the day until it fits in the year and month |
||||
|
found after their operations. |
||||
|
|
||||
|
5. Add the relative 'days' argument to the absolute day. Notice |
||||
|
that the 'weeks' argument is multiplied by 7 and added to |
||||
|
'days'. |
||||
|
|
||||
|
6. Do steps 1 and 2 for hour/hours, minute/minutes, second/seconds, |
||||
|
microsecond/microseconds. |
||||
|
|
||||
|
7. If the 'weekday' argument is present, calculate the weekday, |
||||
|
with the given (wday, nth) tuple. wday is the index of the |
||||
|
weekday (0-6, 0=Mon), and nth is the number of weeks to add |
||||
|
forward or backward, depending on its signal. Notice that if |
||||
|
the calculated date is already Monday, for example, using |
||||
|
(0, 1) or (0, -1) won't change the day. |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, dt1=None, dt2=None, |
||||
|
years=0, months=0, days=0, leapdays=0, weeks=0, |
||||
|
hours=0, minutes=0, seconds=0, microseconds=0, |
||||
|
year=None, month=None, day=None, weekday=None, |
||||
|
yearday=None, nlyearday=None, |
||||
|
hour=None, minute=None, second=None, microsecond=None): |
||||
|
|
||||
|
# Check for non-integer values in integer-only quantities |
||||
|
if any(x is not None and x != int(x) for x in (years, months)): |
||||
|
raise ValueError("Non-integer years and months are " |
||||
|
"ambiguous and not currently supported.") |
||||
|
|
||||
|
if dt1 and dt2: |
||||
|
# datetime is a subclass of date. So both must be date |
||||
|
if not (isinstance(dt1, datetime.date) and |
||||
|
isinstance(dt2, datetime.date)): |
||||
|
raise TypeError("relativedelta only diffs datetime/date") |
||||
|
|
||||
|
# We allow two dates, or two datetimes, so we coerce them to be |
||||
|
# of the same type |
||||
|
if (isinstance(dt1, datetime.datetime) != |
||||
|
isinstance(dt2, datetime.datetime)): |
||||
|
if not isinstance(dt1, datetime.datetime): |
||||
|
dt1 = datetime.datetime.fromordinal(dt1.toordinal()) |
||||
|
elif not isinstance(dt2, datetime.datetime): |
||||
|
dt2 = datetime.datetime.fromordinal(dt2.toordinal()) |
||||
|
|
||||
|
self.years = 0 |
||||
|
self.months = 0 |
||||
|
self.days = 0 |
||||
|
self.leapdays = 0 |
||||
|
self.hours = 0 |
||||
|
self.minutes = 0 |
||||
|
self.seconds = 0 |
||||
|
self.microseconds = 0 |
||||
|
self.year = None |
||||
|
self.month = None |
||||
|
self.day = None |
||||
|
self.weekday = None |
||||
|
self.hour = None |
||||
|
self.minute = None |
||||
|
self.second = None |
||||
|
self.microsecond = None |
||||
|
self._has_time = 0 |
||||
|
|
||||
|
# Get year / month delta between the two |
||||
|
months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month) |
||||
|
self._set_months(months) |
||||
|
|
||||
|
# Remove the year/month delta so the timedelta is just well-defined |
||||
|
# time units (seconds, days and microseconds) |
||||
|
dtm = self.__radd__(dt2) |
||||
|
|
||||
|
# If we've overshot our target, make an adjustment |
||||
|
if dt1 < dt2: |
||||
|
compare = operator.gt |
||||
|
increment = 1 |
||||
|
else: |
||||
|
compare = operator.lt |
||||
|
increment = -1 |
||||
|
|
||||
|
while compare(dt1, dtm): |
||||
|
months += increment |
||||
|
self._set_months(months) |
||||
|
dtm = self.__radd__(dt2) |
||||
|
|
||||
|
# Get the timedelta between the "months-adjusted" date and dt1 |
||||
|
delta = dt1 - dtm |
||||
|
self.seconds = delta.seconds + delta.days * 86400 |
||||
|
self.microseconds = delta.microseconds |
||||
|
else: |
||||
|
# Relative information |
||||
|
self.years = years |
||||
|
self.months = months |
||||
|
self.days = days + weeks * 7 |
||||
|
self.leapdays = leapdays |
||||
|
self.hours = hours |
||||
|
self.minutes = minutes |
||||
|
self.seconds = seconds |
||||
|
self.microseconds = microseconds |
||||
|
|
||||
|
# Absolute information |
||||
|
self.year = year |
||||
|
self.month = month |
||||
|
self.day = day |
||||
|
self.hour = hour |
||||
|
self.minute = minute |
||||
|
self.second = second |
||||
|
self.microsecond = microsecond |
||||
|
|
||||
|
if any(x is not None and int(x) != x |
||||
|
for x in (year, month, day, hour, |
||||
|
minute, second, microsecond)): |
||||
|
# For now we'll deprecate floats - later it'll be an error. |
||||
|
warn("Non-integer value passed as absolute information. " + |
||||
|
"This is not a well-defined condition and will raise " + |
||||
|
"errors in future versions.", DeprecationWarning) |
||||
|
|
||||
|
|
||||
|
if isinstance(weekday, integer_types): |
||||
|
self.weekday = weekdays[weekday] |
||||
|
else: |
||||
|
self.weekday = weekday |
||||
|
|
||||
|
yday = 0 |
||||
|
if nlyearday: |
||||
|
yday = nlyearday |
||||
|
elif yearday: |
||||
|
yday = yearday |
||||
|
if yearday > 59: |
||||
|
self.leapdays = -1 |
||||
|
if yday: |
||||
|
ydayidx = [31, 59, 90, 120, 151, 181, 212, |
||||
|
243, 273, 304, 334, 366] |
||||
|
for idx, ydays in enumerate(ydayidx): |
||||
|
if yday <= ydays: |
||||
|
self.month = idx+1 |
||||
|
if idx == 0: |
||||
|
self.day = yday |
||||
|
else: |
||||
|
self.day = yday-ydayidx[idx-1] |
||||
|
break |
||||
|
else: |
||||
|
raise ValueError("invalid year day (%d)" % yday) |
||||
|
|
||||
|
self._fix() |
||||
|
|
||||
|
def _fix(self): |
||||
|
if abs(self.microseconds) > 999999: |
||||
|
s = _sign(self.microseconds) |
||||
|
div, mod = divmod(self.microseconds * s, 1000000) |
||||
|
self.microseconds = mod * s |
||||
|
self.seconds += div * s |
||||
|
if abs(self.seconds) > 59: |
||||
|
s = _sign(self.seconds) |
||||
|
div, mod = divmod(self.seconds * s, 60) |
||||
|
self.seconds = mod * s |
||||
|
self.minutes += div * s |
||||
|
if abs(self.minutes) > 59: |
||||
|
s = _sign(self.minutes) |
||||
|
div, mod = divmod(self.minutes * s, 60) |
||||
|
self.minutes = mod * s |
||||
|
self.hours += div * s |
||||
|
if abs(self.hours) > 23: |
||||
|
s = _sign(self.hours) |
||||
|
div, mod = divmod(self.hours * s, 24) |
||||
|
self.hours = mod * s |
||||
|
self.days += div * s |
||||
|
if abs(self.months) > 11: |
||||
|
s = _sign(self.months) |
||||
|
div, mod = divmod(self.months * s, 12) |
||||
|
self.months = mod * s |
||||
|
self.years += div * s |
||||
|
if (self.hours or self.minutes or self.seconds or self.microseconds |
||||
|
or self.hour is not None or self.minute is not None or |
||||
|
self.second is not None or self.microsecond is not None): |
||||
|
self._has_time = 1 |
||||
|
else: |
||||
|
self._has_time = 0 |
||||
|
|
||||
|
@property |
||||
|
def weeks(self): |
||||
|
return self.days // 7 |
||||
|
@weeks.setter |
||||
|
def weeks(self, value): |
||||
|
self.days = self.days - (self.weeks * 7) + value * 7 |
||||
|
|
||||
|
def _set_months(self, months): |
||||
|
self.months = months |
||||
|
if abs(self.months) > 11: |
||||
|
s = _sign(self.months) |
||||
|
div, mod = divmod(self.months * s, 12) |
||||
|
self.months = mod * s |
||||
|
self.years = div * s |
||||
|
else: |
||||
|
self.years = 0 |
||||
|
|
||||
|
def normalized(self): |
||||
|
""" |
||||
|
Return a version of this object represented entirely using integer |
||||
|
values for the relative attributes. |
||||
|
|
||||
|
>>> relativedelta(days=1.5, hours=2).normalized() |
||||
|
relativedelta(days=1, hours=14) |
||||
|
|
||||
|
:return: |
||||
|
Returns a :class:`dateutil.relativedelta.relativedelta` object. |
||||
|
""" |
||||
|
# Cascade remainders down (rounding each to roughly nearest microsecond) |
||||
|
days = int(self.days) |
||||
|
|
||||
|
hours_f = round(self.hours + 24 * (self.days - days), 11) |
||||
|
hours = int(hours_f) |
||||
|
|
||||
|
minutes_f = round(self.minutes + 60 * (hours_f - hours), 10) |
||||
|
minutes = int(minutes_f) |
||||
|
|
||||
|
seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8) |
||||
|
seconds = int(seconds_f) |
||||
|
|
||||
|
microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds)) |
||||
|
|
||||
|
# Constructor carries overflow back up with call to _fix() |
||||
|
return self.__class__(years=self.years, months=self.months, |
||||
|
days=days, hours=hours, minutes=minutes, |
||||
|
seconds=seconds, microseconds=microseconds, |
||||
|
leapdays=self.leapdays, year=self.year, |
||||
|
month=self.month, day=self.day, |
||||
|
weekday=self.weekday, hour=self.hour, |
||||
|
minute=self.minute, second=self.second, |
||||
|
microsecond=self.microsecond) |
||||
|
|
||||
|
def __add__(self, other): |
||||
|
if isinstance(other, relativedelta): |
||||
|
return self.__class__(years=other.years + self.years, |
||||
|
months=other.months + self.months, |
||||
|
days=other.days + self.days, |
||||
|
hours=other.hours + self.hours, |
||||
|
minutes=other.minutes + self.minutes, |
||||
|
seconds=other.seconds + self.seconds, |
||||
|
microseconds=(other.microseconds + |
||||
|
self.microseconds), |
||||
|
leapdays=other.leapdays or self.leapdays, |
||||
|
year=other.year or self.year, |
||||
|
month=other.month or self.month, |
||||
|
day=other.day or self.day, |
||||
|
weekday=other.weekday or self.weekday, |
||||
|
hour=other.hour or self.hour, |
||||
|
minute=other.minute or self.minute, |
||||
|
second=other.second or self.second, |
||||
|
microsecond=(other.microsecond or |
||||
|
self.microsecond)) |
||||
|
if not isinstance(other, datetime.date): |
||||
|
raise TypeError("unsupported type for add operation") |
||||
|
elif self._has_time and not isinstance(other, datetime.datetime): |
||||
|
other = datetime.datetime.fromordinal(other.toordinal()) |
||||
|
year = (self.year or other.year)+self.years |
||||
|
month = self.month or other.month |
||||
|
if self.months: |
||||
|
assert 1 <= abs(self.months) <= 12 |
||||
|
month += self.months |
||||
|
if month > 12: |
||||
|
year += 1 |
||||
|
month -= 12 |
||||
|
elif month < 1: |
||||
|
year -= 1 |
||||
|
month += 12 |
||||
|
day = min(calendar.monthrange(year, month)[1], |
||||
|
self.day or other.day) |
||||
|
repl = {"year": year, "month": month, "day": day} |
||||
|
for attr in ["hour", "minute", "second", "microsecond"]: |
||||
|
value = getattr(self, attr) |
||||
|
if value is not None: |
||||
|
repl[attr] = value |
||||
|
days = self.days |
||||
|
if self.leapdays and month > 2 and calendar.isleap(year): |
||||
|
days += self.leapdays |
||||
|
ret = (other.replace(**repl) |
||||
|
+ datetime.timedelta(days=days, |
||||
|
hours=self.hours, |
||||
|
minutes=self.minutes, |
||||
|
seconds=self.seconds, |
||||
|
microseconds=self.microseconds)) |
||||
|
if self.weekday: |
||||
|
weekday, nth = self.weekday.weekday, self.weekday.n or 1 |
||||
|
jumpdays = (abs(nth) - 1) * 7 |
||||
|
if nth > 0: |
||||
|
jumpdays += (7 - ret.weekday() + weekday) % 7 |
||||
|
else: |
||||
|
jumpdays += (ret.weekday() - weekday) % 7 |
||||
|
jumpdays *= -1 |
||||
|
ret += datetime.timedelta(days=jumpdays) |
||||
|
return ret |
||||
|
|
||||
|
def __radd__(self, other): |
||||
|
return self.__add__(other) |
||||
|
|
||||
|
def __rsub__(self, other): |
||||
|
return self.__neg__().__radd__(other) |
||||
|
|
||||
|
def __sub__(self, other): |
||||
|
if not isinstance(other, relativedelta): |
||||
|
raise TypeError("unsupported type for sub operation") |
||||
|
return self.__class__(years=self.years - other.years, |
||||
|
months=self.months - other.months, |
||||
|
days=self.days - other.days, |
||||
|
hours=self.hours - other.hours, |
||||
|
minutes=self.minutes - other.minutes, |
||||
|
seconds=self.seconds - other.seconds, |
||||
|
microseconds=self.microseconds - other.microseconds, |
||||
|
leapdays=self.leapdays or other.leapdays, |
||||
|
year=self.year or other.year, |
||||
|
month=self.month or other.month, |
||||
|
day=self.day or other.day, |
||||
|
weekday=self.weekday or other.weekday, |
||||
|
hour=self.hour or other.hour, |
||||
|
minute=self.minute or other.minute, |
||||
|
second=self.second or other.second, |
||||
|
microsecond=self.microsecond or other.microsecond) |
||||
|
|
||||
|
def __neg__(self): |
||||
|
return self.__class__(years=-self.years, |
||||
|
months=-self.months, |
||||
|
days=-self.days, |
||||
|
hours=-self.hours, |
||||
|
minutes=-self.minutes, |
||||
|
seconds=-self.seconds, |
||||
|
microseconds=-self.microseconds, |
||||
|
leapdays=self.leapdays, |
||||
|
year=self.year, |
||||
|
month=self.month, |
||||
|
day=self.day, |
||||
|
weekday=self.weekday, |
||||
|
hour=self.hour, |
||||
|
minute=self.minute, |
||||
|
second=self.second, |
||||
|
microsecond=self.microsecond) |
||||
|
|
||||
|
def __bool__(self): |
||||
|
return not (not self.years and |
||||
|
not self.months and |
||||
|
not self.days and |
||||
|
not self.hours and |
||||
|
not self.minutes and |
||||
|
not self.seconds and |
||||
|
not self.microseconds and |
||||
|
not self.leapdays and |
||||
|
self.year is None and |
||||
|
self.month is None and |
||||
|
self.day is None and |
||||
|
self.weekday is None and |
||||
|
self.hour is None and |
||||
|
self.minute is None and |
||||
|
self.second is None and |
||||
|
self.microsecond is None) |
||||
|
# Compatibility with Python 2.x |
||||
|
__nonzero__ = __bool__ |
||||
|
|
||||
|
def __mul__(self, other): |
||||
|
f = float(other) |
||||
|
return self.__class__(years=int(self.years * f), |
||||
|
months=int(self.months * f), |
||||
|
days=int(self.days * f), |
||||
|
hours=int(self.hours * f), |
||||
|
minutes=int(self.minutes * f), |
||||
|
seconds=int(self.seconds * f), |
||||
|
microseconds=int(self.microseconds * f), |
||||
|
leapdays=self.leapdays, |
||||
|
year=self.year, |
||||
|
month=self.month, |
||||
|
day=self.day, |
||||
|
weekday=self.weekday, |
||||
|
hour=self.hour, |
||||
|
minute=self.minute, |
||||
|
second=self.second, |
||||
|
microsecond=self.microsecond) |
||||
|
|
||||
|
__rmul__ = __mul__ |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
if not isinstance(other, relativedelta): |
||||
|
return False |
||||
|
if self.weekday or other.weekday: |
||||
|
if not self.weekday or not other.weekday: |
||||
|
return False |
||||
|
if self.weekday.weekday != other.weekday.weekday: |
||||
|
return False |
||||
|
n1, n2 = self.weekday.n, other.weekday.n |
||||
|
if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)): |
||||
|
return False |
||||
|
return (self.years == other.years and |
||||
|
self.months == other.months and |
||||
|
self.days == other.days and |
||||
|
self.hours == other.hours and |
||||
|
self.minutes == other.minutes and |
||||
|
self.seconds == other.seconds and |
||||
|
self.microseconds == other.microseconds and |
||||
|
self.leapdays == other.leapdays and |
||||
|
self.year == other.year and |
||||
|
self.month == other.month and |
||||
|
self.day == other.day and |
||||
|
self.hour == other.hour and |
||||
|
self.minute == other.minute and |
||||
|
self.second == other.second and |
||||
|
self.microsecond == other.microsecond) |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
def __div__(self, other): |
||||
|
return self.__mul__(1/float(other)) |
||||
|
|
||||
|
__truediv__ = __div__ |
||||
|
|
||||
|
def __repr__(self): |
||||
|
l = [] |
||||
|
for attr in ["years", "months", "days", "leapdays", |
||||
|
"hours", "minutes", "seconds", "microseconds"]: |
||||
|
value = getattr(self, attr) |
||||
|
if value: |
||||
|
l.append("{attr}={value:+g}".format(attr=attr, value=value)) |
||||
|
for attr in ["year", "month", "day", "weekday", |
||||
|
"hour", "minute", "second", "microsecond"]: |
||||
|
value = getattr(self, attr) |
||||
|
if value is not None: |
||||
|
l.append("{attr}={value}".format(attr=attr, value=repr(value))) |
||||
|
return "{classname}({attrs})".format(classname=self.__class__.__name__, |
||||
|
attrs=", ".join(l)) |
||||
|
|
||||
|
def _sign(x): |
||||
|
return int(copysign(1, x)) |
||||
|
|
||||
|
# vim:ts=4:sw=4:et |
File diff suppressed because it is too large
@ -0,0 +1,4 @@ |
|||||
|
from .tz import * |
||||
|
|
||||
|
__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", |
||||
|
"tzstr", "tzical", "tzwin", "tzwinlocal", "gettz"] |
@ -0,0 +1,18 @@ |
|||||
|
from six import PY3 |
||||
|
|
||||
|
__all__ = ['tzname_in_python2'] |
||||
|
|
||||
|
def tzname_in_python2(namefunc): |
||||
|
"""Change unicode output into bytestrings in Python 2 |
||||
|
|
||||
|
tzname() API changed in Python 3. It used to return bytes, but was changed |
||||
|
to unicode strings |
||||
|
""" |
||||
|
def adjust_encoding(*args, **kwargs): |
||||
|
name = namefunc(*args, **kwargs) |
||||
|
if name is not None and not PY3: |
||||
|
name = name.encode() |
||||
|
|
||||
|
return name |
||||
|
|
||||
|
return adjust_encoding |
@ -0,0 +1,979 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
""" |
||||
|
This module offers timezone implementations subclassing the abstract |
||||
|
:py:`datetime.tzinfo` type. There are classes to handle tzfile format files |
||||
|
(usually are in :file:`/etc/localtime`, :file:`/usr/share/zoneinfo`, etc), TZ |
||||
|
environment string (in all known formats), given ranges (with help from |
||||
|
relative deltas), local machine timezone, fixed offset timezone, and UTC |
||||
|
timezone. |
||||
|
""" |
||||
|
import datetime |
||||
|
import struct |
||||
|
import time |
||||
|
import sys |
||||
|
import os |
||||
|
|
||||
|
from six import string_types, PY3 |
||||
|
from ._common import tzname_in_python2 |
||||
|
|
||||
|
try: |
||||
|
from .win import tzwin, tzwinlocal |
||||
|
except ImportError: |
||||
|
tzwin = tzwinlocal = None |
||||
|
|
||||
|
relativedelta = None |
||||
|
parser = None |
||||
|
rrule = None |
||||
|
|
||||
|
ZERO = datetime.timedelta(0) |
||||
|
EPOCHORDINAL = datetime.datetime.utcfromtimestamp(0).toordinal() |
||||
|
|
||||
|
class tzutc(datetime.tzinfo): |
||||
|
|
||||
|
def utcoffset(self, dt): |
||||
|
return ZERO |
||||
|
|
||||
|
def dst(self, dt): |
||||
|
return ZERO |
||||
|
|
||||
|
@tzname_in_python2 |
||||
|
def tzname(self, dt): |
||||
|
return "UTC" |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
return (isinstance(other, tzutc) or |
||||
|
(isinstance(other, tzoffset) and other._offset == ZERO)) |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "%s()" % self.__class__.__name__ |
||||
|
|
||||
|
__reduce__ = object.__reduce__ |
||||
|
|
||||
|
|
||||
|
class tzoffset(datetime.tzinfo): |
||||
|
|
||||
|
def __init__(self, name, offset): |
||||
|
self._name = name |
||||
|
self._offset = datetime.timedelta(seconds=offset) |
||||
|
|
||||
|
def utcoffset(self, dt): |
||||
|
return self._offset |
||||
|
|
||||
|
def dst(self, dt): |
||||
|
return ZERO |
||||
|
|
||||
|
@tzname_in_python2 |
||||
|
def tzname(self, dt): |
||||
|
return self._name |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
return (isinstance(other, tzoffset) and |
||||
|
self._offset == other._offset) |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "%s(%s, %s)" % (self.__class__.__name__, |
||||
|
repr(self._name), |
||||
|
self._offset.days*86400+self._offset.seconds) |
||||
|
|
||||
|
__reduce__ = object.__reduce__ |
||||
|
|
||||
|
|
||||
|
class tzlocal(datetime.tzinfo): |
||||
|
def __init__(self): |
||||
|
self._std_offset = datetime.timedelta(seconds=-time.timezone) |
||||
|
if time.daylight: |
||||
|
self._dst_offset = datetime.timedelta(seconds=-time.altzone) |
||||
|
else: |
||||
|
self._dst_offset = self._std_offset |
||||
|
|
||||
|
def utcoffset(self, dt): |
||||
|
if dt is None: |
||||
|
return dt |
||||
|
|
||||
|
if self._isdst(dt): |
||||
|
return self._dst_offset |
||||
|
else: |
||||
|
return self._std_offset |
||||
|
|
||||
|
def dst(self, dt): |
||||
|
if self._isdst(dt): |
||||
|
return self._dst_offset-self._std_offset |
||||
|
else: |
||||
|
return ZERO |
||||
|
|
||||
|
@tzname_in_python2 |
||||
|
def tzname(self, dt): |
||||
|
return time.tzname[self._isdst(dt)] |
||||
|
|
||||
|
def _isdst(self, dt): |
||||
|
# We can't use mktime here. It is unstable when deciding if |
||||
|
# the hour near to a change is DST or not. |
||||
|
# |
||||
|
# timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, |
||||
|
# dt.minute, dt.second, dt.weekday(), 0, -1)) |
||||
|
# return time.localtime(timestamp).tm_isdst |
||||
|
# |
||||
|
# The code above yields the following result: |
||||
|
# |
||||
|
# >>> import tz, datetime |
||||
|
# >>> t = tz.tzlocal() |
||||
|
# >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() |
||||
|
# 'BRDT' |
||||
|
# >>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname() |
||||
|
# 'BRST' |
||||
|
# >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() |
||||
|
# 'BRST' |
||||
|
# >>> datetime.datetime(2003,2,15,22,tzinfo=t).tzname() |
||||
|
# 'BRDT' |
||||
|
# >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() |
||||
|
# 'BRDT' |
||||
|
# |
||||
|
# Here is a more stable implementation: |
||||
|
# |
||||
|
timestamp = ((dt.toordinal() - EPOCHORDINAL) * 86400 |
||||
|
+ dt.hour * 3600 |
||||
|
+ dt.minute * 60 |
||||
|
+ dt.second) |
||||
|
return time.localtime(timestamp+time.timezone).tm_isdst |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
return (isinstance(other, tzlocal) and |
||||
|
(self._std_offset == other._std_offset and |
||||
|
self._dst_offset == other._dst_offset)) |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "%s()" % self.__class__.__name__ |
||||
|
|
||||
|
__reduce__ = object.__reduce__ |
||||
|
|
||||
|
|
||||
|
class _ttinfo(object): |
||||
|
__slots__ = ["offset", "delta", "isdst", "abbr", "isstd", "isgmt"] |
||||
|
|
||||
|
def __init__(self): |
||||
|
for attr in self.__slots__: |
||||
|
setattr(self, attr, None) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
l = [] |
||||
|
for attr in self.__slots__: |
||||
|
value = getattr(self, attr) |
||||
|
if value is not None: |
||||
|
l.append("%s=%s" % (attr, repr(value))) |
||||
|
return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
if not isinstance(other, _ttinfo): |
||||
|
return False |
||||
|
return (self.offset == other.offset and |
||||
|
self.delta == other.delta and |
||||
|
self.isdst == other.isdst and |
||||
|
self.abbr == other.abbr and |
||||
|
self.isstd == other.isstd and |
||||
|
self.isgmt == other.isgmt) |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
def __getstate__(self): |
||||
|
state = {} |
||||
|
for name in self.__slots__: |
||||
|
state[name] = getattr(self, name, None) |
||||
|
return state |
||||
|
|
||||
|
def __setstate__(self, state): |
||||
|
for name in self.__slots__: |
||||
|
if name in state: |
||||
|
setattr(self, name, state[name]) |
||||
|
|
||||
|
|
||||
|
class tzfile(datetime.tzinfo): |
||||
|
|
||||
|
# http://www.twinsun.com/tz/tz-link.htm |
||||
|
# ftp://ftp.iana.org/tz/tz*.tar.gz |
||||
|
|
||||
|
def __init__(self, fileobj, filename=None): |
||||
|
file_opened_here = False |
||||
|
if isinstance(fileobj, string_types): |
||||
|
self._filename = fileobj |
||||
|
fileobj = open(fileobj, 'rb') |
||||
|
file_opened_here = True |
||||
|
elif filename is not None: |
||||
|
self._filename = filename |
||||
|
elif hasattr(fileobj, "name"): |
||||
|
self._filename = fileobj.name |
||||
|
else: |
||||
|
self._filename = repr(fileobj) |
||||
|
|
||||
|
# From tzfile(5): |
||||
|
# |
||||
|
# The time zone information files used by tzset(3) |
||||
|
# begin with the magic characters "TZif" to identify |
||||
|
# them as time zone information files, followed by |
||||
|
# sixteen bytes reserved for future use, followed by |
||||
|
# six four-byte values of type long, written in a |
||||
|
# ``standard'' byte order (the high-order byte |
||||
|
# of the value is written first). |
||||
|
try: |
||||
|
if fileobj.read(4).decode() != "TZif": |
||||
|
raise ValueError("magic not found") |
||||
|
|
||||
|
fileobj.read(16) |
||||
|
|
||||
|
( |
||||
|
# The number of UTC/local indicators stored in the file. |
||||
|
ttisgmtcnt, |
||||
|
|
||||
|
# The number of standard/wall indicators stored in the file. |
||||
|
ttisstdcnt, |
||||
|
|
||||
|
# The number of leap seconds for which data is |
||||
|
# stored in the file. |
||||
|
leapcnt, |
||||
|
|
||||
|
# The number of "transition times" for which data |
||||
|
# is stored in the file. |
||||
|
timecnt, |
||||
|
|
||||
|
# The number of "local time types" for which data |
||||
|
# is stored in the file (must not be zero). |
||||
|
typecnt, |
||||
|
|
||||
|
# The number of characters of "time zone |
||||
|
# abbreviation strings" stored in the file. |
||||
|
charcnt, |
||||
|
|
||||
|
) = struct.unpack(">6l", fileobj.read(24)) |
||||
|
|
||||
|
# The above header is followed by tzh_timecnt four-byte |
||||
|
# values of type long, sorted in ascending order. |
||||
|
# These values are written in ``standard'' byte order. |
||||
|
# Each is used as a transition time (as returned by |
||||
|
# time(2)) at which the rules for computing local time |
||||
|
# change. |
||||
|
|
||||
|
if timecnt: |
||||
|
self._trans_list = struct.unpack(">%dl" % timecnt, |
||||
|
fileobj.read(timecnt*4)) |
||||
|
else: |
||||
|
self._trans_list = [] |
||||
|
|
||||
|
# Next come tzh_timecnt one-byte values of type unsigned |
||||
|
# char; each one tells which of the different types of |
||||
|
# ``local time'' types described in the file is associated |
||||
|
# with the same-indexed transition time. These values |
||||
|
# serve as indices into an array of ttinfo structures that |
||||
|
# appears next in the file. |
||||
|
|
||||
|
if timecnt: |
||||
|
self._trans_idx = struct.unpack(">%dB" % timecnt, |
||||
|
fileobj.read(timecnt)) |
||||
|
else: |
||||
|
self._trans_idx = [] |
||||
|
|
||||
|
# Each ttinfo structure is written as a four-byte value |
||||
|
# for tt_gmtoff of type long, in a standard byte |
||||
|
# order, followed by a one-byte value for tt_isdst |
||||
|
# and a one-byte value for tt_abbrind. In each |
||||
|
# structure, tt_gmtoff gives the number of |
||||
|
# seconds to be added to UTC, tt_isdst tells whether |
||||
|
# tm_isdst should be set by localtime(3), and |
||||
|
# tt_abbrind serves as an index into the array of |
||||
|
# time zone abbreviation characters that follow the |
||||
|
# ttinfo structure(s) in the file. |
||||
|
|
||||
|
ttinfo = [] |
||||
|
|
||||
|
for i in range(typecnt): |
||||
|
ttinfo.append(struct.unpack(">lbb", fileobj.read(6))) |
||||
|
|
||||
|
abbr = fileobj.read(charcnt).decode() |
||||
|
|
||||
|
# Then there are tzh_leapcnt pairs of four-byte |
||||
|
# values, written in standard byte order; the |
||||
|
# first value of each pair gives the time (as |
||||
|
# returned by time(2)) at which a leap second |
||||
|
# occurs; the second gives the total number of |
||||
|
# leap seconds to be applied after the given time. |
||||
|
# The pairs of values are sorted in ascending order |
||||
|
# by time. |
||||
|
|
||||
|
# Not used, for now (but read anyway for correct file position) |
||||
|
if leapcnt: |
||||
|
leap = struct.unpack(">%dl" % (leapcnt*2), |
||||
|
fileobj.read(leapcnt*8)) |
||||
|
|
||||
|
# Then there are tzh_ttisstdcnt standard/wall |
||||
|
# indicators, each stored as a one-byte value; |
||||
|
# they tell whether the transition times associated |
||||
|
# with local time types were specified as standard |
||||
|
# time or wall clock time, and are used when |
||||
|
# a time zone file is used in handling POSIX-style |
||||
|
# time zone environment variables. |
||||
|
|
||||
|
if ttisstdcnt: |
||||
|
isstd = struct.unpack(">%db" % ttisstdcnt, |
||||
|
fileobj.read(ttisstdcnt)) |
||||
|
|
||||
|
# Finally, there are tzh_ttisgmtcnt UTC/local |
||||
|
# indicators, each stored as a one-byte value; |
||||
|
# they tell whether the transition times associated |
||||
|
# with local time types were specified as UTC or |
||||
|
# local time, and are used when a time zone file |
||||
|
# is used in handling POSIX-style time zone envi- |
||||
|
# ronment variables. |
||||
|
|
||||
|
if ttisgmtcnt: |
||||
|
isgmt = struct.unpack(">%db" % ttisgmtcnt, |
||||
|
fileobj.read(ttisgmtcnt)) |
||||
|
|
||||
|
# ** Everything has been read ** |
||||
|
finally: |
||||
|
if file_opened_here: |
||||
|
fileobj.close() |
||||
|
|
||||
|
# Build ttinfo list |
||||
|
self._ttinfo_list = [] |
||||
|
for i in range(typecnt): |
||||
|
gmtoff, isdst, abbrind = ttinfo[i] |
||||
|
# Round to full-minutes if that's not the case. Python's |
||||
|
# datetime doesn't accept sub-minute timezones. Check |
||||
|
# http://python.org/sf/1447945 for some information. |
||||
|
gmtoff = (gmtoff+30)//60*60 |
||||
|
tti = _ttinfo() |
||||
|
tti.offset = gmtoff |
||||
|
tti.delta = datetime.timedelta(seconds=gmtoff) |
||||
|
tti.isdst = isdst |
||||
|
tti.abbr = abbr[abbrind:abbr.find('\x00', abbrind)] |
||||
|
tti.isstd = (ttisstdcnt > i and isstd[i] != 0) |
||||
|
tti.isgmt = (ttisgmtcnt > i and isgmt[i] != 0) |
||||
|
self._ttinfo_list.append(tti) |
||||
|
|
||||
|
# Replace ttinfo indexes for ttinfo objects. |
||||
|
trans_idx = [] |
||||
|
for idx in self._trans_idx: |
||||
|
trans_idx.append(self._ttinfo_list[idx]) |
||||
|
self._trans_idx = tuple(trans_idx) |
||||
|
|
||||
|
# Set standard, dst, and before ttinfos. before will be |
||||
|
# used when a given time is before any transitions, |
||||
|
# and will be set to the first non-dst ttinfo, or to |
||||
|
# the first dst, if all of them are dst. |
||||
|
self._ttinfo_std = None |
||||
|
self._ttinfo_dst = None |
||||
|
self._ttinfo_before = None |
||||
|
if self._ttinfo_list: |
||||
|
if not self._trans_list: |
||||
|
self._ttinfo_std = self._ttinfo_first = self._ttinfo_list[0] |
||||
|
else: |
||||
|
for i in range(timecnt-1, -1, -1): |
||||
|
tti = self._trans_idx[i] |
||||
|
if not self._ttinfo_std and not tti.isdst: |
||||
|
self._ttinfo_std = tti |
||||
|
elif not self._ttinfo_dst and tti.isdst: |
||||
|
self._ttinfo_dst = tti |
||||
|
if self._ttinfo_std and self._ttinfo_dst: |
||||
|
break |
||||
|
else: |
||||
|
if self._ttinfo_dst and not self._ttinfo_std: |
||||
|
self._ttinfo_std = self._ttinfo_dst |
||||
|
|
||||
|
for tti in self._ttinfo_list: |
||||
|
if not tti.isdst: |
||||
|
self._ttinfo_before = tti |
||||
|
break |
||||
|
else: |
||||
|
self._ttinfo_before = self._ttinfo_list[0] |
||||
|
|
||||
|
# Now fix transition times to become relative to wall time. |
||||
|
# |
||||
|
# I'm not sure about this. In my tests, the tz source file |
||||
|
# is setup to wall time, and in the binary file isstd and |
||||
|
# isgmt are off, so it should be in wall time. OTOH, it's |
||||
|
# always in gmt time. Let me know if you have comments |
||||
|
# about this. |
||||
|
laststdoffset = 0 |
||||
|
self._trans_list = list(self._trans_list) |
||||
|
for i in range(len(self._trans_list)): |
||||
|
tti = self._trans_idx[i] |
||||
|
if not tti.isdst: |
||||
|
# This is std time. |
||||
|
self._trans_list[i] += tti.offset |
||||
|
laststdoffset = tti.offset |
||||
|
else: |
||||
|
# This is dst time. Convert to std. |
||||
|
self._trans_list[i] += laststdoffset |
||||
|
self._trans_list = tuple(self._trans_list) |
||||
|
|
||||
|
def _find_ttinfo(self, dt, laststd=0): |
||||
|
timestamp = ((dt.toordinal() - EPOCHORDINAL) * 86400 |
||||
|
+ dt.hour * 3600 |
||||
|
+ dt.minute * 60 |
||||
|
+ dt.second) |
||||
|
idx = 0 |
||||
|
for trans in self._trans_list: |
||||
|
if timestamp < trans: |
||||
|
break |
||||
|
idx += 1 |
||||
|
else: |
||||
|
return self._ttinfo_std |
||||
|
if idx == 0: |
||||
|
return self._ttinfo_before |
||||
|
if laststd: |
||||
|
while idx > 0: |
||||
|
tti = self._trans_idx[idx-1] |
||||
|
if not tti.isdst: |
||||
|
return tti |
||||
|
idx -= 1 |
||||
|
else: |
||||
|
return self._ttinfo_std |
||||
|
else: |
||||
|
return self._trans_idx[idx-1] |
||||
|
|
||||
|
def utcoffset(self, dt): |
||||
|
if dt is None: |
||||
|
return None |
||||
|
|
||||
|
if not self._ttinfo_std: |
||||
|
return ZERO |
||||
|
return self._find_ttinfo(dt).delta |
||||
|
|
||||
|
def dst(self, dt): |
||||
|
if not self._ttinfo_dst: |
||||
|
return ZERO |
||||
|
tti = self._find_ttinfo(dt) |
||||
|
if not tti.isdst: |
||||
|
return ZERO |
||||
|
|
||||
|
# The documentation says that utcoffset()-dst() must |
||||
|
# be constant for every dt. |
||||
|
return tti.delta-self._find_ttinfo(dt, laststd=1).delta |
||||
|
|
||||
|
# An alternative for that would be: |
||||
|
# |
||||
|
# return self._ttinfo_dst.offset-self._ttinfo_std.offset |
||||
|
# |
||||
|
# However, this class stores historical changes in the |
||||
|
# dst offset, so I belive that this wouldn't be the right |
||||
|
# way to implement this. |
||||
|
|
||||
|
@tzname_in_python2 |
||||
|
def tzname(self, dt): |
||||
|
if not self._ttinfo_std: |
||||
|
return None |
||||
|
return self._find_ttinfo(dt).abbr |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
if not isinstance(other, tzfile): |
||||
|
return False |
||||
|
return (self._trans_list == other._trans_list and |
||||
|
self._trans_idx == other._trans_idx and |
||||
|
self._ttinfo_list == other._ttinfo_list) |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "%s(%s)" % (self.__class__.__name__, repr(self._filename)) |
||||
|
|
||||
|
def __reduce__(self): |
||||
|
if not os.path.isfile(self._filename): |
||||
|
raise ValueError("Unpickable %s class" % self.__class__.__name__) |
||||
|
return (self.__class__, (self._filename,)) |
||||
|
|
||||
|
|
||||
|
class tzrange(datetime.tzinfo): |
||||
|
def __init__(self, stdabbr, stdoffset=None, |
||||
|
dstabbr=None, dstoffset=None, |
||||
|
start=None, end=None): |
||||
|
global relativedelta |
||||
|
if not relativedelta: |
||||
|
from dateutil import relativedelta |
||||
|
self._std_abbr = stdabbr |
||||
|
self._dst_abbr = dstabbr |
||||
|
if stdoffset is not None: |
||||
|
self._std_offset = datetime.timedelta(seconds=stdoffset) |
||||
|
else: |
||||
|
self._std_offset = ZERO |
||||
|
if dstoffset is not None: |
||||
|
self._dst_offset = datetime.timedelta(seconds=dstoffset) |
||||
|
elif dstabbr and stdoffset is not None: |
||||
|
self._dst_offset = self._std_offset+datetime.timedelta(hours=+1) |
||||
|
else: |
||||
|
self._dst_offset = ZERO |
||||
|
if dstabbr and start is None: |
||||
|
self._start_delta = relativedelta.relativedelta( |
||||
|
hours=+2, month=4, day=1, weekday=relativedelta.SU(+1)) |
||||
|
else: |
||||
|
self._start_delta = start |
||||
|
if dstabbr and end is None: |
||||
|
self._end_delta = relativedelta.relativedelta( |
||||
|
hours=+1, month=10, day=31, weekday=relativedelta.SU(-1)) |
||||
|
else: |
||||
|
self._end_delta = end |
||||
|
|
||||
|
def utcoffset(self, dt): |
||||
|
if dt is None: |
||||
|
return None |
||||
|
|
||||
|
if self._isdst(dt): |
||||
|
return self._dst_offset |
||||
|
else: |
||||
|
return self._std_offset |
||||
|
|
||||
|
def dst(self, dt): |
||||
|
if self._isdst(dt): |
||||
|
return self._dst_offset-self._std_offset |
||||
|
else: |
||||
|
return ZERO |
||||
|
|
||||
|
@tzname_in_python2 |
||||
|
def tzname(self, dt): |
||||
|
if self._isdst(dt): |
||||
|
return self._dst_abbr |
||||
|
else: |
||||
|
return self._std_abbr |
||||
|
|
||||
|
def _isdst(self, dt): |
||||
|
if not self._start_delta: |
||||
|
return False |
||||
|
year = datetime.datetime(dt.year, 1, 1) |
||||
|
start = year+self._start_delta |
||||
|
end = year+self._end_delta |
||||
|
dt = dt.replace(tzinfo=None) |
||||
|
if start < end: |
||||
|
return dt >= start and dt < end |
||||
|
else: |
||||
|
return dt >= start or dt < end |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
if not isinstance(other, tzrange): |
||||
|
return False |
||||
|
return (self._std_abbr == other._std_abbr and |
||||
|
self._dst_abbr == other._dst_abbr and |
||||
|
self._std_offset == other._std_offset and |
||||
|
self._dst_offset == other._dst_offset and |
||||
|
self._start_delta == other._start_delta and |
||||
|
self._end_delta == other._end_delta) |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "%s(...)" % self.__class__.__name__ |
||||
|
|
||||
|
__reduce__ = object.__reduce__ |
||||
|
|
||||
|
|
||||
|
class tzstr(tzrange): |
||||
|
|
||||
|
def __init__(self, s): |
||||
|
global parser |
||||
|
if not parser: |
||||
|
from dateutil import parser |
||||
|
self._s = s |
||||
|
|
||||
|
res = parser._parsetz(s) |
||||
|
if res is None: |
||||
|
raise ValueError("unknown string format") |
||||
|
|
||||
|
# Here we break the compatibility with the TZ variable handling. |
||||
|
# GMT-3 actually *means* the timezone -3. |
||||
|
if res.stdabbr in ("GMT", "UTC"): |
||||
|
res.stdoffset *= -1 |
||||
|
|
||||
|
# We must initialize it first, since _delta() needs |
||||
|
# _std_offset and _dst_offset set. Use False in start/end |
||||
|
# to avoid building it two times. |
||||
|
tzrange.__init__(self, res.stdabbr, res.stdoffset, |
||||
|
res.dstabbr, res.dstoffset, |
||||
|
start=False, end=False) |
||||
|
|
||||
|
if not res.dstabbr: |
||||
|
self._start_delta = None |
||||
|
self._end_delta = None |
||||
|
else: |
||||
|
self._start_delta = self._delta(res.start) |
||||
|
if self._start_delta: |
||||
|
self._end_delta = self._delta(res.end, isend=1) |
||||
|
|
||||
|
def _delta(self, x, isend=0): |
||||
|
kwargs = {} |
||||
|
if x.month is not None: |
||||
|
kwargs["month"] = x.month |
||||
|
if x.weekday is not None: |
||||
|
kwargs["weekday"] = relativedelta.weekday(x.weekday, x.week) |
||||
|
if x.week > 0: |
||||
|
kwargs["day"] = 1 |
||||
|
else: |
||||
|
kwargs["day"] = 31 |
||||
|
elif x.day: |
||||
|
kwargs["day"] = x.day |
||||
|
elif x.yday is not None: |
||||
|
kwargs["yearday"] = x.yday |
||||
|
elif x.jyday is not None: |
||||
|
kwargs["nlyearday"] = x.jyday |
||||
|
if not kwargs: |
||||
|
# Default is to start on first sunday of april, and end |
||||
|
# on last sunday of october. |
||||
|
if not isend: |
||||
|
kwargs["month"] = 4 |
||||
|
kwargs["day"] = 1 |
||||
|
kwargs["weekday"] = relativedelta.SU(+1) |
||||
|
else: |
||||
|
kwargs["month"] = 10 |
||||
|
kwargs["day"] = 31 |
||||
|
kwargs["weekday"] = relativedelta.SU(-1) |
||||
|
if x.time is not None: |
||||
|
kwargs["seconds"] = x.time |
||||
|
else: |
||||
|
# Default is 2AM. |
||||
|
kwargs["seconds"] = 7200 |
||||
|
if isend: |
||||
|
# Convert to standard time, to follow the documented way |
||||
|
# of working with the extra hour. See the documentation |
||||
|
# of the tzinfo class. |
||||
|
delta = self._dst_offset-self._std_offset |
||||
|
kwargs["seconds"] -= delta.seconds+delta.days*86400 |
||||
|
return relativedelta.relativedelta(**kwargs) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "%s(%s)" % (self.__class__.__name__, repr(self._s)) |
||||
|
|
||||
|
|
||||
|
class _tzicalvtzcomp(object): |
||||
|
def __init__(self, tzoffsetfrom, tzoffsetto, isdst, |
||||
|
tzname=None, rrule=None): |
||||
|
self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom) |
||||
|
self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto) |
||||
|
self.tzoffsetdiff = self.tzoffsetto-self.tzoffsetfrom |
||||
|
self.isdst = isdst |
||||
|
self.tzname = tzname |
||||
|
self.rrule = rrule |
||||
|
|
||||
|
|
||||
|
class _tzicalvtz(datetime.tzinfo): |
||||
|
def __init__(self, tzid, comps=[]): |
||||
|
self._tzid = tzid |
||||
|
self._comps = comps |
||||
|
self._cachedate = [] |
||||
|
self._cachecomp = [] |
||||
|
|
||||
|
def _find_comp(self, dt): |
||||
|
if len(self._comps) == 1: |
||||
|
return self._comps[0] |
||||
|
dt = dt.replace(tzinfo=None) |
||||
|
try: |
||||
|
return self._cachecomp[self._cachedate.index(dt)] |
||||
|
except ValueError: |
||||
|
pass |
||||
|
lastcomp = None |
||||
|
lastcompdt = None |
||||
|
for comp in self._comps: |
||||
|
if not comp.isdst: |
||||
|
# Handle the extra hour in DST -> STD |
||||
|
compdt = comp.rrule.before(dt-comp.tzoffsetdiff, inc=True) |
||||
|
else: |
||||
|
compdt = comp.rrule.before(dt, inc=True) |
||||
|
if compdt and (not lastcompdt or lastcompdt < compdt): |
||||
|
lastcompdt = compdt |
||||
|
lastcomp = comp |
||||
|
if not lastcomp: |
||||
|
# RFC says nothing about what to do when a given |
||||
|
# time is before the first onset date. We'll look for the |
||||
|
# first standard component, or the first component, if |
||||
|
# none is found. |
||||
|
for comp in self._comps: |
||||
|
if not comp.isdst: |
||||
|
lastcomp = comp |
||||
|
break |
||||
|
else: |
||||
|
lastcomp = comp[0] |
||||
|
self._cachedate.insert(0, dt) |
||||
|
self._cachecomp.insert(0, lastcomp) |
||||
|
if len(self._cachedate) > 10: |
||||
|
self._cachedate.pop() |
||||
|
self._cachecomp.pop() |
||||
|
return lastcomp |
||||
|
|
||||
|
def utcoffset(self, dt): |
||||
|
if dt is None: |
||||
|
return None |
||||
|
|
||||
|
return self._find_comp(dt).tzoffsetto |
||||
|
|
||||
|
def dst(self, dt): |
||||
|
comp = self._find_comp(dt) |
||||
|
if comp.isdst: |
||||
|
return comp.tzoffsetdiff |
||||
|
else: |
||||
|
return ZERO |
||||
|
|
||||
|
@tzname_in_python2 |
||||
|
def tzname(self, dt): |
||||
|
return self._find_comp(dt).tzname |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "<tzicalvtz %s>" % repr(self._tzid) |
||||
|
|
||||
|
__reduce__ = object.__reduce__ |
||||
|
|
||||
|
|
||||
|
class tzical(object): |
||||
|
def __init__(self, fileobj): |
||||
|
global rrule |
||||
|
if not rrule: |
||||
|
from dateutil import rrule |
||||
|
|
||||
|
if isinstance(fileobj, string_types): |
||||
|
self._s = fileobj |
||||
|
# ical should be encoded in UTF-8 with CRLF |
||||
|
fileobj = open(fileobj, 'r') |
||||
|
elif hasattr(fileobj, "name"): |
||||
|
self._s = fileobj.name |
||||
|
else: |
||||
|
self._s = repr(fileobj) |
||||
|
|
||||
|
self._vtz = {} |
||||
|
|
||||
|
self._parse_rfc(fileobj.read()) |
||||
|
|
||||
|
def keys(self): |
||||
|
return list(self._vtz.keys()) |
||||
|
|
||||
|
def get(self, tzid=None): |
||||
|
if tzid is None: |
||||
|
keys = list(self._vtz.keys()) |
||||
|
if len(keys) == 0: |
||||
|
raise ValueError("no timezones defined") |
||||
|
elif len(keys) > 1: |
||||
|
raise ValueError("more than one timezone available") |
||||
|
tzid = keys[0] |
||||
|
return self._vtz.get(tzid) |
||||
|
|
||||
|
def _parse_offset(self, s): |
||||
|
s = s.strip() |
||||
|
if not s: |
||||
|
raise ValueError("empty offset") |
||||
|
if s[0] in ('+', '-'): |
||||
|
signal = (-1, +1)[s[0] == '+'] |
||||
|
s = s[1:] |
||||
|
else: |
||||
|
signal = +1 |
||||
|
if len(s) == 4: |
||||
|
return (int(s[:2])*3600+int(s[2:])*60)*signal |
||||
|
elif len(s) == 6: |
||||
|
return (int(s[:2])*3600+int(s[2:4])*60+int(s[4:]))*signal |
||||
|
else: |
||||
|
raise ValueError("invalid offset: "+s) |
||||
|
|
||||
|
def _parse_rfc(self, s): |
||||
|
lines = s.splitlines() |
||||
|
if not lines: |
||||
|
raise ValueError("empty string") |
||||
|
|
||||
|
# Unfold |
||||
|
i = 0 |
||||
|
while i < len(lines): |
||||
|
line = lines[i].rstrip() |
||||
|
if not line: |
||||
|
del lines[i] |
||||
|
elif i > 0 and line[0] == " ": |
||||
|
lines[i-1] += line[1:] |
||||
|
del lines[i] |
||||
|
else: |
||||
|
i += 1 |
||||
|
|
||||
|
tzid = None |
||||
|
comps = [] |
||||
|
invtz = False |
||||
|
comptype = None |
||||
|
for line in lines: |
||||
|
if not line: |
||||
|
continue |
||||
|
name, value = line.split(':', 1) |
||||
|
parms = name.split(';') |
||||
|
if not parms: |
||||
|
raise ValueError("empty property name") |
||||
|
name = parms[0].upper() |
||||
|
parms = parms[1:] |
||||
|
if invtz: |
||||
|
if name == "BEGIN": |
||||
|
if value in ("STANDARD", "DAYLIGHT"): |
||||
|
# Process component |
||||
|
pass |
||||
|
else: |
||||
|
raise ValueError("unknown component: "+value) |
||||
|
comptype = value |
||||
|
founddtstart = False |
||||
|
tzoffsetfrom = None |
||||
|
tzoffsetto = None |
||||
|
rrulelines = [] |
||||
|
tzname = None |
||||
|
elif name == "END": |
||||
|
if value == "VTIMEZONE": |
||||
|
if comptype: |
||||
|
raise ValueError("component not closed: "+comptype) |
||||
|
if not tzid: |
||||
|
raise ValueError("mandatory TZID not found") |
||||
|
if not comps: |
||||
|
raise ValueError( |
||||
|
"at least one component is needed") |
||||
|
# Process vtimezone |
||||
|
self._vtz[tzid] = _tzicalvtz(tzid, comps) |
||||
|
invtz = False |
||||
|
elif value == comptype: |
||||
|
if not founddtstart: |
||||
|
raise ValueError("mandatory DTSTART not found") |
||||
|
if tzoffsetfrom is None: |
||||
|
raise ValueError( |
||||
|
"mandatory TZOFFSETFROM not found") |
||||
|
if tzoffsetto is None: |
||||
|
raise ValueError( |
||||
|
"mandatory TZOFFSETFROM not found") |
||||
|
# Process component |
||||
|
rr = None |
||||
|
if rrulelines: |
||||
|
rr = rrule.rrulestr("\n".join(rrulelines), |
||||
|
compatible=True, |
||||
|
ignoretz=True, |
||||
|
cache=True) |
||||
|
comp = _tzicalvtzcomp(tzoffsetfrom, tzoffsetto, |
||||
|
(comptype == "DAYLIGHT"), |
||||
|
tzname, rr) |
||||
|
comps.append(comp) |
||||
|
comptype = None |
||||
|
else: |
||||
|
raise ValueError("invalid component end: "+value) |
||||
|
elif comptype: |
||||
|
if name == "DTSTART": |
||||
|
rrulelines.append(line) |
||||
|
founddtstart = True |
||||
|
elif name in ("RRULE", "RDATE", "EXRULE", "EXDATE"): |
||||
|
rrulelines.append(line) |
||||
|
elif name == "TZOFFSETFROM": |
||||
|
if parms: |
||||
|
raise ValueError( |
||||
|
"unsupported %s parm: %s " % (name, parms[0])) |
||||
|
tzoffsetfrom = self._parse_offset(value) |
||||
|
elif name == "TZOFFSETTO": |
||||
|
if parms: |
||||
|
raise ValueError( |
||||
|
"unsupported TZOFFSETTO parm: "+parms[0]) |
||||
|
tzoffsetto = self._parse_offset(value) |
||||
|
elif name == "TZNAME": |
||||
|
if parms: |
||||
|
raise ValueError( |
||||
|
"unsupported TZNAME parm: "+parms[0]) |
||||
|
tzname = value |
||||
|
elif name == "COMMENT": |
||||
|
pass |
||||
|
else: |
||||
|
raise ValueError("unsupported property: "+name) |
||||
|
else: |
||||
|
if name == "TZID": |
||||
|
if parms: |
||||
|
raise ValueError( |
||||
|
"unsupported TZID parm: "+parms[0]) |
||||
|
tzid = value |
||||
|
elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"): |
||||
|
pass |
||||
|
else: |
||||
|
raise ValueError("unsupported property: "+name) |
||||
|
elif name == "BEGIN" and value == "VTIMEZONE": |
||||
|
tzid = None |
||||
|
comps = [] |
||||
|
invtz = True |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "%s(%s)" % (self.__class__.__name__, repr(self._s)) |
||||
|
|
||||
|
if sys.platform != "win32": |
||||
|
TZFILES = ["/etc/localtime", "localtime"] |
||||
|
TZPATHS = ["/usr/share/zoneinfo", "/usr/lib/zoneinfo", "/etc/zoneinfo"] |
||||
|
else: |
||||
|
TZFILES = [] |
||||
|
TZPATHS = [] |
||||
|
|
||||
|
|
||||
|
def gettz(name=None): |
||||
|
tz = None |
||||
|
if not name: |
||||
|
try: |
||||
|
name = os.environ["TZ"] |
||||
|
except KeyError: |
||||
|
pass |
||||
|
if name is None or name == ":": |
||||
|
for filepath in TZFILES: |
||||
|
if not os.path.isabs(filepath): |
||||
|
filename = filepath |
||||
|
for path in TZPATHS: |
||||
|
filepath = os.path.join(path, filename) |
||||
|
if os.path.isfile(filepath): |
||||
|
break |
||||
|
else: |
||||
|
continue |
||||
|
if os.path.isfile(filepath): |
||||
|
try: |
||||
|
tz = tzfile(filepath) |
||||
|
break |
||||
|
except (IOError, OSError, ValueError): |
||||
|
pass |
||||
|
else: |
||||
|
tz = tzlocal() |
||||
|
else: |
||||
|
if name.startswith(":"): |
||||
|
name = name[:-1] |
||||
|
if os.path.isabs(name): |
||||
|
if os.path.isfile(name): |
||||
|
tz = tzfile(name) |
||||
|
else: |
||||
|
tz = None |
||||
|
else: |
||||
|
for path in TZPATHS: |
||||
|
filepath = os.path.join(path, name) |
||||
|
if not os.path.isfile(filepath): |
||||
|
filepath = filepath.replace(' ', '_') |
||||
|
if not os.path.isfile(filepath): |
||||
|
continue |
||||
|
try: |
||||
|
tz = tzfile(filepath) |
||||
|
break |
||||
|
except (IOError, OSError, ValueError): |
||||
|
pass |
||||
|
else: |
||||
|
tz = None |
||||
|
if tzwin is not None: |
||||
|
try: |
||||
|
tz = tzwin(name) |
||||
|
except WindowsError: |
||||
|
tz = None |
||||
|
if not tz: |
||||
|
from dateutil.zoneinfo import gettz |
||||
|
tz = gettz(name) |
||||
|
if not tz: |
||||
|
for c in name: |
||||
|
# name must have at least one offset to be a tzstr |
||||
|
if c in "0123456789": |
||||
|
try: |
||||
|
tz = tzstr(name) |
||||
|
except ValueError: |
||||
|
pass |
||||
|
break |
||||
|
else: |
||||
|
if name in ("GMT", "UTC"): |
||||
|
tz = tzutc() |
||||
|
elif name in time.tzname: |
||||
|
tz = tzlocal() |
||||
|
return tz |
||||
|
|
||||
|
# vim:ts=4:sw=4:et |
@ -0,0 +1,330 @@ |
|||||
|
# This code was originally contributed by Jeffrey Harris. |
||||
|
import datetime |
||||
|
import struct |
||||
|
|
||||
|
from six.moves import winreg |
||||
|
from six import text_type |
||||
|
|
||||
|
try: |
||||
|
import ctypes |
||||
|
from ctypes import wintypes |
||||
|
except ValueError: |
||||
|
# ValueError is raised on non-Windows systems for some horrible reason. |
||||
|
raise ImportError("Running tzwin on non-Windows system") |
||||
|
|
||||
|
from ._common import tzname_in_python2 |
||||
|
|
||||
|
__all__ = ["tzwin", "tzwinlocal", "tzres"] |
||||
|
|
||||
|
ONEWEEK = datetime.timedelta(7) |
||||
|
|
||||
|
TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" |
||||
|
TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones" |
||||
|
TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" |
||||
|
|
||||
|
|
||||
|
def _settzkeyname(): |
||||
|
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) |
||||
|
try: |
||||
|
winreg.OpenKey(handle, TZKEYNAMENT).Close() |
||||
|
TZKEYNAME = TZKEYNAMENT |
||||
|
except WindowsError: |
||||
|
TZKEYNAME = TZKEYNAME9X |
||||
|
handle.Close() |
||||
|
return TZKEYNAME |
||||
|
|
||||
|
TZKEYNAME = _settzkeyname() |
||||
|
|
||||
|
|
||||
|
class tzres(object): |
||||
|
""" |
||||
|
Class for accessing `tzres.dll`, which contains timezone name related |
||||
|
resources. |
||||
|
|
||||
|
..versionadded:: 2.5.0 |
||||
|
""" |
||||
|
p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char |
||||
|
|
||||
|
def __init__(self, tzres_loc='tzres.dll'): |
||||
|
# Load the user32 DLL so we can load strings from tzres |
||||
|
user32 = ctypes.WinDLL('user32') |
||||
|
|
||||
|
# Specify the LoadStringW function |
||||
|
user32.LoadStringW.argtypes = (wintypes.HINSTANCE, |
||||
|
wintypes.UINT, |
||||
|
wintypes.LPWSTR, |
||||
|
ctypes.c_int) |
||||
|
|
||||
|
self.LoadStringW = user32.LoadStringW |
||||
|
self._tzres = ctypes.WinDLL(tzres_loc) |
||||
|
self.tzres_loc = tzres_loc |
||||
|
|
||||
|
def load_name(self, offset): |
||||
|
""" |
||||
|
Load a timezone name from a DLL offset (integer). |
||||
|
|
||||
|
>>> from dateutil.tzwin import tzres |
||||
|
>>> tzr = tzres() |
||||
|
>>> print(tzr.load_name(112)) |
||||
|
'Eastern Standard Time' |
||||
|
|
||||
|
:param offset: |
||||
|
A positive integer value referring to a string from the tzres dll. |
||||
|
|
||||
|
..note: |
||||
|
Offsets found in the registry are generally of the form |
||||
|
`@tzres.dll,-114`. The offset in this case if 114, not -114. |
||||
|
|
||||
|
""" |
||||
|
resource = self.p_wchar() |
||||
|
lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR) |
||||
|
nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0) |
||||
|
return resource[:nchar] |
||||
|
|
||||
|
def name_from_string(self, tzname_str): |
||||
|
""" |
||||
|
Parse strings as returned from the Windows registry into the time zone |
||||
|
name as defined in the registry. |
||||
|
|
||||
|
>>> from dateutil.tzwin import tzres |
||||
|
>>> tzr = tzres() |
||||
|
>>> print(tzr.name_from_string('@tzres.dll,-251')) |
||||
|
'Dateline Daylight Time' |
||||
|
>>> print(tzr.name_from_string('Eastern Standard Time')) |
||||
|
'Eastern Standard Time' |
||||
|
|
||||
|
:param tzname_str: |
||||
|
A timezone name string as returned from a Windows registry key. |
||||
|
|
||||
|
:return: |
||||
|
Returns the localized timezone string from tzres.dll if the string |
||||
|
is of the form `@tzres.dll,-offset`, else returns the input string. |
||||
|
""" |
||||
|
if not tzname_str.startswith('@'): |
||||
|
return tzname_str |
||||
|
|
||||
|
name_splt = tzname_str.split(',-') |
||||
|
try: |
||||
|
offset = int(name_splt[1]) |
||||
|
except: |
||||
|
raise ValueError("Malformed timezone string.") |
||||
|
|
||||
|
return self.load_name(offset) |
||||
|
|
||||
|
|
||||
|
class tzwinbase(datetime.tzinfo): |
||||
|
"""tzinfo class based on win32's timezones available in the registry.""" |
||||
|
def __eq__(self, other): |
||||
|
# Compare on all relevant dimensions, including name. |
||||
|
return (isinstance(other, tzwinbase) and |
||||
|
(self._stdoffset == other._stdoffset and |
||||
|
self._dstoffset == other._dstoffset and |
||||
|
self._stddayofweek == other._stddayofweek and |
||||
|
self._dstdayofweek == other._dstdayofweek and |
||||
|
self._stdweeknumber == other._stdweeknumber and |
||||
|
self._dstweeknumber == other._dstweeknumber and |
||||
|
self._stdhour == other._stdhour and |
||||
|
self._dsthour == other._dsthour and |
||||
|
self._stdminute == other._stdminute and |
||||
|
self._dstminute == other._dstminute and |
||||
|
self._stdname == other._stdname and |
||||
|
self._dstname == other._dstname)) |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
def utcoffset(self, dt): |
||||
|
isdst = self._isdst(dt) |
||||
|
|
||||
|
if isdst is None: |
||||
|
return None |
||||
|
elif isdst: |
||||
|
return datetime.timedelta(minutes=self._dstoffset) |
||||
|
else: |
||||
|
return datetime.timedelta(minutes=self._stdoffset) |
||||
|
|
||||
|
def dst(self, dt): |
||||
|
isdst = self._isdst(dt) |
||||
|
|
||||
|
if isdst is None: |
||||
|
return None |
||||
|
elif isdst: |
||||
|
minutes = self._dstoffset - self._stdoffset |
||||
|
return datetime.timedelta(minutes=minutes) |
||||
|
else: |
||||
|
return datetime.timedelta(0) |
||||
|
|
||||
|
@tzname_in_python2 |
||||
|
def tzname(self, dt): |
||||
|
if self._isdst(dt): |
||||
|
return self._dstname |
||||
|
else: |
||||
|
return self._stdname |
||||
|
|
||||
|
@staticmethod |
||||
|
def list(): |
||||
|
"""Return a list of all time zones known to the system.""" |
||||
|
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) |
||||
|
tzkey = winreg.OpenKey(handle, TZKEYNAME) |
||||
|
result = [winreg.EnumKey(tzkey, i) |
||||
|
for i in range(winreg.QueryInfoKey(tzkey)[0])] |
||||
|
tzkey.Close() |
||||
|
handle.Close() |
||||
|
return result |
||||
|
|
||||
|
def display(self): |
||||
|
return self._display |
||||
|
|
||||
|
def _isdst(self, dt): |
||||
|
if not self._dstmonth: |
||||
|
# dstmonth == 0 signals the zone has no daylight saving time |
||||
|
return False |
||||
|
elif dt is None: |
||||
|
return None |
||||
|
|
||||
|
dston = picknthweekday(dt.year, self._dstmonth, self._dstdayofweek, |
||||
|
self._dsthour, self._dstminute, |
||||
|
self._dstweeknumber) |
||||
|
dstoff = picknthweekday(dt.year, self._stdmonth, self._stddayofweek, |
||||
|
self._stdhour, self._stdminute, |
||||
|
self._stdweeknumber) |
||||
|
if dston < dstoff: |
||||
|
return dston <= dt.replace(tzinfo=None) < dstoff |
||||
|
else: |
||||
|
return not dstoff <= dt.replace(tzinfo=None) < dston |
||||
|
|
||||
|
|
||||
|
class tzwin(tzwinbase): |
||||
|
|
||||
|
def __init__(self, name): |
||||
|
self._name = name |
||||
|
|
||||
|
# multiple contexts only possible in 2.7 and 3.1, we still support 2.6 |
||||
|
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: |
||||
|
tzkeyname = text_type("{kn}\{name}").format(kn=TZKEYNAME, name=name) |
||||
|
with winreg.OpenKey(handle, tzkeyname) as tzkey: |
||||
|
keydict = valuestodict(tzkey) |
||||
|
|
||||
|
self._stdname = keydict["Std"] |
||||
|
self._dstname = keydict["Dlt"] |
||||
|
|
||||
|
self._display = keydict["Display"] |
||||
|
|
||||
|
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm |
||||
|
tup = struct.unpack("=3l16h", keydict["TZI"]) |
||||
|
self._stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 |
||||
|
self._dstoffset = self._stdoffset-tup[2] # + DaylightBias * -1 |
||||
|
|
||||
|
# for the meaning see the win32 TIME_ZONE_INFORMATION structure docs |
||||
|
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx |
||||
|
(self._stdmonth, |
||||
|
self._stddayofweek, # Sunday = 0 |
||||
|
self._stdweeknumber, # Last = 5 |
||||
|
self._stdhour, |
||||
|
self._stdminute) = tup[4:9] |
||||
|
|
||||
|
(self._dstmonth, |
||||
|
self._dstdayofweek, # Sunday = 0 |
||||
|
self._dstweeknumber, # Last = 5 |
||||
|
self._dsthour, |
||||
|
self._dstminute) = tup[12:17] |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "tzwin(%s)" % repr(self._name) |
||||
|
|
||||
|
def __reduce__(self): |
||||
|
return (self.__class__, (self._name,)) |
||||
|
|
||||
|
|
||||
|
class tzwinlocal(tzwinbase): |
||||
|
|
||||
|
def __init__(self): |
||||
|
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: |
||||
|
|
||||
|
with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: |
||||
|
keydict = valuestodict(tzlocalkey) |
||||
|
|
||||
|
self._stdname = keydict["StandardName"] |
||||
|
self._dstname = keydict["DaylightName"] |
||||
|
|
||||
|
try: |
||||
|
tzkeyname = text_type('{kn}\{sn}').format(kn=TZKEYNAME, |
||||
|
sn=self._stdname) |
||||
|
with winreg.OpenKey(handle, tzkeyname) as tzkey: |
||||
|
_keydict = valuestodict(tzkey) |
||||
|
self._display = _keydict["Display"] |
||||
|
except OSError: |
||||
|
self._display = None |
||||
|
|
||||
|
self._stdoffset = -keydict["Bias"]-keydict["StandardBias"] |
||||
|
self._dstoffset = self._stdoffset-keydict["DaylightBias"] |
||||
|
|
||||
|
# For reasons unclear, in this particular key, the day of week has been |
||||
|
# moved to the END of the SYSTEMTIME structure. |
||||
|
tup = struct.unpack("=8h", keydict["StandardStart"]) |
||||
|
|
||||
|
(self._stdmonth, |
||||
|
self._stdweeknumber, # Last = 5 |
||||
|
self._stdhour, |
||||
|
self._stdminute) = tup[1:5] |
||||
|
|
||||
|
self._stddayofweek = tup[7] |
||||
|
|
||||
|
tup = struct.unpack("=8h", keydict["DaylightStart"]) |
||||
|
|
||||
|
(self._dstmonth, |
||||
|
self._dstweeknumber, # Last = 5 |
||||
|
self._dsthour, |
||||
|
self._dstminute) = tup[1:5] |
||||
|
|
||||
|
self._dstdayofweek = tup[7] |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "tzwinlocal()" |
||||
|
|
||||
|
def __str__(self): |
||||
|
# str will return the standard name, not the daylight name. |
||||
|
return "tzwinlocal(%s)" % repr(self._stdname) |
||||
|
|
||||
|
def __reduce__(self): |
||||
|
return (self.__class__, ()) |
||||
|
|
||||
|
|
||||
|
def picknthweekday(year, month, dayofweek, hour, minute, whichweek): |
||||
|
""" dayofweek == 0 means Sunday, whichweek 5 means last instance """ |
||||
|
first = datetime.datetime(year, month, 1, hour, minute) |
||||
|
|
||||
|
# This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6), |
||||
|
# Because 7 % 7 = 0 |
||||
|
weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1) |
||||
|
wd = weekdayone + ((whichweek - 1) * ONEWEEK) |
||||
|
if (wd.month != month): |
||||
|
wd -= ONEWEEK |
||||
|
|
||||
|
return wd |
||||
|
|
||||
|
|
||||
|
def valuestodict(key): |
||||
|
"""Convert a registry key's values to a dictionary.""" |
||||
|
dout = {} |
||||
|
size = winreg.QueryInfoKey(key)[1] |
||||
|
tz_res = None |
||||
|
|
||||
|
for i in range(size): |
||||
|
key_name, value, dtype = winreg.EnumValue(key, i) |
||||
|
if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN: |
||||
|
# If it's a DWORD (32-bit integer), it's stored as unsigned - convert |
||||
|
# that to a proper signed integer |
||||
|
if value & (1 << 31): |
||||
|
value = value - (1 << 32) |
||||
|
elif dtype == winreg.REG_SZ: |
||||
|
# If it's a reference to the tzres DLL, load the actual string |
||||
|
if value.startswith('@tzres'): |
||||
|
tz_res = tz_res or tzres() |
||||
|
value = tz_res.name_from_string(value) |
||||
|
|
||||
|
value = value.rstrip('\x00') # Remove trailing nulls |
||||
|
|
||||
|
dout[key_name] = value |
||||
|
|
||||
|
return dout |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue