def attr_get(args):
    '''Return a dict of attribute name/value pairs: if entity name & type
    are specified then attributes will be retrieved from that entity,
    otherwise workspace-level attributes will be returned.  By default all
    attributes attached to the given object will be returned, but a subset
    can be selected by specifying a list of attribute names; names which
    refer to a non-existent attribute will be silently ignored. By default
    a special __header__ entry is optionally added to the result. '''

    if args.entity_type and args.entity:
        r = fapi.get_entity(args.project, args.workspace, args.entity_type, args.entity)
        fapi._check_response_code(r, 200)
        attrs = r.json()['attributes']
        # It is wrong for the members of container objects to appear as metadata
        # (attributes) attached to the container, as it conflates fundamentally
        # different things: annotation vs membership. This can be seen by using
        # a suitcase model of reasoning: ATTRIBUTES are METADATA like the weight
        # & height of the suitcase, the shipping label and all of its passport
        # stamps; while MEMBERS are the actual CONTENTS INSIDE the suitcase.
        # This conflation also contradicts the design & docs (which make a clear
        # distinction between MEMBERSHIP and UPDATE loadfiles).  For this reason
        # a change has been requested of the FireCloud dev team (via forum), and
        # until it is implemented we will elide "list of members" attributes here
        # (but later may provide a way for users to request such, if wanted)
        for k in ["samples", "participants", "pairs"]:
            attrs.pop(k, None)
    else:
        r = fapi.get_workspace(args.project, args.workspace)
        fapi._check_response_code(r, 200)
        attrs = r.json()['workspace']['attributes']

    if args.attributes:         # return a subset of attributes, if requested
        attrs = {k:attrs[k] for k in set(attrs).intersection(args.attributes)}

    # If some attributes have been collected, return in appropriate format
    if attrs:
        if args.entity:                     # Entity attributes

            def textify(thing):
                if isinstance(thing, dict):
                    thing = thing.get("items", thing.get("entityName", "__UNKNOWN__"))
                return "{0}".format(thing)

            result = {args.entity : u'\t'.join(map(textify, attrs.values()))}
            # Add "hidden" header of attribute names, for downstream convenience
            object_id = u'entity:%s_id' % args.entity_type
            result['__header__'] = [object_id] + list(attrs.keys())
        else:
            result = attrs                  # Workspace attributes
    else:
        result = {}

    return result