Example #1
0
 def __init__(self, name, configuration):
     super().__init__()
     checkIdentifier(name)
     self._propertyCollection = PropertyCollectionImpl(name, configuration.propertyCollection())
     self._graph = FilterGraph(self)
     self._graph.dirtyChanged.connect(configuration.setDirty)
     self._config = configuration
     self._name = name
Example #2
0
 def __init__(self):
     super().__init__()
     self._compositeFilters = []
     self._applications = []
     self._propertyCollection = PropertyCollectionImpl("root", None)
     self._guiState = PropertyCollectionImpl("_guiState",
                                             self._propertyCollection)
     self._dirty = False
Example #3
0
    def load(self, cfg):
        """
        Load the configuration from a dictionary.
        :param cfg: dictionary loaded from a json file
        :return: None
        """
        self.close()
        try:
            self._propertyCollection.defineProperty(
                "CFGFILE",
                cfg["CFGFILE"],
                "The absolute path to the configuration file.",
                options=dict(enum=[cfg["CFGFILE"]]))
            try:
                self._propertyCollection.deleteChild("_guiState")
            except PropertyCollectionChildNotFound:
                pass
            self._guiState = PropertyCollectionImpl("_guiState",
                                                    self._propertyCollection,
                                                    cfg["_guiState"])
            recursiveset = set()

            def compositeLookup(name):
                nonlocal recursiveset
                if name in recursiveset:
                    raise CompositeRecursion(name)
                try:
                    return self.compositeFilterByName(name)
                except NodeNotFoundError:
                    recursiveset.add(name)
                    try:
                        for cfg_cf in cfg["composite_filters"]:
                            if cfg_cf["name"] == name:
                                cf = CompositeFilter(name, self)
                                cf.load(cfg_cf, compositeLookup)
                                return cf
                        raise NodeNotFoundError("name")
                    finally:
                        recursiveset.remove(name)

            for cfg_cf in cfg["composite_filters"]:
                compositeLookup(cfg_cf["name"])
            for cfg_app in cfg["applications"]:
                app = Application(cfg_app["name"], self)
                app.load(cfg_app, compositeLookup)
            self.setDirty(False)
            self.configNameChanged.emit(cfg["CFGFILE"])
            self.configLoaded.emit()
        except RuntimeError as e:
            self.close(avoidSave=True)
            raise e
Example #4
0
 def load(self, cfg, compositeLookup):
     """
     load graph from config dictionary (inverse operation of save(...))
     :param cfg: dictionary loaded from json file
     :return: None
     """
     # apply subconfig gui state
     if "_guiState" in cfg and len(cfg["_guiState"]) > 0:
         guistateCC = self._propertyCollection.getChildCollection(
             "_guiState")
         for k in cfg["_guiState"]:
             PropertyCollectionImpl(k, guistateCC, cfg["_guiState"][k])
     for n in cfg["nodes"]:
         if not n["library"].startswith("composite://"):
             p = PropertyCollectionImpl(n["name"], self._propertyCollection,
                                        n["properties"])
             # apply node gui state
             nextP = PropertyCollectionImpl("_nexxT", p,
                                            {"thread": n["thread"]})
             logger.debug("loading: subconfig %s / node %s -> thread: %s",
                          self._name, n["name"], n["thread"])
             tmp = self._graph.addNode(n["library"],
                                       n["factoryFunction"],
                                       suggestedName=n["name"])
             if tmp != n["name"]:
                 raise NexTInternalError(
                     "addNode(...) has set unexpected name for node.")
         else:
             # composite node handling
             if n["library"] == "composite://port":
                 # the special nodes are already there, nothing to do here
                 pass
             elif n["library"] == "composite://ref":
                 name = n["factoryFunction"]
                 cf = compositeLookup(name)
                 tmp = self._graph.addNode(cf,
                                           "compositeNode",
                                           suggestedName=n["name"])
                 if tmp != n["name"]:
                     raise NexTInternalError(
                         "addNode(...) has set unexpected name for node.")
         for dip in n["dynamicInputPorts"]:
             self._graph.addDynamicInputPort(n["name"], dip)
         for dop in n["dynamicOutputPorts"]:
             self._graph.addDynamicOutputPort(n["name"], dop)
         # make sure that the filter is instantiated and the port information is updated immediately
         self._graph.getMockup(n["name"]).createFilterAndUpdate()
     for c in cfg["connections"]:
         contuple = self.connectionStringToTuple(c)
         self._graph.addConnection(*contuple)
Example #5
0
 def __init__(self, library, factoryFunction, propertyCollection, graph):
     super().__init__(None, None, propertyCollection, self)
     assertMainThread()
     self._library = library
     self._graph = graph
     self._factoryFunction = factoryFunction
     self._propertyCollectionImpl = propertyCollection
     self._pluginClass = None
     self._createFilterAndUpdatePending = None
     try:
         # add also a child collection for the nexxT internals
         pc = PropertyCollectionImpl("_nexxT", propertyCollection)
     except PropertyCollectionChildExists:
         pc = propertyCollection.getChildCollection("_nexxT")
     pc.defineProperty("thread", "main", "The thread this filter belongs to.")
Example #6
0
def test_dynamic_out_filter():
    with FilterEnvironment(
            "pyfile://" + os.path.dirname(__file__) +
            "/../interface/SimpleDynamicFilter.py", "SimpleDynOutFilter",
            PropertyCollectionImpl("root", None)) as dynOutPyFilter:
        f = dynOutPyFilter.getPlugin()

        dip = InputPort(True, "dynInPort", dynOutPyFilter)
        expect_exception(dynOutPyFilter.addPort, dip)
        dop = OutputPort(True, "dynOutPort", dynOutPyFilter)
        dynOutPyFilter.addPort(dop)
        expect_exception(dynOutPyFilter.addPort, dop)

        assert dynOutPyFilter.getDynamicInputPorts() == []
        if useCImpl:
            assert [p.data() for p in dynOutPyFilter.getDynamicOutputPorts()
                    ] == [dop.data()]
        else:
            assert dynOutPyFilter.getDynamicOutputPorts() == [dop]
        sip = dynOutPyFilter.getStaticInputPorts()
        assert len(sip) == 1
        assert sip[0].name() == "inPort"
        assert sip[0] is dynOutPyFilter.getInputPort("inPort")
        sop = dynOutPyFilter.getStaticOutputPorts()
        assert len(sop) == 1
        assert sop[0].name() == "outPort"
        assert sop[0] is dynOutPyFilter.getOutputPort("outPort")
        assert dynOutPyFilter.state() == FilterState.CONSTRUCTED

        dynOutPyFilter.init()
        dop2 = OutputPort(True, "dynOutPort2", dynOutPyFilter)
        expect_exception(dynOutPyFilter.addPort, dop2)

        dynOutPyFilter.open()
        expect_exception(dynOutPyFilter.addPort, dop2)
Example #7
0
def test_smoke():
    # most of this is already tested in testGraph
    Services.addService("Profiling", None)
    mockup = FilterMockup(
        "pyfile://" + os.path.dirname(__file__) +
        "/../interface/SimpleDynamicFilter.py", "SimpleDynInFilter",
        PropertyCollectionImpl("mockup", None), None)
    mockup.createFilterAndUpdate()
    mockup.addDynamicPort("dynin", InputPortInterface)
    res = mockup.createFilter()
 def newPropColl(name, parent):
     res = PropertyCollectionImpl(name, parent)
     for s in ["propertyChanged",
               "propertyAdded",
               "propertyRemoved",
               "childAdded",
               "childRemoved",
               "childRenamed"]:
         getattr(res, s).connect(lambda *args, signal=s: trace_signal(args, signal))
     return res
Example #9
0
 def guiState(self, name):
     """
     Return the gui state of the entity referenced by 'name' (eitehr a full qualified filter name or a service)
     :param name: a string
     :return: a PropertyCollectionImpl instance
     """
     name = re.sub(r'[^a-zA-Z0-9_]', '_', name)
     pc = self.getPropertyCollection()
     gs = pc.getChildCollection("_guiState")
     try:
         cc = gs.getChildCollection(name)
     except PropertyCollectionChildNotFound:
         cc = PropertyCollectionImpl(name, gs)
     return cc
Example #10
0
 def close(self, avoidSave=False):
     """
     Closing the configuration instance and free allocated resources.
     :return:
     """
     logger.internal("entering Configuration.close")
     if not avoidSave:
         ConfigFileLoader.saveGuiState(self)
     Application.unactivate()
     for sc in self._compositeFilters + self._applications:
         sc.cleanup()
     for c in self._compositeFilters:
         self.subConfigRemoved.emit(c.getName(), self.CONFIG_TYPE_COMPOSITE)
     self._compositeFilters = []
     for a in self._applications:
         self.subConfigRemoved.emit(a.getName(),
                                    self.CONFIG_TYPE_APPLICATION)
     self._applications = []
     self._propertyCollection.deleteLater()
     self._propertyCollection = PropertyCollectionImpl("root", None)
     self.configNameChanged.emit(None)
     self.appActivated.emit("", None)
     PluginManager.singleton().unloadAll()
     logger.internal("leaving Configuration.close")
Example #11
0
 def addNode(self, library, factoryFunction, suggestedName=None):
     """
     Add a node to the graph, given a library and a factory function for instantiating the plugin.
     :param library: definition file of the plugin
     :param factoryFunction: function for instantiating the plugin
     :param suggestedName: name suggested by used (if None, factoryFunction is used)
     :return: the name of the created node
     """
     assertMainThread()
     if suggestedName is None:
         suggestedName = factoryFunction
     name = super().uniqueNodeName(suggestedName)
     try:
         propColl = self._properties.getChildCollection(name)
     except PropertyCollectionChildNotFound:
         propColl = PropertyCollectionImpl(name, self._properties)
     propColl.propertyChanged.connect(self.setDirty)
     filterMockup = FilterMockup(library, factoryFunction, propColl, self)
     filterMockup.createFilterAndUpdate()
     self._filters[name] = filterMockup
     assert super().addNode(name) == name
     if factoryFunction == "compositeNode" and hasattr(
             library, "checkRecursion"):
         try:
             library.checkRecursion()
         except CompositeRecursion as e:
             self.deleteNode(name)
             raise e
     for port in filterMockup.getStaticInputPorts():
         self.addInputPort(name, port.name())
     for port in filterMockup.getStaticOutputPorts():
         self.addOutputPort(name, port.name())
     filterMockup.portInformationUpdated.connect(
         self.portInformationUpdated)
     self.dirtyChanged.emit()
     return name
Example #12
0
def test_EntryPoint(ep):
    env = FilterEnvironment("entry_point://" + ep, "entry_point",
                            PropertyCollectionImpl('propColl', None))
    PluginManager.singleton().unloadAll()
Example #13
0
 def __init__(self):
     self.pc = PropertyCollectionImpl("root", None)
Example #14
0
 def __init__(self, name, configuration):
     super().__init__(name, configuration)
     configuration.addApplication(self)
     PropertyCollectionImpl("_guiState", self.getPropertyCollection())
Example #15
0
class SubConfiguration(QObject):
    """
    This class handles a sub-configuration of a nexxT application. Sub configs are either applications or
    SubGraphs (which behave like a filter).
    """
    nameChanged = Signal(object, str)

    def __init__(self, name, configuration):
        super().__init__()
        checkIdentifier(name)
        self._propertyCollection = PropertyCollectionImpl(name, configuration.propertyCollection())
        self._graph = FilterGraph(self)
        self._graph.dirtyChanged.connect(configuration.setDirty)
        self._config = configuration
        self._name = name

    #def dump(self):
    #    self._graph.dump()

    def cleanup(self):
        """
        Cleanup function
        :return:
        """
        self._graph.cleanup()

    def getName(self):
        """
        Get the name of this subconfiguration
        :return: string
        """
        return self._name

    def setName(self, name):
        """
        Sets the name of this subconfiguration
        :param name: new name (string)
        :return: None
        """
        if name != self._name:
            oldName = self._name
            self._name = name
            self._propertyCollection.setObjectName(name)
            self.nameChanged.emit(self, oldName)
            self._config.setDirty()

    def getPropertyCollection(self):
        """
        Get the property collection of this sub configuration
        :return: a PropertyCollectionImpl instance
        """
        return self._propertyCollection

    def getGraph(self):
        """
        Get the filter graph of this sub config
        :return: a FilterGraph instance
        """
        return self._graph

    def getConfiguration(self):
        """
        Get the corresponding nexxT configuration
        :return: a Configuration instance
        """
        return self._config

    @staticmethod
    def connectionStringToTuple(con):
        """
        Converts a connection string to a 4 tuple.
        :param con: a string containing a connection "name1.port1 -> name2.port2"
        :return: a 4-tuple ("name1", "port1", "name2", "port2")
        """
        f, t = con.split("->")
        fromNode, fromPort = f.strip().split(".")
        toNode, toPort = t.strip().split(".")
        return fromNode.strip(), fromPort.strip(), toNode.strip(), toPort.strip()

    @staticmethod
    def tupleToConnectionString(connection):
        """
        Converts a 4-tuple connection to a string.
        :param connection: a 4-tuple ("name1", "port1", "name2", "port2")
        :return: a string "name1.port1 -> name2.port2"
        """
        return "%s.%s -> %s.%s" % connection

    def load(self, cfg, compositeLookup):
        """
        load graph from config dictionary (inverse operation of save(...))
        :param cfg: dictionary loaded from json file
        :return: None
        """
        # apply subconfig gui state
        if "_guiState" in cfg and len(cfg["_guiState"]) > 0:
            guistateCC = self._propertyCollection.getChildCollection("_guiState")
            for k in cfg["_guiState"]:
                PropertyCollectionImpl(k, guistateCC, cfg["_guiState"][k])
        for n in cfg["nodes"]:
            if not n["library"].startswith("composite://"):
                p = PropertyCollectionImpl(n["name"], self._propertyCollection, n["properties"])
                # apply node gui state
                nextP = PropertyCollectionImpl("_nexxT", p, {"thread": n["thread"]})
                logger.debug("loading: subconfig %s / node %s -> thread: %s", self._name, n["name"], n["thread"])
                tmp = self._graph.addNode(n["library"], n["factoryFunction"], suggestedName=n["name"])
                if tmp != n["name"]:
                    raise NexTInternalError("addNode(...) has set unexpected name for node.")
            else:
                # composite node handling
                if n["library"] == "composite://port":
                    # the special nodes are already there, nothing to do here
                    pass
                elif n["library"] == "composite://ref":
                    name = n["factoryFunction"]
                    cf = compositeLookup(name)
                    tmp = self._graph.addNode(cf, "compositeNode", suggestedName=n["name"])
                    if tmp != n["name"]:
                        raise NexTInternalError("addNode(...) has set unexpected name for node.")
            for dip in n["dynamicInputPorts"]:
                self._graph.addDynamicInputPort(n["name"], dip)
            for dop in n["dynamicOutputPorts"]:
                self._graph.addDynamicOutputPort(n["name"], dop)
            # make sure that the filter is instantiated and the port information is updated immediately
            self._graph.getMockup(n["name"]).createFilterAndUpdate()
        for c in cfg["connections"]:
            contuple = self.connectionStringToTuple(c)
            self._graph.addConnection(*contuple)

    def save(self):
        """
        save graph to config dictionary (inverse operation of load(...))
        :return: dictionary which can be saved as json file
        """
        def adaptLibAndFactory(lib, factory):
            if not isinstance(lib, str):
                if factory in ["CompositeInputNode", "CompositeOutputNode"]:
                    ncfg["library"] = "composite://port"
                    ncfg["factoryFunction"] = factory[:-len("Node")]
                elif factory in ["compositeNode"]:
                    ncfg["library"] = "composite://ref"
                    ncfg["factoryFunction"] = lib.getName()
                else:
                    raise NexTInternalError("Unexpected factory function '%s'" % factory)
            else:
                ncfg["library"] = lib
                ncfg["factoryFunction"] = factory
            return lib, factory
        #self.dump()
        cfg = dict(name=self.getName())
        try:
            gs = self._propertyCollection.getChildCollection("_guiState")
            cfg["_guiState"] = {}
            for obj in gs.children():
                if isinstance(obj, PropertyCollectionImpl):
                    cfg["_guiState"][obj.objectName()] = obj.saveDict()
        except PropertyCollectionChildNotFound:
            pass
        cfg["nodes"] = []
        for name in self._graph.allNodes():
            ncfg = OrderedDict(name=name)
            mockup = self._graph.getMockup(name)
            lib = mockup.getLibrary()
            factory = mockup.getFactoryFunction()
            lib, factory = adaptLibAndFactory(lib, factory)
            ncfg["dynamicInputPorts"] = []
            ncfg["staticInputPorts"] = []
            ncfg["dynamicOutputPorts"] = []
            ncfg["staticOutputPorts"] = []
            for ip in mockup.getDynamicInputPorts():
                ncfg["dynamicInputPorts"].append(ip.name())
            for ip in self._graph.allInputPorts(name):
                if not ip in ncfg["dynamicInputPorts"]:
                    ncfg["staticInputPorts"].append(ip)
            for op in mockup.getDynamicOutputPorts():
                ncfg["dynamicOutputPorts"].append(op.name())
            for op in self._graph.allOutputPorts(name):
                if not op in ncfg["dynamicOutputPorts"]:
                    ncfg["staticOutputPorts"].append(op)
            p = self._propertyCollection.getChildCollection(name)
            try:
                ncfg["thread"] = p.getChildCollection("_nexxT").getProperty("thread")
                logger.debug("saving: subconfig %s / node %s -> thread: %s", self._name, name, ncfg["thread"])
            except PropertyCollectionChildNotFound:
                pass
            except PropertyCollectionPropertyNotFound:
                pass
            ncfg["properties"] = p.saveDict()
            cfg["nodes"].append(ncfg)
        cfg["connections"] = [self.tupleToConnectionString(c) for c in self._graph.allConnections()]
        return cfg

    @staticmethod
    def getThreadSet(item):
        """
        Returns all threads (as strings) used by the given filter mockup. Usually this is only one, but
        for composite filters, this function performs a recursive lookup.
        :param mockup:
        :return: set of strings
        """
        # avoid recursive import
        from nexxT.core.CompositeFilter import CompositeFilter
        from nexxT.core.FilterMockup import FilterMockup
        if isinstance(item, FilterMockup):
            mockup = item
            if (issubclass(mockup.getPluginClass(), CompositeFilter.CompositeInputNode) or
                    issubclass(mockup.getPluginClass(), CompositeFilter.CompositeOutputNode)):
                return set()
            if not issubclass(mockup.getPluginClass(), CompositeFilter.CompositeNode):
                pc = mockup.propertyCollection().getChildCollection("_nexxT")
                thread = pc.getProperty("thread")
                return set([thread])
            g = mockup.getLibrary().getGraph()
            res = set()
            for n in g.allNodes():
                res = res.union(SubConfiguration.getThreadSet(g.getMockup(n)))
            return res
        if isinstance(item, SubConfiguration):
            g = item.getGraph()
            res = set()
            for n in g.allNodes():
                res = res.union(SubConfiguration.getThreadSet(g.getMockup(n)))
            return res
        raise RuntimeError("Don't know how to handle instances of this type.")
Example #16
0
class Configuration(QObject):
    """
    A configuration is a collection of subgraphs, i.e., applications and composite filters.
    """

    subConfigAdded = Signal(object)
    subConfigRemoved = Signal(str, int)
    configNameChanged = Signal(object)
    appActivated = Signal(str, object)
    configLoaded = Signal()
    configAboutToSave = Signal()
    dirtyChanged = Signal(bool)

    CONFIG_TYPE_COMPOSITE = 0
    CONFIG_TYPE_APPLICATION = 1

    @staticmethod
    def configType(subConfig):
        """
        return the config type (either CONFIG_TYPE_COMPOSITE or CONFIG_TYPE_APPLICATION) of a SubConfiguration
        instance
        :param subConfig: a SubConfiguration instance
        :return:
        """
        if isinstance(subConfig, Application):
            return Configuration.CONFIG_TYPE_APPLICATION
        if isinstance(subConfig, CompositeFilter):
            return Configuration.CONFIG_TYPE_COMPOSITE
        raise NexTRuntimeError("Unexpected instance type")

    def __init__(self):
        super().__init__()
        self._compositeFilters = []
        self._applications = []
        self._propertyCollection = PropertyCollectionImpl("root", None)
        self._guiState = PropertyCollectionImpl("_guiState",
                                                self._propertyCollection)
        self._dirty = False

    @Slot(bool)
    def setDirty(self, dirty=True):
        """
        Slot to actualize the dirty flag of the configuration file. Emits the dirtyChanged signal if necessary.
        :param dirty: new dirty state given as a boolean
        :return:
        """
        if dirty != self._dirty:
            self._dirty = dirty
            self.dirtyChanged.emit(self._dirty)

    def dirty(self):
        """
        Returns the current dirty state.
        :return: a boolean
        """
        return self._dirty

    @Slot()
    def close(self, avoidSave=False):
        """
        Closing the configuration instance and free allocated resources.
        :return:
        """
        logger.internal("entering Configuration.close")
        if not avoidSave:
            ConfigFileLoader.saveGuiState(self)
        Application.unactivate()
        for sc in self._compositeFilters + self._applications:
            sc.cleanup()
        for c in self._compositeFilters:
            self.subConfigRemoved.emit(c.getName(), self.CONFIG_TYPE_COMPOSITE)
        self._compositeFilters = []
        for a in self._applications:
            self.subConfigRemoved.emit(a.getName(),
                                       self.CONFIG_TYPE_APPLICATION)
        self._applications = []
        self._propertyCollection.deleteLater()
        self._propertyCollection = PropertyCollectionImpl("root", None)
        self.configNameChanged.emit(None)
        self.appActivated.emit("", None)
        PluginManager.singleton().unloadAll()
        logger.internal("leaving Configuration.close")

    @Slot(object)
    def load(self, cfg):
        """
        Load the configuration from a dictionary.
        :param cfg: dictionary loaded from a json file
        :return: None
        """
        self.close()
        try:
            self._propertyCollection.defineProperty(
                "CFGFILE",
                cfg["CFGFILE"],
                "The absolute path to the configuration file.",
                options=dict(enum=[cfg["CFGFILE"]]))
            try:
                self._propertyCollection.deleteChild("_guiState")
            except PropertyCollectionChildNotFound:
                pass
            self._guiState = PropertyCollectionImpl("_guiState",
                                                    self._propertyCollection,
                                                    cfg["_guiState"])
            recursiveset = set()

            def compositeLookup(name):
                nonlocal recursiveset
                if name in recursiveset:
                    raise CompositeRecursion(name)
                try:
                    return self.compositeFilterByName(name)
                except NodeNotFoundError:
                    recursiveset.add(name)
                    try:
                        for cfg_cf in cfg["composite_filters"]:
                            if cfg_cf["name"] == name:
                                cf = CompositeFilter(name, self)
                                cf.load(cfg_cf, compositeLookup)
                                return cf
                        raise NodeNotFoundError("name")
                    finally:
                        recursiveset.remove(name)

            for cfg_cf in cfg["composite_filters"]:
                compositeLookup(cfg_cf["name"])
            for cfg_app in cfg["applications"]:
                app = Application(cfg_app["name"], self)
                app.load(cfg_app, compositeLookup)
            self.setDirty(False)
            self.configNameChanged.emit(cfg["CFGFILE"])
            self.configLoaded.emit()
        except RuntimeError as e:
            self.close(avoidSave=True)
            raise e

    def save(self, file=None):
        """
        return a dictionary suitable for saving to json (inverse of load)
        :return: dictionary
        """
        self.configAboutToSave.emit()
        cfg = {}
        if file is not None:
            # TODO: we assume here that this is a new config; a "save to file" feature is not yet implemented.
            self._propertyCollection.defineProperty(
                "CFGFILE",
                str(file),
                "The absolute path to the configuration file.",
                options=dict(enum=[str(file)]))
        try:
            cfg["CFGFILE"] = self._propertyCollection.getProperty("CFGFILE")
        except PropertyCollectionPropertyNotFound:
            cfg["CFGFILE"] = None
        cfg["_guiState"] = self._guiState.saveDict()
        cfg["composite_filters"] = [cf.save() for cf in self._compositeFilters]
        cfg["applications"] = [app.save() for app in self._applications]
        self.configNameChanged.emit(cfg["CFGFILE"])
        self.setDirty(False)
        return cfg

    def filename(self):
        """
        Get the configuration file name or None if it is not set.
        :return:
        """
        try:
            return self._propertyCollection.getProperty("CFGFILE")
        except PropertyCollectionPropertyNotFound:
            return None

    def propertyCollection(self):
        """
        Get the (root) property collection.
        :return: PropertyCollectionImpl instance
        """
        return self._propertyCollection

    def guiState(self):
        """
        Return the per-config gui state.
        :return: a PropertyCollection instance
        """
        return self._guiState

    def compositeFilterByName(self, name):
        """
        Search for the composite filter with the given name
        :param name: a string
        :return: a CompositeFilter instance
        """
        match = [cf for cf in self._compositeFilters if cf.getName() == name]
        if len(match) == 1:
            return match[0]
        if len(match) == 0:
            raise NodeNotFoundError(name)
        raise NexTInternalError("non unique name %s" % name)

    def applicationByName(self, name):
        """
        Search for the application with the given name
        :param name: a string
        :return: an Application instance
        """
        match = [app for app in self._applications if app.getName() == name]
        if len(match) == 1:
            return match[0]
        if len(match) == 0:
            raise NodeNotFoundError(name)
        raise NexTInternalError("non unique name %s" % name)

    def subConfigByNameAndTye(self, name, typeid):
        """
        Return the subconfiguration with the given name and type
        :param name: a string
        :param typeid: either CONFIG_TYPE_APPLICATION or CONFIG_TYPE_COMPOSITE
        :return: a SubConfiguration instance
        """
        if typeid == self.CONFIG_TYPE_APPLICATION:
            return self.applicationByName(name)
        if typeid == self.CONFIG_TYPE_COMPOSITE:
            return self.compositeFilterByName(name)
        raise NexTInternalError("unknown typeid %s" % typeid)

    @Slot(str)
    def activate(self, appname):
        """
        Activate the application with the given name.
        :param appname: a string
        :return: None
        """
        for app in self._applications:
            if app.getName() == appname:
                app.activate()
                self.appActivated.emit(appname, Application.activeApplication)
                return
        raise NexTRuntimeError("Application '%s' not found." % appname)

    @Slot(str, str)
    def renameComposite(self, oldName, newName):
        """
        Rename a composite subgraph
        :param oldName: the old name
        :param newName: the new name
        :return:
        """
        if oldName != newName:
            self._checkUniqueName(self._compositeFilters, newName)
            self.compositeFilterByName(oldName).setName(newName)
            self.setDirty(True)

    @Slot(str, str)
    def renameApp(self, oldName, newName):
        """
        Rename an application subgraph
        :param oldName: the old name
        :param newName: the new name
        :return:
        """
        if oldName != newName:
            self._checkUniqueName(self._applications, newName)
            self.applicationByName(oldName).setName(newName)
            self.setDirty(True)

    def addComposite(self, compFilter):
        """
        Add a composite filter instance to this configuration.
        :param compFilter: a CompositeFilter instance
        :return: None
        """
        self._checkUniqueName(self._compositeFilters, compFilter.getName())
        self._compositeFilters.append(compFilter)
        self.subConfigAdded.emit(compFilter)
        self.setDirty(True)

    def addApplication(self, app):
        """
        Add an application instance to this configuration
        :param app: an Application instance
        :return: None
        """
        self._checkUniqueName(self._applications, app.getName())
        self._applications.append(app)
        self.subConfigAdded.emit(app)
        self.setDirty(True)

    def addNewApplication(self):
        """
        Add a new application to this configuration. The name will be chosen automatically to be unique.
        :return: the chosen name
        """
        name = "application"
        idx = 1
        existing = [a.getName() for a in self._applications]
        while name in existing:
            idx += 1
            name = "application_%d" % idx
        Application(name, self)
        return name

    def addNewCompositeFilter(self):
        """
        Add a new composite filter to this configuration. The name will be chosen automaitcally to be unique.
        :return: the chosen name
        """
        name = "composite"
        idx = 1
        while len([c for c in self._compositeFilters if c.getName() == name
                   ]) > 0:
            idx += 1
            name = "composite_%d" % idx
        CompositeFilter(name, self)
        return name

    def getApplicationNames(self):
        """
        Return list of application names
        :return: list of strings
        """
        return [app.getName() for app in self._applications]

    def getCompositeFilterNames(self):
        """
        Return list of composite filters
        :return: list of strings
        """
        return [cf.getName() for cf in self._compositeFilters]

    @staticmethod
    def _checkUniqueName(collection, name):
        for i in collection:
            if i.getName() == name:
                raise NexTRuntimeError("Name '%s' is not unique." % name)

    def checkRecursion(self):
        """
        Checks for recursions in the composite filters, raises a CompositeRecursion exception if necessary.
        :return: None
        """
        for cf in self._compositeFilters:
            self._checkRecursion(cf.getGraph(), set([cf.getName()]))

    @staticmethod
    def _checkRecursion(graph, activeNames):
        if activeNames is None:
            activeNames = set()
        nodes = graph.allNodes()
        allComposites = []
        for n in nodes:
            # check whether this node is itself a composite node
            mockup = graph.getMockup(n)
            if issubclass(mockup.getPluginClass(),
                          CompositeFilter.CompositeNode):
                allComposites.append(mockup)
                cf = mockup.getLibrary()
                name = cf.getName()
                #print("active:", activeNames, "; curr:", name)
                if name in activeNames:
                    raise CompositeRecursion(name)
                activeNames.add(name)
                Configuration._checkRecursion(cf.getGraph(), activeNames)
                activeNames.remove(name)
Example #17
0
def test_static_filter():
    function_calls = []
    with FilterEnvironment(
            "pyfile://" + os.path.dirname(__file__) +
            "/../interface/SimpleStaticFilter.py", "SimpleStaticFilter",
            PropertyCollectionImpl("root", None)) as staticPyFilter:
        f = staticPyFilter.getPlugin()

        # call the methods to check everything is sane
        assert f.environment() is staticPyFilter
        assert len(f.onSuggestDynamicPorts()[0]) == 0
        assert len(f.onSuggestDynamicPorts()[1]) == 0

        origCallbacks = dict(onInit=f.onInit,
                             onOpen=f.onOpen,
                             onStart=f.onStart,
                             onStop=f.onStop,
                             onClose=f.onClose,
                             onDeinit=f.onDeinit,
                             onPortDataChanged=f.onPortDataChanged)
        for callback in origCallbacks:
            setattr(f,
                    callback,
                    lambda *args, callback=callback: function_calls.append(
                        (callback, origCallbacks[callback](*args))))

        def exceptionCallback(*args):
            raise RuntimeError()

        assert staticPyFilter.getDynamicInputPorts() == []
        assert staticPyFilter.getDynamicOutputPorts() == []
        sip = staticPyFilter.getStaticInputPorts()
        assert len(sip) == 1
        assert sip[0].name() == "inPort"
        assert sip[0] is staticPyFilter.getInputPort("inPort")
        sop = staticPyFilter.getStaticOutputPorts()
        assert len(sop) == 1
        assert sop[0].name() == "outPort"
        assert sop[0] is staticPyFilter.getOutputPort("outPort")
        expect_exception(staticPyFilter.addPort,
                         InputPort(True, "dynOutPort", staticPyFilter))
        expect_exception(staticPyFilter.addPort,
                         OutputPort(True, "dynOutPort", staticPyFilter))
        assert staticPyFilter.state() == FilterState.CONSTRUCTED

        assert len(function_calls) == 0
        expect_exception(staticPyFilter.start)
        expect_exception(staticPyFilter.open)
        expect_exception(staticPyFilter.stop)
        expect_exception(staticPyFilter.close)
        expect_exception(staticPyFilter.deinit)
        staticPyFilter.init()
        assert function_calls == [("onInit", None)]
        assert staticPyFilter.state() == FilterState.INITIALIZED
        function_calls.clear()

        expect_exception(staticPyFilter.init)
        expect_exception(staticPyFilter.stop)
        expect_exception(staticPyFilter.start)
        expect_exception(staticPyFilter.close)
        staticPyFilter.open()
        assert function_calls == [("onOpen", None)]
        assert staticPyFilter.state() == FilterState.OPENED
        function_calls.clear()

        expect_exception(staticPyFilter.init)
        expect_exception(staticPyFilter.open)
        expect_exception(staticPyFilter.stop)
        expect_exception(staticPyFilter.deinit)
        staticPyFilter.start()
        assert function_calls == [("onStart", None)]
        assert staticPyFilter.state() == FilterState.ACTIVE
        function_calls.clear()

        assert len(function_calls) == 0
        expect_exception(staticPyFilter.init)
        expect_exception(staticPyFilter.open)
        expect_exception(staticPyFilter.start)
        expect_exception(staticPyFilter.close)
        expect_exception(staticPyFilter.deinit)
        staticPyFilter.stop()
        assert function_calls == [("onStop", None)]
        assert staticPyFilter.state() == FilterState.OPENED
        function_calls.clear()

        assert len(function_calls) == 0
        expect_exception(staticPyFilter.init)
        expect_exception(staticPyFilter.open)
        expect_exception(staticPyFilter.stop)
        expect_exception(staticPyFilter.deinit)
        staticPyFilter.close()
        assert function_calls == [("onClose", None)]
        assert staticPyFilter.state() == FilterState.INITIALIZED
        function_calls.clear()

        assert len(function_calls) == 0
        staticPyFilter.deinit()
        assert function_calls == [("onDeinit", None)]
        assert staticPyFilter.state() == FilterState.CONSTRUCTED
        function_calls.clear()

        # check exception call backs
        f.onInit = exceptionCallback
        staticPyFilter.init()
        assert staticPyFilter.state() == FilterState.INITIALIZED
        staticPyFilter.deinit()
        function_calls.clear()

    # check auto cleanup functionality
    with FilterEnvironment(
            "pyfile://" + os.path.dirname(__file__) +
            "/../interface/SimpleStaticFilter.py", "SimpleStaticFilter",
            PropertyCollectionImpl("root", None)) as staticPyFilter:
        f = staticPyFilter.getPlugin()
        origCallbacks = dict(onInit=f.onInit,
                             onOpen=f.onOpen,
                             onStart=f.onStart,
                             onStop=f.onStop,
                             onClose=f.onClose,
                             onDeinit=f.onDeinit,
                             onPortDataChanged=f.onPortDataChanged)
        for callback in origCallbacks:
            setattr(f,
                    callback,
                    lambda *args, callback=callback: function_calls.append(
                        (callback, origCallbacks[callback](*args))))

    assert len(function_calls) == 0

    with FilterEnvironment(
            "pyfile://" + os.path.dirname(__file__) +
            "/../interface/SimpleStaticFilter.py", "SimpleStaticFilter",
            PropertyCollectionImpl("root", None)) as staticPyFilter:
        f = staticPyFilter.getPlugin()
        origCallbacks = dict(onInit=f.onInit,
                             onOpen=f.onOpen,
                             onStart=f.onStart,
                             onStop=f.onStop,
                             onClose=f.onClose,
                             onDeinit=f.onDeinit,
                             onPortDataChanged=f.onPortDataChanged)
        for callback in origCallbacks:
            setattr(f,
                    callback,
                    lambda *args, callback=callback: function_calls.append(
                        (callback, origCallbacks[callback](*args))))
        staticPyFilter.init()

    assert function_calls == [("onInit", None), ("onDeinit", None)]
    function_calls.clear()

    with FilterEnvironment(
            "pyfile://" + os.path.dirname(__file__) +
            "/../interface/SimpleStaticFilter.py", "SimpleStaticFilter",
            PropertyCollectionImpl("root", None)) as staticPyFilter:
        f = staticPyFilter.getPlugin()
        origCallbacks = dict(onInit=f.onInit,
                             onStart=f.onStart,
                             onOpen=f.onOpen,
                             onStop=f.onStop,
                             onClose=f.onClose,
                             onDeinit=f.onDeinit,
                             onPortDataChanged=f.onPortDataChanged)
        for callback in origCallbacks:
            setattr(f,
                    callback,
                    lambda *args, callback=callback: function_calls.append(
                        (callback, origCallbacks[callback](*args))))
        staticPyFilter.init()
        staticPyFilter.open()
        staticPyFilter.start()

    assert function_calls == [("onInit", None), ("onOpen", None),
                              ("onStart", None), ("onStop", None),
                              ("onClose", None), ("onDeinit", None)]
    function_calls.clear()

    expect_exception(FilterEnvironment, "weird.plugin.extension", "factory",
                     PropertyCollectionImpl("root", None))