Example #1
0
    def from_uri(cls, uri):
        """
        Creates a ``GirderDataElement`` from ``uri``.

        The parsing is accomplished by passing ``uri`` to ``urlparse``.
        This allows for quite a bit of flexibility in the types of URIs that
        can be passed. Valid Girder URIs are:

        (a) girder://token:<token>@<api_url>/file/<file_id>
        (b) girder://api_key:<api_key>@<api_url>/file/<file_id>

        Currently, only GirderDataElements can be built from Files, so the
        URL should end in /file/{id}.

        :return: Data element created from ``uri``
        :rtype: GirderDataElement

        :raises ValueError: An invalid URI was passed.

        :raises AssertionError: If the path parsed from a URI of the form (a)
            does not have '/file/' as its penultimate location before the
            identifier.

        """
        # urlparse seems to not be phased by the 'girder' protocol instead of
        # http, so no replacing needs to be done.
        parsed_uri = urlparse(uri)
        if parsed_uri.scheme != 'girder':
            raise InvalidUriError(
                uri, 'Invalid Girder URI. Girder URIs must '
                'start with girder://')

        if not parsed_uri.netloc:
            raise InvalidUriError(uri, 'No parsed netloc from given URI.')

        token = api_key = None

        if '@' in parsed_uri.netloc:
            credentials, scheme = parsed_uri.netloc.split('@')
            cred_type, cred = credentials.split(':')

            if cred_type == 'token':
                token = cred
            elif cred_type == 'api_key':
                api_key = cred
        else:
            scheme = parsed_uri.netloc

        try:
            path, file_id = parsed_uri.path.split('/file/')
        except ValueError:
            raise InvalidUriError(
                uri, 'Invalid Girder URI. Girder URIs must '
                'contain a /file/<file_id> segment.')

        return cls(file_id, '%s%s' % (scheme, path), api_key, token)
Example #2
0
    def from_uri(cls, uri):
        m = cls.URI_RE.match(uri)
        if m is not None:
            # simply pass on URI as URL address
            return DataUrlElement(uri)

        raise InvalidUriError(uri, "Invalid web URI")
Example #3
0
    def from_uri(cls, uri):
        """
        Construct a new instance based on the given URI.

        Memory elements resolve byte-string formats. Currently, this method
        accepts a base64 using the standard and URL-safe alphabet as the python
        ``base64.urlsafe_b64decode`` module function would expect.

        This method accepts URIs in two formats:
            - ``base64://<data>``
            - ``data:<mimetype>;base64,<data>``
            - Empty string (no data)

        Filling in ``<data>`` with the actual byte string, and ``<mimetype>``
        with the actual MIMETYPE of the bytes.

        :param uri: URI string to resolve into an element instance
        :type uri: str

        :raises smqtk.exceptions.InvalidUriError: The given URI was not a
            base64 format

        :return: New element instance of our type.
        :rtype: DataElement

        """
        if uri is None:
            raise InvalidUriError(uri, 'None value given')

        if len(uri) == 0:
            return DataMemoryElement(b'', None)

        data_b64_m = cls.URI_B64_RE.match(uri)
        if data_b64_m is not None:
            m_d = data_b64_m.groupdict()
            return DataMemoryElement.from_base64(m_d['base64'], None)

        data_b64_m = cls.URI_DATA_B64_RE.match(uri)
        if data_b64_m is not None:
            m_d = data_b64_m.groupdict()
            return DataMemoryElement.from_base64(
                m_d['base64'], m_d['ct']
            )

        raise InvalidUriError(uri, "Did not detect byte format URI")
Example #4
0
    def from_uri(cls, uri):
        """
        Construct a new instance based on the given URI.

        File elements can resolve any URI that looks like an absolute or
        relative path, or if the URI explicitly has the "file://" header.

        When the "file://" header is used, we expect an absolute path, including
        the leading slash. This means it will look like there are 3 slashes
        after the "file:", for example: "file:///home/me/somefile.txt".

        If this is given a URI with what looks like another URI header (e.g.
        "base64://..."), we thrown an InvalidUriError. This ends up being due to
        the `//` component, which we treat as an invalid path, not because of
        any special parsing.

        :param uri: URI string to resolve into an element instance
        :type uri: str

        :raises smqtk.exceptions.InvalidUriError: This element type could not
            resolve the provided URI string.

        :return: New element instance of our type.
        :rtype: DataElement

        """
        path_match = cls.FILE_URI_RE.match(uri)

        # if did not match RE, then not a valid path to a file (i.e. had a
        # trailing slash, file:// prefix malformed, etc.)
        if path_match is None:
            raise InvalidUriError(uri, "Malformed URI")

        path = path_match.group(1)

        # When given the file:// prefix, the encoded path must be absolute
        # Stealing the notion based on how Google Chrome handles file:// URIs
        if uri.startswith("file://") and not osp.isabs(path):
            raise InvalidUriError(
                uri, "Found file:// prefix, but path was not "
                "absolute")

        return DataFileElement(path)
Example #5
0
def from_uri(uri, impl_generator=DataElement.get_impls):
    """
    Create a data element instance from available plugin implementations.

    The first implementation that can resolve the URI is what is returned. If no
    implementations can resolve the URL, an ``InvalidUriError`` is raised.

    :param uri: URI to try to resolve into a DataElement instance.
    :type uri: str

    :param impl_generator: Function that returns a dictionary mapping
        implementation type names to the class type. By default this refers to
        the standard ``*.get_impls()`` function, however this can be
        changed to refer to a custom set of classes if desired.
    :type impl_generator: () -> collections.abc.Iterable[type[DataElement]]

    :raises smqtk.exceptions.InvalidUriError: No data element implementations
        could resolve the given URI.

    :return: New data element instance providing access to the data pointed to
        by the input URI.
    :rtype: DataElement

    """
    log = logging.getLogger(__name__)
    log.debug("Trying to parse URI: '%s'", uri)

    de_type_iter = impl_generator()
    inst = None
    for de_type in de_type_iter:
        try:
            # noinspection PyUnresolvedReferences
            inst = de_type.from_uri(uri)
        except NoUriResolutionError:
            # Expected error signaling that DataElement implementation does not
            # or cannot resolve from a URI.
            pass
        except InvalidUriError as ex:
            log.debug("Implementation '%s' failed to parse URI: %s",
                      de_type.__name__, ex.reason)
        if inst is not None:
            break
    if inst is None:
        # TODO: Assume final fallback of FileElement?
        #       Since any string could be a file?
        raise InvalidUriError(uri,
                              "No available implementation to handle URI.")
    return inst
Example #6
0
    def from_uri(cls, uri):
        """
        Creates a ``GirderDataElement`` from ``uri``.

        The parsing is accomplished by passing ``uri`` to ``urlparse``.
        This allows for quite a bit of flexibility in the types of URIs that
        can be passed. Valid Girder URIs are:

        (a) girder(s)://<user>:<pass>@<host>:<port>/api/v1/file/<file_id>
        (b) girder://<user>:<pass>@file:<file_id>

        <user> and <pass> are optional in both (a) and (b). <port> is optional
        in (a).

        If the (b) form of a Girder URI is used, then the ``api_root`` member
        will be the default of 'http://localhost:8080/api/v1'.

        Currently, only GirderDataElements can be built from Files, so the
        URL should end in /file/{id}.

        :param uri: A URI of the form
            girder(s)://<user>:<pass>@<host>:<port>/api/<version>/file/<file_id>
            or girder://<user>:<pass>@file:<file_id>.
        :type uri: str

        :return: Data element created from ``uri``
        :rtype: GirderDataElement

        :raises ValueError: An invalid URI was passed.

        :raises AssertionError: If the path parsed from a URI of the form (a)
            does not have '/file/' as its penultimate location before the
            identifier.

        """
        api_root = None
        # urlparse seems to not be phased by the 'girder' protocol instead of
        # http, so no replacing needs to be done.
        parsed_uri = urlparse(uri)
        if not parsed_uri.scheme.startswith('girder'):
            raise InvalidUriError(
                uri, 'Invalid Girder URI. Girder URIs must start '
                'with girder:// or girders://')

        # For the API root to be valid, the URI must have a netloc
        # and the ``path`` must start with '/api'. This clause deals with
        # URIs of the form:
        # girder://<user>:<pass>@<host>:<port>/api/v1/file/<file_id>
        # The logic is constructed by understanding how urlparse parses a
        # URI of the above form. <port> is optional
        if parsed_uri.path.startswith('/api') and parsed_uri.netloc:
            assert parsed_uri.path.split('/file/')[0] != parsed_uri.path
            # If you're passing a URI of the form (a), either <girder> or
            # <girders> are valid tags. We determine the scheme to use in
            # constructing the api_root here based on that tag.
            if parsed_uri.scheme == 'girder':
                api_root = 'http://'
            elif parsed_uri.scheme == 'girders':
                api_root = 'https://'

            # We rsplit on '/file/' to get the preceding path information
            # before /file/<file_id>, which is used to construct the api root.
            api_root += '%s%s' % (parsed_uri.netloc,
                                  parsed_uri.path.rsplit('/file/')[0])
            file_id = parsed_uri.path.split('/')[-1]

        # This covers the case of a URI of the form:
        # girder://<user>:<pass>@file:<file_id>
        elif parsed_uri.netloc.startswith('file'):
            file_id = parsed_uri.netloc.split(':')[-1]

        # The above are the two currently supported forms of a Girder URI in
        # SMQTK. Anything else will be considered invalid.
        else:
            raise InvalidUriError(
                uri, 'Invalid Girder URI. Girder URIs must be of the form: \n'
                '* girder://<user>:<pass>@<host>:<port>/api/<version>/'
                'file/<file_id>\n'
                '* girder://<user>:<pass>@file:<file_id>\n'
                'Where <user> and <pass> are optional.')

        if api_root:
            girder_element = cls(file_id, api_root)
        else:
            girder_element = cls(file_id)
        return girder_element