Search code examples
pythonisinstance

Lie to isinstance


So, this is a kluge. I'm working with luigi and sciluigi.

The isinstance check is in the sciluigi package, and I'd rather kluge it then have to branch the entire sciluigi for this small issue :)

Simply put, I had to subclass one of the package's classes (luigi.LocalTarget)- to add additional functionality. THAT functionality works great, BUT there's an object check in the sciluigi package...

sciluigi.dependencies.DependencyHelpers._parse_outputitem()

...which causes the run to fail simply because an isinstance line is set to check only for 'TargetInfo' objects.

What I'd LIKE to do is just tell my child class to 'lie' to isinstance so it reports as a TargetInfo object and passes :D

Forgiveness asked in advance :D

def _parse_outputitem(self, val, targets):
    '''
    Recursively loop through lists of TargetInfos, or
    callables returning TargetInfos, or lists of ...
    (repeat recursively) ... and return all targets.
    '''
    if callable(val):
        val = val()
    if isinstance(val, TargetInfo):
        targets.append(val.target)
    elif isinstance(val, list):
        for valitem in val:
            targets = self._parse_outputitem(valitem, targets)
    elif isinstance(val, dict):
        for _, valitem in iteritems(val):
            targets = self._parse_outputitem(valitem, targets)
    else:
        raise Exception('Input item is neither callable, TargetInfo, nor list: %s' % val)
    return targets

Error message:

2017-04-06 22:26:09,753 - PipeineTest1 - DEBUG - RunSubprocess:Traceback (most recent call last):
2017-04-06 22:26:09,754 - PipeineTest1 - DEBUG - RunSubprocess:  File "/Library/Python/2.7/site-packages/luigi/worker.py", line 305, in check_complete
2017-04-06 22:26:09,754 - PipeineTest1 - DEBUG - RunSubprocess:    is_complete = task.complete()
2017-04-06 22:26:09,754 - PipeineTest1 - DEBUG - RunSubprocess:  File "/Library/Python/2.7/site-packages/luigi/task.py", line 482, in complete
2017-04-06 22:26:09,754 - PipeineTest1 - DEBUG - RunSubprocess:    outputs = flatten(self.output())
2017-04-06 22:26:09,754 - PipeineTest1 - DEBUG - RunSubprocess:  File "/Library/Python/2.7/site-packages/sciluigi/dependencies.py", line 99, in output
2017-04-06 22:26:09,754 - PipeineTest1 - DEBUG - RunSubprocess:    return self._output_targets()
2017-04-06 22:26:09,755 - PipeineTest1 - DEBUG - RunSubprocess:  File "/Library/Python/2.7/site-packages/sciluigi/dependencies.py", line 111, in _output_targets
2017-04-06 22:26:09,755 - PipeineTest1 - DEBUG - RunSubprocess:    output_targets = self._parse_outputitem(attrval, output_targets)
2017-04-06 22:26:09,755 - PipeineTest1 - DEBUG - RunSubprocess:  File "/Library/Python/2.7/site-packages/sciluigi/dependencies.py", line 132, in _parse_outputitem
2017-04-06 22:26:09,755 - PipeineTest1 - DEBUG - RunSubprocess:    raise Exception('Input item is neither callable, TargetInfo, nor list: %s' % val)
2017-04-06 22:26:09,755 - PipeineTest1 - DEBUG - RunSubprocess:Exception: Input item is neither callable, TargetInfo, nor list: <Bioproximity.common.luigi_extensions.local_target.ToppasLocalTarget object at 0x110e48190>

...unfortunately, thats 100% of the error traceback that Sciluigi provides as output.

sciluigi.dependencies.TargetInfo(object)

class TargetInfo(object):
    '''
    Class to be used for sending specification of which target, from which
    task, to use, when stitching workflow tasks' outputs and inputs together.
    '''
    task = None
    path = None
    target = None

    def __init__(self, task, path, format=None, is_tmp=False):
        self.task = task
        self.path = path
        self.target = luigi.LocalTarget(path, format, is_tmp)

    def open(self, *args, **kwargs):
        '''
        Forward open method, from luigi's target class
        '''
        return self.target.open(*args, **kwargs)

# ==============================================================================

Solution

  • It looks like you just need to subclass TargetInfo so that your object will pass the isinstance check. You can have it like this:

    class Foo(<whatever other base classes you have>, TargetInfo):
        ...
    

    If you have TargetInfo as the root descendant then it shouldn't interfere with the class's functionality because the other base classes will override any conflicting methods.