<commit_before>#!/usr/bin/env python
# PYTHON_ARGCOMPLETE_OK
""" Install packages and setup home configs on fresh system. """
from __future__ import print_function
from BuildSrc import get_code
import argparse
import functools
import glob
import os
import shlex
import shutil
import subprocess
try:
    import apt
except ImportError:
    print("Don't use `debian` option.")
try:
    from argcomplete import autocomplete
except ImportError:
    def autocomplete(dummy):
        """ Dummy func. """
        pass

# Packages to install follow, broken down into categories.
MINIMUM = """ \
    vim build-essential git-core mercurial ccache fontconfig gdb lynx \
    colormake colordiff colorgcc exuberant-ctags dkms python-dev python-pip \
    p7zip-full rar zip unzip gzip lzip xz-utils liblzma-dev liblzma5 \
    htop iotop itop mytop dos2unix tree dfc \
    """

PROGRAMS = """ \
    htop iotop itop mytop pkg-config aptitude synaptic dos2unix \
    oxygen-molecule kubuntu-restricted-extras kubuntu-restricted-addons \
    brother-cups-wrapper-laser brother-cups-wrapper-laser1 \
    brother-lpr-drivers-laser brother-lpr-drivers-laser1 \
    k3b kid3 krita kolourpaint4 kchmviewer yakuake plasma-widget-quickaccess \
    kdirstat keepassx screen tmux \
    chromium-browser firefox rekonq \
    abiword baobab dvdrip easytag chm2pdf dia catdoc \
    gimp gimp-plugin-registry \
    fuse gparted quicksynergy tree dfc sharutils sharutils-doc \
    libnotify-bin libnotify-dev \
    redshift geoclue geoclue-hostip numlockx \
    samba samba-doc samba-dev samba-dbg cifs-utils wireshark libwireshark-dev \
    wondershaper vlc ffmpeg ffmpeg-doc mplayer mencoder \
    p7zip-full rar zip unzip gzip lzip xz-utils liblzma-dev liblzma5 \
    virtualbox-qt wine vagrant \
    ttf-xfree86-nonfree \
    """

KEYRINGS = """ \
    debian-keyring debian-archive-keyring gnome-keyring \
    debian-ports-archive-keyring python-gnomekeyring python-keyring \
    ubuntu-keyring \
    """

PROGRAMMING = """ \
    build-essential debianutils ubuntu-dev-tools mesa-utils openssh-server \
    automake automake1.9-doc ant ant-doc checkinstall checkbox \
    dkms docbook make-doc lynx kdiff3 kdiff3-doc patch rpm2cpio rpm \
    codeblocks kdevelop qt-sdk graphviz \
    colormake colordiff colorgcc jq nmap \
    vim vim-doc vim-gtk vim-rails vim-syntax-go vim-syntax-gtk vim-doc \
    flex flex-doc bison bison-doc exuberant-ctags sphinx-common sphinx-doc \
    bash-doc bash-builtins bashdb \
    zsh zsh-dbg zsh-dev zsh-doc zsh-lovers zshdb \
    clisp clisp-doc clisp-dev clisp-module-gdbm \
    coffeescript coffeescript-doc \
    docutils-common docutils-doc asciidoc xmlto docbook2x \
    erlang erlang-eunit \
    gdc golang golang-doc golang-src golang-codesearch-dev \
    gcc gcc-doc gcc-4.7-source libcunit1 gdb gdb-doc cgdb xxgdb ccache \
    libboost-all-dev libglm-dev libglew-dev libglfw-dev ncurses-doc \
    libncurses5-dev libncursesw5-dev libpcre3-dev zlib1g-dev libbz2-dev \
    libgnome2-dev libgnomeui-dev libgtk2.0-dev libatk1.0-dev libbonoboui2-dev \
    libcairo2-dev libx11-dev libxpm-dev libxt-dev libgmp3-dev libmpc-dev \
    libmpfr-dev libcurl4-openssl-dev libevent-dev libarchive-dev \
    libcurl4-gnutls-dev openmpi-bin openmpi-checkpoint openmpi-common \
    gfortran \
    ghc ghc-doc ghc-haddock ghc-prof haskell-debian-utils \
    haskell-devscripts haskell-doc cabal-install \
    junit junit-doc maven openjdk-7-doc openjdk-7-jdk openjdk-7-dbg \
    openjdk-7-source openjdk-7-demo icedtea-7-plugin \
    lua5.2 lua5.2-doc luadoc liblua5.2-dev \
    monodevelop-debugger-gdb monodevelop-nunit \
    nodejs nodejs-dev nodejs-legacy npm \
    perl perl-doc libperl-dev perl-modules libpadwalker-perl \
    libfile-next-perl \
    php5 php5-mysql phpunit php5-dev \
    swi-prolog swi-prolog-doc \
    python python-dev python-doc python-pip \
    python3 python3-dev python3-doc python3-pip jython jython-doc \
    pychecker pylint pep8 python-autopep8 ruby-full shunit2 \
    tcl-dev tcl-doc \
    tex-common texlive-latex-base texlive-font-utils \
    bzr bzr-builddeb bzr-doc python-bzrlib bzrtools git git-gui git-doc \
    mercurial subversion cvs \
    """

BABUN = """ \
    clisp lua perl python python3 ruby make cmake colordiff colorgcc \
    p7zip unzip zip gzip ncurses pcre mercurial bzr subversion \
    libboost-devel libboost_python-devel cppunit
    """

CABAL = "buildwrapper scion-browser hoogle terminfo happy hlint"

PY_PACKS = "argcomplete cram Pygments pytest trash-cli"

class NotSudo(Exception):
    """ Throw this if we aren't sudo but need to be. """
    pass

def make_cmd(src, dst):
    """ Generate a function helper. """
    def cmd_when_dst_empty(files, command, opts=None):
        """ When dst doesn't have file do:
                command(src + file, dst + file, *opts)
        """
        if opts is None:
            opts = ()
        for fil in files:
            sfile, dfile = [x + fil for x in (src, dst)]
            if not os.path.exists(dfile):
                print("{0} >>>>> {1}".format(sfile, dfile))
                command(sfile, dfile, *opts)
    return cmd_when_dst_empty

def home_config():
    """ Setup the dev environment, stuff goes in the user's home folder. """
    script_path = os.path.relpath(__file__)
    script_dir = os.path.dirname(script_path)
    if script_dir == '':
        script_dir = '.'

    dot_dir = script_dir + os.sep + 'dot_files' + os.sep
    home = os.path.expanduser('~') + os.sep

    # Get shell utilities
    shell_dir = home + '.shell' + os.sep
    get_code('https://bitbucket.org/sjl/hg-prompt/', shell_dir + '.hg-prompt')

    git_urls = [
        'https://github.com/magicmonty/bash-git-prompt.git',
        'https://github.com/starcraftman/zsh-git-prompt.git',
        'https://github.com/zsh-users/zsh-completions.git',
        'https://github.com/zsh-users/zsh-syntax-highlighting.git',
        'https://github.com/starcraftman/hhighlighter.git',
    ]

    for url in git_urls:
        target = '.' + url[url.rindex('/')+1:url.rindex('.git')]
        get_code(url, shell_dir + target)

    # Setup powerline fonts if not done.
    ddir = home + '.fonts'
    dpow = ddir + os.sep + 'powerline-fonts'
    if not os.path.exists(ddir) and subprocess.call(['which', 'fc-cache']) == 0:
        os.mkdir(ddir)
        get_code('https://github.com/Lokaltog/powerline-fonts', dpow)
        subprocess.call(['fc-cache', '-vf', ddir])

    ddir = home + '.ccache'
    if not os.path.exists(ddir):
        os.mkdir(ddir)

    files = [os.path.basename(x) for x in glob.glob(dot_dir + '.*')]
    helper = make_cmd(dot_dir, home)
    helper(files, os.symlink)

    print("NOTE: Remember to add user to smb.\nsudo smbpasswd -a username")

def home_restore():
    """ Undo changes by home_config & restore backup if exists. """
    arc_dir = os.path.expanduser('~/.home_bak/')
    home = os.path.expanduser('~/')
    dot_dir = home + '.my_scripts' + os.path.sep + 'dot_files' + os.path.sep

    for folder in ('.shell', '.fonts', '.ccache',):
        try:
            shutil.rmtree(home + folder)
        except OSError:
            pass

    # Clear existing configs if they are symlinks
    files = [os.path.basename(x) for x in glob.glob(dot_dir + '.*')]
    for fil in [home + fil for fil in files]:
        if os.path.islink(fil):
            os.remove(fil)

    arc_files = [os.path.basename(x) for x in glob.glob(arc_dir + '.*')]
    helper = make_cmd(arc_dir, home)
    helper(arc_files, os.rename)

    try:
        os.rmdir(arc_dir)
    except OSError:
        pass

def home_save():
    """ Save existing home configs to a backup dir. """
    arc_dir = os.path.expanduser('~/.home_bak/')
    home = os.path.expanduser('~/')
    dot_dir = home + '.my_scripts' + os.path.sep + 'dot_files' + os.path.sep

    if not os.path.exists(arc_dir):
        os.makedirs(arc_dir)

    files = [os.path.basename(x) for x in glob.glob(dot_dir + '.*')]
    files = [x for x in files if os.path.exists(home + x)]

    helper = make_cmd(home, arc_dir)
    helper(files, os.rename)

def packs_babun():
    """ Setup a fresh babun install. """
    # Install packages
    cmd = 'pact install ' + BABUN
    subprocess.call(shlex.split(cmd))

    # Now prepare then invoke regular setup link common files
    home = os.path.expanduser('~') + os.sep

    # Prevent fonts from running with home_config
    ddir = home + '.fonts'
    if not os.path.exists(ddir):
        os.mkdir(ddir)

    # Backup defaults and allow for new ones
    for name in ['.gitconfig', '.vim']:
        dfile = home + name
        dfile_bak = dfile + '_bak'
        if os.path.exists(dfile) and not os.path.exists(dfile_bak):
            os.rename(dfile, dfile_bak)

    home_config()

def packs_cabal():
    """ Installs haskell packages for Eclipse Haskell plugin. """
    cmd = 'cabal update'
    subprocess.call(shlex.split(cmd))

    cmd = 'cabal install ' + CABAL
    subprocess.call(shlex.split(cmd))

def packs_debian(server=False):
    """ Install packages on the current system. """
    if os.getuid() != 0:
        raise NotSudo

    if server:
        packages = MINIMUM.split()
    else:
        packages = (PROGRAMS + PROGRAMMING + KEYRINGS).split()

    cache = apt.Cache()
    print("One moment while we update cache.")
    cache.update()
    cache.open(None)
    print("Update done.")

    cmd = 'sudo apt-get install'.split()
    for pack in packages:
        try:
            package = cache[pack]
            if not package.is_installed:
                cmd.append(pack)
        except KeyError:
            print("Package couldn't be selected: %s" % pack)

    print("Please wait, running: " + " ".join(cmd))
    subprocess.call(cmd)

def packs_py():
    """ Installs python packages using pip. """
    if os.getuid() != 0:
        raise NotSudo

    # Use python package manager.
    cmd = 'sudo pip install ' + PY_PACKS
    subprocess.call(shlex.split(cmd))

    # Install python completion to system bash_completion.d.
    cmd = 'activate-global-python-argcomplete --user'
    subprocess.call(shlex.split(cmd))

def install_jshint():
    """ Setup jshint for progrmaming javascript with vim. """
    cmd = 'npm install jshint -g'
    subprocess.call(shlex.split(cmd))

def install_pipelight():
    """ Silverlight plugin for firefox/chrome on linux.
    http://www.webupd8.org/2013/08/pipelight-use-silverlight-in-your-linux.html
    """
    if os.getuid() != 0:
        raise NotSudo

    cmds = [
        'sudo apt-get remove flashplugin-installer',
        'sudo apt-add-repository ppa:pipelight/stable',
        'sudo apt-get update',
        'sudo apt-get install pipelight-multi',
        'pipelight-plugin --accept --enable silverlight',
        'pipelight-plugin --accept --enable flash',
    ]
    cmds = [x.split() for x in cmds]
    for cmd in cmds:
        subprocess.call(cmd)
    print("Installation over, remember to use a useragent switcher.")

def main():
    """ Main function. """
    mesg = """This script sets up a dev environment.

    choice       effect
    ------------------------------------------------------
    home         Setup home config files.
    home_restore Restore home files and undo home_save.
    home_save    Save existing home files.
    babun        Install babun packages.
    cabal        Install haskell packages for eclipse.
    debian       Install debian packages.
    server       Install debian packages for server, minimal.
    jshint       Install jshint via npm for javascript vim.
    pip          Install python libraries via pip.
    pipelight    Install pipelight flash & silverlight.
    """
    # Use a dict of funcs to process args
    actions = {
        'home':         home_config,
        'home_restore': home_restore,
        'home_save':    home_save,
        'babun':        packs_babun,
        'cabal':        packs_cabal,
        'debian':       packs_debian,
        'server':       functools.partial(packs_debian, True),
        'jshint':       install_jshint,
        'pip':          packs_py,
        'pipelight':    install_pipelight,
    }
    parser = argparse.ArgumentParser(description=mesg,
                                     formatter_class=argparse.
                                     RawDescriptionHelpFormatter)
    parser.add_argument('stages', nargs='+', help='stages to execute',
                        choices=sorted(actions.keys()))

    autocomplete(parser)
    args = parser.parse_args()  # Default parses argv[1:]

    try:
        for stage in args.stages:
            actions[stage]()
    except NotSudo:
        print("Rerun this part of script with sudo.")

if __name__ == '__main__':
    main()
<commit_msg>Redundant.<commit_after>#!/usr/bin/env python
# PYTHON_ARGCOMPLETE_OK
""" Install packages and setup home configs on fresh system. """
from __future__ import print_function
from BuildSrc import get_code
import argparse
import functools
import glob
import os
import shlex
import shutil
import subprocess
try:
    import apt
except ImportError:
    print("Don't use `debian` option.")
try:
    from argcomplete import autocomplete
except ImportError:
    def autocomplete(dummy):
        """ Dummy func. """
        pass

# Packages to install follow, broken down into categories.
MINIMUM = """ \
    vim build-essential git-core mercurial ccache fontconfig gdb lynx \
    colormake colordiff colorgcc exuberant-ctags dkms python-dev python-pip \
    p7zip-full rar zip unzip gzip lzip xz-utils liblzma-dev liblzma5 \
    htop iotop itop mytop dos2unix tree dfc \
    """

PROGRAMS = """ \
    htop iotop itop mytop pkg-config aptitude synaptic dos2unix \
    oxygen-molecule kubuntu-restricted-extras kubuntu-restricted-addons \
    brother-cups-wrapper-laser brother-cups-wrapper-laser1 \
    brother-lpr-drivers-laser brother-lpr-drivers-laser1 \
    k3b kid3 krita kolourpaint4 kchmviewer yakuake plasma-widget-quickaccess \
    kdirstat keepassx screen tmux \
    chromium-browser firefox rekonq \
    abiword baobab dvdrip easytag chm2pdf dia catdoc \
    gimp gimp-plugin-registry \
    fuse gparted quicksynergy tree dfc sharutils sharutils-doc \
    libnotify-bin libnotify-dev \
    redshift geoclue geoclue-hostip numlockx \
    samba samba-doc samba-dev samba-dbg cifs-utils wireshark libwireshark-dev \
    wondershaper vlc ffmpeg ffmpeg-doc mplayer mencoder \
    p7zip-full rar zip unzip gzip lzip xz-utils liblzma-dev liblzma5 \
    virtualbox-qt wine vagrant \
    ttf-xfree86-nonfree \
    """

KEYRINGS = """ \
    debian-keyring debian-archive-keyring gnome-keyring \
    debian-ports-archive-keyring python-gnomekeyring python-keyring \
    ubuntu-keyring \
    """

PROGRAMMING = """ \
    build-essential debianutils ubuntu-dev-tools mesa-utils openssh-server \
    automake automake1.9-doc ant ant-doc checkinstall checkbox \
    dkms docbook make-doc lynx kdiff3 kdiff3-doc patch rpm2cpio rpm \
    codeblocks kdevelop qt-sdk graphviz \
    colormake colordiff colorgcc jq nmap \
    vim vim-doc vim-gtk vim-rails vim-syntax-go vim-syntax-gtk vim-doc \
    flex flex-doc bison bison-doc exuberant-ctags sphinx-common sphinx-doc \
    bash-doc bash-builtins bashdb \
    zsh zsh-dbg zsh-dev zsh-doc zsh-lovers zshdb \
    clisp clisp-doc clisp-dev clisp-module-gdbm \
    coffeescript coffeescript-doc \
    docutils-common docutils-doc asciidoc xmlto docbook2x \
    erlang erlang-eunit \
    gdc golang golang-doc golang-src golang-codesearch-dev \
    gcc gcc-doc gcc-4.7-source libcunit1 gdb gdb-doc cgdb xxgdb ccache \
    libboost-all-dev libglm-dev libglew-dev libglfw-dev ncurses-doc \
    libncurses5-dev libncursesw5-dev libpcre3-dev zlib1g-dev libbz2-dev \
    libgnome2-dev libgnomeui-dev libgtk2.0-dev libatk1.0-dev libbonoboui2-dev \
    libcairo2-dev libx11-dev libxpm-dev libxt-dev libgmp3-dev libmpc-dev \
    libmpfr-dev libcurl4-openssl-dev libevent-dev libarchive-dev \
    libcurl4-gnutls-dev openmpi-bin openmpi-checkpoint openmpi-common \
    gfortran \
    ghc ghc-doc ghc-haddock ghc-prof haskell-debian-utils \
    haskell-devscripts haskell-doc cabal-install \
    junit junit-doc maven openjdk-7-doc openjdk-7-jdk openjdk-7-dbg \
    openjdk-7-source openjdk-7-demo icedtea-7-plugin \
    lua5.2 lua5.2-doc luadoc liblua5.2-dev \
    monodevelop-debugger-gdb monodevelop-nunit \
    nodejs nodejs-dev nodejs-legacy npm \
    perl perl-doc libperl-dev perl-modules libpadwalker-perl \
    libfile-next-perl \
    php5 php5-mysql phpunit php5-dev \
    swi-prolog swi-prolog-doc \
    python python-dev python-doc python-pip \
    python3 python3-dev python3-doc python3-pip jython jython-doc \
    pychecker pylint pep8 python-autopep8 ruby-full shunit2 \
    tcl-dev tcl-doc \
    tex-common texlive-latex-base texlive-font-utils \
    bzr bzr-builddeb bzr-doc python-bzrlib bzrtools git git-gui git-doc \
    mercurial subversion cvs \
    """

BABUN = """ \
    clisp lua perl python python3 ruby make cmake colordiff colorgcc \
    p7zip unzip zip gzip ncurses pcre mercurial bzr subversion \
    libboost-devel libboost_python-devel cppunit
    """

CABAL = "buildwrapper scion-browser hoogle terminfo happy hlint"

PY_PACKS = "argcomplete cram Pygments pytest trash-cli"

class NotSudo(Exception):
    """ Throw this if we aren't sudo but need to be. """
    pass

def make_cmd(src, dst):
    """ Generate a function helper. """
    def cmd_when_dst_empty(files, command, opts=None):
        """ When dst doesn't have file do:
                command(src + file, dst + file, *opts)
        """
        if opts is None:
            opts = ()
        for fil in files:
            sfile, dfile = [x + fil for x in (src, dst)]
            if not os.path.exists(dfile):
                print("{0} >>>>> {1}".format(sfile, dfile))
                command(sfile, dfile, *opts)
    return cmd_when_dst_empty

def home_config():
    """ Setup the dev environment, stuff goes in the user's home folder. """
    script_path = os.path.relpath(__file__)
    script_dir = os.path.dirname(script_path)
    if script_dir == '':
        script_dir = '.'

    dot_dir = script_dir + os.sep + 'dot_files' + os.sep
    home = os.path.expanduser('~') + os.sep

    # Get shell utilities
    shell_dir = home + '.shell' + os.sep
    get_code('https://bitbucket.org/sjl/hg-prompt/', shell_dir + '.hg-prompt')

    git_urls = [
        'https://github.com/magicmonty/bash-git-prompt.git',
        'https://github.com/starcraftman/zsh-git-prompt.git',
        'https://github.com/zsh-users/zsh-completions.git',
        'https://github.com/zsh-users/zsh-syntax-highlighting.git',
        'https://github.com/starcraftman/hhighlighter.git',
    ]

    for url in git_urls:
        target = '.' + url[url.rindex('/')+1:url.rindex('.git')]
        get_code(url, shell_dir + target)

    # Setup powerline fonts if not done.
    ddir = home + '.fonts'
    dpow = ddir + os.sep + 'powerline-fonts'
    if not os.path.exists(ddir) and subprocess.call(['which', 'fc-cache']) == 0:
        os.mkdir(ddir)
        get_code('https://github.com/Lokaltog/powerline-fonts', dpow)
        subprocess.call(['fc-cache', '-vf', ddir])

    ddir = home + '.ccache'
    if not os.path.exists(ddir):
        os.mkdir(ddir)

    files = [os.path.basename(x) for x in glob.glob(dot_dir + '.*')]
    helper = make_cmd(dot_dir, home)
    helper(files, os.symlink)

    print("NOTE: Remember to add user to smb.\nsudo smbpasswd -a username")

def home_restore():
    """ Undo changes by home_config & restore backup if exists. """
    arc_dir = os.path.expanduser('~/.home_bak/')
    home = os.path.expanduser('~/')
    dot_dir = home + '.my_scripts' + os.path.sep + 'dot_files' + os.path.sep

    for folder in ('.shell', '.fonts', '.ccache',):
        try:
            shutil.rmtree(home + folder)
        except OSError:
            pass

    # Clear existing configs if they are symlinks
    files = [os.path.basename(x) for x in glob.glob(dot_dir + '.*')]
    for fil in [home + fil for fil in files]:
        if os.path.islink(fil):
            os.remove(fil)

    arc_files = [os.path.basename(x) for x in glob.glob(arc_dir + '.*')]
    helper = make_cmd(arc_dir, home)
    helper(arc_files, os.rename)

    try:
        os.rmdir(arc_dir)
    except OSError:
        pass

def home_save():
    """ Save existing home configs to a backup dir. """
    arc_dir = os.path.expanduser('~/.home_bak/')
    home = os.path.expanduser('~/')
    dot_dir = home + '.my_scripts' + os.path.sep + 'dot_files' + os.path.sep

    if not os.path.exists(arc_dir):
        os.makedirs(arc_dir)

    files = [os.path.basename(x) for x in glob.glob(dot_dir + '.*')]
    files = [x for x in files if os.path.exists(home + x)]

    helper = make_cmd(home, arc_dir)
    helper(files, os.rename)

def packs_babun():
    """ Setup a fresh babun install. """
    # Install packages
    cmd = 'pact install ' + BABUN
    subprocess.call(shlex.split(cmd))

    home_save()
    home_config()

def packs_cabal():
    """ Installs haskell packages for Eclipse Haskell plugin. """
    cmd = 'cabal update'
    subprocess.call(shlex.split(cmd))

    cmd = 'cabal install ' + CABAL
    subprocess.call(shlex.split(cmd))

def packs_debian(server=False):
    """ Install packages on the current system. """
    if os.getuid() != 0:
        raise NotSudo

    if server:
        packages = MINIMUM.split()
    else:
        packages = (PROGRAMS + PROGRAMMING + KEYRINGS).split()

    cache = apt.Cache()
    print("One moment while we update cache.")
    cache.update()
    cache.open(None)
    print("Update done.")

    cmd = 'sudo apt-get install'.split()
    for pack in packages:
        try:
            package = cache[pack]
            if not package.is_installed:
                cmd.append(pack)
        except KeyError:
            print("Package couldn't be selected: %s" % pack)

    print("Please wait, running: " + " ".join(cmd))
    subprocess.call(cmd)

def packs_py():
    """ Installs python packages using pip. """
    if os.getuid() != 0:
        raise NotSudo

    # Use python package manager.
    cmd = 'sudo pip install ' + PY_PACKS
    subprocess.call(shlex.split(cmd))

    # Install python completion to system bash_completion.d.
    cmd = 'activate-global-python-argcomplete --user'
    subprocess.call(shlex.split(cmd))

def install_jshint():
    """ Setup jshint for progrmaming javascript with vim. """
    cmd = 'npm install jshint -g'
    subprocess.call(shlex.split(cmd))

def install_pipelight():
    """ Silverlight plugin for firefox/chrome on linux.
    http://www.webupd8.org/2013/08/pipelight-use-silverlight-in-your-linux.html
    """
    if os.getuid() != 0:
        raise NotSudo

    cmds = [
        'sudo apt-get remove flashplugin-installer',
        'sudo apt-add-repository ppa:pipelight/stable',
        'sudo apt-get update',
        'sudo apt-get install pipelight-multi',
        'pipelight-plugin --accept --enable silverlight',
        'pipelight-plugin --accept --enable flash',
    ]
    cmds = [x.split() for x in cmds]
    for cmd in cmds:
        subprocess.call(cmd)
    print("Installation over, remember to use a useragent switcher.")

def main():
    """ Main function. """
    mesg = """This script sets up a dev environment.

    choice       effect
    ------------------------------------------------------
    home         Setup home config files.
    home_restore Restore home files and undo home_save.
    home_save    Save existing home files.
    babun        Install babun packages.
    cabal        Install haskell packages for eclipse.
    debian       Install debian packages.
    server       Install debian packages for server, minimal.
    jshint       Install jshint via npm for javascript vim.
    pip          Install python libraries via pip.
    pipelight    Install pipelight flash & silverlight.
    """
    # Use a dict of funcs to process args
    actions = {
        'home':         home_config,
        'home_restore': home_restore,
        'home_save':    home_save,
        'babun':        packs_babun,
        'cabal':        packs_cabal,
        'debian':       packs_debian,
        'server':       functools.partial(packs_debian, True),
        'jshint':       install_jshint,
        'pip':          packs_py,
        'pipelight':    install_pipelight,
    }
    parser = argparse.ArgumentParser(description=mesg,
                                     formatter_class=argparse.
                                     RawDescriptionHelpFormatter)
    parser.add_argument('stages', nargs='+', help='stages to execute',
                        choices=sorted(actions.keys()))

    autocomplete(parser)
    args = parser.parse_args()  # Default parses argv[1:]

    try:
        for stage in args.stages:
            actions[stage]()
    except NotSudo:
        print("Rerun this part of script with sudo.")

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before>#!/usr/bin/env python3
#
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import flask
import glob
import json
import logging
import os
import re
import shutil
import subprocess
import sys
import time
import threading
import traceback
import urllib

from mypy import api
from streamer import node_base
from streamer.controller_node import ControllerNode
from streamer.configuration import ConfigError

OUTPUT_DIR = 'output_files/'
TEST_DIR = 'test_assets/'
CLOUD_TEST_ASSETS = (
    'https://storage.googleapis.com/shaka-streamer-assets/test-assets/')

# Turn down Flask's logging so that the console isn't flooded with messages
# about every request.  Because flask is built on top of another tool called
# "werkzeug", this the name we use to retrieve the log instance.
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)

# Changes relative path to where this file is.
os.chdir(os.path.dirname(os.path.abspath(__file__)))
controller = None

app = flask.Flask(__name__, static_folder=OUTPUT_DIR)
# Stops browser from caching files to prevent cross-test contamination.
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0

def cleanup():
  # If the controller is running, stop it.
  global controller
  if controller is not None:
    controller.stop()
  controller = None

  # If the output directory exists, delete it and make a new one.
  if os.path.exists(OUTPUT_DIR):
    shutil.rmtree(OUTPUT_DIR)
  os.mkdir(OUTPUT_DIR)

def createCrossOriginResponse(body=None, status=200, mimetype='text/plain'):
  # Enable CORS because karma and flask are cross-origin.
  resp = flask.Response(response=body, status=status)
  resp.headers.add('Content-Type', mimetype)
  resp.headers.add('Access-Control-Allow-Origin', '*')
  resp.headers.add('Access-Control-Allow-Methods', 'GET,POST')
  return resp

def dashStreamsReady(manifest_path):
  """Wait for DASH streams to be ready.

  Return True if the DASH manifest exists and each Representation has at least
  one segment in it.
  """

  # Check to see if the DASH manifest exists yet.
  if not os.path.exists(manifest_path):
    return False

  # Waiting until every Representation has a segment.
  pattern = re.compile(r'<Representation.*?((\n).*?)*?Representation>')
  with open(manifest_path) as manifest_file:
    for representation in pattern.finditer(manifest_file.read()):
      if not re.search(r'<S t', representation.group()):
        # This Representation has no segments.
        return False

  return True

def hlsStreamsReady(master_playlist_path):
  """Wait for HLS streams to be ready.

  Return True if the HLS master playlist exists, and all of the media playlists
  referenced by it exist, and each of those media playlists have at least one
  segment in it.
  """

  # Check to see if the HLS master playlist exists yet.
  if not os.path.exists(master_playlist_path):
    return False

  # Parsing master playlist to see how many media playlists there are.
  # Do this every time, since the master playlist contents may change.
  with open(master_playlist_path) as hls_file:
    contents = hls_file.read()
    media_playlist_list = re.findall(r'^.*\.m3u8$', contents, re.MULTILINE)
    media_playlist_count = len(media_playlist_list)

  # See how many playlists exist so far.
  playlist_list = glob.glob(OUTPUT_DIR + '*.m3u8')

  # Return False if we don't have the right number.  The +1 accounts for the
  # master playlist.
  if len(playlist_list) != media_playlist_count + 1:
    return False

  for playlist_path in playlist_list:
    if playlist_path == master_playlist_path:
      # Skip the master playlist
      continue

    with open(playlist_path) as playlist_file:
      if '#EXTINF' not in playlist_file.read():
        # This doesn't have segments yet.
        return False

  return True

@app.route('/start', methods = ['POST'])
def start():
  global controller
  if controller is not None:
    return createCrossOriginResponse(
        status=403, body='Instance already running!')
  cleanup()

  # Receives configs from the tests to start Shaka Streamer.
  try:
    configs = json.loads(flask.request.data)
  except:
    return createCrossOriginResponse(status=400, body=str(e))

  # Enforce quiet mode without needing it specified in every test.
  configs['pipeline_config']['quiet'] = True

  controller = ControllerNode()
  try:
    controller.start(OUTPUT_DIR,
                     configs['input_config'],
                     configs['pipeline_config'],
                     configs['bitrate_config'])
  except Exception as e:
    # If the controller throws an exception during startup, we want to call
    # stop() to shut down any external processes that have already been started.
    controller.stop()
    controller = None

    # Then, fail the request with a message that indicates what the error was.
    if isinstance(e, ConfigError):
      body = json.dumps({
        'error_type': type(e).__name__,
        'class_name': e.class_name,
        'field_name': e.field_name,
        'field_type': e.field.get_type_name(),
        'message': str(e),
      })
      return createCrossOriginResponse(
          status=418, mimetype='application/json', body=body)
    else:
      traceback.print_exc()
      return createCrossOriginResponse(status=500, body=str(e))

  return createCrossOriginResponse()

@app.route('/stop')
def stop():
  global controller
  resp = createCrossOriginResponse()
  if controller is not None:
    # Check status to see if one of the processes exited.
    if controller.check_status() == node_base.ProcessStatus.Errored:
      resp = createCrossOriginResponse(
          status=500, body='Some processes exited with non-zero exit codes')

  cleanup()
  return resp

@app.route('/output_files/<path:filename>', methods = ['GET','OPTIONS'])
def send_file(filename):
  if controller.is_vod():
    # If streaming mode is vod, needs to wait until packager is completely
    # done packaging contents.
    while True:
      status = controller.check_status()
      if status == node_base.ProcessStatus.Finished:
        break
      elif status != node_base.ProcessStatus.Running:
        return createCrossOriginResponse(
            status=500, body='Some processes exited with non-zero exit codes')

      time.sleep(1)
  else:
    # If streaming mode is live, needs to wait for specific content in
    # manifest until it can be loaded by the player.
    if filename.endswith('.mpd'):
      while not dashStreamsReady(OUTPUT_DIR + filename):
        time.sleep(1)
    elif filename.endswith('.m3u8') and not filename.startswith('stream_'):
      while not hlsStreamsReady(OUTPUT_DIR + filename):
        time.sleep(1)

  # Sending over requested files.
  try:
    response = flask.send_file(OUTPUT_DIR + filename);
  except FileNotFoundError:
    response = flask.Response(response='File not found', status=404)

  response.headers.add('Access-Control-Allow-Origin', '*')
  response.headers.add('Access-Control-Allow-Headers', 'RANGE')
  return response

def fetch_cloud_assets():
  file_list = [
      'BigBuckBunny.1080p.mp4',
      'Sintel.2010.720p.Small.mkv',
      'Sintel.2010.Arabic.vtt',
      'Sintel.2010.Chinese.vtt',
      'Sintel.2010.English.vtt',
      'Sintel.2010.Esperanto.vtt',
      'Sintel.2010.French.vtt',
      'Sintel.2010.Spanish.vtt',
  ]

  # Downloading all the assests for tests.
  for file in file_list:
    if not os.path.exists(TEST_DIR + file):
      response = urllib.request.urlretrieve(CLOUD_TEST_ASSETS +
                                            file,
                                            TEST_DIR + file)

def main():
  parser = argparse.ArgumentParser()
  parser.add_argument('--runs', default=1, type=int,
                      help='Number of trials to run')
  parser.add_argument('--reporters', nargs='+',
                      help='Enables specified reporters in karma')
  args = parser.parse_args()

  #type_check_result = api.run(['streamer/'])
  #if type_check_result[2] != 0:
  #  print('The type checker found the following errors: ')
  #  print(type_check_result[0])
  #
  #  return 1
  #else:
  #  print('Type checking: ' + type_check_result[0])

  # Install test dependencies.
  subprocess.check_call(['npm', 'install'])

  # Fetch streams used in tests.
  if not os.path.exists(TEST_DIR):
    os.mkdir(TEST_DIR)

  fetch_cloud_assets()

  # Start up flask server on a thread.
  # Daemon is set to True so that this thread automatically gets
  # killed when exiting main.  Flask does not have any clean alternatives
  # to be killed.
  threading.Thread(target=app.run, daemon=True).start()

  fails = 0
  trials = args.runs
  print('Running', trials, 'trials')
  # Start up karma.
  for i in range(trials):
    # Start up karma.
    karma_args = [
        'node_modules/karma/bin/karma',
        'start',
        'tests/karma.conf.js',
        # DRM currently is not compatible with headless, so it's run in Chrome.
        # Linux: If you want to run tests as "headless", wrap it with "xvfb-run -a".
        '--browsers', 'Chrome',
        '--single-run',
      ]

    if args.reporters:
      converted_string = ','.join(args.reporters)
      karma_args += [
          '--reporters',
          converted_string,
      ]
    # If the exit code was not 0, the tests in karma failed or crashed.
    if subprocess.call(karma_args) != 0:
      fails += 1

  print('\n\nNumber of failures:', fails, '\nNumber of trials:', trials)
  print('\nSuccess rate:', 100 * (trials - fails) / trials, '%')
  cleanup()
  return fails

if __name__ == '__main__':
  # Exit code based on test results from subprocess call.
  sys.exit(main())
<commit_msg>Re-enable static type checking with mypy<commit_after>#!/usr/bin/env python3
#
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import flask
import glob
import json
import logging
import os
import re
import shutil
import subprocess
import sys
import time
import threading
import traceback
import urllib

from mypy import api as mypy_api
from streamer import node_base
from streamer.controller_node import ControllerNode
from streamer.configuration import ConfigError

OUTPUT_DIR = 'output_files/'
TEST_DIR = 'test_assets/'
CLOUD_TEST_ASSETS = (
    'https://storage.googleapis.com/shaka-streamer-assets/test-assets/')

# Turn down Flask's logging so that the console isn't flooded with messages
# about every request.  Because flask is built on top of another tool called
# "werkzeug", this the name we use to retrieve the log instance.
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)

# Changes relative path to where this file is.
os.chdir(os.path.dirname(os.path.abspath(__file__)))
controller = None

app = flask.Flask(__name__, static_folder=OUTPUT_DIR)
# Stops browser from caching files to prevent cross-test contamination.
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0

def cleanup():
  # If the controller is running, stop it.
  global controller
  if controller is not None:
    controller.stop()
  controller = None

  # If the output directory exists, delete it and make a new one.
  if os.path.exists(OUTPUT_DIR):
    shutil.rmtree(OUTPUT_DIR)
  os.mkdir(OUTPUT_DIR)

def createCrossOriginResponse(body=None, status=200, mimetype='text/plain'):
  # Enable CORS because karma and flask are cross-origin.
  resp = flask.Response(response=body, status=status)
  resp.headers.add('Content-Type', mimetype)
  resp.headers.add('Access-Control-Allow-Origin', '*')
  resp.headers.add('Access-Control-Allow-Methods', 'GET,POST')
  return resp

def dashStreamsReady(manifest_path):
  """Wait for DASH streams to be ready.

  Return True if the DASH manifest exists and each Representation has at least
  one segment in it.
  """

  # Check to see if the DASH manifest exists yet.
  if not os.path.exists(manifest_path):
    return False

  # Waiting until every Representation has a segment.
  pattern = re.compile(r'<Representation.*?((\n).*?)*?Representation>')
  with open(manifest_path) as manifest_file:
    for representation in pattern.finditer(manifest_file.read()):
      if not re.search(r'<S t', representation.group()):
        # This Representation has no segments.
        return False

  return True

def hlsStreamsReady(master_playlist_path):
  """Wait for HLS streams to be ready.

  Return True if the HLS master playlist exists, and all of the media playlists
  referenced by it exist, and each of those media playlists have at least one
  segment in it.
  """

  # Check to see if the HLS master playlist exists yet.
  if not os.path.exists(master_playlist_path):
    return False

  # Parsing master playlist to see how many media playlists there are.
  # Do this every time, since the master playlist contents may change.
  with open(master_playlist_path) as hls_file:
    contents = hls_file.read()
    media_playlist_list = re.findall(r'^.*\.m3u8$', contents, re.MULTILINE)
    media_playlist_count = len(media_playlist_list)

  # See how many playlists exist so far.
  playlist_list = glob.glob(OUTPUT_DIR + '*.m3u8')

  # Return False if we don't have the right number.  The +1 accounts for the
  # master playlist.
  if len(playlist_list) != media_playlist_count + 1:
    return False

  for playlist_path in playlist_list:
    if playlist_path == master_playlist_path:
      # Skip the master playlist
      continue

    with open(playlist_path) as playlist_file:
      if '#EXTINF' not in playlist_file.read():
        # This doesn't have segments yet.
        return False

  return True

@app.route('/start', methods = ['POST'])
def start():
  global controller
  if controller is not None:
    return createCrossOriginResponse(
        status=403, body='Instance already running!')
  cleanup()

  # Receives configs from the tests to start Shaka Streamer.
  try:
    configs = json.loads(flask.request.data)
  except:
    return createCrossOriginResponse(status=400, body=str(e))

  # Enforce quiet mode without needing it specified in every test.
  configs['pipeline_config']['quiet'] = True

  controller = ControllerNode()
  try:
    controller.start(OUTPUT_DIR,
                     configs['input_config'],
                     configs['pipeline_config'],
                     configs['bitrate_config'])
  except Exception as e:
    # If the controller throws an exception during startup, we want to call
    # stop() to shut down any external processes that have already been started.
    controller.stop()
    controller = None

    # Then, fail the request with a message that indicates what the error was.
    if isinstance(e, ConfigError):
      body = json.dumps({
        'error_type': type(e).__name__,
        'class_name': e.class_name,
        'field_name': e.field_name,
        'field_type': e.field.get_type_name(),
        'message': str(e),
      })
      return createCrossOriginResponse(
          status=418, mimetype='application/json', body=body)
    else:
      traceback.print_exc()
      return createCrossOriginResponse(status=500, body=str(e))

  return createCrossOriginResponse()

@app.route('/stop')
def stop():
  global controller
  resp = createCrossOriginResponse()
  if controller is not None:
    # Check status to see if one of the processes exited.
    if controller.check_status() == node_base.ProcessStatus.Errored:
      resp = createCrossOriginResponse(
          status=500, body='Some processes exited with non-zero exit codes')

  cleanup()
  return resp

@app.route('/output_files/<path:filename>', methods = ['GET','OPTIONS'])
def send_file(filename):
  if controller.is_vod():
    # If streaming mode is vod, needs to wait until packager is completely
    # done packaging contents.
    while True:
      status = controller.check_status()
      if status == node_base.ProcessStatus.Finished:
        break
      elif status != node_base.ProcessStatus.Running:
        return createCrossOriginResponse(
            status=500, body='Some processes exited with non-zero exit codes')

      time.sleep(1)
  else:
    # If streaming mode is live, needs to wait for specific content in
    # manifest until it can be loaded by the player.
    if filename.endswith('.mpd'):
      while not dashStreamsReady(OUTPUT_DIR + filename):
        time.sleep(1)
    elif filename.endswith('.m3u8') and not filename.startswith('stream_'):
      while not hlsStreamsReady(OUTPUT_DIR + filename):
        time.sleep(1)

  # Sending over requested files.
  try:
    response = flask.send_file(OUTPUT_DIR + filename);
  except FileNotFoundError:
    response = flask.Response(response='File not found', status=404)

  response.headers.add('Access-Control-Allow-Origin', '*')
  response.headers.add('Access-Control-Allow-Headers', 'RANGE')
  return response

def fetch_cloud_assets():
  file_list = [
      'BigBuckBunny.1080p.mp4',
      'Sintel.2010.720p.Small.mkv',
      'Sintel.2010.Arabic.vtt',
      'Sintel.2010.Chinese.vtt',
      'Sintel.2010.English.vtt',
      'Sintel.2010.Esperanto.vtt',
      'Sintel.2010.French.vtt',
      'Sintel.2010.Spanish.vtt',
  ]

  # Downloading all the assests for tests.
  for file in file_list:
    if not os.path.exists(TEST_DIR + file):
      response = urllib.request.urlretrieve(CLOUD_TEST_ASSETS +
                                            file,
                                            TEST_DIR + file)

def main():
  parser = argparse.ArgumentParser()
  parser.add_argument('--runs', default=1, type=int,
                      help='Number of trials to run')
  parser.add_argument('--reporters', nargs='+',
                      help='Enables specified reporters in karma')
  args = parser.parse_args()

  # Do static type checking on the project first.
  type_check_result = mypy_api.run(['streamer/'])
  if type_check_result[2] != 0:
    print('The type checker found the following errors: ')
    print(type_check_result[0])
    return 1

  # Install test dependencies.
  subprocess.check_call(['npm', 'install'])

  # Fetch streams used in tests.
  if not os.path.exists(TEST_DIR):
    os.mkdir(TEST_DIR)

  fetch_cloud_assets()

  # Start up flask server on a thread.
  # Daemon is set to True so that this thread automatically gets
  # killed when exiting main.  Flask does not have any clean alternatives
  # to be killed.
  threading.Thread(target=app.run, daemon=True).start()

  fails = 0
  trials = args.runs
  print('Running', trials, 'trials')
  # Start up karma.
  for i in range(trials):
    # Start up karma.
    karma_args = [
        'node_modules/karma/bin/karma',
        'start',
        'tests/karma.conf.js',
        # DRM currently is not compatible with headless, so it's run in Chrome.
        # Linux: If you want to run tests as "headless", wrap it with "xvfb-run -a".
        '--browsers', 'Chrome',
        '--single-run',
      ]

    if args.reporters:
      converted_string = ','.join(args.reporters)
      karma_args += [
          '--reporters',
          converted_string,
      ]
    # If the exit code was not 0, the tests in karma failed or crashed.
    if subprocess.call(karma_args) != 0:
      fails += 1

  print('\n\nNumber of failures:', fails, '\nNumber of trials:', trials)
  print('\nSuccess rate:', 100 * (trials - fails) / trials, '%')
  cleanup()
  return fails

if __name__ == '__main__':
  # Exit code based on test results from subprocess call.
  sys.exit(main())
<|endoftext|>


<commit_before>#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# EVE SRP documentation build configuration file, created by
# sphinx-quickstart on Wed Mar 26 18:10:59 2014.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys
import os
import re

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))

import evesrp

# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.todo',
    'sphinx.ext.coverage',
    'sphinx.ext.viewcode',
    'sphinx.ext.intersphinx',
    'sphinx.ext.graphviz',
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['.templates']

# The suffix of source filenames.
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = 'EVE SRP'
copyright = '2014, Will Ross'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = re.findall(r'^\d+\.\d+', evesrp.__version__)[0]
# The full version, including alpha/beta/rc tags.
release = evesrp.__version__

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['.build']

# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False

# -- Interphinx setup -------------------

intersphinx_mapping = {
    'principal': ('https://pythonhosted.org/Flask-Principal/', None),
    'login': ('https://flask-login.readthedocs.org/en/latest/', None),
    'sqlalchemy': ('http://docs.sqlalchemy.org/en/rel_0_9/', None),
    'requests': ('http://docs.python-requests.org/en/latest/', None),
    'python3': ('https://docs.python.org/3/', None),
    'flasksqlalchemy': ('http://pythonhosted.org/Flask-SQLAlchemy/', None),
}

# -- Autodoc configuration --------------

autodoc_default_flags = ['members', 'special-members']

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'nature'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['.static']

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None

# Output file base name for HTML help builder.
htmlhelp_basename = 'EVESRPdoc'

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#'preamble': '',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
  ('index', 'EVESRP.tex', 'EVE SRP Documentation',
   'Will Ross', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    ('index', 'evesrp', 'EVE SRP Documentation',
     ['Will Ross'], 1)
]

# If true, show URL addresses after external links.
#man_show_urls = False

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
  ('index', 'EVESRP', 'EVE SRP Documentation',
   'Will Ross', 'EVESRP', 'One line description of project.',
   'Miscellaneous'),
]

# Documents to append as an appendix to all manuals.
#texinfo_appendices = []

# If false, no module index is generated.
#texinfo_domain_indices = True

# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'

# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
<commit_msg>Add Intersphinx mapping for six<commit_after>#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# EVE SRP documentation build configuration file, created by
# sphinx-quickstart on Wed Mar 26 18:10:59 2014.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys
import os
import re

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))

import evesrp

# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.todo',
    'sphinx.ext.coverage',
    'sphinx.ext.viewcode',
    'sphinx.ext.intersphinx',
    'sphinx.ext.graphviz',
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['.templates']

# The suffix of source filenames.
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = 'EVE SRP'
copyright = '2014, Will Ross'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = re.findall(r'^\d+\.\d+', evesrp.__version__)[0]
# The full version, including alpha/beta/rc tags.
release = evesrp.__version__

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['.build']

# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False

# -- Interphinx setup -------------------

intersphinx_mapping = {
    'principal': ('https://pythonhosted.org/Flask-Principal/', None),
    'login': ('https://flask-login.readthedocs.org/en/latest/', None),
    'sqlalchemy': ('http://docs.sqlalchemy.org/en/rel_0_9/', None),
    'requests': ('http://docs.python-requests.org/en/latest/', None),
    'python3': ('https://docs.python.org/3/', None),
    'flasksqlalchemy': ('http://pythonhosted.org/Flask-SQLAlchemy/', None),
    'six': ('https://pythonhosted.org/six/', None),
}

# -- Autodoc configuration --------------

autodoc_default_flags = ['members', 'special-members']

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'nature'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['.static']

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None

# Output file base name for HTML help builder.
htmlhelp_basename = 'EVESRPdoc'

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#'preamble': '',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
  ('index', 'EVESRP.tex', 'EVE SRP Documentation',
   'Will Ross', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    ('index', 'evesrp', 'EVE SRP Documentation',
     ['Will Ross'], 1)
]

# If true, show URL addresses after external links.
#man_show_urls = False

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
  ('index', 'EVESRP', 'EVE SRP Documentation',
   'Will Ross', 'EVESRP', 'One line description of project.',
   'Miscellaneous'),
]

# Documents to append as an appendix to all manuals.
#texinfo_appendices = []

# If false, no module index is generated.
#texinfo_domain_indices = True

# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'

# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
<|endoftext|>


<commit_before>"""
    twtxt.cli
    ~~~~~~~~~

    This module implements the command-line interface of twtxt.

    :copyright: (c) 2016 by buckket.
    :license: MIT, see LICENSE for more details.
"""

import logging

import click

from twtxt.config import Config
from twtxt.helper import style_tweet, style_source, style_source_with_status
from twtxt.helper import validate_created_at, validate_text
from twtxt.http import get_tweets, get_status
from twtxt.log import init_logging
from twtxt.types import Tweet, Source

logger = logging.getLogger(__name__)

@click.group()
@click.option("--config", "-c",
              type=click.Path(exists=True, readable=True),
              help="Specify a custom config file location.")
@click.option("--verbose", "-v",
              is_flag=True, default=False,
              help="Enable verbose output for debugging purposes.")
@click.version_option()
@click.pass_context
def cli(ctx, config, verbose):
    """Decentralised, minimalist microblogging service for hackers."""
    init_logging(debug=verbose)

    try:
        if config:
            conf = Config.from_file(config)
        else:
            conf = Config.discover()
    except ValueError:
        click.echo("Error loading config file.")
        if not config:
            if click.confirm("Do you want to run the twtxt quickstart wizard?", abort=True):
                pass

    ctx.default_map = conf.build_default_map()
    ctx.obj = {'conf': conf}

@cli.command()
@click.option("--created-at",
              callback=validate_created_at,
              help="ISO 8601 formatted datetime string to use in Tweet, instead of current time.")
@click.option("--output", "-o",
              type=click.Path(file_okay=True, writable=True),
              help="Location of twtxt file.")
@click.argument("text", callback=validate_text)
@click.pass_context
def tweet(ctx, created_at, output, text):
    """Append a new tweet to your twtxt file."""
    tweet = Tweet(text, created_at) if created_at else Tweet(text)
    with open(output, "a") as fh:
        fh.write("{}\n".format(str(tweet)))

@cli.command()
@click.option("--pager/--no-pager",
              is_flag=True,
              help="Use a pager to display content. (Default: False)")
@click.option("--limit", "-l",
              type=click.INT,
              help="Limit total number of shown tweets. (Default: 20)")
@click.pass_context
def timeline(ctx, pager, limit):
    """Retrieve your personal timeline."""
    sources = ctx.obj['conf'].following
    tweets = get_tweets(sources, limit)

    if pager:
        click.echo_via_pager("\n\n".join(
            (style_tweet(tweet) for tweet in tweets)))
    else:
        click.echo()
        for tweet in tweets:
            click.echo(style_tweet(tweet))
            click.echo()

@cli.command()
@click.option("--check",
              is_flag=True,
              help="Check if source URL is valid and readable.")
@click.pass_context
def following(ctx, check):
    """Return the list of sources you’re following."""
    sources = ctx.obj['conf'].following

    if check:
        sources = get_status(sources)
        for (source, status) in sources:
            click.echo(style_source_with_status(source, status))
    else:
        sources = sorted(sources, key=lambda source: source.nick)
        for source in sources:
            click.echo(style_source(source))

@cli.command()
@click.argument("nick")
@click.argument("url")
@click.pass_context
def follow(ctx, nick, url):
    """Add a new source to your followings."""
    source = Source(nick, url)
    ctx.obj['conf'].add_source(source)
    click.echo("✓ You’re now following {}.".format(
        click.style(source.nick, bold=True)))

@cli.command()
@click.argument("nick")
@click.pass_context
def unfollow(ctx, nick):
    """Remove an existing source from your followings."""
    ret_val = ctx.obj['conf'].remove_source_by_nick(nick)
    if ret_val:
        click.echo("✓ You’ve unfollowed {}.".format(
            click.style(nick, bold=True)))
    else:
        click.echo("✗ You’re not following {}.".format(
            click.style(nick, bold=True)))

@cli.command()
@click.pass_context
def quickstart(ctx):
    """Quickstart wizard for setting up twtxt."""
    pass

main = cli
<commit_msg>Resolve paths whenever needed<commit_after>"""
    twtxt.cli
    ~~~~~~~~~

    This module implements the command-line interface of twtxt.

    :copyright: (c) 2016 by buckket.
    :license: MIT, see LICENSE for more details.
"""

import logging

import click

from twtxt.config import Config
from twtxt.helper import style_tweet, style_source, style_source_with_status
from twtxt.helper import validate_created_at, validate_text
from twtxt.http import get_tweets, get_status
from twtxt.log import init_logging
from twtxt.types import Tweet, Source

logger = logging.getLogger(__name__)

@click.group()
@click.option("--config", "-c",
              type=click.Path(exists=True, readable=True, resolve_path=True),
              help="Specify a custom config file location.")
@click.option("--verbose", "-v",
              is_flag=True, default=False,
              help="Enable verbose output for debugging purposes.")
@click.version_option()
@click.pass_context
def cli(ctx, config, verbose):
    """Decentralised, minimalist microblogging service for hackers."""
    init_logging(debug=verbose)

    try:
        if config:
            conf = Config.from_file(config)
        else:
            conf = Config.discover()
    except ValueError:
        click.echo("Error loading config file.")
        if not config:
            if click.confirm("Do you want to run the twtxt quickstart wizard?", abort=True):
                raise NotImplemented

    ctx.default_map = conf.build_default_map()
    ctx.obj = {'conf': conf}

@cli.command()
@click.option("--created-at",
              callback=validate_created_at,
              help="ISO 8601 formatted datetime string to use in Tweet, instead of current time.")
@click.option("--output", "-o",
              type=click.Path(file_okay=True, writable=True, resolve_path=True),
              help="Location of twtxt file.")
@click.argument("text", callback=validate_text)
@click.pass_context
def tweet(ctx, created_at, output, text):
    """Append a new tweet to your twtxt file."""
    tweet = Tweet(text, created_at) if created_at else Tweet(text)
    with open(output, "a") as fh:
        fh.write("{}\n".format(str(tweet)))

@cli.command()
@click.option("--pager/--no-pager",
              is_flag=True,
              help="Use a pager to display content. (Default: False)")
@click.option("--limit", "-l",
              type=click.INT,
              help="Limit total number of shown tweets. (Default: 20)")
@click.pass_context
def timeline(ctx, pager, limit):
    """Retrieve your personal timeline."""
    sources = ctx.obj['conf'].following
    tweets = get_tweets(sources, limit)

    if pager:
        click.echo_via_pager("\n\n".join(
            (style_tweet(tweet) for tweet in tweets)))
    else:
        click.echo()
        for tweet in tweets:
            click.echo(style_tweet(tweet))
            click.echo()

@cli.command()
@click.option("--check",
              is_flag=True,
              help="Check if source URL is valid and readable.")
@click.pass_context
def following(ctx, check):
    """Return the list of sources you’re following."""
    sources = ctx.obj['conf'].following

    if check:
        sources = get_status(sources)
        for (source, status) in sources:
            click.echo(style_source_with_status(source, status))
    else:
        sources = sorted(sources, key=lambda source: source.nick)
        for source in sources:
            click.echo(style_source(source))

@cli.command()
@click.argument("nick")
@click.argument("url")
@click.pass_context
def follow(ctx, nick, url):
    """Add a new source to your followings."""
    source = Source(nick, url)
    ctx.obj['conf'].add_source(source)
    click.echo("✓ You’re now following {}.".format(
        click.style(source.nick, bold=True)))

@cli.command()
@click.argument("nick")
@click.pass_context
def unfollow(ctx, nick):
    """Remove an existing source from your followings."""
    ret_val = ctx.obj['conf'].remove_source_by_nick(nick)
    if ret_val:
        click.echo("✓ You’ve unfollowed {}.".format(
            click.style(nick, bold=True)))
    else:
        click.echo("✗ You’re not following {}.".format(
            click.style(nick, bold=True)))

@cli.command()
@click.pass_context
def quickstart(ctx):
    """Quickstart wizard for setting up twtxt."""
    pass

main = cli
<|endoftext|>


<commit_before># Copyright (c) 2013-2016 Molly White
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

#==============================================================================
# NOTE/CAUTION/WARNING:
#
# This file is used to store offensive terms so that triggering or offensive
# headlines are not satirized by CyberPrefixer and other bots. Accordingly, it
# contains a lot of terms that are triggering, be they sexual, violent or
# otherwise.
#==============================================================================

import re

offensive = re.compile(
    r"\b(deaths?|dead(ly)?|die(s|d)?|hurts?|(sex(ual)?|child)[ -]?(abuse|trafficking|assault)|"
    r"injur(e|i?es|ed|y)|kill(ing|ed|er|s)?s?|wound(ing|ed|s)?|fatal(ly|ity)?|shoo?t(s|ing|er)?s?|"
    r"crash(es|ed|ing)?|attack(s|ers?|ing|ed)?|murder(s|er|ed|ing)?s?|hostages?|rap(e|es|ed|ist|ists|ing)|"
    r"assault(s|ed)?|pile-?ups?|massacre(s|d)?|assassinate(d|s)?|sla(y|in|yed|ys|ying|yings)|"
    r"victims?|tortur(e|ed|ing|es)|execut(e|ion|ed|ioner)s?|gun(man|men|ned)|suicid(e|al|es)|"
    r"bomb(s|ed|ing|ings|er|ers)?|mass[- ]?graves?|bloodshed|state[- ]?of[- ]?emergency|"
    r"al[- ]?Qaeda|blasts?|violen(t|ce)|lethal|cancer(ous)?|stab(bed|bing|ber)?s?|casualt(y|ies)|"
    r"sla(y|ying|yer|in)|drown(s|ing|ed|ings)?|bod(y|ies)|kidnap(s|ped|per|pers|ping|pings)?|"
    r"rampage|beat(ings?|en)|terminal(ly)?|abduct(s|ed|ion)?s?|missing|behead(s|ed|ings?)?|"
    r"homicid(e|es|al)|burn(s|ed|ing)? alive|decapitated?s?|jihadi?s?t?|hang(ed|ing|s)?|"
    r"funerals?|traged(y|ies)|autops(y|ies)|child sex|sob(s|bing|bed)?|pa?edophil(e|es|ia))\W?\b",
    flags=re.IGNORECASE)

def tact(headline):
    # Avoid producing particularly tactless tweets
    if re.search(offensive, headline) is None:
        return True
    else:
        return False
<commit_msg>Add 9/11 to exclude regex<commit_after># Copyright (c) 2013-2016 Molly White
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

#==============================================================================
# NOTE/CAUTION/WARNING:
#
# This file is used to store offensive terms so that triggering or offensive
# headlines are not satirized by CyberPrefixer and other bots. Accordingly, it
# contains a lot of terms that are triggering, be they sexual, violent or
# otherwise.
#==============================================================================

import re

offensive = re.compile(
    r"\b(deaths?|dead(ly)?|die(s|d)?|hurts?|(sex(ual)?|child)[ -]?(abuse|trafficking|assault)|"
    r"injur(e|i?es|ed|y)|kill(ing|ed|er|s)?s?|wound(ing|ed|s)?|fatal(ly|ity)?|shoo?t(s|ing|er)?s?|"
    r"crash(es|ed|ing)?|attack(s|ers?|ing|ed)?|murder(s|er|ed|ing)?s?|hostages?|rap(e|es|ed|ist|ists|ing)|"
    r"assault(s|ed)?|pile-?ups?|massacre(s|d)?|assassinate(d|s)?|sla(y|in|yed|ys|ying|yings)|"
    r"victims?|tortur(e|ed|ing|es)|execut(e|ion|ed|ioner)s?|gun(man|men|ned)|suicid(e|al|es)|"
    r"bomb(s|ed|ing|ings|er|ers)?|mass[- ]?graves?|bloodshed|state[- ]?of[- ]?emergency|"
    r"al[- ]?Qaeda|blasts?|violen(t|ce)|lethal|cancer(ous)?|stab(bed|bing|ber)?s?|casualt(y|ies)|"
    r"sla(y|ying|yer|in)|drown(s|ing|ed|ings)?|bod(y|ies)|kidnap(s|ped|per|pers|ping|pings)?|"
    r"rampage|beat(ings?|en)|terminal(ly)?|abduct(s|ed|ion)?s?|missing|behead(s|ed|ings?)?|"
    r"homicid(e|es|al)|burn(s|ed|ing)? alive|decapitated?s?|jihadi?s?t?|hang(ed|ing|s)?|"
    r"funerals?|traged(y|ies)|autops(y|ies)|child sex|sob(s|bing|bed)?|pa?edophil(e|es|ia)|9(/|-)11)\W?\b",
    flags=re.IGNORECASE)

def tact(headline):
    # Avoid producing particularly tactless tweets
    if re.search(offensive, headline) is None:
        return True
    else:
        return False
<|endoftext|>