Как рассчитать корневой относительный путь в python3?

Задача - реализовать функцию root_relative_path(root : str, path : str) -> str, которая вычисляет относительный путь относительно корня, при этом промежуточные .. не выходят за пределы root. например, root_relative_path('/abc', '/../def') == '/abc/def'

Этот вопрос отличается от Как рассчитать относительный путь в Python?, потому что в этом случае root_relative_path(root='/tmp/abc', path='/../def') должен возвращать /tmp/abc/def вместо /tmp/def.

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

Klaus D. 26.10.2018 04:31

@KlausD. Спасибо. Уточнил описание проблемы.

lz96 26.10.2018 04:43

Я понимаю, почему вы хотите предотвратить выход за пределы root, но почему тихо отбрасывать некоторые ".." вместо того, чтобы генерировать исключение?

Francis Colas 26.10.2018 08:54

@FrancisColas Эта функция полезна при реализации HTTP-сервера, где GET /../../abc с root = /tmp/abc должен возвращать файлы из /tmp/abc/abc.

lz96 26.10.2018 20:20
0
4
225
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

import os.path

def root_relative_path(root : str, path : str) -> str:
    return (os.path.join(root,
        os.path.join(os.sep, os.path.normpath(path)).split(os.sep)[1:])))
return os.path.join(root, *os.path.join(os.sep, os.path.normpath(path)).split(os.sep)[1:])?
lz96 26.10.2018 20:26

Добавление * до того, как os.path.join сломает его. Что ты спрашиваешь?

Eddy Pronk 26.10.2018 23:27
Ответ принят как подходящий

Мне удалось реализовать вашу функцию root_relative_path, используя комбинацию модулей posixpath и pathlib. Результат

  • Независимость от платформы (если корневой путь соответствует текущей платформе)
  • Путь может начинаться с /, ./ или ../.
  • И путь будет нормализован с использованием всех методов, охватываемых функцией нормальный путь, которая включает в себя разрешение ...

 

from pathlib import Path
import posixpath

def root_relative_path(root : str, path : str) -> str:
    ''' Calculates the relative path with respect to the root. 
        Any ".."s in the relative path will be resolved, 
        but not in a way that would cause the relative path to go beyond the root. '''

    # posixpath.normpath doesn't work as expected if the path doesn't start with a slash, so we make sure it does
    if not path.startswith('/'):
        path = '/' + path

    # The normalization process includes resolving any ".."s
    # we're only using posixpath for the relative portion of the path, the outcome will still be platform independent
    path = posixpath.normpath(path)

    # Remove the beginning slash so that we're not trying to join two absolute paths
    path = path[1:]

    joined = Path(root) / path

    # Per the OPs requirements the result needed to be a string,
    # But if you're allowed to use Pathlib objects, you could just return joined without converting it to a string
    return str(joined)

Как насчет этого:

from pathlib import Path

def get_relative_path(path_from: Path, path_to: Path) -> Path:
    """
    Calculate and return a relative path between the `path_from` and
    `path_to` paths. Both paths must be absolute paths!
    """
    if not (path_from.is_absolute() and path_to.is_absolute()):
        raise ValueError('One or both of the passed paths are not absolute.')
    items_from = path_from.parts
    items_to = path_to.parts
    while items_from[0] == items_to[0]:
        items_from = items_from[1:]
        items_to = items_to[1:]
    return Path(*('..' for x in range(1, len(items_from))), *items_to)

Другие вопросы по теме