Есть ли удобный способ сопоставить файл uri с os.path?

Подсистема, на которую я не могу контролировать, настаивает на предоставлении путей файловой системы в виде uri. Есть ли модуль/функция python, который может преобразовать этот путь в соответствующую форму, ожидаемую файловой системой, независимо от платформы?

Ответы

Ответ 1

Модуль urlparse предоставляет путь от URI:

import os, urlparse
p = urlparse.urlparse('file://C:/test/doc.txt')
finalPath = os.path.abspath(os.path.join(p.netloc, p.path))

Ответ 2

Для будущих читателей. Решение от @Jakob Bowyer не конвертирует URL-адреса в ascii. После небольшого рытья я нашел это решение:

>>> import urllib, urlparse
>>> urllib.url2pathname(urlparse.urlparse('file:///home/user/some%20file.txt').path)
'/home/user/some file.txt'

EDIT:

Вот что я в итоге использовал:

>>> import urllib
>>> urllib.unquote('file:///home/user/some%20file.txt')[7:]
'/home/user/some file.txt'

Ответ 3

Чтобы преобразовать файл uri в путь с python (конкретно для 3, я могу сделать для python 2, если кто-то действительно этого хочет):

  1. Разбор Uri с urllib.parse.urlparse
  2. Скомментируйте компонент пути проанализированного URI с помощью urllib.parse.unquote

  3. тогда...

    а. Если путь является оконным путем и начинается с /: уберите первый символ компонента пути без кавычек (компонент пути в file:///C:/some/file.txt равен /C:/some/file.txt, который не интерпретируется как эквивалентный C:\some\file.txt pathlib.PureWindowsPath)

    б. В противном случае просто используйте компонент пути без кавычек как есть.

Вот функция, которая делает это:

import urllib
import pathlib

def file_uri_to_path(file_uri, path_class=pathlib.PurePath):
    """
    This function returns a pathlib.PurePath object for the supplied file URI.

    :param str file_uri: The file URI ...
    :param class path_class: The type of path in the file_uri. By default it uses
        the system specific path pathlib.PurePath, to force a specific type of path
        pass pathlib.PureWindowsPath or pathlib.PurePosixPath
    :returns: the pathlib.PurePath object
    :rtype: pathlib.PurePath
    """
    windows_path = isinstance(path_class(),pathlib.PureWindowsPath)
    file_uri_parsed = urllib.parse.urlparse(file_uri)
    file_uri_path_unquoted = urllib.parse.unquote(file_uri_parsed.path)
    if windows_path and file_uri_path_unquoted.startswith("/"):
        result = path_class(file_uri_path_unquoted[1:])
    else:
        result = path_class(file_uri_path_unquoted)
    if result.is_absolute() == False:
        raise ValueError("Invalid file uri {} : resulting path {} not absolute".format(
            file_uri, result))
    return result

Примеры использования (работали в Linux):

>>> file_uri_to_path("file:///etc/hosts")
PurePosixPath('/etc/hosts')

>>> file_uri_to_path("file:///etc/hosts", pathlib.PurePosixPath)
PurePosixPath('/etc/hosts')

>>> file_uri_to_path("file:///C:/Program Files/Steam/", pathlib.PureWindowsPath)
PureWindowsPath('C:/Program Files/Steam')

>>> file_uri_to_path("file:/proc/cpuinfo", pathlib.PurePosixPath)
PurePosixPath('/proc/cpuinfo')

>>> file_uri_to_path("file:c:/system32/etc/hosts", pathlib.PureWindowsPath)
PureWindowsPath('c:/system32/etc/hosts')

Эта функция работает для URI окон и файлов posix и будет обрабатывать URI файлов без раздела полномочий. Однако он НЕ будет выполнять проверку полномочий URI, поэтому это не будет выполнено:

IETF RFC 8089: "файловая" схема URI/2. Синтаксис

"Хост" - это полное доменное имя системы, на которой файл доступен. Это позволяет клиенту в другой системе знать, что он не может получить доступ к файловой системе, или, возможно, что ему нужно использовать другой локальный механизм для доступа к файлу.

Проверка (pytest) для функции:

import os
import pytest

def validate(file_uri, expected_windows_path, expected_posix_path):
    if expected_windows_path is not None:
        expected_windows_path_object = pathlib.PureWindowsPath(expected_windows_path)
    if expected_posix_path is not None:
        expected_posix_path_object = pathlib.PurePosixPath(expected_posix_path)

    if expected_windows_path is not None:
        if os.name == "nt":
            assert file_uri_to_path(file_uri) == expected_windows_path_object
        assert file_uri_to_path(file_uri, pathlib.PureWindowsPath) == expected_windows_path_object

    if expected_posix_path is not None:
        if os.name != "nt":
            assert file_uri_to_path(file_uri) == expected_posix_path_object
        assert file_uri_to_path(file_uri, pathlib.PurePosixPath) == expected_posix_path_object


def test_some_paths():
    validate(pathlib.PureWindowsPath(r"C:\Windows\System32\Drivers\etc\hosts").as_uri(),
        expected_windows_path=r"C:\Windows\System32\Drivers\etc\hosts",
        expected_posix_path=r"/C:/Windows/System32/Drivers/etc/hosts")

    validate(pathlib.PurePosixPath(r"/C:/Windows/System32/Drivers/etc/hosts").as_uri(),
        expected_windows_path=r"C:\Windows\System32\Drivers\etc\hosts",
        expected_posix_path=r"/C:/Windows/System32/Drivers/etc/hosts")

    validate(pathlib.PureWindowsPath(r"C:\some dir\some file").as_uri(),
        expected_windows_path=r"C:\some dir\some file",
        expected_posix_path=r"/C:/some dir/some file")

    validate(pathlib.PurePosixPath(r"/C:/some dir/some file").as_uri(),
        expected_windows_path=r"C:\some dir\some file",
        expected_posix_path=r"/C:/some dir/some file")

def test_invalid_url():
    with pytest.raises(ValueError) as excinfo:
        validate(r"file://C:/test/doc.txt",
            expected_windows_path=r"test\doc.txt",
            expected_posix_path=r"/test/doc.txt")
        assert "is not absolute" in str(excinfo.value)

def test_escaped():
    validate(r"file:///home/user/some%20file.txt",
        expected_windows_path=None,
        expected_posix_path=r"/home/user/some file.txt")
    validate(r"file:///C:/some%20dir/some%20file.txt",
        expected_windows_path="C:\some dir\some file.txt",
        expected_posix_path=r"/C:/some dir/some file.txt")

def test_no_authority():
    validate(r"file:c:/path/to/file",
        expected_windows_path=r"c:\path\to\file",
        expected_posix_path=None)
    validate(r"file:/path/to/file",
        expected_windows_path=None,
        expected_posix_path=r"/path/to/file")

Этот вклад лицензируется (в дополнение к любым другим лицензиям, которые могут применяться) в соответствии с лицензией Zero-Clause BSD (0BSD) license

Разрешение на использование, копирование, изменение и/или распространение данного программного обеспечения для любых Настоящая цель предоставляется с платой или без нее.

ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ "КАК ЕСТЬ", И АВТОР ОТКАЗЫВАЕТСЯ ОТ ВСЕХ ГАРАНТИЙ. В ОТНОШЕНИИ НАСТОЯЩЕГО ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ, ВКЛЮЧАЯ ВСЕ ПОДРАЗУМЕВАЕМЫЕ ГАРАНТИИ ТОРГОВЛЯ И ФИТНЕС. НИ ПРИ КАКИХ ОБСТОЯТЕЛЬСТВАХ АВТОР НЕ НЕСЕТ ОТВЕТСТВЕННОСТИ ЗА ЛЮБЫЕ СПЕЦИАЛЬНЫЕ, НЕПОСРЕДСТВЕННЫЕ, НЕПОСРЕДСТВЕННЫЕ ИЛИ КОСВЕННЫЕ УБЫТКИ ИЛИ ЛЮБОЙ УЩЕРБ ЧТО ТАКОЕ РЕЗУЛЬТАТ ОТ УТРАТЫ ИСПОЛЬЗОВАНИЯ, ДАННЫХ ИЛИ ПРИБЫЛИ ДЕЙСТВИЕ КОНТРАКТА, ХАРАКТЕРИСТИКИ ИЛИ ДРУГОГО ПРАВОВОГО ДЕЙСТВИЯ, ВЫТЕКАЮЩЕГО ИЗ ИЛИ В СВЯЗИ С ИСПОЛЬЗОВАНИЕМ ИЛИ ВЫПОЛНЕНИЕМ НАСТОЯЩЕГО ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ.


Public Domain

Насколько это возможно в соответствии с законодательством, Iwan Aucamp отказался от всех авторских и смежных или смежных прав на этот вклад в обмен на стек. Эта работа издана из: Норвегия.

Ответ 4

Решение от @colton7909 в основном правильное и помогло мне получить этот ответ, но имеет некоторые ошибки импорта с Python 3. Это и я думаю, что это лучший способ справиться с частью 'file://' URL, чем просто отрубая первые 7 символов. Поэтому я считаю, что это самый идиоматичный способ сделать это с помощью стандартной библиотеки:

import urllib.parse
url_data = urllib.parse.urlparse('file:///home/user/some%20file.txt')
path = urllib.parse.unquote(url_data.path)

Этот пример должен создать строку '/home/user/some file.txt'