コード例 #1
0
ファイル: io.py プロジェクト: rowhit/napari
def _write_multiple_layers_with_plugins(
    path: str,
    layers: List[Layer],
    *,
    plugin_name: Optional[str] = None,
    plugin_manager=napari_plugin_manager,
) -> List[str]:
    """Write data from multiple layers data with a plugin.

    If a ``plugin_name`` is not provided we loop through plugins to find the
    first one that knows how to handle the combination of layers and is able to
    write the file. If no plugins offer ``napari_get_writer`` for that
    combination of layers then the default ``napari_get_writer`` will create a
    folder and call ``napari_write_<layer>`` for each layer using the
    ``layer.name`` variable to modify the path such that the layers are written
    to unique files in the folder.

    If a ``plugin_name`` is provided, then call ``napari_get_writer`` for that
    plugin. If it doesn’t return a ``WriterFunction`` we error, otherwise we
    call it and if that fails if it we error.

    Exceptions will be caught and stored as PluginErrors
    (in plugins.exceptions.PLUGIN_ERRORS)

    Parameters
    ----------
    path : str
        The path (file, directory, url) to write.
    layers : List of napari.layers.Layer
        List of napari layers to write.
    plugin_name : str, optional
        If provided, force the plugin manager to use the ``napari_get_writer``
        from the requested ``plugin_name``.  If none is available, or if it is
        incapable of handling the layers, this function will fail.
    plugin_manager : plugins.PluginManager, optional
        Instance of a PluginManager.  by default the main napari
        plugin_manager will be used.

    Returns
    -------
    list of str
        A list of filenames, if any, that were written.
    """
    layer_data = [layer.as_layer_data_tuple() for layer in layers]
    layer_types = [ld[2] for ld in layer_data]

    hook_caller = plugin_manager.hook.napari_get_writer
    path = abspath_or_url(path)
    if plugin_name:
        # if plugin has been specified we just directly call napari_get_writer
        # with that plugin_name.
        if plugin_name not in plugin_manager.plugins:
            names = {i.plugin_name for i in hook_caller.get_hookimpls()}
            raise ValueError(
                f"There is no registered plugin named '{plugin_name}'.\n"
                f"Names of plugins offering writers are: {names}"
            )
        implementation = hook_caller.get_plugin_implementation(plugin_name)
        writer_function = hook_caller(
            _plugin=plugin_name, path=path, layer_types=layer_types
        )
    else:
        result = hook_caller.call_with_result_obj(
            path=path, layer_types=layer_types, _return_impl=True
        )
        writer_function = result.result
        implementation = result.implementation

    if not callable(writer_function):
        if plugin_name:
            msg = f'Requested plugin "{plugin_name}" is not capable'
        else:
            msg = 'Unable to find plugin capable'
        msg += f' of writing this combination of layer types: {layer_types}'
        raise ValueError(msg)

    try:
        return writer_function(abspath_or_url(path), layer_data)
    except Exception as exc:
        raise PluginCallError(implementation, cause=exc)
コード例 #2
0
ファイル: io.py プロジェクト: rowhit/napari
def read_data_with_plugins(
    path: Union[str, Sequence[str]],
    plugin: Optional[str] = None,
    plugin_manager: PluginManager = napari_plugin_manager,
) -> List[LayerData]:
    """Iterate reader hooks and return first non-None LayerData or None.

    This function returns as soon as the path has been read successfully,
    while catching any plugin exceptions, storing them for later retrievial,
    providing useful error messages, and relooping until either layer data is
    returned, or no valid readers are found.

    Exceptions will be caught and stored as PluginErrors
    (in plugins.exceptions.PLUGIN_ERRORS)

    Parameters
    ----------
    path : str
        The path (file, directory, url) to open
    plugin : str, optional
        Name of a plugin to use.  If provided, will force ``path`` to be read
        with the specified ``plugin``.  If the requested plugin cannot read
        ``path``, a PluginCallError will be raised.
    plugin_manager : plugins.PluginManager, optional
        Instance of a napari PluginManager.  by default the main napari
        plugin_manager will be used.

    Returns
    -------
    LayerData : list of tuples, or None
        LayerData that can be passed to :func:`Viewer._add_layer_from_data()
        <napari.components.viewer_model.ViewerModel._add_layer_from_data>`.
        ``LayerData`` is a list tuples, where each tuple is one of
        ``(data,)``, ``(data, meta)``, or ``(data, meta, layer_type)`` .

        If no reader plugins are (or they all error), returns ``None``

    Raises
    ------
    PluginCallError
        If ``plugin`` is specified but raises an Exception while reading.
    """
    hook_caller = plugin_manager.hook.napari_get_reader

    if plugin:
        if plugin not in plugin_manager.plugins:
            names = {i.plugin_name for i in hook_caller.get_hookimpls()}
            raise ValueError(
                f"There is no registered plugin named '{plugin}'.\n"
                f"Names of plugins offering readers are: {names}"
            )
        reader = hook_caller._call_plugin(plugin, path=path)
        if not callable(reader):
            raise ValueError(f'Plugin {plugin!r} does not support file {path}')
        return reader(path) or []

    errors: List[PluginCallError] = []
    path = abspath_or_url(path)
    skip_impls: List[HookImplementation] = []
    layer_data = None
    while True:
        result = hook_caller.call_with_result_obj(
            path=path, _skip_impls=skip_impls
        )
        reader = result.result  # will raise exceptions if any occurred
        if not reader:
            # we're all out of reader plugins
            break
        try:
            layer_data = reader(path)  # try to read data
            if layer_data:
                break
        except Exception as exc:
            # collect the error and log it, but don't raise it.
            err = PluginCallError(result.implementation, cause=exc)
            err.log(logger=logger)
            errors.append(err)
        # don't try this impl again
        skip_impls.append(result.implementation)

    if not layer_data:
        # if layer_data is empty, it means no plugin could read path
        # we just want to provide some useful feedback, which includes
        # whether or not paths were passed to plugins as a list.
        if isinstance(path, (tuple, list)):
            path_repr = f"[{path[0]}, ...] as stack"
        else:
            path_repr = repr(path)
        # TODO: change to a warning notification in a later PR
        raise ValueError(f'No plugin found capable of reading {path_repr}.')

    if errors:
        names = set([repr(e.plugin_name) for e in errors])
        err_msg = f"({len(errors)}) error{'s' if len(errors) > 1 else ''} "
        err_msg += f"occurred in plugins: {', '.join(names)}. "
        err_msg += 'See full error logs in "Plugins → Plugin Errors..."'
        logger.error(err_msg)

    return layer_data or []
コード例 #3
0
ファイル: io.py プロジェクト: kne42/napari
def _write_multiple_layers_with_plugins(
    path: str,
    layers: List[Layer],
    *,
    plugin_name: Optional[str] = None,
    _writer: Optional[WriterContribution] = None,
) -> List[str]:
    """Write data from multiple layers data with a plugin.

    If a ``plugin_name`` is not provided we loop through plugins to find the
    first one that knows how to handle the combination of layers and is able to
    write the file. If no plugins offer ``napari_get_writer`` for that
    combination of layers then the default ``napari_get_writer`` will create a
    folder and call ``napari_write_<layer>`` for each layer using the
    ``layer.name`` variable to modify the path such that the layers are written
    to unique files in the folder.

    If a ``plugin_name`` is provided, then call ``napari_get_writer`` for that
    plugin. If it doesn’t return a ``WriterFunction`` we error, otherwise we
    call it and if that fails if it we error.

    Exceptions will be caught and stored as PluginErrors
    (in plugins.exceptions.PLUGIN_ERRORS)

    Parameters
    ----------
    path : str
        The path (file, directory, url) to write.
    layers : List of napari.layers.Layer
        List of napari layers to write.
    plugin_name : str, optional
        If provided, force the plugin manager to use the ``napari_get_writer``
        from the requested ``plugin_name``.  If none is available, or if it is
        incapable of handling the layers, this function will fail.

    Returns
    -------
    list of str
        A list of filenames, if any, that were written.
    """

    # Try to use NPE2 first
    written_paths = _npe2.write_layers(path, layers, plugin_name, _writer)
    if written_paths:
        return written_paths
    logger.debug("Falling back to original plugin engine.")

    layer_data = [layer.as_layer_data_tuple() for layer in layers]
    layer_types = [ld[2] for ld in layer_data]

    if not plugin_name and isinstance(path, (str, pathlib.Path)):
        extension = os.path.splitext(path)[-1]
        plugin_name = plugin_manager.get_writer_for_extension(extension)

    hook_caller = plugin_manager.hook.napari_get_writer
    path = abspath_or_url(path)
    logger.debug(f"Writing to {path}.  Hook caller: {hook_caller}")
    if plugin_name:
        # if plugin has been specified we just directly call napari_get_writer
        # with that plugin_name.
        if plugin_name not in plugin_manager.plugins:
            names = {i.plugin_name for i in hook_caller.get_hookimpls()}
            raise ValueError(
                trans._(
                    "There is no registered plugin named '{plugin_name}'.\nNames of plugins offering writers are: {names}",
                    deferred=True,
                    plugin_name=plugin_name,
                    names=names,
                ))
        implementation = hook_caller.get_plugin_implementation(plugin_name)
        writer_function = hook_caller(_plugin=plugin_name,
                                      path=path,
                                      layer_types=layer_types)
    else:
        result = hook_caller.call_with_result_obj(path=path,
                                                  layer_types=layer_types,
                                                  _return_impl=True)
        writer_function = result.result
        implementation = result.implementation

    if not callable(writer_function):
        if plugin_name:
            msg = trans._(
                'Requested plugin "{plugin_name}" is not capable of writing this combination of layer types: {layer_types}',
                deferred=True,
                plugin_name=plugin_name,
                layer_types=layer_types,
            )
        else:
            msg = trans._(
                'Unable to find plugin capable of writing this combination of layer types: {layer_types}',
                deferred=True,
                layer_types=layer_types,
            )

        raise ValueError(msg)

    try:
        return writer_function(abspath_or_url(path), layer_data)
    except Exception as exc:
        raise PluginCallError(implementation, cause=exc)
コード例 #4
0
ファイル: io.py プロジェクト: kne42/napari
def read_data_with_plugins(
    paths: Sequence[str],
    plugin: Optional[str] = None,
    stack: bool = False,
) -> Tuple[Optional[List[LayerData]], Optional[HookImplementation]]:
    """Iterate reader hooks and return first non-None LayerData or None.

    This function returns as soon as the path has been read successfully,
    while catching any plugin exceptions, storing them for later retrieval,
    providing useful error messages, and re-looping until either a read
    operation was successful, or no valid readers were found.

    Exceptions will be caught and stored as PluginErrors
    (in plugins.exceptions.PLUGIN_ERRORS)

    Parameters
    ----------
    paths : str, or list of string
        The of path (file, directory, url) to open
    plugin : str, optional
        Name of a plugin to use.  If provided, will force ``path`` to be read
        with the specified ``plugin``.  If the requested plugin cannot read
        ``path``, a PluginCallError will be raised.
    stack : bool
        See `Viewer.open`

    Returns
    -------
    LayerData : list of tuples, or None
        LayerData that can be passed to :func:`Viewer._add_layer_from_data()
        <napari.components.viewer_model.ViewerModel._add_layer_from_data>`.
        ``LayerData`` is a list tuples, where each tuple is one of
        ``(data,)``, ``(data, meta)``, or ``(data, meta, layer_type)`` .

        If no reader plugins were found (or they all failed), returns ``None``

    Raises
    ------
    PluginCallError
        If ``plugin`` is specified but raises an Exception while reading.
    """
    if plugin == 'builtins':
        warnings.warn(
            'The "builtins" plugin name is deprecated and will not work in a future '
            'version. Please use "napari" instead.', )
        plugin = 'napari'

    assert isinstance(paths, list)
    if not stack:
        assert len(paths) == 1
    hookimpl: Optional[HookImplementation]

    res = _npe2.read(paths, plugin, stack=stack)
    if res is not None:
        _ld, hookimpl = res
        return [] if _is_null_layer_sentinel(
            _ld) else _ld, hookimpl  # type: ignore [return-value]

    hook_caller = plugin_manager.hook.napari_get_reader
    paths = [abspath_or_url(p, must_exist=True) for p in paths]
    if not plugin and not stack:
        extension = os.path.splitext(paths[0])[-1]
        plugin = plugin_manager.get_reader_for_extension(extension)

    # npe1 compact whether we are reading as stack or not is carried in the type
    # of paths
    npe1_path = paths if stack else paths[0]
    hookimpl = None
    if plugin:
        if plugin == 'napari':
            # napari is npe2 only
            message = trans._(
                'No plugin found capable of reading {repr_path!r}.',
                deferred=True,
                repr_path=npe1_path,
            )
            raise ValueError(message)

        if plugin not in plugin_manager.plugins:
            names = {i.plugin_name for i in hook_caller.get_hookimpls()}
            raise ValueError(
                trans._(
                    "There is no registered plugin named '{plugin}'.\nNames of plugins offering readers are: {names}",
                    deferred=True,
                    plugin=plugin,
                    names=names,
                ))
        reader = hook_caller._call_plugin(plugin, path=npe1_path)
        if not callable(reader):
            raise ValueError(
                trans._(
                    'Plugin {plugin!r} does not support file(s) {paths}',
                    deferred=True,
                    plugin=plugin,
                    paths=paths,
                ))

        hookimpl = hook_caller.get_plugin_implementation(plugin)
        layer_data = reader(npe1_path)
        # if the reader returns a "null layer" sentinel indicating an empty
        # file, return an empty list, otherwise return the result or None
        if _is_null_layer_sentinel(layer_data):
            return [], hookimpl

        return layer_data or None, hookimpl

    layer_data = None
    result = hook_caller.call_with_result_obj(path=npe1_path)
    if reader := result.result:  # will raise exceptions if any occurred
        try:
            layer_data = reader(npe1_path)  # try to read data
            hookimpl = result.implementation
        except Exception as exc:
            raise PluginCallError(result.implementation, cause=exc) from exc
コード例 #5
0
ファイル: io.py プロジェクト: guiwitz/napari
def read_data_with_plugins(
    path: Union[str, Sequence[str]],
    plugin: Optional[str] = None,
) -> Tuple[Optional[List[LayerData]], Optional[HookImplementation]]:
    """Iterate reader hooks and return first non-None LayerData or None.

    This function returns as soon as the path has been read successfully,
    while catching any plugin exceptions, storing them for later retrieval,
    providing useful error messages, and re-looping until either a read
    operation was successful, or no valid readers were found.

    Exceptions will be caught and stored as PluginErrors
    (in plugins.exceptions.PLUGIN_ERRORS)

    Parameters
    ----------
    path : str
        The path (file, directory, url) to open
    plugin : str, optional
        Name of a plugin to use.  If provided, will force ``path`` to be read
        with the specified ``plugin``.  If the requested plugin cannot read
        ``path``, a PluginCallError will be raised.

    Returns
    -------
    LayerData : list of tuples, or None
        LayerData that can be passed to :func:`Viewer._add_layer_from_data()
        <napari.components.viewer_model.ViewerModel._add_layer_from_data>`.
        ``LayerData`` is a list tuples, where each tuple is one of
        ``(data,)``, ``(data, meta)``, or ``(data, meta, layer_type)`` .

        If no reader plugins were found (or they all failed), returns ``None``

    Raises
    ------
    PluginCallError
        If ``plugin`` is specified but raises an Exception while reading.
    """
    hookimpl: Optional[HookImplementation]
    res = _npe2.read(path, plugin)
    if res is not None:
        _ld, hookimpl = res
        return [] if _is_null_layer_sentinel(_ld) else _ld, hookimpl

    hook_caller = plugin_manager.hook.napari_get_reader
    path = abspath_or_url(path)
    if not plugin and isinstance(path, (str, pathlib.Path)):
        extension = os.path.splitext(path)[-1]
        plugin = plugin_manager.get_reader_for_extension(extension)

    hookimpl = None
    if plugin:
        if plugin not in plugin_manager.plugins:
            names = {i.plugin_name for i in hook_caller.get_hookimpls()}
            raise ValueError(
                trans._(
                    "There is no registered plugin named '{plugin}'.\nNames of plugins offering readers are: {names}",
                    deferred=True,
                    plugin=plugin,
                    names=names,
                ))
        reader = hook_caller._call_plugin(plugin, path=path)
        if not callable(reader):
            raise ValueError(
                trans._(
                    'Plugin {plugin!r} does not support file {path}',
                    deferred=True,
                    plugin=plugin,
                    path=path,
                ))

        hookimpl = hook_caller.get_plugin_implementation(plugin)
        layer_data = reader(path)
        # if the reader returns a "null layer" sentinel indicating an empty
        # file, return an empty list, otherwise return the result or None
        if _is_null_layer_sentinel(layer_data):
            return [], hookimpl

        return layer_data or None, hookimpl

    errors: List[PluginCallError] = []
    skip_impls: List[HookImplementation] = []
    layer_data = None
    while True:
        result = hook_caller.call_with_result_obj(path=path,
                                                  _skip_impls=skip_impls)
        reader = result.result  # will raise exceptions if any occurred
        if not reader:
            # we're all out of reader plugins
            break
        try:
            layer_data = reader(path)  # try to read data
            if layer_data:
                hookimpl = result.implementation
                break
        except Exception as exc:
            # collect the error and log it, but don't raise it.
            err = PluginCallError(result.implementation, cause=exc)
            err.log(logger=logger)
            errors.append(err)
        # don't try this impl again
        skip_impls.append(result.implementation)

    if not layer_data:
        # if layer_data is empty, it means no plugin could read path
        # we just want to provide some useful feedback, which includes
        # whether or not paths were passed to plugins as a list.
        if isinstance(path, (tuple, list)):
            message = trans._(
                'No plugin found capable of reading [{repr_path}, ...] as stack.',
                deferred=True,
                repr_path=path[0],
            )
        else:
            message = trans._(
                'No plugin found capable of reading {repr_path}.',
                deferred=True,
                repr_path=repr(path),
            )

        # TODO: change to a warning notification in a later PR
        raise ValueError(message)

    if errors:
        names = {repr(e.plugin_name) for e in errors}
        err_msg = f"({len(errors)}) error{'s' if len(errors) > 1 else ''} "
        err_msg += f"occurred in plugins: {', '.join(names)}. "
        err_msg += 'See full error logs in "Plugins → Plugin Errors..."'
        logger.error(err_msg)

    # if the reader returns a "null layer" sentinel indicating an empty file,
    # return an empty list, otherwise return the result or None
    _data = [] if _is_null_layer_sentinel(layer_data) else layer_data or None
    return _data, hookimpl
コード例 #6
0
ファイル: io.py プロジェクト: helen330/napari
def read_data_with_plugins(
    path: Union[str, Sequence[str]],
    plugin: Optional[str] = None,
    plugin_manager=napari_plugin_manager,
) -> Optional[LayerData]:
    """Iterate reader hooks and return first non-None LayerData or None.

    This function returns as soon as the path has been read successfully,
    while catching any plugin exceptions, storing them for later retrievial,
    providing useful error messages, and relooping until either layer data is
    returned, or no valid readers are found.

    Exceptions will be caught and stored as PluginErrors
    (in plugins.exceptions.PLUGIN_ERRORS)

    Parameters
    ----------
    path : str
        The path (file, directory, url) to open
    plugin : str, optional
        Name of a plugin to use.  If provided, will force ``path`` to be read
        with the specified ``plugin``.  If the requested plugin cannot read
        ``path``, a PluginCallError will be raised.
    plugin_manager : plugins.PluginManager, optional
        Instance of a napari PluginManager.  by default the main napari
        plugin_manager will be used.

    Returns
    -------
    LayerData : list of tuples, or None
        LayerData that can be passed to :func:`Viewer._add_layer_from_data()
        <napari.components.add_layers_mixin.AddLayersMixin._add_layer_from_data>`.
        ``LayerData`` is a list tuples, where each tuple is one of
        ``(data,)``, ``(data, meta)``, or ``(data, meta, layer_type)`` .

        If no reader plugins are (or they all error), returns ``None``

    Raises
    ------
    PluginCallError
        If ``plugin`` is specified but raises an Exception while reading.
    """
    hook_caller = plugin_manager.hook.napari_get_reader

    if plugin:
        reader = hook_caller._call_plugin(plugin, path=path)
        return reader(path)

    path = abspath_or_url(path)
    skip_impls: List[HookImplementation] = []
    while True:
        result = hook_caller.call_with_result_obj(path=path,
                                                  _skip_impls=skip_impls)
        reader = result.result  # will raise exceptions if any occured
        if not reader:
            # we're all out of reader plugins
            return None
        try:
            return reader(path)  # try to read data
        except Exception as exc:
            err = PluginCallError(result.implementation, cause=exc)
            # don't try this impl again
            skip_impls.append(result.implementation)
            if result.implementation != 'builtins':
                # If builtins doesn't work, they will get a "no reader" found
                # error anyway, so it looks a bit weird to show them that the
                # "builtin plugin" didn't work.
                err.log(logger=logger)