Skip to content

Instantly share code, notes, and snippets.

@ivdunin
Last active October 5, 2021 17:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ivdunin/e9f3e1a20f5ad7dff45a83c490f242c3 to your computer and use it in GitHub Desktop.
Save ivdunin/e9f3e1a20f5ad7dff45a83c490f242c3 to your computer and use it in GitHub Desktop.
canonical_path
import sys
MAX_FILENAME_LENGTH = 255
MAX_FILEPATH_LENGTH = 4096
SEP = '/'
"""
Дана строка, которая является абсолютным путем к файлу или директории в системе Unix.
Нужно упростить эту строку до каноничного пути и написать автотесты для своей реализации. Использовать Python + pytest
"""
def canonical_path(path: str) -> str:
if sys.platform != "linux":
raise OSError('Works only on linux os')
canonical = ['']
if not path.strip():
raise ValueError('Empty path value!')
if not path.startswith(SEP):
raise ValueError(f'Not an absolute path! Path should start with {SEP}')
if path.strip() == SEP:
return SEP
tokens = path.split(SEP)
for token in tokens:
if token and token != '.':
if token != '..':
if len(token) > MAX_FILENAME_LENGTH:
raise ValueError('File or directory name too long!')
canonical.append(f'{token}')
else:
del canonical[-1]
# there are few cases when linux realpath differs from python os.path.realpath behavior
# as I compare results with python implementation, I took Python approach
if not canonical:
canonical.append('')
if len(canonical) == 1:
return f'/{canonical[0]}'
else:
res = '/'.join(canonical)
if len(res) > MAX_FILEPATH_LENGTH:
raise ValueError('Resulted file path too long!')
else:
return res
from math import ceil
from canonical_path import MAX_FILEPATH_LENGTH
def generate_long_path(length: int = MAX_FILEPATH_LENGTH) -> str:
prefix = '/home/user'
fn = '/{0}'.format('AbCdE' * 51)
mul = ceil((MAX_FILEPATH_LENGTH - len(prefix)) / len(fn))
final_path = '{p}{f}'.format(p=prefix, f=fn * mul)
return final_path[:length]
import sys
import pytest
from canonical_path import canonical_path, MAX_FILENAME_LENGTH, MAX_FILEPATH_LENGTH
from lib import generate_long_path
MAX_FILE_NAME_PLUS = 'a' * (MAX_FILENAME_LENGTH + 1)
MAX_PATH_PLUS = generate_long_path(MAX_FILEPATH_LENGTH + 1)
@pytest.mark.parametrize(
'initial_path, msg', [
pytest.param('user/data', 'Not an absolute path! Path should start with /', id='Valid path'),
pytest.param(' ', 'Empty path value!', id='Empty path'),
pytest.param('/home/data/{0}'.format(MAX_FILE_NAME_PLUS), 'File or directory name too long!',
id='Exceed linux file length'),
pytest.param(MAX_PATH_PLUS, 'Resulted file path too long!', id='Exceed linux path length')
]
)
def test_negative(initial_path, msg):
with pytest.raises(ValueError) as excinfo:
canonical_path(initial_path)
assert str(excinfo.value) == msg
def test_invalid_platform(monkeypatch):
monkeypatch.setattr(sys, 'platform', 'win32')
with pytest.raises(OSError) as excinfo:
canonical_path('c:/dir')
assert str(excinfo.value) == 'Works only on linux os'
from os import path
import pytest
from canonical_path import canonical_path, MAX_FILENAME_LENGTH
from lib import generate_long_path
MAX_FILE_NAME = 'a' * MAX_FILENAME_LENGTH
MAX_PATH = generate_long_path()
@pytest.mark.parametrize(
'initial_path', [
pytest.param('/', id='Root path'),
pytest.param('//', id='Root path with double /'),
pytest.param('/../data', id='Root dir'),
pytest.param('/home/data/file.txt', id='Valid path'),
pytest.param('/home/data///dir/', id='Path ends with /'),
pytest.param('/home/data/../data.txt', id='Path contains multiple dots'),
pytest.param('/home/data/{0}'.format(MAX_FILE_NAME), id='Max unix filename length'),
pytest.param(MAX_PATH, id='Max unix filepath length'),
pytest.param(f'///{MAX_PATH}', id='Resulted filepath less or equal to limit'),
pytest.param('/home/user/../test/../../../../data/text.txt', id='Multiple dots in path'),
pytest.param('/home/user/././text.file', id='file with dot dot')
]
)
def test_positive(initial_path):
assert canonical_path(initial_path) == path.realpath(initial_path)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment