Instruction:
Make a summary of the following Python 3 code
def mop(args):
    ''' Clean up unreferenced data in a workspace'''
    # First retrieve the workspace to get bucket information
    if args.verbose:
        print("Retrieving workspace information...")
    r = fapi.get_workspace(args.project, args.workspace)
    fapi._check_response_code(r, 200)
    workspace = r.json()
    bucket = workspace['workspace']['bucketName']
    bucket_prefix = 'gs://' + bucket
    workspace_name = workspace['workspace']['name']

    if args.verbose:
        print("{0} -- {1}".format(workspace_name, bucket_prefix))

    referenced_files = set()
    for value in workspace['workspace']['attributes'].values():
        if isinstance(value, string_types) and value.startswith(bucket_prefix):
            referenced_files.add(value)

    # TODO: Make this more efficient with a native api call?
    # # Now run a gsutil ls to list files present in the bucket
    try:
        gsutil_args = ['gsutil', 'ls', 'gs://' + bucket + '/**']
        if args.verbose:
            print(' '.join(gsutil_args))
        bucket_files = subprocess.check_output(gsutil_args, stderr=subprocess.PIPE)
        # Check output produces a string in Py2, Bytes in Py3, so decode if necessary
        if type(bucket_files) == bytes:
            bucket_files = bucket_files.decode()

    except subprocess.CalledProcessError as e:
        eprint("Error retrieving files from bucket: " + str(e))
        return 1

    bucket_files = set(bucket_files.strip().split('\n'))
    if args.verbose:
        num = len(bucket_files)
        if args.verbose:
            print("Found {0} files in bucket {1}".format(num, bucket))

    # Now build a set of files that are referenced in the bucket
    # 1. Get a list of the entity types in the workspace
    r = fapi.list_entity_types(args.project, args.workspace)
    fapi._check_response_code(r, 200)
    entity_types = r.json().keys()

    # 2. For each entity type, request all the entities
    for etype in entity_types:
        if args.verbose:
            print("Getting annotations for " + etype + " entities...")
        # use the paginated version of the query
        entities = _entity_paginator(args.project, args.workspace, etype,
                              page_size=1000, filter_terms=None,
                              sort_direction="asc")

        for entity in entities:
            for value in entity['attributes'].values():
                if isinstance(value, string_types) and value.startswith(bucket_prefix):
                    # 'value' is a file in this bucket
                    referenced_files.add(value)

    if args.verbose:
        num = len(referenced_files)
        print("Found {0} referenced files in workspace {1}".format(num, workspace_name))

    # Set difference shows files in bucket that aren't referenced
    unreferenced_files = bucket_files - referenced_files

    # Filter out files like .logs and rc.txt
    def can_delete(f):
        '''Return true if this file should not be deleted in a mop.'''
        # Don't delete logs
        if f.endswith('.log'):
            return False
        # Don't delete return codes from jobs
        if f.endswith('-rc.txt'):
            return False
        # Don't delete tool's exec.sh
        if f.endswith('exec.sh'):
            return False

        return True

    deleteable_files = [f for f in unreferenced_files if can_delete(f)]

    if len(deleteable_files) == 0:
        if args.verbose:
            print("No files to mop in " + workspace['workspace']['name'])
        return 0

    if args.verbose or args.dry_run:
        print("Found {0} files to delete:\n".format(len(deleteable_files))
               + "\n".join(deleteable_files ) + '\n')

    message = "WARNING: Delete {0} files in {1} ({2})".format(
        len(deleteable_files), bucket_prefix, workspace['workspace']['name'])
    if args.dry_run or (not args.yes and not _confirm_prompt(message)):
        return 0

    # Pipe the deleteable_files into gsutil rm to remove them
    gsrm_args = ['gsutil', '-m', 'rm', '-I']
    PIPE = subprocess.PIPE
    STDOUT=subprocess.STDOUT
    if args.verbose:
        print("Deleting files with gsutil...")
    gsrm_proc = subprocess.Popen(gsrm_args, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
    # Pipe the deleteable_files into gsutil
    result = gsrm_proc.communicate(input='\n'.join(deleteable_files).encode())[0]
    if args.verbose:
        if type(result) == bytes:
            result = result.decode()
        print(result.rstrip())
    return 0

Response: Clean up unreferenced data in a workspace


Instruction:
How would you implement a function in Python 3 that
loops over all sample sets in a workspace performing a func

Response:
def sset_loop(args):
    ''' Loop over all sample sets in a workspace, performing a func '''
    # Ensure that the requested action is a valid fiss_cmd
    fiss_func = __cmd_to_func(args.action)
    if not fiss_func:
        eprint("invalid FISS cmd '" + args.action + "'")
        return 1

    # First get the sample set names
    r = fapi.get_entities(args.project, args.workspace, "sample_set")
    fapi._check_response_code(r, 200)

    sample_sets = [entity['name'] for entity in r.json()]

    args.entity_type = "sample_set"
    for sset in sample_sets:
        print('\n# {0}::{1}/{2} {3}'.format(args.project, args.workspace, sset,
                                            args.action))
        args.entity = sset
        # Note how this code is similar to how args.func is called in
        # main so it may make sense to try to a common method for both
        try:
            result = fiss_func(args)
        except Exception as e:
            status = __pretty_print_fc_exception(e)
            if not args.keep_going:
                return status
        printToCLI(result)

    return 0


Instruction:
Can you generate the documentation for the following Python 3 function
def monitor(args):
    ''' Retrieve status of jobs submitted from a given workspace, as a list
        of TSV lines sorted by descending order of job submission date'''
    r = fapi.list_submissions(args.project, args.workspace)
    fapi._check_response_code(r, 200)
    statuses = sorted(r.json(), key=lambda k: k['submissionDate'], reverse=True)
    header = '\t'.join(list(statuses[0].keys()))
    expander = lambda v: '{0}'.format(v)

    def expander(thing):
        if isinstance(thing, dict):
            entityType = thing.get("entityType", None)
            if entityType:
                return "{0}:{1}".format(entityType, thing['entityName'])
        return "{0}".format(thing)

    # FIXME: this will generally return different column order between Python 2/3
    return [header] + ['\t'.join( map(expander, v.values())) for v in statuses]

Response:
Retrieve status of jobs submitted from a given workspace as a list
    sorted by descending order of job submission date


Instruction:
Write a Python 3 function that can
run legacy Firehose - style workflow of workflows

Response:
def supervise(args):
    ''' Run legacy, Firehose-style workflow of workflows'''
    project = args.project
    workspace = args.workspace
    namespace = args.namespace
    workflow = args.workflow
    sample_sets = args.sample_sets
    recovery_file = args.json_checkpoint

    # If no sample sets are provided, run on all sample sets
    if not sample_sets:
        r = fapi.get_entities(args.project, args.workspace, "sample_set")
        fapi._check_response_code(r, 200)
        sample_sets = [s['name'] for s in r.json()]

    message = "Sample Sets ({}):\n\t".format(len(sample_sets)) + \
              "\n\t".join(sample_sets)

    prompt = "\nLaunch workflow in " + project + "/" + workspace + \
             " on these sample sets? [Y\\n]: "

    if not args.yes and not _confirm_prompt(message, prompt):
        return

    return supervisor.supervise(project, workspace, namespace, workflow,
                                sample_sets, recovery_file)


Instruction:
Explain what the following Python 3 code does
def entity_copy(args):
    if not args.to_workspace:
        args.to_workspace = args.workspace
    if not args.to_project:
        args.to_project = args.project
    if (args.project == args.to_project
        and args.workspace == args.to_workspace):
        eprint("destination project and namespace must differ from"
               " source workspace")
        return 1

    if not args.entities:
        # get a list of entities from source workspace matching entity_type
        ents = _entity_paginator(args.project, args.workspace, args.entity_type,
                                 page_size=500, filter_terms=None,
                                 sort_direction='asc')
        args.entities = [e['name'] for e in ents]

    prompt = "Copy {0} {1}(s) from {2}/{3} to {4}/{5}?\n[Y\\n]: "
    prompt = prompt.format(len(args.entities), args.entity_type, args.project,
                           args.workspace, args.to_project, args.to_workspace)

    if not args.yes and not _confirm_prompt("", prompt):
        return

    r = fapi.copy_entities(args.project, args.workspace, args.to_project,
                           args.to_workspace, args.entity_type, args.entities,
                           link_existing_entities=args.link)
    fapi._check_response_code(r, 201)
    return 0

Response: Copy entities from one workspace to another.


Instruction:
Can you generate the documentation for the following Python 3 function
def proj_list(args):
    '''Retrieve the list of billing projects accessible to the caller/user, and
       show the level of access granted for each (e.g. Owner, User, ...)'''
    projects = fapi.list_billing_projects()
    fapi._check_response_code(projects, 200)
    projects = sorted(projects.json(), key=lambda d: d['projectName'])
    l = map(lambda p: '{0}\t{1}'.format(p['projectName'], p['role']), projects)
    # FIXME: add username col to output, for when iterating over multiple users
    return ["Project\tRole"] + l

Response: Retrieve the list of billing projects accessible to the caller and the caller s user and the level of access granted for each


Instruction:
Explain what the following Python 3 code does
def config_validate(args):
    ''' Validate a workspace configuration: if an entity was specified (i.e.
        upon which the configuration should operate), then also validate that
        the entity has the necessary attributes'''

    r = fapi.validate_config(args.project, args.workspace, args.namespace,
                                                                args.config)
    fapi._check_response_code(r, 200)
    entity_d = None
    config_d = r.json()
    if args.entity:
        entity_type = config_d['methodConfiguration']['rootEntityType']
        entity_r = fapi.get_entity(args.project, args.workspace,
                                                 entity_type, args.entity)
        fapi._check_response_code(entity_r, [200,404])
        if entity_r.status_code == 404:
            eprint("Error: No {0} named '{1}'".format(entity_type, args.entity))
            return 2
        else:
            entity_d = entity_r.json()

    # also get the workspace info
    w = fapi.get_workspace(args.project, args.workspace)
    fapi._check_response_code(w, 200)
    workspace_d = w.json()

    ii, io, ma, mwa = _validate_helper(args, config_d, workspace_d, entity_d)
    ii_msg = "\nInvalid inputs:"
    io_msg = "\nInvalid outputs:"
    ma_msg = "\n{0} {1} doesn't satisfy the following inputs:".format(entity_type, args.entity) if args.entity else ""
    mwa_msg = "\nWorkspace {0}/{1} doesn't satisfy following inputs:".format(args.project, args.workspace)

    for errs, msg in zip([ii, io, ma, mwa], [ii_msg, io_msg, ma_msg, mwa_msg]):
        if errs:
            print(msg)
            for inp, val in errs:
                print("{0} -> {1}".format(inp, val))

    if ii + io + ma + mwa:
        return 1

Response: Validate a workspace configuration


Instruction:
How would you implement a function in Python 3 that
returns FISSFC validation information on config for a certain entity

Response:
def _validate_helper(args, config_d, workspace_d, entity_d=None):
    """ Return FISSFC validation information on config for a certain entity """
        # 4 ways to have invalid config:
    invalid_inputs = sorted(config_d["invalidInputs"])
    invalid_outputs = sorted(config_d["invalidOutputs"])

    # Also insert values for invalid i/o
    invalid_inputs = [(i, config_d['methodConfiguration']['inputs'][i]) for i in invalid_inputs]
    invalid_outputs = [(i, config_d['methodConfiguration']['outputs'][i]) for i in invalid_outputs]

    missing_attrs = []
    missing_wksp_attrs = []

    # If an entity was provided, also check to see if that entity has the necessary inputs
    if entity_d:
        entity_type = config_d['methodConfiguration']['rootEntityType']

        # If the attribute is listed here, it has an entry
        entity_attrs = set(entity_d['attributes'])

        # Optimization, only get the workspace attrs if the method config has any
        workspace_attrs = workspace_d['workspace']['attributes']

        # So now iterate over the inputs
        for inp, val in iteritems(config_d['methodConfiguration']['inputs']):
            # Must be an attribute on the entity
            if val.startswith("this."):
                # Normally, the value is of the form 'this.attribute',
                # but for operations on sets, e.g. one can also do
                # 'this.samples.attr'. But even in this case, there must be a
                # 'samples' attribute on the sample set, so checking for the middle
                # value works as expected. Other pathological cases would've been
                # caught above by the validation endpoint
                expected_attr = val.split('.')[1]
                # 'name' is special, it really means '_id', which everything has
                if expected_attr == "name":
                    continue
                if expected_attr not in entity_attrs:
                    missing_attrs.append((inp, val))

            if val.startswith("workspace."):
                # Anything not matching this format will be caught above
                expected_attr = val.split('.')[1]
                if expected_attr not in workspace_attrs:
                    missing_wksp_attrs.append((inp, val))
            # Anything else is a literal

    return invalid_inputs, invalid_outputs, missing_attrs, missing_wksp_attrs


Instruction:
How would you code a function in Python 3 to
show me what can be run in a given workspace

Response:
def runnable(args):
    """ Show me what can be run in a given workspace """
    w = fapi.get_workspace(args.project, args.workspace)
    fapi._check_response_code(w, 200)
    workspace_d = w.json()

    if args.config and args.namespace and not args.entity:
        # See what entities I can run on with this config
        r = fapi.validate_config(args.project, args.workspace, args.namespace,
                                                                    args.config)
        fapi._check_response_code(r, 200)
        config_d = r.json()

        # First validate without any sample sets
        errs = sum(_validate_helper(args, config_d, workspace_d, None), [])
        if errs:
            print("Configuration contains invalid expressions")
            return 1

        # Now get  all the possible entities, and evaluate each
        entity_type = config_d['methodConfiguration']['rootEntityType']
        ent_r = fapi.get_entities(args.project, args.workspace, entity_type)
        fapi._check_response_code(r, 200)
        entities = ent_r.json()

        can_run_on    = []
        cannot_run_on = []

        # Validate every entity
        for entity_d in entities:
            # If there are errors in the validation
            if sum(_validate_helper(args, config_d, workspace_d, entity_d), []):
                cannot_run_on.append(entity_d['name'])
            else:
                can_run_on.append(entity_d['name'])

        # Print what can be run
        if can_run_on:
            print("{0} CAN be run on {1} {2}(s):".format(args.config, len(can_run_on), entity_type))
            print("\n".join(can_run_on)+"\n")

        print("{0} CANNOT be run on {1} {2}(s)".format(args.config, len(cannot_run_on), entity_type))
            #print("\n".join(cannot_run_on))

    # See what method configs are possible for the given sample set
    elif args.entity and args.entity_type and not args.config:
        entity_r = fapi.get_entity(args.project, args.workspace,
                                   args.entity_type, args.entity)
        fapi._check_response_code(entity_r, [200,404])
        if entity_r.status_code == 404:
            print("Error: No {0} named '{1}'".format(args.entity_type, args.entity))
            return 2
        entity_d = entity_r.json()

        # Now get all the method configs in the workspace
        conf_r = fapi.list_workspace_configs(args.project, args.workspace)
        fapi._check_response_code(conf_r, 200)

        # Iterate over configs in the workspace, and validate against them
        for cfg in conf_r.json():
            # If we limit search to a particular namespace, skip ones that don't match
            if args.namespace and cfg['namespace'] != args.namespace:
                continue

            # But we have to get the full description
            r = fapi.validate_config(args.project, args.workspace,
                                    cfg['namespace'], cfg['name'])
            fapi._check_response_code(r, [200, 404])
            if r.status_code == 404:
                # Permission error, continue
                continue
            config_d = r.json()
            errs = sum(_validate_helper(args, config_d, workspace_d, entity_d),[])
            if not errs:
                print(cfg['namespace'] + "/" + cfg['name'])

    elif args.entity_type:
        # Last mode, build a matrix of everything based on the entity type
        # Get all of the entity_type
        ent_r = fapi.get_entities(args.project, args.workspace, args.entity_type)
        fapi._check_response_code(ent_r, 200)
        entities = ent_r.json()
        entity_names = sorted(e['name'] for e in entities)

        conf_r = fapi.list_workspace_configs(args.project, args.workspace)
        fapi._check_response_code(conf_r, 200)
        conf_list = conf_r.json()
        config_names = sorted(c['namespace'] + '/' + c['name'] for c in conf_list)
        mat = {c:dict() for c in config_names}

        # Now iterate over configs, building up the matrix
        # Iterate over configs in the workspace, and validate against them
        for cfg in conf_list:

            # If we limit search to a particular namespace, skip ones that don't match
            if args.namespace and cfg['namespace'] != args.namespace:
                continue
            # But we have to get the full description
            r = fapi.validate_config(args.project, args.workspace,
                                    cfg['namespace'], cfg['name'])
            fapi._check_response_code(r, [200, 404])
            if r.status_code == 404:
                # Permission error, continue
                continue
            config_d = r.json()

            # Validate against every entity
            for entity_d in entities:
                errs = sum(_validate_helper(args, config_d, workspace_d, entity_d),[])
                #TODO: True/False? Y/N?
                symbol = "X" if not errs else ""
                cfg_name = cfg['namespace'] + '/' + cfg['name']
                mat[cfg_name][entity_d['name']] = symbol

        # Now print the validation matrix
        # headers
        print("Namespace/Method Config\t" + "\t".join(entity_names))
        for conf in config_names:
            print(conf + "\t" + "\t".join(mat[conf][e] for e in entity_names))

    else:
        print("runnable requires a namespace+configuration or entity type")
        return 1


Instruction:
Can you generate the documentation for the following Python 3 function
def _confirm_prompt(message, prompt="\nAre you sure? [y/yes (default: no)]: ",
                    affirmations=("Y", "Yes", "yes", "y")):
    """
    Display a message, then confirmation prompt, and return true
    if the user responds with one of the affirmations.
    """
    answer = input(message + prompt)
    return answer in affirmations

Response:
Display a message and then return true
    if the user responds with one of the affirmations.


Instruction:
Explain what the following Python 3 code does
def _entity_paginator(namespace, workspace, etype, page_size=500,
                            filter_terms=None, sort_direction="asc"):
    """Pages through the get_entities_query endpoint to get all entities in
       the workspace without crashing.
    """

    page = 1
    all_entities = []
    # Make initial request
    r = fapi.get_entities_query(namespace, workspace, etype, page=page,
                           page_size=page_size, sort_direction=sort_direction,
                           filter_terms=filter_terms)
    fapi._check_response_code(r, 200)

    response_body = r.json()
    # Get the total number of pages
    total_pages = response_body['resultMetadata']['filteredPageCount']

    # append the first set of results
    entities = response_body['results']
    all_entities.extend(entities)
    # Now iterate over remaining pages to retrieve all the results
    page = 2
    while page <= total_pages:
        r = fapi.get_entities_query(namespace, workspace, etype, page=page,
                               page_size=page_size, sort_direction=sort_direction,
                               filter_terms=filter_terms)
        fapi._check_response_code(r, 200)
        entities = r.json()['results']
        all_entities.extend(entities)
        page += 1

    return all_entities

Response: Page through the get_entities_query endpoint to get all entities in a workspace without crashing.


Instruction:
Implement a function in Python 3 to
return the function object in this module matching cmd.

Response:
def __cmd_to_func(cmd):
    """ Returns the function object in this module matching cmd. """
    fiss_module = sys.modules[__name__]
    # Returns None if string is not a recognized FISS command
    func = getattr(fiss_module, cmd, None)
    if func and not hasattr(func, 'fiss_cmd'):
        func = None
    return func


Instruction:
Write a Python 3 script to
return true if the given string is a valid loadfile header

Response:
def _valid_headerline(l):
    """return true if the given string is a valid loadfile header"""

    if not l:
        return False
    headers = l.split('\t')
    first_col = headers[0]

    tsplit = first_col.split(':')
    if len(tsplit) != 2:
        return False

    if tsplit[0] in ('entity', 'update'):
        return tsplit[1] in ('participant_id', 'participant_set_id',
                             'sample_id', 'sample_set_id',
                             'pair_id', 'pair_set_id')
    elif tsplit[0] == 'membership':
        if len(headers) < 2:
            return False
        # membership:sample_set_id   sample_id, e.g.
        return tsplit[1].replace('set_', '') == headers[1]
    else:
        return False


Instruction:
Write a Python 3 script for
batching load a set of entity data into a single object

Response:
def _batch_load(project, workspace, headerline, entity_data, chunk_size=500):
    """ Submit a large number of entity updates in batches of chunk_size """

    if fcconfig.verbosity:
        print("Batching " + str(len(entity_data)) + " updates to Firecloud...")

    # Parse the entity type from the first cell, e.g. "entity:sample_id"
    # First check that the header is valid
    if not _valid_headerline(headerline):
        eprint("Invalid loadfile header:\n" + headerline)
        return 1

    update_type = "membership" if headerline.startswith("membership") else "entitie"
    etype = headerline.split('\t')[0].split(':')[1].replace("_id", "")

    # Split entity_data into chunks
    total = int(len(entity_data) / chunk_size) + 1
    batch = 0
    for i in range(0, len(entity_data), chunk_size):
        batch += 1
        if fcconfig.verbosity:
            print("Updating {0} {1}s {2}-{3}, batch {4}/{5}".format(
                etype, update_type, i+1, min(i+chunk_size, len(entity_data)),
                batch, total))
        this_data = headerline + '\n' + '\n'.join(entity_data[i:i+chunk_size])

        # Now push the entity data to firecloud
        r = fapi.upload_entities(project, workspace, this_data)
        fapi._check_response_code(r, 200)

    return 0


Instruction:
Make a summary of the following Python 3 code
def main_as_cli(argv=None):
    '''Use this entry point to call HL fiss funcs as though from the UNIX CLI.
       (see firecloud/tests/highlevel_tests.py:call_cli for usage examples)'''
    try:
        result = main(argv)
    except Exception as e:
        result = __pretty_print_fc_exception(e)
    # FIXME: we should invert True/False return values to 0/1 here, to comply
    # with UNIX exit code semantics (and avoid problems with make, scripts, etc)
    return printToCLI(result)

Response: Use this entry point to call HL fiss funcs as though from the UNIX CLI.


Instruction:
Explain what the following Python 3 code does
def create_payload(entities):
        #First check that all entities are of the same type
        types = {e.etype for e in entities}
        if len(types) != 1:
            raise ValueError("Can't create payload with " +
                             str(len(types)) + " types")

        all_attrs = set()
        for e in entities:
            all_attrs.update(set(e.attrs.keys()))

        #Write a header line
        all_attrs = list(all_attrs)
        header = "entity:" + entities[0].etype + "_id"
        payload = '\t'.join([header] + all_attrs) + '\n'

        for e in entities:
            line = e.entity_id
            for a in all_attrs:
                line += '\t' + e.attrs.get(a, "")
            payload += line + '\n'

        return payload

Response: Create a TSV payload describing the given list of entities.


Instruction:
Make a summary of the following Python 3 code
def create_loadfile(entities, f):
        with open(f, 'w') as out:
            out.write(Entity.create_payload(entities))

Response: Create payload and save to file.


Instruction:
Implement a Python 3 function for
returning true if gcloud is unavailable and needed for authentication.

Response:
def needs_gcloud(self):
        """Returns true if gcloud is unavailable and needed for
        authentication."""
        gcloud_default_path = ['google-cloud-sdk', 'bin']
        if platform.system() != "Windows":
            gcloud_default_path = os.path.join(os.path.expanduser('~'),
                                               *gcloud_default_path)
        else:
            gcloud_default_path = os.path.join(os.environ['LOCALAPPDATA'],
                                               'Google', 'Cloud SDK',
                                               *gcloud_default_path)
        return not os.getenv('SERVER_SOFTWARE',
                             '').startswith('Google App Engine/') \
               and gcloud_default_path not in os.environ["PATH"].split(os.pathsep) \
               and which('gcloud') is None


Instruction:
Create a Python 3 function for
running mogrify. Most of the action is in convert, this just creates a temp file for the output.

Response:
def action(arguments):
    """
    Run mogrify.  Most of the action is in convert, this just creates a temp
    file for the output.
    """
    for input_file in arguments.input_files:
        logging.info(input_file)
        # Generate a temporary file
        with common.atomic_write(
                input_file.name, file_factory=common.FileType('wt')) as tf:
            convert.transform_file(input_file, tf, arguments)
            if hasattr(input_file, 'close'):
                input_file.close()


Instruction:
Here you have a function in Python 3, explain what it does
def all_unambiguous(sequence_str):
    result = [[]]
    for c in sequence_str:
        result = [i + [a] for i in result for a in _AMBIGUOUS_MAP.get(c, c)]
    return [''.join(i) for i in result]

Response: Returns a list of all unambiguous versions of sequence_str.


Instruction:
Create a Python 3 function to
build a subparser for the command line

Response:
def build_parser(parser):
    """
    Generate a subparser
    """
    parser.add_argument(
        'sequence_file',
        type=FileType('r'),
        help="""Input fastq file. A fasta-format file may also be provided
            if --input-qual is also specified.""")
    parser.add_argument(
        '--input-qual',
        type=FileType('r'),
        help="""The quality scores associated with the input file. Only
            used if input file is fasta.""")
    parser.add_argument(
        'output_file',
        type=FileType('w'),
        help="""Output file. Format determined from extension.""")

    output_group = parser.add_argument_group("Output")
    output_group.add_argument(
        '--report-out',
        type=FileType('w'),
        default=sys.stdout,
        help="""Output file for report [default:
            stdout]""")
    output_group.add_argument(
        '--details-out',
        type=FileType('w'),
        help="""Output file to report fate of each sequence""")
    output_group.add_argument(
        '--no-details-comment',
        action='store_false',
        default=True,
        dest='details_comment',
        help="""Do not write comment
            lines with version and call to start --details-out""")

    parser.add_argument(
        '--min-mean-quality',
        metavar='QUALITY',
        type=float,
        default=DEFAULT_MEAN_SCORE,
        help="""Minimum mean quality score for
            each read [default: %(default)s]""")
    parser.add_argument(
        '--min-length',
        metavar='LENGTH',
        type=int,
        default=200,
        help="""Minimum length to keep sequence [default:
            %(default)s]""")
    parser.add_argument(
        '--max-length',
        metavar='LENGTH',
        type=int,
        default=1000,
        help="""Maximum length to keep before truncating
            [default: %(default)s]. This operation occurs before
            --max-ambiguous""")

    window_group = parser.add_argument_group('Quality window options')
    window_group.add_argument(
        '--quality-window-mean-qual',
        type=float,
        help="""Minimum quality score within the window defined by
            --quality-window. [default: same as --min-mean-quality]""")
    window_group.add_argument(
        '--quality-window-prop',
        help="""Proportion of
            reads within quality window to that must pass filter. Floats are [default:
            %(default).1f]""",
        default=1.0,
        type=typed_range(float, 0.0, 1.0))
    window_group.add_argument(
        '--quality-window',
        type=int,
        metavar='WINDOW_SIZE',
        default=0,
        help="""Window size for truncating sequences.  When set
            to a non-zero value, sequences are truncated where the mean mean
            quality within the window drops below --min-mean-quality.
            [default: %(default)s]""")

    parser.add_argument(
        '--ambiguous-action',
        choices=('truncate', 'drop'),
        help="""Action to take on ambiguous base in sequence (N's).
            [default: no action]""")
    parser.add_argument(
        '--max-ambiguous',
        default=None,
        help="""Maximum number
            of ambiguous bases in a sequence. Sequences exceeding this count
            will be removed.""",
        type=int)
    parser.add_argument(
        '--pct-ambiguous',
        help="""Maximun percent of
            ambiguous bases in a sequence.  Sequences exceeding this percent
            will be removed.""",
        type=float)

    barcode_group = parser.add_argument_group('Barcode/Primer')
    primer_group = barcode_group.add_mutually_exclusive_group()
    primer_group.add_argument(
        '--primer', help="""IUPAC ambiguous primer to
            require""")
    primer_group.add_argument(
        '--no-primer',
        help="""Do not use a primer.""",
        action='store_const',
        const='',
        dest='primer')
    barcode_group.add_argument(
        '--barcode-file',
        help="""CSV file containing
            sample_id,barcode[,primer] in the rows. A single primer for all
            sequences may be specified with `--primer`, or `--no-primer` may be
            used to indicate barcodes should be used without a primer
            check.""",
        type=FileType('r'))
    barcode_group.add_argument(
        '--barcode-header',
        action='store_true',
        default=False,
        help="""Barcodes have a header row [default:
            %(default)s]""")
    barcode_group.add_argument(
        '--map-out',
        help="""Path to write
            sequence_id,sample_id pairs""",
        type=FileType('w'),
        metavar='SAMPLE_MAP')
    barcode_group.add_argument(
        '--quoting',
        help="""A string naming an
            attribute of the csv module defining the quoting behavior for
            `SAMPLE_MAP`.  [default: %(default)s]""",
        default='QUOTE_MINIMAL',
        choices=[s for s in dir(csv) if s.startswith('QUOTE_')])


Instruction:
Write a Python 3 function that can
load label barcode primer records from a CSV file.

Response:
def parse_barcode_file(fp, primer=None, header=False):
    """
    Load label, barcode, primer records from a CSV file.

    Returns a map from barcode -> label

    Any additional columns are ignored
    """
    tr = trie.trie()
    reader = csv.reader(fp)

    if header:
        # Skip header
        next(reader)

    # Skip blank rows
    records = (record for record in reader if record)

    for record in records:
        specimen, barcode = record[:2]
        if primer is not None:
            pr = primer
        else:
            pr = record[2]
        for sequence in all_unambiguous(barcode + pr):
            if sequence in tr:
                raise ValueError("Duplicate sample: {0}, {1} both have {2}",
                                 specimen, tr[sequence], sequence)
            logging.info('%s->%s', sequence, specimen)
            tr[sequence] = specimen

    return tr


Instruction:
How would you code a function in Python 3 to
give parsed arguments filter input files and return a new tree of BioXML objects.

Response:
def action(arguments):
    """
    Given parsed arguments, filter input files.
    """
    if arguments.quality_window_mean_qual and not arguments.quality_window:
        raise ValueError("--quality-window-mean-qual specified without "
                         "--quality-window")

    if trie is None or triefind is None:
        raise ValueError(
            'Missing Bio.trie and/or Bio.triefind modules. Cannot continue')

    filters = []
    input_type = fileformat.from_handle(arguments.sequence_file)
    output_type = fileformat.from_handle(arguments.output_file)
    with arguments.sequence_file as fp:
        if arguments.input_qual:
            sequences = QualityIO.PairedFastaQualIterator(
                fp, arguments.input_qual)
        else:
            sequences = SeqIO.parse(fp, input_type)

        listener = RecordEventListener()
        if arguments.details_out:
            rh = RecordReportHandler(arguments.details_out, arguments.argv,
                                     arguments.details_comment)
            rh.register_with(listener)

        # Track read sequences
        sequences = listener.iterable_hook('read', sequences)

        # Add filters
        if arguments.min_mean_quality and input_type == 'fastq':
            qfilter = QualityScoreFilter(arguments.min_mean_quality)
            filters.append(qfilter)
        if arguments.max_length:
            max_length_filter = MaxLengthFilter(arguments.max_length)
            filters.append(max_length_filter)
        if arguments.min_length:
            min_length_filter = MinLengthFilter(arguments.min_length)
            filters.append(min_length_filter)
        if arguments.max_ambiguous is not None:
            max_ambig_filter = MaxAmbiguousFilter(arguments.max_ambiguous)
            filters.append(max_ambig_filter)
        if arguments.pct_ambiguous is not None:
            pct_ambig_filter = PctAmbiguousFilter(arguments.pct_ambiguous)
            filters.append(pct_ambig_filter)
        if arguments.ambiguous_action:
            ambiguous_filter = AmbiguousBaseFilter(arguments.ambiguous_action)
            filters.append(ambiguous_filter)
        if arguments.quality_window:
            min_qual = (arguments.quality_window_mean_qual or
                        arguments.min_mean_quality)
            window_filter = WindowQualityScoreFilter(arguments.quality_window,
                                                     min_qual)
            filters.insert(0, window_filter)

        if arguments.barcode_file:
            with arguments.barcode_file:
                tr = parse_barcode_file(arguments.barcode_file,
                                        arguments.primer,
                                        arguments.barcode_header)
            f = PrimerBarcodeFilter(tr)
            filters.append(f)

            if arguments.map_out:
                barcode_writer = csv.writer(
                    arguments.map_out,
                    quoting=getattr(csv, arguments.quoting),
                    lineterminator='\n')

                def barcode_handler(record, sample, barcode=None):
                    barcode_writer.writerow((record.id, sample))

                listener.register_handler('found_barcode', barcode_handler)
        for f in filters:
            f.listener = listener
            sequences = f.filter_records(sequences)

        # Track sequences which passed all filters
        sequences = listener.iterable_hook('write', sequences)

        with arguments.output_file:
            SeqIO.write(sequences, arguments.output_file, output_type)

    rpt_rows = (f.report_dict() for f in filters)

    # Write report
    with arguments.report_out as fp:
        writer = csv.DictWriter(
            fp, BaseFilter.report_fields, lineterminator='\n', delimiter='\t')
        writer.writeheader()
        writer.writerows(rpt_rows)


Instruction:
Implement a Python 3 function for
firing an event named ``name`` with each item in iterable

Response:
def iterable_hook(self, name, iterable):
        """
        Fire an event named ``name`` with each item in iterable
        """
        for record in iterable:
            self(name, record)
            yield record


Instruction:
Implement a function in Python 3 to
hook called when barcode is found

Response:
def _found_barcode(self, record, sample, barcode=None):
        """Hook called when barcode is found"""
        assert record.id == self.current_record['sequence_name']
        self.current_record['sample'] = sample


Instruction:
Explain what the following Python 3 code does
def filter_records(self, records):
        for record in records:
            try:
                filtered = self.filter_record(record)
                assert (filtered)
                # Quick tracking whether the sequence was modified
                if filtered.seq == record.seq:
                    self.passed_unchanged += 1
                else:
                    self.passed_changed += 1
                yield filtered
            except FailedFilter as e:
                self.failed += 1
                v = e.value
                if self.listener:
                    self.listener(
                        'failed_filter',
                        record,
                        filter_name=self.name,
                        value=v)

Response: Apply the filter to the records and yield the filtered records.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def filter_record(self, record):
        quality_scores = record.letter_annotations['phred_quality']

        mean_score = mean(quality_scores)
        if mean_score >= self.min_mean_score:
            return record
        else:
            raise FailedFilter(mean_score)

Response: Filter a single record by the mean score.


Instruction:
Given the following Python 3 function, write the documentation
def filter_record(self, record):
        quality_scores = record.letter_annotations['phred_quality']

        # Simple case - window covers whole sequence
        if len(record) <= self.window_size:
            mean_score = mean(quality_scores)
            if mean_score >= self.min_mean_score:
                return record
            else:
                raise FailedFilter(mean_score)

        # Find the right clipping point. Start clipping at the beginning of the
        # sequence, then extend the window to include regions with acceptable
        # mean quality scores.
        clip_right = 0
        for i, a in enumerate(
                moving_average(quality_scores, self.window_size)):

            if a >= self.min_mean_score:
                clip_right = i + self.window_size
            else:
                break

        if clip_right:
            return record[:clip_right]
        else:
            # First window failed - record fails
            raise FailedFilter()

Response: Filter a single record by the minimum and acceptable mean score.


Instruction:
Explain what the following Python 3 code does
def filter_record(self, record):
        nloc = record.seq.find('N')
        if nloc == -1:
            return record
        elif self.action == 'truncate':
            return record[:nloc]
        elif self.action == 'drop':
            raise FailedFilter()
        else:
            assert False

Response: Filter a record by truncating or dropping at an N


Instruction:
How would you code a function in Python 3 to
filter the record to only contain any that don t meet minimum length.

Response:
def filter_record(self, record):
        """
        Filter record, dropping any that don't meet minimum length
        """

        if len(record) >= self.min_length:
            return record
        else:
            raise FailedFilter(len(record))


Instruction:
How would you code a function in Python 3 to
filter the record to only contain the ones that are longer than self. max_length.

Response:
def filter_record(self, record):
        """
        Filter record, truncating any over some maximum length
        """
        if len(record) >= self.max_length:
            return record[:self.max_length]
        else:
            return record


Instruction:
Implement a Python 3 function for
summarizing a sequence file returning a tuple containing the name whether the file is an alignment minimum sequence length maximum sequence length average length number of sequences.

Response:
def summarize_sequence_file(source_file, file_type=None):
    """
    Summarizes a sequence file, returning a tuple containing the name,
    whether the file is an alignment, minimum sequence length, maximum
    sequence length, average length, number of sequences.
    """
    is_alignment = True
    avg_length = None
    min_length = sys.maxsize
    max_length = 0
    sequence_count = 0

    # Get an iterator and analyze the data.
    with common.FileType('rt')(source_file) as fp:
        if not file_type:
            file_type = fileformat.from_handle(fp)
        for record in SeqIO.parse(fp, file_type):
            sequence_count += 1
            sequence_length = len(record)
            if max_length != 0:
                # If even one sequence is not the same length as the others,
                # we don't consider this an alignment.
                if sequence_length != max_length:
                    is_alignment = False

            # Lengths
            if sequence_length > max_length:
                max_length = sequence_length
            if sequence_length < min_length:
                min_length = sequence_length

            # Average length
            if sequence_count == 1:
                avg_length = float(sequence_length)
            else:
                avg_length = avg_length + ((sequence_length - avg_length) /
                                           sequence_count)

    # Handle an empty file:
    if avg_length is None:
        min_length = max_length = avg_length = 0
    if sequence_count <= 1:
        is_alignment = False

    return (source_file, str(is_alignment).upper(), min_length,
            max_length, avg_length, sequence_count)


Instruction:
Here you have a function in Python 3, explain what it does
def action(arguments):
    # Ignore SIGPIPE, for head support
    common.exit_on_sigpipe()
    common.exit_on_sigint()

    handle = arguments.destination_file
    output_format = arguments.output_format
    if not output_format:
        try:
            output_format = 'align' if handle.isatty() else 'tab'
        except AttributeError:
            output_format = 'tab'

    writer_cls = _WRITERS[output_format]

    ssf = partial(summarize_sequence_file, file_type = arguments.input_format)

    # if only one thread, do not use the multithreading so parent process
    # can be terminated using ctrl+c
    if arguments.threads > 1:
        pool = multiprocessing.Pool(processes=arguments.threads)
        summary = pool.imap(ssf, arguments.source_files)
    else:
        summary = (ssf(f) for f in arguments.source_files)

    with handle:
        writer = writer_cls(arguments.source_files, summary, handle)
        writer.write()

Response: This function is called by the command line interface to determine if the file is an alignment or tab - delimited.


Instruction:
Write a Python 3 function for
buffering for transform functions which require multiple passes through data.

Response:
def _record_buffer(records, buffer_size=DEFAULT_BUFFER_SIZE):
    """
    Buffer for transform functions which require multiple passes through data.

    Value returned by context manager is a function which returns an iterator
    through records.
    """
    with tempfile.SpooledTemporaryFile(buffer_size, mode='wb+') as tf:
        pickler = pickle.Pickler(tf)
        for record in records:
            pickler.dump(record)

        def record_iter():
            tf.seek(0)
            unpickler = pickle.Unpickler(tf)
            while True:
                try:
                    yield unpickler.load()
                except EOFError:
                    break

        yield record_iter


Instruction:
How would you explain what the following Python 3 function does
def dashes_cleanup(records, prune_chars='.:?~'):
    logging.info(
        "Applying _dashes_cleanup: converting any of '{}' to '-'.".format(prune_chars))
    translation_table = {ord(c): '-' for c in prune_chars}
    for record in records:
        record.seq = Seq(str(record.seq).translate(translation_table),
                         record.seq.alphabet)
        yield record

Response:
Take an alignment and convert any undesirable characters such as? or ~ to
    -.


Instruction:
How would you explain what the following Python 3 function does
def deduplicate_sequences(records, out_file):

    logging.info('Applying _deduplicate_sequences generator: '
                 'removing any duplicate records with identical sequences.')
    checksum_sequences = collections.defaultdict(list)
    for record in records:
        checksum = seguid(record.seq)
        sequences = checksum_sequences[checksum]
        if not sequences:
            yield record
        sequences.append(record.id)

    if out_file is not None:
        with out_file:
            for sequences in checksum_sequences.values():
                out_file.write('%s\n' % (' '.join(sequences),))

Response:
Remove any duplicate records with identical sequences keep the first
Portal instance seen and discard additional occurences.


Instruction:
Implement a Python 3 function for
removing any duplicate records with identical IDs keep the first duplicate record seen and discard additional occurences.

Response:
def deduplicate_taxa(records):
    """
    Remove any duplicate records with identical IDs, keep the first
    instance seen and discard additional occurences.
    """
    logging.info('Applying _deduplicate_taxa generator: ' + \
                 'removing any duplicate records with identical IDs.')
    taxa = set()
    for record in records:
        # Default to full ID, split if | is found.
        taxid = record.id
        if '|' in record.id:
            try:
                taxid = int(record.id.split("|")[0])
            except:
                # If we couldn't parse an integer from the ID, just fall back
                # on the ID
                logging.warn("Unable to parse integer taxid from %s",
                        taxid)
        if taxid in taxa:
            continue
        taxa.add(taxid)
        yield record


Instruction:
How would you explain what the following Python 3 function does
def first_name_capture(records):
    logging.info('Applying _first_name_capture generator: '
                 'making sure ID only contains the first whitespace-delimited '
                 'word.')
    whitespace = re.compile(r'\s+')
    for record in records:
        if whitespace.search(record.description):
            yield SeqRecord(record.seq, id=record.id,
                            description="")
        else:
            yield record

Response: Yields SeqRecords that contain only the first whitespace - delimited word as the name of the sequence.


Instruction:
Write a Python 3 script to
filter the records keeping only sequences whose ID is contained in the handle.

Response:
def include_from_file(records, handle):
    """
    Filter the records, keeping only sequences whose ID is contained in the
    handle.
    """
    ids = set(i.strip() for i in handle)

    for record in records:
        if record.id.strip() in ids:
            yield record


Instruction:
How would you code a function in Python 3 to
yield sequences from the given start and end indices.

Response:
def isolate_region(sequences, start, end, gap_char='-'):
    """
    Replace regions before and after start:end with gap chars
    """
    # Check arguments
    if end <= start:
        raise ValueError("start of slice must precede end ({0} !> {1})".format(
            end, start))

    for sequence in sequences:
        seq = sequence.seq
        start_gap = gap_char * start
        end_gap = gap_char * (len(seq) - end)
        seq = Seq(start_gap + str(seq[start:end]) + end_gap,
                alphabet=seq.alphabet)
        sequence.seq = seq
        yield sequence


Instruction:
How would you code a function in Python 3 to
drop all columns present in slices from records

Response:
def drop_columns(records, slices):
    """
    Drop all columns present in ``slices`` from records
    """
    for record in records:
        # Generate a set of indices to remove
        drop = set(i for slice in slices
                   for i in range(*slice.indices(len(record))))
        keep = [i not in drop for i in range(len(record))]
        record.seq = Seq(''.join(itertools.compress(record.seq, keep)), record.seq.alphabet)
        yield record


Instruction:
Implement a Python 3 function for
cutting records to slices indexed by non - gap positions in record_id

Response:
def cut_sequences_relative(records, slices, record_id):
    """
    Cuts records to slices, indexed by non-gap positions in record_id
    """
    with _record_buffer(records) as r:
        try:
            record = next(i for i in r() if i.id == record_id)
        except StopIteration:
            raise ValueError("Record with id {0} not found.".format(record_id))

        new_slices = _update_slices(record, slices)
        for record in multi_cut_sequences(r(), new_slices):
            yield record


Instruction:
How would you implement a function in Python 3 that
masks sequences in a list of records by the given slices.

Response:
def multi_mask_sequences(records, slices):
    """
    Replace characters sliced by slices with gap characters.
    """
    for record in records:
        record_indices = list(range(len(record)))
        keep_indices = reduce(lambda i, s: i - frozenset(record_indices[s]),
                              slices, frozenset(record_indices))
        seq = ''.join(b if i in keep_indices else '-'
                      for i, b in enumerate(str(record.seq)))
        record.seq = Seq(seq)
        yield record


Instruction:
How would you explain what the following Python 3 function does
def prune_empty(records):
    for record in records:
        if not all(c == '-' for c in str(record.seq)):
            yield record

Response: Remove any empty sequences from the sequence list.


Instruction:
How would you explain what the following Python 3 function does
def _reverse_annotations(old_record, new_record):
    # Copy the annotations over
    for k, v in list(old_record.annotations.items()):
        # Trim if appropriate
        if isinstance(v, (tuple, list)) and len(v) == len(old_record):
            assert len(v) == len(old_record)
            v = v[::-1]
        new_record.annotations[k] = v

    # Letter annotations must be lists / tuples / strings of the same
    # length as the sequence
    for k, v in list(old_record.letter_annotations.items()):
        assert len(v) == len(old_record)
        new_record.letter_annotations[k] = v[::-1]

Response: Reverse the annotations of the old_record to the new_record.


Instruction:
Explain what the following Python 3 code does
def reverse_sequences(records):
    logging.info('Applying _reverse_sequences generator: '
                 'reversing the order of sites in sequences.')
    for record in records:
        rev_record = SeqRecord(record.seq[::-1], id=record.id,
                               name=record.name,
                               description=record.description)
        # Copy the annotations over
        _reverse_annotations(record, rev_record)

        yield rev_record

Response: Reverse the order of sites in sequences.


Instruction:
Can you create a Python 3 function that
transforms sequences into reverse complements.

Response:
def reverse_complement_sequences(records):
    """
    Transform sequences into reverse complements.
    """
    logging.info('Applying _reverse_complement_sequences generator: '
                 'transforming sequences into reverse complements.')
    for record in records:
        rev_record = SeqRecord(record.seq.reverse_complement(),
                               id=record.id, name=record.name,
                               description=record.description)
        # Copy the annotations over
        _reverse_annotations(record, rev_record)

        yield rev_record


Instruction:
Explain what the following Python 3 code does
def ungap_sequences(records, gap_chars=GAP_TABLE):
    logging.info('Applying _ungap_sequences generator: removing all gap characters')
    for record in records:
        yield ungap_all(record, gap_chars)

Response: Remove gaps from sequences given an alignment.


Instruction:
How would you explain what the following Python 3 function does
def _update_id(record, new_id):
    old_id = record.id
    record.id = new_id

    # At least for FASTA, record ID starts the description
    record.description = re.sub('^' + re.escape(old_id), new_id, record.description)
    return record

Response: Update a record ID to new_id


Instruction:
How would you implement a function in Python 3 that
gives a set of sequences append a suffix for each sequence s name.

Response:
def name_append_suffix(records, suffix):
    """
    Given a set of sequences, append a suffix for each sequence's name.
    """
    logging.info('Applying _name_append_suffix generator: '
                 'Appending suffix ' + suffix + ' to all '
                 'sequence IDs.')
    for record in records:
        new_id = record.id + suffix
        _update_id(record, new_id)
        yield record


Instruction:
How would you code a function in Python 3 to
give a set of sequences insert a prefix for each sequence s name.

Response:
def name_insert_prefix(records, prefix):
    """
    Given a set of sequences, insert a prefix for each sequence's name.
    """
    logging.info('Applying _name_insert_prefix generator: '
                 'Inserting prefix ' + prefix + ' for all '
                 'sequence IDs.')
    for record in records:
        new_id = prefix + record.id
        _update_id(record, new_id)
        yield record


Instruction:
Can you write a function in Python 3 where it
gives a set of sequences and a regular expression filter out any sequences with names that do not match the regular expression.

Response:
def name_include(records, filter_regex):
    """
    Given a set of sequences, filter out any sequences with names
    that do not match the specified regular expression.  Ignore case.
    """
    logging.info('Applying _name_include generator: '
                 'including only IDs matching ' + filter_regex +
                 ' in results.')
    regex = re.compile(filter_regex)
    for record in records:
        if regex.search(record.id) or regex.search(record.description):
            yield record


Instruction:
Make a summary of the following Python 3 code
def name_replace(records, search_regex, replace_pattern):
    regex = re.compile(search_regex)
    for record in records:
        maybe_id = record.description.split(None, 1)[0]
        if maybe_id == record.id:
            record.description = regex.sub(replace_pattern, record.description)
            record.id = record.description.split(None, 1)[0]
        else:
            record.id = regex.sub(replace_pattern, record.id)
            record.description = regex.sub(replace_pattern, record.description)
        yield record

Response: Given a set of sequences replace all occurrences of search_regex with replace_pattern. Ignore case.


Instruction:
Given the following Python 3 function, write the documentation
def seq_include(records, filter_regex):
    regex = re.compile(filter_regex)
    for record in records:
        if regex.search(str(record.seq)):
            yield record

Response: Filter any sequences who s seq does not match the filter_regex. Ignore case.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def sample(records, k, random_seed=None):

    if random_seed is not None:
        random.seed(random_seed)

    result = []
    for i, record in enumerate(records):
        if len(result) < k:
            result.append(record)
        else:
            r = random.randint(0, i)
            if r < k:
                result[r] = record
    return result

Response:
Choose a length - k subset of records retaining the input
    order.


Instruction:
Create a Python 3 function to
yield the records from the specified head.

Response:
def head(records, head):
    """
    Limit results to the top N records.
    With the leading `-', print all but the last N records.
    """
    logging.info('Applying _head generator: '
                 'limiting results to top ' + head + ' records.')

    if head == '-0':
        for record in records:
            yield record
    elif '-' in head:
        with _record_buffer(records) as r:
            record_count = sum(1 for record in r())
            end_index = max(record_count + int(head), 0)
            for record in itertools.islice(r(), end_index):
                yield record
    else:
        for record in itertools.islice(records, int(head)):
            yield record


Instruction:
Implement a Python 3 function for
yielding the records from the top N records.

Response:
def tail(records, tail):
    """
    Limit results to the bottom N records.
    Use +N to output records starting with the Nth.
    """
    logging.info('Applying _tail generator: '
                 'limiting results to top ' + tail + ' records.')

    if tail == '+0':
        for record in records:
            yield record
    elif '+' in tail:
        tail = int(tail) - 1
        for record in itertools.islice(records, tail, None):
            yield record
    else:
        with _record_buffer(records) as r:
            record_count = sum(1 for record in r())
            start_index = max(record_count - int(tail), 0)
            for record in itertools.islice(r(), start_index, None):
                yield record


Instruction:
How would you code a function in Python 3 to
generate a list with the proportion of gaps by index in a set of sequences.

Response:
def gap_proportion(sequences, gap_chars='-'):
    """
    Generates a list with the proportion of gaps by index in a set of
    sequences.
    """
    aln_len = None
    gaps = []
    for i, sequence in enumerate(sequences):
        if aln_len is None:
            aln_len = len(sequence)
            gaps = [0] * aln_len
        else:
            if not len(sequence) == aln_len:
                raise ValueError(("Unexpected sequence length {0}. Is this "
                                  "an alignment?").format(len(sequence)))

        # Update any gap positions in gap list
        for j, char in enumerate(sequence.seq):
            if char in gap_chars:
                gaps[j] += 1

    sequence_count = float(i + 1)
    gap_props = [i / sequence_count for i in gaps]
    return gap_props


Instruction:
Can you create a Python 3 function that
takes a sequence iterator for determining gap between all sequences AttributeNames and trims any gaps that are present in the same position across all sequences AttributeNames.

Response:
def squeeze(records, gap_threshold=1.0):
    """
    Remove any gaps that are present in the same position across all sequences
    in an alignment.  Takes a second sequence iterator for determining gap
    positions.
    """
    with _record_buffer(records) as r:
        gap_proportions = gap_proportion(r())

        keep_columns = [g < gap_threshold for g in gap_proportions]

        for record in r():
            sequence = str(record.seq)
            # Trim
            squeezed = itertools.compress(sequence, keep_columns)
            yield SeqRecord(Seq(''.join(squeezed)), id=record.id,
                            description=record.description)


Instruction:
Write a Python 3 function that can
give a list of SeqRecords yield SeqRecords that are in the order they appear in the ID.

Response:
def strip_range(records):
    """
    Cut off trailing /<start>-<stop> ranges from IDs.  Ranges must be 1-indexed and
    the stop integer must not be less than the start integer.
    """
    logging.info('Applying _strip_range generator: '
                 'removing /<start>-<stop> ranges from IDs')
    # Split up and be greedy.
    cut_regex = re.compile(r"(?P<id>.*)\/(?P<start>\d+)\-(?P<stop>\d+)")
    for record in records:
        name = record.id
        match = cut_regex.match(str(record.id))
        if match:
            sequence_id = match.group('id')
            start = int(match.group('start'))
            stop = int(match.group('stop'))
            if start > 0 and start <= stop:
                name = sequence_id
        yield SeqRecord(record.seq, id=name,
                        description='')


Instruction:
Create a Python 3 function for
yielding the records in the sequence that are transcribed by the transcribe operation.

Response:
def transcribe(records, transcribe):
    """
    Perform transcription or back-transcription.
    transcribe must be one of the following:
        dna2rna
        rna2dna
    """
    logging.info('Applying _transcribe generator: '
                 'operation to perform is ' + transcribe + '.')
    for record in records:
        sequence = str(record.seq)
        description = record.description
        name = record.id
        if transcribe == 'dna2rna':
            dna = Seq(sequence, IUPAC.ambiguous_dna)
            rna = dna.transcribe()
            yield SeqRecord(rna, id=name, description=description)
        elif transcribe == 'rna2dna':
            rna = Seq(sequence, IUPAC.ambiguous_rna)
            dna = rna.back_transcribe()
            yield SeqRecord(dna, id=name, description=description)


Instruction:
Write a Python 3 script for
translating a list of records into a sequence sequence.

Response:
def translate(records, translate):
    """
    Perform translation from generic DNA/RNA to proteins.  Bio.Seq
    does not perform back-translation because the codons would
    more-or-less be arbitrary.  Option to translate only up until
    reaching a stop codon.  translate must be one of the following:
        dna2protein
        dna2proteinstop
        rna2protein
        rna2proteinstop
    """
    logging.info('Applying translation generator: '
                 'operation to perform is ' + translate + '.')

    to_stop = translate.endswith('stop')

    source_type = translate[:3]
    alphabet = {'dna': IUPAC.ambiguous_dna, 'rna': IUPAC.ambiguous_rna}[source_type]

    # Get a translation table
    table = {'dna': CodonTable.ambiguous_dna_by_name["Standard"],
             'rna': CodonTable.ambiguous_rna_by_name["Standard"]}[source_type]

    # Handle ambiguities by replacing ambiguous codons with 'X'
    # TODO: this copy operation causes infinite recursion with python3.6 -
    # not sure why it was here to begin with.
    # table = copy.deepcopy(table)
    table.forward_table = CodonWarningTable(table.forward_table)

    for record in records:
        sequence = str(record.seq)
        seq = Seq(sequence, alphabet)
        protein = seq.translate(table, to_stop=to_stop)
        yield SeqRecord(protein, id=record.id, description=record.description)


Instruction:
Implement a Python 3 function for
discarding any records that are longer than max_length.

Response:
def max_length_discard(records, max_length):
    """
    Discard any records that are longer than max_length.
    """
    logging.info('Applying _max_length_discard generator: '
                 'discarding records longer than '
                 '.')
    for record in records:
        if len(record) > max_length:
            # Discard
            logging.debug('Discarding long sequence: %s, length=%d',
                record.id, len(record))
        else:
            yield record


Instruction:
Create a Python 3 function to
discard any records that are shorter than min_length.

Response:
def min_length_discard(records, min_length):
    """
    Discard any records that are shorter than min_length.
    """
    logging.info('Applying _min_length_discard generator: '
                 'discarding records shorter than %d.', min_length)
    for record in records:
        if len(record) < min_length:
            logging.debug('Discarding short sequence: %s, length=%d',
                record.id, len(record))
        else:
            yield record


Instruction:
How would you explain what the following Python 3 function does
def min_ungap_length_discard(records, min_length):
    for record in records:
        if len(ungap_all(record)) >= min_length:
            yield record

Response: Discard any records that are shorter than min_length after removing gaps.


Instruction:
Implement a function in Python 3 to
sort sequences by length.

Response:
def sort_length(source_file, source_file_type, direction=1):
    """
    Sort sequences by length. 1 is ascending (default) and 0 is descending.
    """
    direction_text = 'ascending' if direction == 1 else 'descending'

    logging.info('Indexing sequences by length: %s', direction_text)

    # Adapted from the Biopython tutorial example.

    # Get the lengths and ids, and sort on length
    len_and_ids = sorted((len(rec), rec.id)
                         for rec in SeqIO.parse(source_file, source_file_type))

    if direction == 0:
        ids = reversed([seq_id for (length, seq_id) in len_and_ids])
    else:
        ids = [seq_id for (length, seq_id) in len_and_ids]
    del len_and_ids  # free this memory

    # SeqIO.index does not handle gzip instances
    if isinstance(source_file, gzip.GzipFile):
        tmpfile = tempfile.NamedTemporaryFile()
        source_file.seek(0)
        tmpfile.write(source_file.read())
        tmpfile.seek(0)
        source_file = tmpfile

    record_index = SeqIO.index(source_file.name, source_file_type)

    for seq_id in ids:
        yield record_index[seq_id]


Instruction:
Here you have a function in Python 3, explain what it does
def batch(iterable, chunk_size):
    i = iter(iterable)
    while True:
        r = list(itertools.islice(i, chunk_size))
        if not r:
            break
        yield r

Response: Yields items from iterable in chunk_size bits.


Instruction:
Create a Python 3 function to
run the alignment of a single sequence.

Response:
def action(arguments):
    """
    Run
    """
    # Ignore SIGPIPE, for head support
    common.exit_on_sigpipe()
    logging.basicConfig()

    prot_sequences = SeqIO.parse(arguments.protein_align,
                                 fileformat.from_handle(arguments.protein_align))
    nucl_sequences = SeqIO.parse(arguments.nucl_align,
                                 fileformat.from_handle(arguments.nucl_align))

    instance = AlignmentMapper(TRANSLATION_TABLES[arguments.translation_table],
                               arguments.fail_action)

    SeqIO.write(instance.map_all(prot_sequences, nucl_sequences),
                arguments.out_file, fileformat.from_filename(arguments.out_file.name))


Instruction:
Create a Python 3 function for
validating that the translation table is valid for the given protein and nucleotide.

Response:
def _validate_translation(self, aligned_prot, aligned_nucl):
        """
        Given a seq for protein and nucleotide, ensure that the translation holds
        """
        codons = [''.join(i) for i in batch(str(aligned_nucl), 3)]
        for codon, aa in zip(codons, str(aligned_prot)):
            # Check gaps
            if codon == '---' and aa == '-':
                continue

            try:
                trans = self.translation_table.forward_table[codon]
                if not trans == aa:
                    raise ValueError("Codon {0} translates to {1}, not {2}".format(
                        codon, trans, aa))
            except (KeyError, CodonTable.TranslationError):
                if aa != 'X':
                    if self.unknown_action == 'fail':
                        raise ValueError("Unknown codon: {0} mapped to {1}".format(
                            codon, aa))
                    elif self.unknown_action == 'warn':
                        logging.warn('Cannot verify that unknown codon %s '
                                     'maps to %s', codon, aa)
        return True


Instruction:
Can you write a function in Python 3 where it
maps alignment to the original sequence.

Response:
def map_alignment(self, prot_seq, nucl_seq):
        """
        Use aligned prot_seq to align nucl_seq
        """
        if prot_seq.id != nucl_seq.id:
            logging.warning(
                'ID mismatch: %s != %s. Are the sequences in the same order?',
                prot_seq.id, nucl_seq.id)

        # Ungap nucleotides
        codons = batch(str(nucl_seq.seq.ungap('-')), 3)
        codons = [''.join(i) for i in codons]
        codon_iter = iter(codons)

        ungapped_prot = str(prot_seq.seq).replace('-', '')

        if len(ungapped_prot) != len(codons):
            table = self.translation_table.forward_table
            prot_str = ' '.join(' ' + p + ' ' for p in ungapped_prot)
            codon_str = ' '.join(codons)
            trans_str = ' '.join(' ' + table.get(codon, 'X') + ' '
                                 for codon in codons)
            raise ValueError("""Length of codon sequence ({0}) does not match \
length of protein sequence ({1}) for {2}
Protein:       {3}
Codons:        {4}
Trans. Codons: {5}""".format(len(codons), len(ungapped_prot), nucl_seq.id, prot_str,
                            codon_str, trans_str))

        try:
            nucl_align = ['---' if p == '-' else next(codon_iter)
                          for p in str(prot_seq.seq)]
        except StopIteration:
            assert False  # Should be checked above

        result = SeqRecord(Seq(''.join(nucl_align)), id=nucl_seq.id,
                           description=nucl_seq.description)

        # Validate
        self._validate_translation(prot_seq.seq.upper(), result.seq.upper())

        return result


Instruction:
How would you implement a function in Python 3 that
maps all sequences in the protein alignment to nucleotide sequences.

Response:
def map_all(self, prot_alignment, nucl_sequences):
        """
        Convert protein sequences to nucleotide alignment
        """
        zipped = itertools.zip_longest(prot_alignment, nucl_sequences)
        for p, n in zipped:
            if p is None:
                raise ValueError("Exhausted protein sequences")
            elif n is None:
                raise ValueError("Exhausted nucleotide sequences")
            yield self.map_alignment(p, n)


Instruction:
Write a Python 3 script to
return the BioPython file type corresponding with the input extension.

Response:
def from_extension(extension):
    """
    Look up the BioPython file type corresponding with input extension.

    Look up is case insensitive.
    """
    if not extension.startswith('.'):
        raise ValueError("Extensions must begin with a period.")
    try:
        return EXTENSION_TO_TYPE[extension.lower()]
    except KeyError:
        raise UnknownExtensionError(
            "seqmagick does not know how to handle " +
            "files with extensions like this: " + extension)


Instruction:
Can you tell what is the following Python 3 function doing
def from_filename(file_name):
    base, extension = os.path.splitext(file_name)
    if extension in COMPRESS_EXT:
        # Compressed file
        extension = os.path.splitext(base)[1]
    return from_extension(extension)

Response: Returns a BioPython object corresponding to the file name.


Instruction:
Given the following Python 3 function, write the documentation
def from_handle(fh, stream_default='fasta'):
    if fh in (sys.stdin, sys.stdout, sys.stderr):
        return stream_default
    return from_filename(fh.name)

Response: Return a BioPython object from a file - like object.


Instruction:
How would you implement a function in Python 3 that
parses command - line arguments for different actions.

Response:
def parse_arguments(argv):
    """
    Extract command-line arguments for different actions.
    """
    parser = argparse.ArgumentParser(description='seqmagick - Manipulate ' + \
       ' sequence files.', prog='seqmagick')

    parser.add_argument('-V', '--version', action='version',
            version='seqmagick v' + version,
            help="Print the version number and exit")
    parser.add_argument('-v', '--verbose', dest='verbosity',
            action='count', default=1,
            help="Be more verbose. Specify -vv or -vvv for even more")
    parser.add_argument('-q', '--quiet', action='store_const', const=0,
            dest='verbosity', help="Suppress output")

    # Subparsers
    subparsers = parser.add_subparsers(dest='subparser_name')

    parser_help = subparsers.add_parser('help',
            help='Detailed help for actions using help <action>')

    parser_help.add_argument('action')

    # Add actions
    actions = {}
    for name, mod in subcommands.itermodules():
        subparser = subparsers.add_parser(name, help=mod.__doc__,
                description=mod.__doc__)
        mod.build_parser(subparser)
        actions[name] = mod.action

    arguments = parser.parse_args(argv)
    arguments.argv = argv
    action = arguments.subparser_name

    if action == 'help':
        return parse_arguments([str(arguments.action), '-h'])

    return actions[action], arguments


Instruction:
Write a Python 3 script for
returning a dict mapping from an index in the ungapped sequence to an index in the gapped sequence.

Response:
def ungap_index_map(sequence, gap_chars='-'):
    """
    Returns a dict mapping from an index in the ungapped sequence to an index
    in the gapped sequence.

    >>> ungap_index_map('AC-TG-')
    {0: 0, 1: 1, 2: 3, 3: 4}
    """
    counter = itertools.count(0).__next__
    ungap_indexes = [
        counter() if c not in gap_chars else None for c in iter(sequence)
    ]
    return dict(
        (ungapped, gapped)
        for ungapped, gapped in zip(ungap_indexes, range(len(sequence)))
        if ungapped is not None)


Instruction:
Implement a Python 3 function for
returning mapping from gapped index to ungapped index.

Response:
def gap_index_map(sequence, gap_chars='-'):
    """
    Opposite of ungap_index_map: returns mapping from gapped index to ungapped
    index.

    >>> gap_index_map('AC-TG-')
    {0: 0, 1: 1, 3: 2, 4: 3}
    """
    return dict(
        (v, k) for k, v in list(ungap_index_map(sequence, gap_chars).items()))


Instruction:
Create a Python 3 function to
test if two bases are equal.

Response:
def _iupac_ambiguous_equal(ambig_base, unambig_base):
    """
    Tests two bases for equality, accounting for IUPAC ambiguous DNA

    ambiguous base may be IUPAC ambiguous, unambiguous must be one of ACGT
    """
    iupac_translation = {
        'A': 'A',
        'C': 'C',
        'G': 'G',
        'T': 'T',
        'U': 'U',
        'R': 'AG',
        'Y': 'CT',
        'S': 'GC',
        'W': 'AT',
        'K': 'GT',
        'M': 'AC',
        'B': 'CGT',
        'D': 'AGT',
        'H': 'ACT',
        'V': 'ACG',
        'N': 'ACGT',
        '-': '-'
    }
    for i in (ambig_base, unambig_base):
        if not len(i) == 1:
            raise ValueError("only one base may be passed.")

    return unambig_base.upper() in iupac_translation[ambig_base.upper()]


Instruction:
Given the following Python 3 function, write the documentation
def hamming_distance(s1, s2, equality_function=operator.eq):
    if not len(s1) == len(s2):
        raise ValueError("String lengths are not equal")

    # Number of non-matching characters:
    return sum(not equality_function(c1, c2) for c1, c2 in zip(s1, s2))

Response: Returns the hamming distance between two strings.


Instruction:
How would you implement a function in Python 3 that
finds the forward and reverse primers in a set of sequences.

Response:
def locate_primers(sequences, forward_primer, reverse_primer,
                   reverse_complement, max_hamming_distance):
    """
    Find forward and reverse primers in a set of sequences, return two tuples:
    (forward_start, forward_end), (reverse_start, reverse_end)
    """
    forward_loc = None
    reverse_loc = None
    seq_length = None

    # Reverse complement the reverse primer, if appropriate
    if reverse_complement:
        reverse_primer = reverse_primer.reverse_complement()

    forward_aligner = PrimerAligner(forward_primer)
    reverse_aligner = PrimerAligner(reverse_primer)

    for i, sequence in enumerate(sequences):
        if seq_length is None:
            seq_length = len(sequence)
        elif len(sequence) != seq_length:
            raise ValueError(("Sequence Length Heterogeneity: {0} != {1}. "
                              "Is this an alignment?").format(
                                  len(sequence), seq_length))
        index_map = ungap_index_map(sequence.seq)
        if forward_loc is None:
            ham_dist, start, end = forward_aligner.align(sequence.seq.ungap())
            if ham_dist <= max_hamming_distance:
                forward_loc = index_map[start], index_map[end]
                logging.info("Forward in sequence %d: indexes %d to %d", i + 1,
                             *forward_loc)
        if reverse_loc is None:
            ham_dist, start, end = reverse_aligner.align(sequence.seq.ungap())
            if ham_dist <= max_hamming_distance:
                reverse_loc = index_map[start], index_map[end]
                logging.info("Reverse in sequence %d: indexes %d to %d", i + 1,
                             *reverse_loc)
        if forward_loc and reverse_loc:
            # Both found
            # Check order
            if forward_loc[0] > reverse_loc[0]:
                raise PrimerOrderError(forward_loc[0], reverse_loc[0])
            return forward_loc, reverse_loc
        else:
            logging.debug(
                "Sequence %d: %d/2 primers found", i + 1,
                sum(j is not None for j in (forward_loc, reverse_loc)))

    # Did not find either the forward or reverse primer:
    if not forward_loc:
        raise PrimerNotFound(forward_primer)
    else:
        raise PrimerNotFound(reverse_primer)


Instruction:
Write a Python 3 function for
slicing the input sequences from start to end

Response:
def trim(sequences, start, end):
    """
    Slice the input sequences from start to end
    """
    logging.info("Trimming from %d to %d", start, end)
    return (sequence[start:end] for sequence in sequences)


Instruction:
Can you generate a brief explanation for the following Python 3 code
def action(arguments):
    # Determine file format for input and output
    source_format = (arguments.source_format or
                     fileformat.from_handle(arguments.source_file))
    output_format = (arguments.output_format or
                     fileformat.from_handle(arguments.output_file))

    # Load the alignment
    with arguments.source_file:
        sequences = SeqIO.parse(
            arguments.source_file,
            source_format,
            alphabet=Alphabet.Gapped(Alphabet.single_letter_alphabet))

        # Locate primers
        (forward_start, forward_end), (reverse_start, reverse_end) = locate_primers(
            sequences, arguments.forward_primer,
            arguments.reverse_primer, arguments.reverse_complement,
            arguments.max_hamming_distance)

        # Generate slice indexes
        if arguments.include_primers:
            start = forward_start
            end = reverse_end + 1
        else:
            start = forward_end + 1
            end = reverse_start

        # Rewind the input file
        arguments.source_file.seek(0)
        sequences = SeqIO.parse(
            arguments.source_file,
            source_format,
            alphabet=Alphabet.Gapped(Alphabet.single_letter_alphabet))

        # Apply the transformation
        prune_action = _ACTIONS[arguments.prune_action]
        transformed_sequences = prune_action(sequences, start, end)

        with arguments.output_file:
            SeqIO.write(transformed_sequences, arguments.output_file,
                        output_format)

Response: This function performs the alignment processing.


Instruction:
Can you implement a function in Python 3 that
aligns the primer to the input sequence returning a tuple of hamming distance start and end index.

Response:
def align(self, sequence):
        """
        Aligns the primer to the given query sequence, returning a tuple of:

            hamming_distance, start, end

        Where hamming distance is the distance between the primer and aligned
        sequence, and start and end give the start and end index of the primer
        relative to the input sequence.
        """
        seq_aln, primer_aln, score, start, end = pairwise2.align.globalms(
            str(sequence).upper(), str(self.primer).upper(),
            self.match, self.difference, self.gap_open,
            self.gap_extend, one_alignment_only=True,
            penalize_end_gaps=self.penalize_end_gaps)[0]

        # Get an ungapped mapping on the sequence
        index_map = gap_index_map(seq_aln)
        ungap_map = ungap_index_map(primer_aln)

        # Trim to primer
        start = ungap_map[0]
        end = ungap_map[len(self.primer) - 1]

        trimmed = seq_aln[start:end + 1]

        ham_dist = hamming_distance(primer_aln[start:end + 1], trimmed,
                                    _iupac_ambiguous_equal)
        # assert primer_aln[start:end].replace('-', '') == str(self.primer)

        # TODO: handle start or end being gap better. For now, just give up
        # and return maxint for the hamming distance
        if trimmed.endswith('-'):
            tail = len(trimmed) - len(trimmed.rstrip('-'))
            end = index_map[end - tail] + 1
            ham_dist = sys.maxsize
        else:
            end = index_map[end]
        if trimmed.startswith('-'):
            start = 0
            ham_dist = sys.maxsize
        else:
            start = index_map[start]

        return ham_dist, start, end


Instruction:
Given the following Python 3 function, write the documentation
def hash_starts_numeric(records):
    for record in records:
        seq_hash = hashlib.sha1(str(record.seq)).hexdigest()
        if seq_hash[0].isdigit():
            yield record

Response:
A simple function that only accepts records with a numeric start to
CTYPE their sha - 1 hash.


Instruction:
Write a Python 3 script to
open a file for atomic writing. Generates a temp file, renames to value of ``path``. Arguments: ``permissions``: Permissions to set (default: umask) ``file_factory``: If given, the handle yielded will be the result of calling file_factory(path) Additional arguments are passed to tempfile.NamedTemporaryFile

Response:
def atomic_write(path, mode='wt', permissions=None, file_factory=None, **kwargs):
    """
    Open a file for atomic writing.

    Generates a temp file, renames to value of ``path``.

    Arguments:
    ``permissions``: Permissions to set (default: umask)
    ``file_factory``: If given, the handle yielded will be the result of
        calling file_factory(path)

    Additional arguments are passed to tempfile.NamedTemporaryFile
    """
    if permissions is None:
        permissions = apply_umask()
    # Handle stdout:
    if path == '-':
        yield sys.stdout
    else:
        base_dir = os.path.dirname(path)
        kwargs['suffix'] = os.path.basename(path)
        tf = tempfile.NamedTemporaryFile(
            dir=base_dir, mode=mode, delete=False, **kwargs)

        # If a file_factory is given, close, and re-open a handle using the
        # file_factory
        if file_factory is not None:
            tf.close()
            tf = file_factory(tf.name)
        try:
            with tf:
                yield tf
            # Move
            os.rename(tf.name, path)
            os.chmod(path, permissions)
        except:
            os.remove(tf.name)
            raise


Instruction:
Can you generate a brief explanation for the following Python 3 code
def cut_range(string):
    value_range = string.split(':')
    if len(value_range) == 1:
        start = int(value_range[0])
        stop = start
    elif len(value_range) == 2:
        start, stop = tuple(int(i) if i else None for i in value_range)
    else:
        msg = "{0} is not a valid, 1-indexed range.".format(string)
        raise argparse.ArgumentTypeError(msg)

    if start == 0 or (stop or sys.maxsize) < (start or 0):
        msg = "{0} is not a valid, 1-indexed range.".format(string)
        raise argparse.ArgumentTypeError(msg)

    # Convert from 1-indexed to 0-indexed
    if start is not None and start > 0:
        start -= 1

    return slice(start, stop)

Response: A custom argparse type to deal with sequences ranges such as 5 : 500.


Instruction:
How would you explain what the following Python 3 function does
def typed_range(type_func, minimum, maximum):
    @functools.wraps(type_func)
    def inner(string):
        result = type_func(string)
        if not result >= minimum and result <= maximum:
            raise argparse.ArgumentTypeError(
                    "Please provide a value between {0} and {1}".format(
                        minimum, maximum))
        return result
    return inner

Response: Require variables to be of the specified type, between minimum and maximum


Instruction:
Here you have a function in Python 3, explain what it does
def partial_append_action(fn, argument_keys=None):
    if isinstance(argument_keys, str):
        argument_keys = [argument_keys]
    argument_keys = argument_keys or []

    class PartialAppendAction(argparse.Action):
        def __init__(self,
                     option_strings,
                     dest,
                     const=None,
                     default=None,
                     required=False,
                     help=None,
                     type=None,
                     metavar=None,
                     nargs=None,
                     **kwargs):
            super(PartialAppendAction, self).__init__(
                option_strings=option_strings,
                dest=dest,
                nargs=len(argument_keys),
                const=const,
                default=default,
                required=required,
                metavar=metavar,
                type=type,
                help=help, **kwargs)

        def __call__(self, parser, namespace, values, option_string=None):
            items = copy.copy(getattr(namespace, self.dest, None)) or []

            # If no value was set default to empty list
            if values is None:
                values = []
            elif not isinstance(values, list):
                values = [values]

            if len(argument_keys) != len(values):
                raise ValueError("Unexpected number of values")

            # Generate keyword arguments for the input function
            kwargs = dict(list(zip(argument_keys, values)))
            f = functools.partial(fn, **kwargs)
            items.append(f)
            setattr(namespace, self.dest, items)

    return PartialAppendAction

Response: Returns an argparse. Action which appends a partially - applied function to dest.


Instruction:
Given the following Python 3 function, write the documentation
def positive_value(target_type):
    def inner(string):
        value = target_type(string)
        if not value >= 0:
            raise argparse.ArgumentTypeError("Invalid positive number: " +
                    string)
        return value

    return inner

Response: Returns a function that checks that the argument is a positive number.