def get_contigs_to_keep(args):
    '''
    Return contigs matching criteria:
    - novel block size >= MIN_CLIP
    - novel blocks are spliced in some way
    - novel exons have corresponding novel splice
      sites and novel splice donor/acceptor motifs
    - TSVs occur in exonic regions
    '''
    try:
        cinfo_file = args.contig_info_file
        contigs = pd.read_csv(cinfo_file, sep='\t')
    except IOError as exception:
        exit_with_error(str(exception), constants.EXIT_FILE_IO_ERROR)

    gene_tree, ex_trees, ex_ref = ac.get_gene_lookup(args.tx_ref_file)
    contigs['large_varsize'] = contigs.contig_varsize >= MIN_CLIP
    contigs['is_contig_spliced'] = contigs.contig_cigar.apply(
        lambda x: bool(re.search('N', x)))
    contigs['spliced_exon'] = match_splice_juncs(contigs)
    contigs['overlaps_exon'] = vars_overlap_exon(contigs, ex_trees)
    contigs['overlaps_gene'] = contigs.apply(overlaps_gene,
                                             axis=1,
                                             args=(gene_tree, ))

    # keep exons falling outside the gene
    is_intergenic_exon = np.logical_and.reduce(
        (contigs.spliced_exon, contigs.variant_type == 'NE',
         contigs.large_varsize, np.invert(contigs.overlaps_gene)))
    # novel exon contigs (spliced, valid motif and large variant size)
    is_novel_exon = np.logical_and(contigs.spliced_exon, contigs.large_varsize)
    if args.mismatches < 4:
        contigs = check_for_valid_motifs(contigs, is_novel_exon, args)
        is_novel_exon = np.logical_and(is_novel_exon, contigs.valid_motif)
    ne_vars = contigs[np.logical_or(is_novel_exon,
                                    is_intergenic_exon)].variant_id.values

    # keep all splice vars
    as_vars = contigs.variant_id.values[contigs.variant_type.isin(SPLICE_VARS)]

    # get junc vars
    junc_vars = get_junc_vars(contigs, ex_trees, args)

    # check size of retained introns
    ri_vars = contigs[np.logical_and(contigs.variant_type == 'RI',
                                     contigs.large_varsize)].variant_id.values

    # get fusions and TSVs
    fus_vars = get_fusion_vars(contigs)
    sv_vars = get_tsv_vars(contigs)

    # collate contigs to keep
    keep_vars = np.unique(
        np.concatenate(
            [ri_vars, as_vars, ne_vars, sv_vars, fus_vars, junc_vars]))
    contigs['variant_of_interest'] = contigs.variant_id.isin(keep_vars)
    contigs.to_csv('%s_info.tsv' % args.out_prefix, sep='\t', index=None)

    keep_contigs = contigs[contigs.variant_of_interest].contig_id.values
    return (keep_contigs)
Exemple #2
0
def main():
    args = parse_args(sys.argv[1:])
    init_logging(args.log)

    try:
        contigs = pd.read_csv(args.contig_info, sep='\t',
                              low_memory=False).fillna('')
        de_results = pd.read_csv(args.de_results, sep='\t', low_memory=False)
        vafs = pd.read_csv(args.vaf_estimates, sep='\t', low_memory=False)
        vafs = vafs[['contig_id', 'TPM', 'mean_WT_TPM',
                     'VAF']].drop_duplicates()
        gene_filter = pd.read_csv(args.gene_filter, header=None, low_memory=False) if args.gene_filter != '' \
                                                                                   else pd.DataFrame()
    except IOError as exception:
        exit_with_error(str(exception), EXIT_FILE_IO_ERROR)

    # count the number of variants per contig (count uninteresting vars)
    vars_per_contig = contigs.groupby('contig_id', as_index=False)
    vars_per_contig = vars_per_contig.agg(
        {'variant_id': lambda x: len(np.unique(x))})
    vars_per_contig = vars_per_contig.rename({'variant_id': 'vars_in_contig'},
                                             axis=1)
    contigs = contigs.merge(vars_per_contig)

    # consider only variants of interest
    contigs = contigs[contigs.variant_of_interest]
    contigs['sample'] = args.sample

    if args.var_filter:
        contigs = contigs[contigs.variant_type.apply(
            lambda v: v in args.var_filter).values]

    if len(gene_filter) > 0:
        contigs = filter_by_gene(contigs, gene_filter)

    if len(contigs) == 0:
        logging.info('WARNING: no variants present after filtering. Exiting.')
        contigs.to_csv(sys.stdout, index=False, sep='\t', na_rep='NA')
        sys.exit()

    logging.info('Adding DE and VAF info...')
    contigs = add_de_info(contigs, de_results)
    contigs = pd.merge(contigs, vafs, on='contig_id', how='left')

    short_gnames = contigs.overlapping_genes.map(str).apply(
        get_short_gene_name)
    contig_ids, samples = contigs.contig_id, contigs['sample']
    con_names = [
        '|'.join([s, cid, sg])
        for cid, s, sg in zip(contig_ids, samples, short_gnames)
    ]
    contigs[
        'unique_contig_ID'] = con_names  # TODO: fix this field (it is not really unique)

    contigs = get_variant_seq(contigs, args.contig_fasta)

    logging.info('Outputting to CSV')
    contigs = reformat_fields(contigs)
    contigs.to_csv(sys.stdout, index=False, sep='\t', na_rep='NA')
Exemple #3
0
def main():
    args = parse_args()
    init_logging(args.log)
    set_globals(args)
    keep_contigs = get_contigs_to_keep(args)
    if len(keep_contigs) > 0:
        write_output(args, keep_contigs)
        write_bam(args, keep_contigs)
    else:
        exit_with_error('ERROR: no variants to output.',
                        constants.EXIT_OUTPUT_ERROR)
Exemple #4
0
def write_output(args, keep_contigs):
    cvars_file = args.vcf_file
    try:
        vcf = pd.read_csv(cvars_file, sep='\t', header=None, comment='#')
        cvf = open(cvars_file, 'r')
        for line in cvf:
            if not line.startswith('#'):
                break
            print(line.strip())
        cvf.close()
    except IOError as exception:
        exit_with_error(str(exception), constants.EXIT_FILE_IO_ERROR)

    vcf = vcf[vcf[7].apply(
        lambda x: x.split(';')[0].split('=')[1] in keep_contigs)]
    vcf.to_csv(sys.stdout, sep='\t', index=False, header=False)
Exemple #5
0
def main():
    args = parse_args(sys.argv[1:])
    init_logging(args.log)

    try:
        contigs = pd.read_csv(args.contig_info, sep='\t', low_memory=False)
        st_bed = pd.read_csv(args.st_bed,
                             sep='\t',
                             header=None,
                             names=BED_COLS,
                             low_memory=False)
    except IOError as exception:
        exit_with_error(str(exception), EXIT_FILE_IO_ERROR)

    logging.info('Matching contigs to ST alignments...')
    contigs = get_st_alignments(contigs, args.cont_align)
    logging.info('Counting reads crossing variant boundaries...')
    bamf = pysam.AlignmentFile(args.read_align, "rb")
    contigs = get_read_support(contigs, bamf, st_bed)

    logging.info('Outputting to CSV')
    contigs = contigs.sort_values(by='PValue', ascending=True)
    contigs.to_csv(sys.stdout, index=False, sep='\t', na_rep='NA')
def main():
    args = parse_args()
    init_logging(args.log)

    genome_bed, st_block_bed, st_gene_bed, st_fasta = get_output_files(args.sample, args.outdir)
    if os.path.exists(genome_bed):
        os.remove(genome_bed)
    if os.path.exists(st_block_bed):
        os.remove(st_block_bed)
    if os.path.exists(st_gene_bed):
        os.remove(st_gene_bed)
    if os.path.exists(st_fasta):
        os.remove(st_fasta)

    try:
        gtf = load_gtf_file(args.gtf_file)
        cvcf = load_vcf_file(args.contig_vcf)
        contigs = pd.read_csv(args.contig_info, sep='\t', low_memory=False).fillna('')
    except IOError as exception:
        exit_with_error(str(exception), EXIT_FILE_IO_ERROR)

    make_supertranscripts(args, contigs, cvcf, gtf)
    write_canonical_genes(args, contigs, gtf)
Exemple #7
0
def run_cli(argv=None, config_path=CONFIG_PATH):
    """
    Run a CLI command from arguments (defaults to sys.argv)
    Return the result of the command on success.
    The result will be a dict, and will have 'error' defined on error condition.
    """

    if argv is None:
        argv = sys.argv

    global_cli_args = {
        'debug': {
            'short': '-d',
            'long': '--debug',
            'has_arg': False,
            're-exec': True,
            'env': 'BLOCKSTACK_DEBUG',
            'help': 'Enable global debugging messages',
            'similar': ['--dbg', '-dd', '-ddd', '-dddd', '--verbose'],
            'secret': False,
        },
        'config': {
            'short': '-c',
            'long': '--config',
            'has_arg': True,
            're-exec': True,
            'env': 'BLOCKSTACK_CLIENT_CONFIG',
            'help':
            'Path to alternative configuration file and associated state',
            'similar': ['--conf'],
            'secret': False,
        },
        'default_yes': {
            'short': '-y',
            'long': '--yes',
            'has_arg': False,
            're-exec': False,
            'env': 'BLOCKSTACK_CLIENT_INTERACTIVE_YES',
            'help': 'Assume default/yes response to all queries',
            'similar': [],
            'secret': False,
        },
        'api_pass': {
            'short': '-a',
            'long': '--api_password',
            'has_arg': True,
            're-exec': False,
            'env': 'BLOCKSTACK_API_PASSWORD',
            'help': 'API password to use',
            'similar': ['--api-password', '--api-pass', '--api_pass'],
            'secret': True,
        },
        'api_session': {
            'short': '-A',
            'long': '--api_session',
            'has_arg': True,
            're-exec': False,
            'env': 'BLOCKSTACK_API_SESSION',
            'help': 'API session token to use',
            'similar': ['--api-session', '--session', '--ses'],
            'secret': True,
        },
        'api_bind': {
            'short': '-b',
            'long': '--bind',
            'has_arg': True,
            're-exec': False,
            'env': 'BLOCKSTACK_API_BIND',
            'help': 'Address or hostname to bind the API server',
            'similar': [],
            'secret': False,
        },
        'dry_run': {
            'short': '-n',
            'long': '--dry_run',
            'has_arg': False,
            're-exec': True,
            'env': 'BLOCKSTACK_DRY_RUN',
            'help':
            'Do not send transactions. Return the signed transaction instead.',
            'similar': ['--dry-run', '--dryrun'],
            'secret': False,
        },
        'wallet_password': {
            'short': '-p',
            'long': '--password',
            'has_arg': True,
            're-exec': False,
            'env': 'BLOCKSTACK_CLIENT_WALLET_PASSWORD',
            'help': 'Wallet decryption password',
            'similar': ['--pass', '--passwd'],
            'secret': True,
        },
        'indexer_host': {
            'short': '-H',
            'long': '--host',
            'has_arg': True,
            're-exec': False,
            'env': 'BLOCKSTACK_CLI_SERVER_HOST',
            'help':
            'Hostname or IP address of the Blockstack blockchain indexer',
            'similar': ['--ip', '--ipv4'],
            'secret': False,
        },
        'indexer_port': {
            'short': '-P',
            'long': '--port',
            'has_arg': True,
            're-exec': False,
            'env': 'BLOCKSTACK_CLI_SERVER_PORT',
            'help': 'Port number of the Blockstack blockchain indexer',
            'similar': [],
            'secret': False,
        },
        'secret_fd': {
            'short': '-f',
            'long': '--secrets',
            'has_arg': True,
            're-exec': False,
            'help':
            'Used internally; file descriptor number to serialized secrets preserved across execv(2).',
            'similar': [],
            'secret': False,
        },
    }

    if '-v' in argv or '--version' in argv:
        print(VERSION)
        sys.exit(0)

    arg_info = parse_args(global_cli_args, argv, config_path=config_path)
    if 'error' in arg_info:
        print("Failed to parse global CLI arguments: {}".format(
            arg_info['error']),
              file=sys.stderr)
        print("Global CLI arguments:\n{}\n".format("\n".join([
            "\t{}/{}\n\t\t{}".format(cliarg['short'], cliarg['long'],
                                     cliarg['help'])
            for argname, cliarg in global_cli_args.items()
        ])),
              file=sys.stderr)

        if 'similar' in arg_info:
            siminfo = arg_info['similar']
            assert len(siminfo.keys()) == 1
            opt = siminfo.keys()[0]
            arg = siminfo[opt]

            print("Suggestion:  Use '{}' instead of '{}'".format(opt, arg),
                  file=sys.stderr)

        sys.exit(1)

    cli_debug = arg_info['args'].get('debug')
    cli_config_argv = (arg_info['args'].has_key('config'))

    # set (non-secret) environment variables
    for envar, enval in arg_info['envs'].items():
        if os.environ.get(envar) is None:
            if cli_debug:
                print("Set {} to {}".format(envar, enval), file=sys.stderr)

            os.environ[envar] = enval

    # set secrets...
    for secvar, secval in arg_info['secrets'].items():
        set_secret(secvar, secval)

    # re-exec?
    if arg_info['re-exec']:

        new_argv = arg_info['new_argv']

        if len(arg_info['secrets']) > 0:
            secbuf = serialize_secrets()
            fd = write_secrets(secbuf)

            new_argv += ['--secrets', str(fd)]

        new_argv = [sys.executable] + new_argv
        if cli_debug:
            print("Re-exec as `{}`".format(", ".join(
                ['"{}"'.format(i) for i in new_argv])),
                  file=sys.stderr)

        try:
            os.execv(new_argv[0], new_argv)
        except:
            import traceback as tb
            tb.print_exc()
            sys.exit(1)

    # load secrets
    if arg_info['args'].has_key('secret_fd'):
        fd_str = arg_info['args']['secret_fd']
        if fd_str:
            try:
                fd = int(fd_str)
            except:
                print('Invalid secret fd {}'.format(fd_str), file=sys.stderr)
                sys.exit(1)

            log.debug("Load secrets from {}".format(fd))

            try:
                os.lseek(fd, 0, os.SEEK_SET)
                secbuf = os.read(fd, 65536)
                os.close(fd)

                load_secrets(secbuf)
            except Exception as e:
                traceback.print_exc()
                sys.exit(1)

    # do one-time opt-in request
    uuid_path = client_uuid_path(config_dir=os.path.dirname(config_path))
    first_time = False
    client_uuid = None

    if not os.path.exists(uuid_path):
        first_time = True
        client_uuid = get_or_set_uuid(config_dir=os.path.dirname(config_path))

    res = config.setup_config(
        config_path=config_path,
        interactive=(os.environ.get("BLOCKSTACK_CLIENT_INTERACTIVE_YES") !=
                     '1'))
    if 'error' in res:
        exit_with_error("Failed to load and verify config file: {}".format(
            res['error']))

    conf = res['config']

    # if the wallet exists, make sure that it's the latest version
    wallet_path = os.path.join(os.path.dirname(config_path), WALLET_FILENAME)
    if os.path.exists(wallet_path):
        res = inspect_wallet(wallet_path=wallet_path)
        if 'error' in res:
            exit_with_error(
                "Failed to inspect wallet at {}".format(wallet_path))

        if res['migrate'] or res['format'] != 'current':
            if len(sys.argv) <= 1 or sys.argv[1] != 'setup_wallet':
                exit_with_error(
                    "Wallet is in legacy format.  Please unlock and migrate it with `ysi setup_wallet`."
                )

    parser = BlockstackArgumentParser(
        description='Blockstack cli version {}'.format(config.VERSION))

    all_methods = []
    subparsers = parser.add_subparsers(dest='action')

    # add basic methods
    all_method_names = get_cli_methods()
    all_methods = parse_methods(all_method_names)
    build_method_subparsers(subparsers, all_methods)

    # Print default help message, if no argument is given
    if len(argv) == 1 or '-h' in argv or '--help' in argv:
        parser.print_help()
        sys.exit(0)

    interactive, args, directive = False, None, None

    try:
        # capture stderr so we don't repeat ourselves
        args, unknown_args = parser.parse_known_args(args=argv[1:])
        directive = args.action
    except SystemExit:
        # bad arguments
        # special case: if the method is specified, but no method arguments are given,
        # then switch to prompting the user for individual arguments.
        try:
            directive_parser = BlockstackArgumentParser(
                description='Blockstack cli version {}'.format(config.VERSION))
            directive_subparsers = directive_parser.add_subparsers(
                dest='action')

            # only parse the directive
            build_method_subparsers(directive_subparsers,
                                    all_methods,
                                    include_args=False,
                                    include_opts=False)

            directive_args, directive_unknown_args = directive_parser.parse_known_args(
                args=argv[1:])

            # want interactive prompting
            interactive, directive = True, directive_args.action

        except SystemExit:
            # still invalid
            return {'error': 'Invalid arguments.  Try passing "-h".'}

    result = {}

    ysi_server, ysi_port = conf['ysi-client']['server'], conf['ysi-client'][
        'port']

    # initialize ysi connection
    session(server_host=ysi_server, server_port=ysi_port, set_global=True)

    prompt_func_arg = lambda help, name: raw_input('required: {} ("{}"): '.
                                                   format(help, name))
    prompt_func_opt = lambda help, name: raw_input('optional: {} ("{}"): '.
                                                   format(help, name))

    # dispatch to the apporpriate method
    for method_info in all_methods:
        if directive != method_info['command']:
            continue

        method = method_info['method']
        pragmas = method_info['pragmas']

        # interactive?
        if interactive:
            arg_names = [mi['name'] for mi in method_info['args']]
            opt_names = [mi['name'] for mi in method_info['opts']]
            arg_usage = ' '.join(arg_names)
            opt_usage = ' '.join(['[{}]'.format(opt) for opt in opt_names])

            print('')
            print('Interactive prompt engaged.  Press Ctrl+C to quit')
            print('Help for "{}": {}'.format(method_info['command'],
                                             method_info['help']))
            print('Arguments: {} {} {}'.format(method_info['command'],
                                               arg_usage, opt_usage))
            print('')

            required_args = prompt_args(method_info['args'], prompt_func_arg)
            if required_args is None:
                return {'error': 'Failed to prompt for arguments'}

            optional_args = prompt_args(method_info['opts'], prompt_func_opt)
            if optional_args is None:
                return {'error': 'Failed to prompt for arguments'}

            full_args = [method_info['command']
                         ] + required_args + optional_args
            try:
                args, unknown_args = parser.parse_known_args(args=full_args)
            except SystemExit:
                # invalid arguments
                return {'error': 'Invalid arguments.  Please try again.'}

        result = method(args, config_path=config_path)
        return {'status': True, 'result': result, 'pragmas': pragmas}

    # not found
    return {'error': 'No such command "{}"'.format(args.action)}
Exemple #8
0
            if optional_args is None:
                return {'error': 'Failed to prompt for arguments'}

            full_args = [method_info['command']
                         ] + required_args + optional_args
            try:
                args, unknown_args = parser.parse_known_args(args=full_args)
            except SystemExit:
                # invalid arguments
                return {'error': 'Invalid arguments.  Please try again.'}

        result = method(args, config_path=config_path)
        return {'status': True, 'result': result, 'pragmas': pragmas}

    # not found
    return {'error': 'No such command "{}"'.format(args.action)}


if __name__ == '__main__':
    result = run_cli()
    if 'error' in result:
        exit_with_error(result['error'])
    else:
        if 'raw' in result['pragmas']:
            print(result['result'])

        else:
            print_result(result['result'])

        sys.exit(0)
Exemple #9
0
def load_params():
    if not os.path.exists('tool.cfg'):
        utils.exit_with_error("tool.cfg not found")
    global params
    params = Params.load(fsutils.read_file('tool.cfg'))
    print(params)
Exemple #10
0
import utils as u
import vk
import os
import sys

from functools import lru_cache

WAIT_TIME = u.WAIT_TIME
# Intialize constants
# Initialize vk api

if not os.path.exists('params.json'):
    u.exit_with_error(
        "Файл params.json не найден; Создайте файл и повторите попытку")
    sys.exit(1)

params = u.json_read('params.json')

session = vk.AuthSession(**params)
api = vk.API(session, v='4.104')
_fwd_id = 0


# Initialize functions
# Returns FN LN by user object
def gen_name(user):
    return user['first_name'] + ' ' + user['last_name']


def gen_dir_name(user):
    return u.transliterate(user['first_name']) + '_' + u.transliterate(
Exemple #11
0
def run_cli(argv=None, config_path=CONFIG_PATH):
    """
    Run a CLI command from arguments (defaults to sys.argv)
    Return the result of the command on success.
    The result will be a dict, and will have 'error' defined on error condition.
    """

    if argv is None:
        argv = sys.argv

    # only version?
    if '-v' in argv or '--version' in argv:
        print config.VERSION
        sys.exit(0)

    # alternative config path?
    if '-c' in argv or '--config' in argv:
        i = 1
        while i < len(argv):
            if argv[i] == '-c' or argv[i] == '--config':
                if i + 1 >= len(argv):
                    print >> sys.stderr, "%s: missing path" % argv[i]
                    sys.exit(1)

                config_path = argv[i+1]
                argv.pop(i)
                argv.pop(i)

            else:
                i+=1

    conf = config.get_config(path=config_path)

    if conf is None:
        return {'error': 'Failed to load config'}

    conf_version = conf.get('client_version', '')
    if not semver_match( conf_version, VERSION ):
        exit_with_error("Invalid configuration file: %s != %s" % (conf_version, VERSION), \
                "Your configuration file (%s) is out of date.  Please move it and try again in order to automatically generate a new config file." % config_path)

    advanced_mode = conf.get('advanced_mode', False)

    parser = argparse.ArgumentParser(
            description='Blockstack cli version {}'.format(config.VERSION))

    all_methods = []
    subparsers = parser.add_subparsers(dest='action')
    
    # add basic methods 
    basic_methods = get_cli_basic_methods()
    basic_method_info = parse_methods( basic_methods )
    build_method_subparsers( subparsers, basic_method_info )

    all_methods = basic_method_info 

    if advanced_mode:
        # add advanced methods 
        log.debug("Enabling advanced methods")
        advanced_methods = get_cli_advanced_methods()
        advanced_method_info = parse_methods( advanced_methods )
        build_method_subparsers( subparsers, advanced_method_info )
        all_methods += advanced_method_info

    # Print default help message, if no argument is given
    if len(argv) == 1 or '-h' in argv:
        parser.print_help()
        return {}

    interactive = False
    args = None
    directive = None

    try:
        args, unknown_args = parser.parse_known_args(args=argv[1:])
        directive = args.action
    except SystemExit:
        # bad arguments
        # special case: if the method is specified, but no method arguments are given,
        # then switch to prompting the user for individual arguments.
        try:
            directive_parser = argparse.ArgumentParser(description='Blockstack cli version {}'.format(config.VERSION))
            directive_subparsers = directive_parser.add_subparsers(dest='action')

            # only parse the directive
            build_method_subparsers( directive_subparsers, all_methods, include_args=False, include_opts=False ) 
            directive_args, directive_unknown_args = directive_parser.parse_known_args( args=argv[1:] )

            # want interactive prompting
            interactive = True
            directive = directive_args.action

        except SystemExit:
            # still invalid 
            parser.print_help()
            return {'error': 'Invalid arguments.  Try passing "-h".'}

    result = {}

    blockstack_server = conf['server']
    blockstack_port = conf['port']

    # initialize blockstack connection
    session(conf=conf, server_host=blockstack_server,
            server_port=blockstack_port, set_global=True)

    # dispatch to the apporpriate method  
    for method_info in all_methods:
        if directive != method_info['command']:
            continue

        method = method_info['method']
        
        # interactive?
        if interactive:
            print ""
            print "Interactive prompt engaged.  Press Ctrl+C to quit"
            print "Help for '%s': %s" % (method_info['command'], method_info['help'])
            print ""
            
            required_args = prompt_args( method_info['args'], lambda arghelp, argname: raw_input("%s ('%s'): " % (arghelp, argname)) )
            if required_args is None:
                return {'error': 'Failed to prompt for arguments'}

            optional_args = prompt_args( method_info['opts'], lambda arghelp, argname: raw_input("optional: %s ('%s'): " % (arghelp, argname) ))
            if optional_args is None:
                return {'error': 'Failed to prompt for arguments'}

            full_args = [method_info['command']] + required_args + optional_args
            try:
                args, unknown_args = parser.parse_known_args( args=full_args )
            except SystemExit:
                # invalid arguments
                return {'error': 'Invalid arguments.  Please try again.'}

        result = method( args, config_path=config_path )
        return result

    # not found 
    return {'error': "No such command '%s'" % args.action}
Exemple #12
0
def run_cli(argv=None, config_path=CONFIG_PATH):
    """
    Run a CLI command from arguments (defaults to sys.argv)
    Return the result of the command on success.
    The result will be a dict, and will have 'error' defined on error condition.
    """

    if argv is None:
        argv = sys.argv

    # alternative config path?
    if '-c' in argv or '--config' in argv:
        i = 1
        while i < len(argv):
            if argv[i] == '-c' or argv[i] == '--config':
                if i + 1 >= len(argv):
                    print >> sys.stderr, "%s: missing path" % argv[i]
                    sys.exit(1)

                config_path = argv[i+1]
                argv.pop(i)
                argv.pop(i)

            else:
                i+=1

    conf = config.get_config(path=config_path)

    if conf is None:
        return {'error': 'Failed to load config'}

    conf_version = conf.get('client_version', '')
    if not semver_match( conf_version, VERSION ):
        exit_with_error("Invalid configuration file: %s != %s" % (conf_version, VERSION), \
                "Your configuration file (%s) is out of date.  Please move it and try again in order to automatically generate a new config file." % config_path)

    advanced_mode = conf.get('advanced_mode', False)

    parser = argparse.ArgumentParser(
            description='Blockstack cli version {}'.format(config.VERSION))

    all_methods = []
    subparsers = parser.add_subparsers(dest='action')
    
    # add basic methods 
    basic_methods = get_cli_basic_methods()
    basic_method_info = parse_methods( basic_methods )
    build_method_subparsers( subparsers, basic_method_info )

    all_methods = basic_method_info 

    if advanced_mode:
        # add advanced methods 
        log.debug("Enabling advanced methods")
        advanced_methods = get_cli_advanced_methods()
        advanced_method_info = parse_methods( advanced_methods )
        build_method_subparsers( subparsers, advanced_method_info )
        all_methods += advanced_method_info

    # Print default help message, if no argument is given
    if len(argv) == 1:
        parser.print_help()
        return {}

    interactive = False
    args = None
    directive = None

    try:
        args, unknown_args = parser.parse_known_args(args=argv[1:])
        directive = args.action
    except SystemExit:
        # bad arguments
        # special case: if the method is specified, but no method arguments are given,
        # then switch to prompting the user for individual arguments.
        try:
            directive_parser = argparse.ArgumentParser(description='Blockstack cli version {}'.format(config.VERSION))
            directive_subparsers = directive_parser.add_subparsers(dest='action')

            # only parse the directive
            build_method_subparsers( directive_subparsers, all_methods, include_args=False, include_opts=False ) 
            directive_args, directive_unknown_args = directive_parser.parse_known_args( args=argv[1:] )

            # want interactive prompting
            interactive = True
            directive = directive_args.action

        except SystemExit:
            # still invalid 
            parser.print_help()
            return {'error': 'Invalid arguments.  Try passing "-h".'}

    result = {}

    blockstack_server = conf['server']
    blockstack_port = conf['port']

    # initialize blockstack connection
    session(conf=conf, server_host=blockstack_server,
            server_port=blockstack_port, set_global=True)

    # dispatch to the apporpriate method  
    for method_info in all_methods:
        if directive != method_info['command']:
            continue

        method = method_info['method']
        
        # interactive?
        if interactive:
            print ""
            print "Interactive prompt engaged.  Press Ctrl+C to quit"
            print "Help for '%s': %s" % (method_info['command'], method_info['help'])
            print ""
            
            required_args = prompt_args( method_info['args'], lambda arghelp, argname: raw_input("%s ('%s'): " % (arghelp, argname)) )
            if required_args is None:
                return {'error': 'Failed to prompt for arguments'}

            optional_args = prompt_args( method_info['opts'], lambda arghelp, argname: raw_input("optional: %s ('%s'): " % (arghelp, argname) ))
            if optional_args is None:
                return {'error': 'Failed to prompt for arguments'}

            full_args = [method_info['command']] + required_args + optional_args
            try:
                args, unknown_args = parser.parse_known_args( args=full_args )
            except SystemExit:
                # invalid arguments
                return {'error': 'Invalid arguments.  Please try again.'}

        result = method( args, config_path=config_path )
        return result

    # not found 
    return {'error': "No such command '%s'" % args.action}
Exemple #13
0
            if required_args is None:
                return {'error': 'Failed to prompt for arguments'}

            optional_args = prompt_args( method_info['opts'], lambda arghelp, argname: raw_input("optional: %s ('%s'): " % (arghelp, argname) ))
            if optional_args is None:
                return {'error': 'Failed to prompt for arguments'}

            full_args = [method_info['command']] + required_args + optional_args
            try:
                args, unknown_args = parser.parse_known_args( args=full_args )
            except SystemExit:
                # invalid arguments
                return {'error': 'Invalid arguments.  Please try again.'}

        result = method( args, config_path=config_path )
        return result

    # not found 
    return {'error': "No such command '%s'" % args.action}


if __name__ == '__main__':
    result = run_cli()
    if 'error' in result:
        exit_with_error(result['error'])
    else:
        print_result(result)
        sys.exit(0)


def run_cli(argv=None, config_path=CONFIG_PATH):
    """
    Run a CLI command from arguments (defaults to sys.argv)
    Return the result of the command on success.
    The result will be a dict, and will have 'error' defined on error condition.
    """

    if argv is None:
        argv = sys.argv

    global_cli_args = {
        'debug': {
            'short': '-d',
            'long': '--debug',
            'has_arg': False,
            're-exec': True,
            'env': 'BLOCKSTACK_DEBUG',
            'help': 'Enable global debugging messages',
            'similar': ['--dbg', '-dd', '-ddd', '-dddd', '--verbose'],
            'secret': False,
        },
        'config': {
            'short': '-c',
            'long': '--config',
            'has_arg': True,
            're-exec': True,
            'env': 'BLOCKSTACK_CLIENT_CONFIG',
            'help': 'Path to alternative configuration file and associated state',
            'similar': ['--conf'],
            'secret': False,
        },
        'default_yes': {
            'short': '-y',
            'long': '--yes',
            'has_arg': False,
            're-exec': False,
            'env': 'BLOCKSTACK_CLIENT_INTERACTIVE_YES',
            'help': 'Assume default/yes response to all queries',
            'similar': [],
            'secret': False,
        },
        'api_pass': {
            'short': '-a',
            'long': '--api_password',
            'has_arg': True,
            're-exec': False,
            'env': 'BLOCKSTACK_API_PASSWORD',
            'help': 'API password to use',
            'similar': ['--api-password', '--api-pass', '--api_pass'],
            'secret': True,
        },
        'api_session': {
            'short': '-A',
            'long': '--api_session',
            'has_arg': True,
            're-exec': False,
            'env': 'BLOCKSTACK_API_SESSION',
            'help': 'API session token to use',
            'similar': ['--api-session', '--session', '--ses'],
            'secret': True,
        },
        'api_bind': {
            'short': '-b',
            'long': '--bind',
            'has_arg': True,
            're-exec': False,
            'env': 'BLOCKSTACK_API_BIND',
            'help': 'Address or hostname to bind the API server',
            'similar': [],
            'secret': False,
        },
        'dry_run': {
            'short': '-n',
            'long': '--dry_run',
            'has_arg': False,
            're-exec': True,
            'env': 'BLOCKSTACK_DRY_RUN',
            'help': 'Do not send transactions. Return the signed transaction instead.',
            'similar': ['--dry-run', '--dryrun'],
            'secret': False,
        },
        'wallet_password': {
            'short': '-p',
            'long': '--password',
            'has_arg': True,
            're-exec': False,
            'env': 'BLOCKSTACK_CLIENT_WALLET_PASSWORD',
            'help': 'Wallet decryption password',
            'similar': ['--pass', '--passwd'],
            'secret': True,
        },
        'indexer_host': {
            'short': '-H',
            'long': '--host',
            'has_arg': True,
            're-exec': False,
            'env': 'BLOCKSTACK_CLI_SERVER_HOST',
            'help': 'Hostname or IP address of the Blockstack blockchain indexer',
            'similar': ['--ip', '--ipv4'],
            'secret': False,
        },
        'indexer_port': {
            'short': '-P',
            'long': '--port',
            'has_arg': True,
            're-exec': False,
            'env': 'BLOCKSTACK_CLI_SERVER_PORT',
            'help': 'Port number of the Blockstack blockchain indexer',
            'similar': [],
            'secret': False,
        },
        'secret_fd': {
            'short': '-f',
            'long': '--secrets',
            'has_arg': True,
            're-exec': False,
            'help': 'Used internally; file descriptor number to serialized secrets preserved across execv(2).',
            'similar': [],
            'secret': False,
        },
    }

    if '-v' in argv or '--version' in argv:
        print(VERSION)
        sys.exit(0)

    arg_info = parse_args( global_cli_args, argv, config_path=config_path )
    if 'error' in arg_info:
        print("Failed to parse global CLI arguments: {}".format(arg_info['error']), file=sys.stderr)
        print("Global CLI arguments:\n{}\n".format( "\n".join( ["\t{}/{}\n\t\t{}".format(cliarg['short'], cliarg['long'], cliarg['help']) for argname, cliarg in global_cli_args.items()] )), file=sys.stderr)

        if 'similar' in arg_info:
            siminfo = arg_info['similar']
            assert len(siminfo.keys()) == 1
            opt = siminfo.keys()[0]
            arg = siminfo[opt]

            print("Suggestion:  Use '{}' instead of '{}'".format(opt, arg), file=sys.stderr)

        sys.exit(1)

    cli_debug = arg_info['args'].get('debug')
    cli_config_argv = (arg_info['args'].has_key('config'))

    # set (non-secret) environment variables
    for envar, enval in arg_info['envs'].items():
        if os.environ.get(envar) is None:
            if cli_debug:
                print("Set {} to {}".format(envar, enval), file=sys.stderr)

            os.environ[envar] = enval

    # set secrets...
    for secvar, secval in arg_info['secrets'].items():
        set_secret(secvar, secval)

    # re-exec?
    if arg_info['re-exec']:
 
        new_argv = arg_info['new_argv']

        if len(arg_info['secrets']) > 0:
            secbuf = serialize_secrets()
            fd = write_secrets(secbuf)

            new_argv += ['--secrets', str(fd)]

        new_argv = [sys.executable] + new_argv
        if cli_debug:
            print("Re-exec as `{}`".format(", ".join([
                '"{}"'.format(i) for i in new_argv])), file=sys.stderr)

        try:
            os.execv(new_argv[0], new_argv)
        except:
            import traceback as tb
            tb.print_exc()
            sys.exit(1)

    # load secrets
    if arg_info['args'].has_key('secret_fd'):
        fd_str = arg_info['args']['secret_fd']
        if fd_str:
            try:
                fd = int(fd_str)
            except:
                print('Invalid secret fd {}'.format(fd_str), file=sys.stderr)
                sys.exit(1)

            log.debug("Load secrets from {}".format(fd))

            try:
                os.lseek(fd, 0, os.SEEK_SET)
                secbuf = os.read(fd, 65536)
                os.close(fd)

                load_secrets(secbuf)
            except Exception as e:
                traceback.print_exc()
                sys.exit(1)

    # do one-time opt-in request
    uuid_path = client_uuid_path(config_dir=os.path.dirname(config_path))
    first_time = False
    client_uuid = None
    
    if not os.path.exists(uuid_path):
        first_time = True
        client_uuid = get_or_set_uuid(config_dir=os.path.dirname(config_path))

        if os.environ.get('BLOCKSTACK_CLIENT_INTERACTIVE_YES') != '1':
            # interactive allowed
            # prompt for email 
            print("Would you like to receive an email when there is a new release of this software available?")
            email_addr = raw_input("Email address (leave blank to opt out): ")

            # will only process real email addresses when we email announcements out
            if len(email_addr) > 0:
                analytics_user_register( client_uuid, email_addr )

  
    res = config.setup_config(config_path=config_path, interactive=(os.environ.get("BLOCKSTACK_CLIENT_INTERACTIVE_YES") != '1'))
    if 'error' in res:
        exit_with_error("Failed to load and verify config file: {}".format(res['error']))
   
    conf = res['config']

    # if the wallet exists, make sure that it's the latest version 
    wallet_path = os.path.join(os.path.dirname(config_path), WALLET_FILENAME)
    if os.path.exists(wallet_path):
        res = inspect_wallet(wallet_path=wallet_path)
        if 'error' in res:
            exit_with_error("Failed to inspect wallet at {}".format(wallet_path))

        if res['migrate'] or res['format'] != 'current':
            if len(sys.argv) <= 1 or sys.argv[1] != 'setup_wallet':
                exit_with_error("Wallet is in legacy format.  Please unlock and migrate it with `blockstack setup_wallet`.")

    parser = BlockstackArgumentParser(
        description='Blockstack cli version {}'.format(config.VERSION)
    )

    all_methods = []
    subparsers = parser.add_subparsers(dest='action')

    # add basic methods
    all_method_names = get_cli_methods()
    all_methods = parse_methods(all_method_names)
    build_method_subparsers(subparsers, all_methods)

    # Print default help message, if no argument is given
    if len(argv) == 1 or '-h' in argv or '--help' in argv:
        parser.print_help()
        sys.exit(0)

    interactive, args, directive = False, None, None

    try:
        # capture stderr so we don't repeat ourselves
        args, unknown_args = parser.parse_known_args(args=argv[1:])
        directive = args.action
    except SystemExit:
        # bad arguments
        # special case: if the method is specified, but no method arguments are given,
        # then switch to prompting the user for individual arguments.
        try:
            directive_parser = BlockstackArgumentParser(
                description='Blockstack cli version {}'.format(config.VERSION)
            )
            directive_subparsers = directive_parser.add_subparsers(
                dest='action'
            )

            # only parse the directive
            build_method_subparsers(
                directive_subparsers, all_methods, include_args=False, include_opts=False
            )

            directive_args, directive_unknown_args = directive_parser.parse_known_args(
                args=argv[1:]
            )

            # want interactive prompting
            interactive, directive = True, directive_args.action

        except SystemExit:
            # still invalid
            return {'error': 'Invalid arguments.  Try passing "-h".'}

    result = {}

    blockstack_server, blockstack_port = conf['blockstack-client']['server'], conf['blockstack-client']['port']

    # initialize blockstack connection
    session(
        server_host=blockstack_server,
        server_port=blockstack_port, set_global=True
    )

    prompt_func = lambda help, name: raw_input('optional: {} ("{}"): '.format(help, name))

    # dispatch to the apporpriate method
    for method_info in all_methods:
        if directive != method_info['command']:
            continue

        method = method_info['method']
        pragmas = method_info['pragmas']

        # interactive?
        if interactive:
            arg_names = [mi['name'] for mi in method_info['args']]
            opt_names = [mi['name'] for mi in method_info['opts']]
            arg_usage = ' '.join(arg_names)
            opt_usage = ' '.join( ['[{}]'.format(opt) for opt in opt_names] )

            print('')
            print('Interactive prompt engaged.  Press Ctrl+C to quit')
            print('Help for "{}": {}'.format(method_info['command'], method_info['help']))
            print('Arguments: {} {}'.format(method_info['command'], arg_usage, opt_usage))
            print('')

            required_args = prompt_args(method_info['args'], prompt_func)
            if required_args is None:
                return {'error': 'Failed to prompt for arguments'}

            optional_args = prompt_args(method_info['opts'], prompt_func)
            if optional_args is None:
                return {'error': 'Failed to prompt for arguments'}

            full_args = [method_info['command']] + required_args + optional_args
            try:
                args, unknown_args = parser.parse_known_args(args=full_args)
            except SystemExit:
                # invalid arguments
                return {'error': 'Invalid arguments.  Please try again.'}

        result = method(args, config_path=config_path)
        return {'status': True, 'result': result, 'pragmas': pragmas}

    # not found
    return {'error': 'No such command "{}"'.format(args.action)}
Exemple #15
0
def run_cli(argv=None, config_path=CONFIG_PATH):
    """
    Run a CLI command from arguments (defaults to sys.argv)
    Return the result of the command on success.
    The result will be a dict, and will have 'error' defined on error condition.
    """

    if argv is None:
        argv = sys.argv

    cli_debug = False
    cli_config_argv = False
    cli_default_yes = False
    cli_api_pass = None
    cli_dry_run = False

    if '-v' in argv or '--version' in argv:
        print(VERSION)
        sys.exit(0)

    # debug?
    new_argv, cli_debug = find_arg(argv, False, '-d', '--debug')
    if new_argv is None:
        # invalid
        sys.exit(1)

    if cli_debug:
        os.environ['BLOCKSTACK_DEBUG'] = '1'
        log.setLevel(logging.DEBUG)
        log.debug("Activated debugging")

    # alternative config path?
    new_argv, cli_config_path = find_arg(argv, True, '-c', '--config')
    if new_argv is None:
        # invalid
        sys.exit(1)

    argv = new_argv
    if cli_config_path:
        cli_config_argv = True
        config_path = cli_config_path
        log.debug('Use config file {}'.format(config_path))

    os.environ['BLOCKSTACK_CLIENT_CONFIG'] = config_path

    # CLI-given password?
    new_argv, cli_password = find_arg(argv, True, '-p', '--password')
    if new_argv is None:
        # invalid
        sys.exit(1)

    argv = new_argv
    if cli_password and os.environ.get(
            'BLOCKSTACK_CLIENT_WALLET_PASSWORD') is None:
        log.debug("Use CLI password")
        os.environ["BLOCKSTACK_CLIENT_WALLET_PASSWORD"] = cli_password

    # assume YES to all prompts?
    new_argv, cli_default_yes = find_arg(argv, False, '-y', '--yes')
    if new_argv is None:
        # invalid
        sys.exit(1)

    if cli_default_yes or os.environ.get(
            "BLOCKSTACK_CLIENT_INTERACTIVE_YES") == "1":
        if cli_debug:
            print("Assume YES to all interactive prompts", file=sys.stderr)

        os.environ["BLOCKSTACK_CLIENT_INTERACTIVE_YES"] = '1'

    # API password?
    new_argv, cli_api_pass = find_arg(argv, True, '-a', '--api_password')
    if new_argv is None:
        # invalid
        sys.exit(1)

    argv = new_argv
    if cli_api_pass:
        os.environ['BLOCKSTACK_API_PASSWORD'] = cli_api_pass

    # dry-run?
    new_argv, cli_dry_run = find_arg(argv, False, '-n', '--dry-run')
    if new_argv is None:
        # invalid
        sys.exit(1)

    if cli_dry_run or os.environ.get("BLOCKSTACK_DRY_RUN") == "1":
        if cli_debug:
            print('Dry-run; no transactions will be sent', file=sys.stderr)

        os.environ['BLOCKSTACK_DRY_RUN'] = "1"

    if cli_config_argv or cli_debug or cli_dry_run:
        # re-exec to reset variables
        if cli_debug:
            print("Re-exec {} with {}".format(argv[0], argv), file=sys.stderr)

        os.execv(argv[0], argv)

    # do one-time opt-in request
    uuid_path = client_uuid_path(config_dir=os.path.dirname(config_path))
    first_time = False
    client_uuid = None

    if not os.path.exists(uuid_path):
        first_time = True
        client_uuid = get_or_set_uuid(config_dir=os.path.dirname(config_path))

        if os.environ.get('BLOCKSTACK_CLIENT_INTERACTIVE_YES') != '1':
            # interactive allowed
            # prompt for email
            print(
                "Would you like to receive an email when there is a new release of this software available?"
            )
            email_addr = raw_input("Email address (leave blank to opt out): ")

            # will only process real email addresses when we email announcements out
            if len(email_addr) > 0:
                analytics_user_register(client_uuid, email_addr)

    conf = config.get_config(
        path=config_path,
        interactive=(os.environ.get('BLOCKSTACK_CLIENT_INTERACTIVE_YES') !=
                     '1'))
    if conf is None:
        return {'error': 'Failed to load config'}

    conf_version = conf.get('client_version', '')
    if not semver_match(conf_version, VERSION):
        # back up the config file
        if not cli_config_argv:
            # default config file
            backup_path = config.backup_config_file(config_path=config_path)
            if not backup_path:
                exit_with_error(
                    "Failed to back up legacy configuration file {}".format(
                        config_path))

            else:
                exit_with_error(
                    "Backed up legacy configuration file from {} to {} and re-generated a new, default configuration.  Please restart."
                    .format(config_path, backup_path))

    advanced_mode = conf.get('advanced_mode', False)

    parser = argparse.ArgumentParser(
        description='Blockstack cli version {}'.format(config.VERSION))

    all_methods = []
    subparsers = parser.add_subparsers(dest='action')

    # add basic methods
    all_method_names = get_cli_methods()
    all_methods = parse_methods(all_method_names)
    build_method_subparsers(subparsers, all_methods)

    if not advanced_mode:
        # remove advanced methods
        all_methods = filter(lambda m: 'advanced' not in m['pragmas'],
                             all_methods)

    # Print default help message, if no argument is given
    if len(argv) == 1 or '-h' in argv or '--help' in argv:
        parser.print_help()
        sys.exit(0)

    interactive, args, directive = False, None, None

    try:
        args, unknown_args = parser.parse_known_args(args=argv[1:])
        directive = args.action
    except SystemExit:
        # bad arguments
        # special case: if the method is specified, but no method arguments are given,
        # then switch to prompting the user for individual arguments.
        try:
            directive_parser = argparse.ArgumentParser(
                description='Blockstack cli version {}'.format(config.VERSION))
            directive_subparsers = directive_parser.add_subparsers(
                dest='action')

            # only parse the directive
            build_method_subparsers(directive_subparsers,
                                    all_methods,
                                    include_args=False,
                                    include_opts=False)
            directive_args, directive_unknown_args = directive_parser.parse_known_args(
                args=argv[1:])

            # want interactive prompting
            interactive, directive = True, directive_args.action

        except SystemExit:
            # still invalid
            parser.print_help()
            return {'error': 'Invalid arguments.  Try passing "-h".'}

    result = {}

    blockstack_server, blockstack_port = conf['server'], conf['port']

    # initialize blockstack connection
    session(conf=conf,
            server_host=blockstack_server,
            server_port=blockstack_port,
            set_global=True)

    prompt_func = lambda help, name: raw_input('optional: {} ("{}"): '.format(
        help, name))

    # dispatch to the apporpriate method
    for method_info in all_methods:
        if directive != method_info['command']:
            continue

        method = method_info['method']
        pragmas = method_info['pragmas']

        # interactive?
        if interactive:
            print('')
            print('Interactive prompt engaged.  Press Ctrl+C to quit')
            print('Help for "{}": {}'.format(method_info['command'],
                                             method_info['help']))
            print('')

            required_args = prompt_args(method_info['args'], prompt_func)
            if required_args is None:
                return {'error': 'Failed to prompt for arguments'}

            optional_args = prompt_args(method_info['opts'], prompt_func)
            if optional_args is None:
                return {'error': 'Failed to prompt for arguments'}

            full_args = [method_info['command']
                         ] + required_args + optional_args
            try:
                args, unknown_args = parser.parse_known_args(args=full_args)
            except SystemExit:
                # invalid arguments
                return {'error': 'Invalid arguments.  Please try again.'}

        result = method(args, config_path=config_path)
        return {'status': True, 'result': result, 'pragmas': pragmas}

    # not found
    return {'error': 'No such command "{}"'.format(args.action)}