3
$\begingroup$

Drivers, and in particular driver variable targets have data_paths that will break in the UI using layout.prop(target.id, target.data_path) an example is a mesh target with shape_keys.key_blocks["Key 1"].value as a datapath.

def split_path(data_path):
    '''
    Split a data_path into parts
    '''
    if not len(data_path):
        return []
     # remove all collection names   
    match = re.findall(r'\[\"(.*?)\"\]\.', data_path)

    namedic = {}
    for i, m in enumerate(match):
        key = "Collection___NAME________%d" % i # surely not lol.
        data_path = data_path.replace(m, key, 1)
        namedic[key] = m

    parts = data_path.rpartition(".")
    props = []
    while parts[0] != parts[1] != '':
        dp = parts[0]
        props.append(parts[2])
        parts = dp.rpartition(".")

    props.append(parts[2])

    # replace the names
    propstring = ",".join(props)
    for key, name in namedic.items():
        propstring = propstring.replace(key, name)
    props = propstring.split(",")

    # reverse list
    props.reverse()
    return props

will return ['shape_keys', 'key_blocks["Key 1"]', 'value'] for aforementioned data_path

However it's possible for users to name their objects with quotes " and square brackets et al which will break the re.

Any suggestions on how to parse more effectively for names?

$\endgroup$
6
  • $\begingroup$ better put this on stack-overflow, this is about python parsing. Also include an example that will break your code - the case you want to solve. There is no object name in the data_path, I am confused. $\endgroup$ Commented Nov 26, 2015 at 16:19
  • $\begingroup$ @Jerryno yes and no, each path element needs to resolve to a blender object, hence the solution may lay outside the re used in question using bl_rna for instance, so yeah it's here or there with a blender tag. If I get's migrated I gets migrated.. i suppose. $\endgroup$
    – batFINGER
    Commented Nov 26, 2015 at 16:29
  • $\begingroup$ I think that driver variable with datapath containing for example pose.bones['Bone'] will not work. It only works with parenthesis (" "). You don't need to worry about quotes (' '). Am I understanding the question right? $\endgroup$ Commented Nov 26, 2015 at 16:44
  • $\begingroup$ It's more the case of if the bone is named something dumb like '["Bone["Bone' rather than 'Bone' If the dp is pose.bones["Bone"].location I want to have ['pose', 'bones["Bone"]', 'location'] returned, even if the name is "dumb name" , from which I can ".".join the first two and use layout.prop(id_obj.path_resolve(joinedbit), 'location') for instance $\endgroup$
    – batFINGER
    Commented Nov 26, 2015 at 16:52
  • $\begingroup$ I see now. Parenthesis as part of a name will not work in a driver but quotes will - and that will break your parser. Something that detects string starts and ends will solve this. I'll write an automaton. $\endgroup$ Commented Nov 26, 2015 at 17:07

1 Answer 1

3
$\begingroup$

Problems with parsing data_path can occur when the user gives entities names containing apostrophes(names containing quotes will not work in driver data_paths). Such will still work in drivers, i.e:

"""pose.bones["Bone'bla'"].location.x"""

So we can assume every name has to be enclosed with [""] in such datastring that drivers will accept:

def split_path(data_path):
    '''
    Split a data_path into parts
    '''
    if not data_path:
        return []

    # extract names from data_path
    names = data_path.split('"')[1::2]
    data_path_no_names = ''.join(data_path.split('"')[0::2])

    # segment into chunks
    # ID props will be segmented by replacing ][ with ].[
    data_chunks = data_path_no_names.replace('][', '].[').split('.')
    # probably regex should be used here and things put into dictionary
    # so it's clear what chunk is what
    # depends of use case, the main idea is to extract names, segment, then put back

    # put names back into chunks where [] are
    for id, chunk in enumerate(data_chunks):
        if chunk.find('[]') > 0:
            data_chunks[id] = chunk.replace('[]', '["' + names.pop(0) + '"]')

    return data_chunks

The part # segment into chunks is just basic, some more advanced segmenting can be implemented (for example one that will understand location.x is one thing or that will extract correct target.id and target.data_path)

$\endgroup$
3
  • $\begingroup$ Nice one. Needed the quotes data_chunks[id] = chunk.replace('[]', '["%s"]' % names.pop(0)) and also there's the ID prop case where datapath is '["prop"]'which is returning ['[]'] $\endgroup$
    – batFINGER
    Commented Nov 26, 2015 at 18:16
  • $\begingroup$ Corrected missing "", thanks. Yes the ID prop case I didn't think of. Will look into it in the morning. $\endgroup$ Commented Nov 26, 2015 at 21:35
  • $\begingroup$ Your help is most appreciated, as all the different combinations make it non trivial IMO, and have produced some really ugly code from me (moreso than usual) for each case scenario, the script in OP managed to take out a lot of spag. When I have time I'll make a test list of props.. An ID prop named '["\"Prop[[""]' for instance will create hassles. On one hand I feel if a user is creating such silly names they can learn the hard way. A robust script to return a split path has many applications for scripts dealing with datapaths. $\endgroup$
    – batFINGER
    Commented Nov 27, 2015 at 11:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .