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)
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")
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")
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)
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
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