"""
intelligently parses command lines
flags: true/false values
options: other specified value (e.g. user name)
"""
from __future__ import print_function
import sys
py_ver = sys.version_info[:2]
is_win = sys.platform.startswith('win')
if not is_win:
from pty import fork
import resource
import termios
from syslog import syslog
import signal
KILL_SIGNALS = signal.SIGHUP, signal.SIGINT
elif py_ver >= (2, 7):
import signal
KILL_SIGNALS = signal.CTRL_C_EVENT, signal.CTRL_BREAK_EVENT
else:
KILL_SIGNALS = ()
if py_ver < (3, 0):
from __builtin__ import print as _print
else:
from builtins import print as _print
import datetime
import email
import inspect
import locale
import logging
import os
import re
import select
import shlex
import smtplib
import socket
import tempfile
import textwrap
import time
import traceback
from enum import Enum
from functools import partial
from subprocess import Popen, PIPE, STDOUT
from sys import stdout, stderr
"""
(help, kind, abbrev, type, choices, usage_name, remove)
- help --> the help message
- kind --> what kind of parameter
- flag --> simple boolean
- option --> option_name value
- multi --> option_name value option_name value
- required --> required_name value
- abbrev is a one-character string (defaults to first letter of
argument)
- type is a callable that converts the arguments to any Python
type; by default there is no conversion and type is effectively str
- choices is a discrete sequence of values used to restrict the
number of the valid options; by default there are no restrictions
(i.e. choices=None)
- usage_name is used as the name of the parameter in the help message
- remove determines if this argument is removed from sys.argv
"""
version = 0, 74, 37
# data
__all__ = (
'Alias', 'Command', 'Script', 'Main', 'Run', 'Spec',
'Bool','InputFile', 'OutputFile',
'IniError', 'IniFile', 'OrmError', 'OrmFile',
'FLAG', 'KEYWORD', 'OPTION', 'MULTI', 'REQUIRED',
'ScriptionError', 'ExecuteError', 'Execute',
'abort', 'get_response', 'help', 'mail', 'user_ids', 'print',
'stdout', 'stderr', 'wait_and_check',
)
VERBOSITY = 0
SCRIPTION_DEBUG = 0
LOCALE_ENCODING = locale.getpreferredencoding() or 'utf-8'
# bootstrap SCRIPTION_DEBUG
for arg in sys.argv:
if arg.startswith(('--SCRIPTION_DEBUG', '--SCRIPTION-DEBUG')):
SCRIPTION_DEBUG = 1
if arg[17:] == '=2':
SCRIPTION_DEBUG = 2
elif arg[17:] == '=3':
SCRIPTION_DEBUG = 3
elif arg[17:] == '=4':
SCRIPTION_DEBUG = 4
elif arg[17:] == '=5':
SCRIPTION_DEBUG = 5
module = globals()
script_module = None
registered = False
run_once = False
if py_ver < (3, 0):
bytes = str
else:
raw_input = input
basestring = str
unicode = str
# the __version__ and __VERSION__ are for compatibility with existing code,
# but those names are reserved by the Python interpreter and should not be
# used
_version_strings = 'version', 'VERSION', '__version__', '__VERSION__'
class NullHandler(logging.Handler):
"""
This handler does nothing. It's intended to be used to avoid the
"No handlers could be found for logger XXX" one-off warning. This is
important for library code, which may contain code to log events. If a user
of the library does not configure logging, the one-off warning might be
produced; to avoid this, the library developer simply needs to instantiate
a NullHandler and add it to the top-level logger of the library module or
package.
Taken from 2.7 lib.
"""
def handle(self, record):
"""Stub."""
def emit(self, record):
"""Stub."""
def createLock(self):
self.lock = None
logger = logging.getLogger('scription')
logger.addHandler(NullHandler())
class DocEnum(Enum):
"""compares equal to all cased versions of its name
accepts a doctring for each member
"""
def __new__(cls, *args):
"""Ignores arguments (will be handled in __init__)"""
obj = object.__new__(cls)
obj._value_ = None
return obj
def __init__(self, *args):
"""Can handle 0 or 1 argument; more requires a custom __init__.
0 = auto-number w/o docstring
1 = auto-number w/ docstring
2+ = needs custom __init__
"""
# first, fix _value_
self._value_ = self._name_.lower()
if len(args) == 1 and isinstance(args[0], basestring):
self.__doc__ = args[0]
elif args:
raise TypeError('%s not dealt with -- need custom __init__' % (args,))
def __eq__(self, other):
if isinstance(other, basestring):
return self._value_ == other.lower()
elif not isinstance(other, self.__class__):
return NotImplemented
return self is other
def __ne__(self, other):
return not self == other
@classmethod
def export_to(cls, namespace):
namespace.update(cls.__members__)
class SpecKind(DocEnum):
REQUIRED = "required value"
OPTION = "single value per name"
MULTI = "multiple values per name (list form)"
FLAG = "boolean value per name"
KEYWORD = 'unknown options'
SpecKind.export_to(module)
class ExecuteError(Exception):
"errors raised by Execute"
def __init__(self, msg=None, process=None):
self.process = process
Exception.__init__(self, msg)
class FailedPassword(ExecuteError):
"Bad or too few passwords"
class Timeout(ExecuteError):
"Execute timed out"
# deprecated
ExecutionError = ExecuteError
ExecuteTimeout = Timeout
class OrmError(ValueError):
"""
used to signify errors in the ORM file
"""
class ScriptionError(Exception):
"raised for errors in user script"
class empty(object):
def __add__(self, other):
# adding emptiness to something else is just something else
return other
def __nonzero__(self):
return False
__bool__ = __nonzero__
def __repr__(self):
return '<empty>'
def __str__(self):
return ''
empty = empty()
class Alias(object):
"adds aliases for the function"
def __init__(self, *aliases):
debug('recording aliases', aliases, verbose=2)
self.aliases = aliases
def __call__(self, func):
debug('applying aliases to', func, verbose=2)
global script_module
if script_module is None:
script_module = _func_globals(func)
script_module['module'] = _namespace(script_module)
script_module['script_name'] = '<unknown>'
script_module['script_main'] = None
script_module['script_commands'] = {}
for alias in self.aliases:
alias_name = alias.replace('_', '-')
script_module['script_commands'][alias_name] = func
return func
class Command(object):
"adds __scription__ to decorated function, and adds func to Command.subcommands"
def __init__(self, **annotations):
debug('Command -> initializing', verbose=1)
debug(annotations, verbose=2)
for name, annotation in annotations.items():
spec = Spec(annotation)
annotations[name] = spec
self.annotations = annotations
def __call__(self, func):
debug('Command -> applying to', func, verbose=1)
global script_module
if script_module is None:
script_module = _func_globals(func)
script_module['module'] = _namespace(script_module)
script_module['script_name'] = '<unknown>'
script_module['script_main'] = None
scr