diff --git a/force_bdss/api.py b/force_bdss/api.py index df58bc814e062d2c78479dd3512b6b5d359ebe1e..165dd9bdc1869e369d2f0e80a2669224947941d4 100644 --- a/force_bdss/api.py +++ b/force_bdss/api.py @@ -4,12 +4,26 @@ from .ids import plugin_id, factory_id # noqa from .core.data_value import DataValue # noqa from .core.workflow import Workflow # noqa from .core.slot import Slot # noqa +from .core.i_factory import IFactory # noqa +from .core.input_slot_info import InputSlotInfo # noqa +from .core.output_slot_info import OutputSlotInfo # noqa +from .core.kpi_specification import KPISpecification # noqa +from .core.execution_layer import ExecutionLayer # noqa +from .core.verifier import verify_workflow # noqa +from .core.verifier import VerifierError # noqa from .data_sources.base_data_source_model import BaseDataSourceModel # noqa from .data_sources.base_data_source import BaseDataSource # noqa from .data_sources.base_data_source_factory import BaseDataSourceFactory # noqa from .data_sources.i_data_source_factory import IDataSourceFactory # noqa +from .factory_registry_plugin import IFactoryRegistryPlugin # noqa +from .factory_registry_plugin import FactoryRegistryPlugin # noqa + +from .io.workflow_reader import WorkflowReader # noqa +from .io.workflow_reader import InvalidFileException # noqa +from .io.workflow_writer import WorkflowWriter # noqa + from .mco.base_mco_model import BaseMCOModel # noqa from .mco.base_mco_communicator import BaseMCOCommunicator # noqa from .mco.base_mco import BaseMCO # noqa diff --git a/force_bdss/core/base_factory.py b/force_bdss/core/base_factory.py new file mode 100644 index 0000000000000000000000000000000000000000..e8daa730591b65ebe6cc89398cc95e59d1cd50af --- /dev/null +++ b/force_bdss/core/base_factory.py @@ -0,0 +1,55 @@ +from envisage.plugin import Plugin +from traits.api import HasStrictTraits, Str, Instance + +from force_bdss.ids import factory_id + + +class BaseFactory(HasStrictTraits): + #: Unique identifier that identifies the factory uniquely in the + #: universe of factories. Create one with the function factory_id() + id = Str() + + #: A human readable name of the factory. Spaces allowed + name = Str() + + #: Reference to the plugin that carries this factory + #: This is automatically set by the system. you should not define it + #: in your subclass. + plugin = Instance(Plugin, allow_none=False) + + def __init__(self, plugin, *args, **kwargs): + super(BaseFactory, self).__init__(plugin=plugin, *args, **kwargs) + + self.name = self.get_name() + identifier = self.get_identifier() + try: + id = self._global_id(identifier) + except ValueError: + raise ValueError( + "Invalid identifier {} returned by " + "{}.get_identifier()".format( + identifier, + self.__class__.__name__ + ) + ) + self.id = id + + def get_name(self): + """Must be reimplemented to return a user-visible name of the + data source. + """ + raise NotImplementedError( + "get_name was not implemented in factory {}".format( + self.__class__)) + + def get_identifier(self): + """Must be reimplemented to return a unique string identifying + the factory. The provider is responsible to guarantee this identifier + to be unique across the plugin data sources. + """ + raise NotImplementedError( + "get_identifier was not implemented in factory {}".format( + self.__class__)) + + def _global_id(self, identifier): + return factory_id(self.plugin.id, identifier) diff --git a/force_bdss/core/i_factory.py b/force_bdss/core/i_factory.py new file mode 100644 index 0000000000000000000000000000000000000000..d874885d7088409d7069bf677797bc64eea77774 --- /dev/null +++ b/force_bdss/core/i_factory.py @@ -0,0 +1,21 @@ +from envisage.plugin import Plugin +from traits.api import Interface, Str, Instance + + +class IFactory(Interface): + """Envisage required interface for the BaseDataSourceFactory. + You should not need to use this directly. + + Refer to the BaseDataSourceFactory for documentation. + """ + id = Str() + + name = Str() + + plugin = Instance(Plugin, allow_none=False) + + def get_name(self): + pass + + def get_identifier(self): + pass diff --git a/force_bdss/data_sources/base_data_source_factory.py b/force_bdss/data_sources/base_data_source_factory.py index 6c28f56729b976ad8061b5c2318dc7394fca2407..3793fb49aeb8a1754b7f38bc5878625c4d9e050d 100644 --- a/force_bdss/data_sources/base_data_source_factory.py +++ b/force_bdss/data_sources/base_data_source_factory.py @@ -1,17 +1,16 @@ import logging -from traits.api import provides, Str, Instance, Type, HasStrictTraits -from envisage.plugin import Plugin +from traits.api import provides, Type +from force_bdss.core.base_factory import BaseFactory from force_bdss.data_sources.base_data_source import BaseDataSource from force_bdss.data_sources.base_data_source_model import BaseDataSourceModel from force_bdss.data_sources.i_data_source_factory import IDataSourceFactory -from force_bdss.ids import factory_id log = logging.getLogger(__name__) @provides(IDataSourceFactory) -class BaseDataSourceFactory(HasStrictTraits): +class BaseDataSourceFactory(BaseFactory): """Base class for DataSource factories. Reimplement this class to create your own DataSource. @@ -32,13 +31,6 @@ class BaseDataSourceFactory(HasStrictTraits): """ # NOTE: changes to this class must be ported also to the IDataSourceFactory - #: Unique identifier that identifies the factory uniquely in the - #: universe of factories. Create one with the function factory_id() - id = Str() - - #: A human readable name of the factory. Spaces allowed - name = Str() - #: The data source to be instantiated. Define this to your DataSource data_source_class = Type(BaseDataSource, allow_none=False) @@ -46,30 +38,14 @@ class BaseDataSourceFactory(HasStrictTraits): #: Define this to your DataSourceModel model_class = Type(BaseDataSourceModel, allow_none=False) - #: Reference to the plugin that carries this factory - #: This is automatically set by the system. you should not define it - #: in your subclass. - plugin = Instance(Plugin, allow_none=False) - def __init__(self, plugin, *args, **kwargs): - self.plugin = plugin - super(BaseDataSourceFactory, self).__init__(*args, **kwargs) + super(BaseDataSourceFactory, self).__init__( + plugin=plugin, + *args, + **kwargs) self.data_source_class = self.get_data_source_class() self.model_class = self.get_model_class() - self.name = self.get_name() - identifier = self.get_identifier() - try: - id = factory_id(self.plugin.id, identifier) - except ValueError: - raise ValueError( - "Invalid identifier {} returned by " - "{}.get_identifier()".format( - identifier, - self.__class__.__name__ - ) - ) - self.id = id def get_data_source_class(self): """Must be reimplemented to return the DataSource class. @@ -85,23 +61,6 @@ class BaseDataSourceFactory(HasStrictTraits): "get_model_class was not implemented in factory {}".format( self.__class__)) - def get_name(self): - """Must be reimplemented to return a user-visible name of the - data source. - """ - raise NotImplementedError( - "get_name was not implemented in factory {}".format( - self.__class__)) - - def get_identifier(self): - """Must be reimplemented to return a unique string identifying - the factory. The provider is responsible to guarantee this identifier - to be unique across the plugin data sources. - """ - raise NotImplementedError( - "get_identifier was not implemented in factory {}".format( - self.__class__)) - def create_data_source(self): """Factory method. Must return the factory-specific BaseDataSource instance. diff --git a/force_bdss/data_sources/i_data_source_factory.py b/force_bdss/data_sources/i_data_source_factory.py index bbeb624bbff2d50a83ebb2a6aca64588afa2fa0e..d7a7ffe7bcb2739599a24228eab257b3683e78bd 100644 --- a/force_bdss/data_sources/i_data_source_factory.py +++ b/force_bdss/data_sources/i_data_source_factory.py @@ -1,17 +1,14 @@ -from envisage.api import Plugin -from traits.api import Interface, Str, Instance, Type +from traits.api import Type +from force_bdss.core.i_factory import IFactory -class IDataSourceFactory(Interface): + +class IDataSourceFactory(IFactory): """Envisage required interface for the BaseDataSourceFactory. You should not need to use this directly. Refer to the BaseDataSourceFactory for documentation. """ - id = Str() - - name = Str() - data_source_class = Type( "force_bdss.data_sources.base_data_source.BaseDataSource", allow_none=False @@ -22,16 +19,8 @@ class IDataSourceFactory(Interface): allow_none=False ) - plugin = Instance(Plugin, allow_none=False) - def get_data_source_class(self): pass def get_model_class(self): pass - - def get_name(self): - pass - - def get_identifier(self): - pass diff --git a/force_bdss/mco/base_mco_factory.py b/force_bdss/mco/base_mco_factory.py index c8236553961461ff3e502e0fab73a5abccf10644..58544172c2e1fa43b3575fcbcaffc0b5304f0a11 100644 --- a/force_bdss/mco/base_mco_factory.py +++ b/force_bdss/mco/base_mco_factory.py @@ -1,8 +1,7 @@ import logging -from traits.api import HasStrictTraits, Str, provides, Instance, Type -from envisage.plugin import Plugin +from traits.api import provides, Type -from force_bdss.ids import factory_id +from force_bdss.core.base_factory import BaseFactory from force_bdss.mco.base_mco import BaseMCO from force_bdss.mco.base_mco_communicator import BaseMCOCommunicator from force_bdss.mco.base_mco_model import BaseMCOModel @@ -12,17 +11,11 @@ log = logging.getLogger(__name__) @provides(IMCOFactory) -class BaseMCOFactory(HasStrictTraits): +class BaseMCOFactory(BaseFactory): """Base class for the MultiCriteria Optimizer factory. """ # NOTE: any changes to the interface of this class must be replicated - # in the IMultiCriteriaOptimizerFactory interface class. - - #: A unique ID produced with the factory_id() routine. - id = Str() - - #: A user friendly name of the factory. Spaces allowed. - name = Str() + # in the IMCOFactory interface class. #: The optimizer class to instantiate. Define this to your MCO class. optimizer_class = Type(BaseMCO, allow_none=False) @@ -33,29 +26,15 @@ class BaseMCOFactory(HasStrictTraits): #: The communicator associated to the MCO. Define this to your MCO comm. communicator_class = Type(BaseMCOCommunicator, allow_none=False) - #: A reference to the Plugin that holds this factory. - plugin = Instance(Plugin, allow_none=False) - def __init__(self, plugin, *args, **kwargs): - self.plugin = plugin - super(BaseMCOFactory, self).__init__(*args, **kwargs) + super(BaseMCOFactory, self).__init__( + plugin=plugin, + *args, + **kwargs) - self.name = self.get_name() self.optimizer_class = self.get_optimizer_class() self.model_class = self.get_model_class() self.communicator_class = self.get_communicator_class() - identifier = self.get_identifier() - try: - id = factory_id(self.plugin.id, identifier) - except ValueError: - raise ValueError( - "Invalid identifier {} returned by " - "{}.get_identifier()".format( - identifier, - self.__class__.__name__ - ) - ) - self.id = id def get_optimizer_class(self): raise NotImplementedError( @@ -72,16 +51,6 @@ class BaseMCOFactory(HasStrictTraits): "get_communicator_class was not implemented in factory {}".format( self.__class__)) - def get_identifier(self): - raise NotImplementedError( - "get_identifier was not implemented in factory {}".format( - self.__class__)) - - def get_name(self): - raise NotImplementedError( - "get_name was not implemented in factory {}".format( - self.__class__)) - def create_optimizer(self): """Factory method. Creates the optimizer with the given application @@ -135,3 +104,4 @@ class BaseMCOFactory(HasStrictTraits): ------- List of BaseMCOParameterFactory """ + return [] diff --git a/force_bdss/mco/i_mco_factory.py b/force_bdss/mco/i_mco_factory.py index 97910175fcdece2ded473bea73e9cb5d9c40b0c3..9e2787019a7061115c5a7cefa8710006e091941d 100644 --- a/force_bdss/mco/i_mco_factory.py +++ b/force_bdss/mco/i_mco_factory.py @@ -1,17 +1,14 @@ -from traits.api import Interface, Str, Instance, Type -from envisage.plugin import Plugin +from traits.api import Type +from force_bdss.core.i_factory import IFactory -class IMCOFactory(Interface): + +class IMCOFactory(IFactory): """Interface for the BaseMCOFactory. You should not need it, as its main use is for envisage support. Refer to BaseMCOFactory for documentation """ - id = Str() - - name = Str() - optimizer_class = Type( "force_bdss.mco.base_mco.BaseMCO", allow_none=False @@ -28,8 +25,6 @@ class IMCOFactory(Interface): allow_none=False ) - plugin = Instance(Plugin, allow_none=False) - def get_model_class(self): pass @@ -39,12 +34,6 @@ class IMCOFactory(Interface): def get_optimizer_class(self): pass - def get_identifier(self): - pass - - def get_name(self): - pass - def create_optimizer(self): pass diff --git a/force_bdss/mco/parameters/base_mco_parameter_factory.py b/force_bdss/mco/parameters/base_mco_parameter_factory.py index 63d91fd698526ea1961e1b0e579280b863ae9bde..bc3a0fd078c0001f12267f426a22e363231815fa 100644 --- a/force_bdss/mco/parameters/base_mco_parameter_factory.py +++ b/force_bdss/mco/parameters/base_mco_parameter_factory.py @@ -1,9 +1,13 @@ -from traits.api import HasStrictTraits, Str, Type, Instance +from traits.api import Str, Type, Instance, provides +from force_bdss.core.base_factory import BaseFactory from force_bdss.ids import mco_parameter_id +from force_bdss.mco.parameters.i_mco_parameter_factory import \ + IMCOParameterFactory -class BaseMCOParameterFactory(HasStrictTraits): +@provides(IMCOParameterFactory) +class BaseMCOParameterFactory(BaseFactory): """Factory that produces the model instance of a given BASEMCOParameter instance. @@ -16,12 +20,6 @@ class BaseMCOParameterFactory(HasStrictTraits): mco_factory = Instance('force_bdss.mco.base_mco_factory.BaseMCOFactory', allow_none=False) - #: A unique string identifying the parameter - id = Str() - - #: A user friendly name (for the UI) - name = Str() - #: A long description of the parameter description = Str() @@ -31,16 +29,6 @@ class BaseMCOParameterFactory(HasStrictTraits): allow_none=False ) - def get_identifier(self): - raise NotImplementedError( - "get_identifier was not implemented in factory {}".format( - self.__class__)) - - def get_name(self): - raise NotImplementedError( - "get_name was not implemented in factory {}".format( - self.__class__)) - def get_description(self): raise NotImplementedError( "get_description was not implemented in factory {}".format( @@ -52,24 +40,14 @@ class BaseMCOParameterFactory(HasStrictTraits): self.__class__)) def __init__(self, mco_factory, *args, **kwargs): - self.mco_factory = mco_factory - super(BaseMCOParameterFactory, self).__init__(*args, **kwargs) + super(BaseMCOParameterFactory, self).__init__( + mco_factory=mco_factory, + plugin=mco_factory.plugin, + *args, + **kwargs) - self.name = self.get_name() self.description = self.get_description() self.model_class = self.get_model_class() - identifier = self.get_identifier() - try: - id = mco_parameter_id(self.mco_factory.id, identifier) - except ValueError: - raise ValueError( - "Invalid identifier {} returned by " - "{}.get_identifier()".format( - identifier, - self.__class__.__name__ - ) - ) - self.id = id def create_model(self, data_values=None): """Creates the instance of the model class and returns it. @@ -90,3 +68,6 @@ class BaseMCOParameterFactory(HasStrictTraits): data_values = {} return self.model_class(factory=self, **data_values) + + def _global_id(self, identifier): + return mco_parameter_id(self.mco_factory.id, identifier) diff --git a/force_bdss/mco/parameters/i_mco_parameter_factory.py b/force_bdss/mco/parameters/i_mco_parameter_factory.py new file mode 100644 index 0000000000000000000000000000000000000000..8aa646f50c7d6963bbf79efe806160b869262e65 --- /dev/null +++ b/force_bdss/mco/parameters/i_mco_parameter_factory.py @@ -0,0 +1,23 @@ +from traits.api import Instance, Type, Str +from force_bdss.core.i_factory import IFactory + + +class IMCOParameterFactory(IFactory): + mco_factory = Instance('force_bdss.mco.base_mco_factory.BaseMCOFactory', + allow_none=False) + + description = Str() + + model_class = Type( + "force_bdss.mco.parameters.base_mco_parameter.BaseMCOParameter", + allow_none=False + ) + + def get_description(self): + """""" + + def get_model_class(self): + """""" + + def create_model(self, data_values=None): + """""" diff --git a/force_bdss/notification_listeners/base_notification_listener_factory.py b/force_bdss/notification_listeners/base_notification_listener_factory.py index 0104558a50d9d5b2783b41643cf78a323964ae3a..2b556a9f4d4f07d574630b91999e32916df5dbaa 100644 --- a/force_bdss/notification_listeners/base_notification_listener_factory.py +++ b/force_bdss/notification_listeners/base_notification_listener_factory.py @@ -1,10 +1,8 @@ import logging from traits.api import ( - HasStrictTraits, Instance, Str, provides, Type, Bool + provides, Type, Bool ) -from envisage.plugin import Plugin - -from force_bdss.ids import factory_id +from force_bdss.core.base_factory import BaseFactory from force_bdss.notification_listeners.base_notification_listener import \ BaseNotificationListener from force_bdss.notification_listeners.base_notification_listener_model \ @@ -16,17 +14,11 @@ log = logging.getLogger(__name__) @provides(INotificationListenerFactory) -class BaseNotificationListenerFactory(HasStrictTraits): +class BaseNotificationListenerFactory(BaseFactory): """Base class for notification listeners. Notification listeners are extensions that receive event notifications from the MCO and perform an associated action. """ - #: identifier of the factory - id = Str() - - #: Name of the factory. User friendly for UI - name = Str() - #: If the factor should be visible in the UI. Set to false to make it #: invisible. This is normally useful for notification systems that are #: not supposed to be configured by the user. @@ -40,9 +32,6 @@ class BaseNotificationListenerFactory(HasStrictTraits): #: listener model class. model_class = Type(BaseNotificationListenerModel, allow_none=False) - #: A reference to the containing plugin - plugin = Instance(Plugin, allow_none=False) - def __init__(self, plugin, *args, **kwargs): """Initializes the instance. @@ -51,25 +40,11 @@ class BaseNotificationListenerFactory(HasStrictTraits): plugin: Plugin The plugin that holds this factory. """ - self.plugin = plugin - super(BaseNotificationListenerFactory, self).__init__(*args, **kwargs) + super(BaseNotificationListenerFactory, self).__init__( + plugin=plugin, *args, **kwargs) self.listener_class = self.get_listener_class() self.model_class = self.get_model_class() - self.name = self.get_name() - identifier = self.get_identifier() - try: - id = factory_id(self.plugin.id, identifier) - except ValueError: - raise ValueError( - "Invalid identifier {} returned by " - "{}.get_identifier()".format( - identifier, - self.__class__.__name__ - ) - ) - - self.id = id def get_listener_class(self): raise NotImplementedError( @@ -81,16 +56,6 @@ class BaseNotificationListenerFactory(HasStrictTraits): "get_model_class was not implemented in factory {}".format( self.__class__)) - def get_identifier(self): - raise NotImplementedError( - "get_identifier was not implemented in factory {}".format( - self.__class__)) - - def get_name(self): - raise NotImplementedError( - "get_name was not implemented in factory {}".format( - self.__class__)) - def create_listener(self): """ Creates an instance of the listener. diff --git a/force_bdss/notification_listeners/i_notification_listener_factory.py b/force_bdss/notification_listeners/i_notification_listener_factory.py index 34746855cc191c0c65b8e8ea5aa5c8a7cdd7bfb7..86b80029b49055bcd589c14392752a8863d8177c 100644 --- a/force_bdss/notification_listeners/i_notification_listener_factory.py +++ b/force_bdss/notification_listeners/i_notification_listener_factory.py @@ -1,17 +1,14 @@ -from traits.api import Interface, String, Instance, Type, Bool -from envisage.plugin import Plugin +from traits.api import Type, Bool +from force_bdss.core.i_factory import IFactory -class INotificationListenerFactory(Interface): + +class INotificationListenerFactory(IFactory): """Envisage required interface for the BaseNotificationListenerFactory. You should not need to use this directly. Refer to the BaseNotificationListenerFactory for documentation. """ - id = String() - - name = String() - ui_visible = Bool() listener_class = Type( @@ -26,14 +23,6 @@ class INotificationListenerFactory(Interface): allow_none=False ) - plugin = Instance(Plugin, allow_none=False) - - def get_name(self): - pass - - def get_identifier(self): - pass - def get_model_class(self): pass diff --git a/force_bdss/ui_hooks/base_ui_hooks_factory.py b/force_bdss/ui_hooks/base_ui_hooks_factory.py index 2aaa66c5b067cb47c20a68b5aa1caa9ff57d594a..174794239801f362e5508135f5ec15b06b1164d4 100644 --- a/force_bdss/ui_hooks/base_ui_hooks_factory.py +++ b/force_bdss/ui_hooks/base_ui_hooks_factory.py @@ -1,8 +1,7 @@ import logging -from traits.api import HasStrictTraits, Instance, Str, provides, Type -from envisage.plugin import Plugin +from traits.api import provides, Type -from force_bdss.ids import factory_id +from force_bdss.core.base_factory import BaseFactory from force_bdss.ui_hooks.base_ui_hooks_manager import BaseUIHooksManager from .i_ui_hooks_factory import IUIHooksFactory @@ -10,24 +9,15 @@ log = logging.getLogger(__name__) @provides(IUIHooksFactory) -class BaseUIHooksFactory(HasStrictTraits): +class BaseUIHooksFactory(BaseFactory): """Base class for UIHooksFactory. UI Hooks are extensions that perform actions associated to specific moments of the UI lifetime. """ - #: identifier of the factory - id = Str() - - #: Name of the factory. User friendly for UI - name = Str() - #: The UI Hooks manager class to instantiate. Define this to your #: base hook managers. ui_hooks_manager_class = Type(BaseUIHooksManager, allow_none=False) - #: A reference to the containing plugin - plugin = Instance(Plugin, allow_none=False) - def __init__(self, plugin, *args, **kwargs): """Initializes the instance. @@ -36,23 +26,10 @@ class BaseUIHooksFactory(HasStrictTraits): plugin: Plugin The plugin that holds this factory. """ - self.plugin = plugin - super(BaseUIHooksFactory, self).__init__(*args, **kwargs) + super(BaseUIHooksFactory, self).__init__( + plugin=plugin, *args, **kwargs) self.ui_hooks_manager_class = self.get_ui_hooks_manager_class() - self.name = self.get_name() - identifier = self.get_identifier() - try: - id = factory_id(self.plugin.id, identifier) - except ValueError: - raise ValueError( - "Invalid identifier {} returned by " - "{}.get_identifier()".format( - identifier, - self.__class__.__name__ - ) - ) - self.id = id def get_ui_hooks_manager_class(self): raise NotImplementedError( @@ -60,16 +37,6 @@ class BaseUIHooksFactory(HasStrictTraits): "in factory {}".format( self.__class__)) - def get_name(self): - raise NotImplementedError( - "get_name was not implemented in factory {}".format( - self.__class__)) - - def get_identifier(self): - raise NotImplementedError( - "get_identifier was not implemented in factory {}".format( - self.__class__)) - def create_ui_hooks_manager(self): """Creates an instance of the hook manager. The hooks manager contains a set of methods that are applicable in diff --git a/force_bdss/ui_hooks/i_ui_hooks_factory.py b/force_bdss/ui_hooks/i_ui_hooks_factory.py index c809f3aa0d1d4b87bd809d144bfe0cb143d3cd23..fcdf7059b5baecbb013b715db811358ac1d41656 100644 --- a/force_bdss/ui_hooks/i_ui_hooks_factory.py +++ b/force_bdss/ui_hooks/i_ui_hooks_factory.py @@ -1,21 +1,18 @@ -from traits.api import Interface, Str, Instance, Type +from traits.api import Instance, Type from envisage.plugin import Plugin +from force_bdss.core.i_factory import IFactory -class IUIHooksFactory(Interface): + +class IUIHooksFactory(IFactory): """Envisage required interface for the BaseUIHooksFactory. You should not need to use this directly. Refer to the BaseUIHooksFactory for documentation. """ - id = Str() - - name = Str() - ui_hooks_manager_class = Type( "force_bdss.ui_hooks.base_ui_hooks_manager.BaseUIHooksManager", allow_none=False - ) plugin = Instance(Plugin, allow_none=False) @@ -23,11 +20,5 @@ class IUIHooksFactory(Interface): def get_ui_hooks_manager_class(self): pass - def get_name(self): - pass - - def get_identifier(self): - pass - def create_ui_hooks_manager(self): pass