mirror of
https://github.com/httpie/cli.git
synced 2025-02-23 04:50:49 +01:00
Fixed #451 - OSError: [Errno 36] File name too long
This commit is contained in:
parent
001bda1945
commit
5300b0b490
@ -27,6 +27,7 @@ This project adheres to `Semantic Versioning <http://semver.org/>`_.
|
||||
of light and dark terminals
|
||||
* Improved ``--debug`` output
|
||||
* Fixed ``--session`` when used with ``--download``
|
||||
* Fixed ``--download`` to trim too long filenames before saving the file
|
||||
* Fixed handling of ``Content-Type`` with multiple ``+subtype`` parts
|
||||
|
||||
|
||||
|
@ -7,6 +7,7 @@ from __future__ import division
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import errno
|
||||
import mimetypes
|
||||
import threading
|
||||
from time import sleep, time
|
||||
@ -135,12 +136,43 @@ def filename_from_url(url, content_type):
|
||||
return fn
|
||||
|
||||
|
||||
def trim_filename(filename, max_len):
|
||||
if len(filename) > max_len:
|
||||
trim_by = len(filename) - max_len
|
||||
name, ext = os.path.splitext(filename)
|
||||
if trim_by >= len(name):
|
||||
filename = filename[:-trim_by]
|
||||
else:
|
||||
filename = name[:-trim_by] + ext
|
||||
return filename
|
||||
|
||||
|
||||
def get_filename_max_length(directory):
|
||||
try:
|
||||
max_len = os.pathconf(directory, 'PC_NAME_MAX')
|
||||
except OSError as e:
|
||||
if e.errno == errno.EINVAL:
|
||||
max_len = 255
|
||||
else:
|
||||
raise
|
||||
return max_len
|
||||
|
||||
|
||||
def trim_filename_if_needed(filename, directory='.', extra=0):
|
||||
max_len = get_filename_max_length(directory) - extra
|
||||
if len(filename) > max_len:
|
||||
filename = trim_filename(filename, max_len)
|
||||
return filename
|
||||
|
||||
|
||||
def get_unique_filename(filename, exists=os.path.exists):
|
||||
attempt = 0
|
||||
while True:
|
||||
suffix = '-' + str(attempt) if attempt > 0 else ''
|
||||
if not exists(filename + suffix):
|
||||
return filename + suffix
|
||||
try_filename = trim_filename_if_needed(filename, extra=len(suffix))
|
||||
try_filename += suffix
|
||||
if not exists(try_filename):
|
||||
return try_filename
|
||||
attempt += 1
|
||||
|
||||
|
||||
|
@ -2,6 +2,7 @@ import os
|
||||
import time
|
||||
|
||||
import pytest
|
||||
import mock
|
||||
from requests.structures import CaseInsensitiveDict
|
||||
|
||||
from httpie.compat import urlopen
|
||||
@ -74,7 +75,31 @@ class TestDownloadUtils:
|
||||
content_type='x-foo/bar'
|
||||
)
|
||||
|
||||
def test_unique_filename(self):
|
||||
@pytest.mark.parametrize(
|
||||
'orig_name, unique_on_attempt, expected',
|
||||
[
|
||||
# Simple
|
||||
('foo.bar', 0, 'foo.bar'),
|
||||
('foo.bar', 1, 'foo.bar-1'),
|
||||
('foo.bar', 10, 'foo.bar-10'),
|
||||
# Trim
|
||||
('A' * 20, 0, 'A' * 10),
|
||||
('A' * 20, 1, 'A' * 8 + '-1'),
|
||||
('A' * 20, 10, 'A' * 7 + '-10'),
|
||||
# Trim before ext
|
||||
('A' * 20 + '.txt', 0, 'A' * 6 + '.txt'),
|
||||
('A' * 20 + '.txt', 1, 'A' * 4 + '.txt-1'),
|
||||
# Trim at the end
|
||||
('foo.' + 'A' * 20, 0, 'foo.' + 'A' * 6),
|
||||
('foo.' + 'A' * 20, 1, 'foo.' + 'A' * 4 + '-1'),
|
||||
('foo.' + 'A' * 20, 10, 'foo.' + 'A' * 3 + '-10'),
|
||||
]
|
||||
)
|
||||
@mock.patch('httpie.downloads.get_filename_max_length')
|
||||
def test_unique_filename(self, get_filename_max_length,
|
||||
orig_name, unique_on_attempt,
|
||||
expected):
|
||||
|
||||
def attempts(unique_on_attempt=0):
|
||||
# noinspection PyUnresolvedReferences,PyUnusedLocal
|
||||
def exists(filename):
|
||||
@ -86,9 +111,10 @@ class TestDownloadUtils:
|
||||
exists.attempt = 0
|
||||
return exists
|
||||
|
||||
assert 'foo.bar' == get_unique_filename('foo.bar', attempts(0))
|
||||
assert 'foo.bar-1' == get_unique_filename('foo.bar', attempts(1))
|
||||
assert 'foo.bar-10' == get_unique_filename('foo.bar', attempts(10))
|
||||
get_filename_max_length.return_value = 10
|
||||
|
||||
actual = get_unique_filename(orig_name, attempts(unique_on_attempt))
|
||||
assert expected == actual
|
||||
|
||||
|
||||
class TestDownloads:
|
||||
|
Loading…
Reference in New Issue
Block a user