summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lower-constraints.txt6
-rw-r--r--requirements.txt7
-rw-r--r--setup.cfg2
-rw-r--r--setup.py14
-rw-r--r--swiftclient/client.py32
-rwxr-xr-xswiftclient/shell.py11
-rw-r--r--tests/unit/test_shell.py35
-rw-r--r--tests/unit/test_swiftclient.py61
8 files changed, 132 insertions, 36 deletions
diff --git a/lower-constraints.txt b/lower-constraints.txt
index 6488b28..9aae792 100644
--- a/lower-constraints.txt
+++ b/lower-constraints.txt
@@ -28,14 +28,14 @@ pep8==1.5.7
PrettyTable==0.7
pyflakes==0.8.1
Pygments==2.2.0
-python-keystoneclient==3.8.0
+python-keystoneclient==0.7.0
python-mimeparse==1.6.0
python-subunit==1.0.0
pytz==2013.6
PyYAML==3.12
reno==2.5.0
-requests==2.14.2
-six==1.10.0
+requests==1.1.0
+six==1.9.0
snowballstemmer==1.2.1
sphinx==1.6.2
sphinxcontrib-websupport==1.0.1
diff --git a/requirements.txt b/requirements.txt
index 6b52791..1c2ce33 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,3 @@
-# The order of packages is significant, because pip processes them in the order
-# of appearance. Changing the order has an impact on the overall integration
-# process, which may cause wedges in the gate later.
futures>=3.0.0;python_version=='2.7' or python_version=='2.6' # BSD
-requests>=2.14.2 # Apache-2.0
-six>=1.10.0 # MIT
+requests>=1.1.0
+six>=1.9.0
diff --git a/setup.cfg b/setup.cfg
index 2ac5f9c..ddb64a0 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -33,7 +33,7 @@ data_files =
[extras]
keystone =
- python-keystoneclient>=3.8.0 # Apache-2.0
+ python-keystoneclient>=0.7.0
[entry_points]
console_scripts =
diff --git a/setup.py b/setup.py
index 518f1d3..16a18f6 100644
--- a/setup.py
+++ b/setup.py
@@ -17,16 +17,10 @@
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
import setuptools, sys
-import setuptools
-
-# In python < 2.7.4, a lazy loading of package `pbr` will break
-# setuptools if some other modules registered functions in `atexit`.
-# solution from: http://bugs.python.org/issue15881#msg170215
-try:
- import multiprocessing # noqa
-except ImportError:
- pass
+if sys.version_info < (2, 7):
+ sys.exit('Sorry, Python < 2.7 is not supported for'
+ ' python-swiftclient>=3.0')
setuptools.setup(
- setup_requires=['pbr>=2.0.0'],
+ setup_requires=['pbr'],
pbr=True)
diff --git a/swiftclient/client.py b/swiftclient/client.py
index 8cbdf45..410a001 100644
--- a/swiftclient/client.py
+++ b/swiftclient/client.py
@@ -151,7 +151,7 @@ def http_log(args, kwargs, resp, body):
elif element in ('GET', 'POST', 'PUT'):
string_parts.append(' -X %s' % element)
else:
- string_parts.append(' %s' % element)
+ string_parts.append(' %s' % parse_header_string(element))
if 'headers' in kwargs:
headers = scrub_headers(kwargs['headers'])
for element in headers:
@@ -273,6 +273,9 @@ class _ObjectBody(object):
def __next__(self):
return self.next()
+ def close(self):
+ self.resp.close()
+
class _RetryBody(_ObjectBody):
"""
@@ -455,11 +458,23 @@ class HTTPConnection(object):
self.resp.status = self.resp.status_code
old_getheader = self.resp.raw.getheader
+ def _decode_header(string):
+ if string is None or six.PY2:
+ return string
+ return string.encode('iso-8859-1').decode('utf-8')
+
+ def _encode_header(string):
+ if string is None or six.PY2:
+ return string
+ return string.encode('utf-8').decode('iso-8859-1')
+
def getheaders():
- return self.resp.headers.items()
+ return [(_decode_header(k), _decode_header(v))
+ for k, v in self.resp.headers.items()]
def getheader(k, v=None):
- return old_getheader(k.lower(), v)
+ return _decode_header(old_getheader(
+ _encode_header(k.lower()), _encode_header(v)))
def releasing_read(*args, **kwargs):
chunk = self.resp.raw.read(*args, **kwargs)
@@ -513,8 +528,11 @@ def get_auth_1_0(url, user, key, snet, **kwargs):
netloc = parsed[1]
parsed[1] = 'snet-' + netloc
url = urlunparse(parsed)
- return url, resp.getheader('x-storage-token',
- resp.getheader('x-auth-token'))
+
+ auth_token = resp.getheader('x-auth-token')
+ if auth_token is not None:
+ auth_token = parse_header_string(auth_token)
+ return url, resp.getheader('x-storage-token', auth_token)
def get_keystoneclient_2_0(auth_url, user, key, os_options, **kwargs):
@@ -694,10 +712,14 @@ def get_auth(auth_url, user, key, **kwargs):
raise ClientException('Unknown auth_version %s specified and no '
'session found.' % auth_version)
+ if token is not None:
+ token = parse_header_string(token)
# Override storage url, if necessary
if os_options.get('object_storage_url'):
return os_options['object_storage_url'], token
else:
+ if storage_url is not None:
+ return parse_header_string(storage_url), token
return storage_url, token
diff --git a/swiftclient/shell.py b/swiftclient/shell.py
index ff5b2be..9ca28b1 100755
--- a/swiftclient/shell.py
+++ b/swiftclient/shell.py
@@ -1942,9 +1942,14 @@ Examples:
parser.usage = globals()['st_%s_help' % args[0]]
if options['insecure']:
import requests
- from requests.packages.urllib3.exceptions import \
- InsecureRequestWarning
- requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
+ try:
+ from requests.packages.urllib3.exceptions import \
+ InsecureRequestWarning
+ except ImportError:
+ pass
+ else:
+ requests.packages.urllib3.disable_warnings(
+ InsecureRequestWarning)
try:
globals()['st_%s' % args[0]](parser, argv[1:], output)
except ClientException as err:
diff --git a/tests/unit/test_shell.py b/tests/unit/test_shell.py
index 8c995e5..91496b8 100644
--- a/tests/unit/test_shell.py
+++ b/tests/unit/test_shell.py
@@ -14,8 +14,8 @@
# limitations under the License.
from __future__ import unicode_literals
+import contextlib
from genericpath import getmtime
-
import getpass
import hashlib
import json
@@ -27,7 +27,6 @@ import unittest
import textwrap
from time import localtime, mktime, strftime, strptime
-from requests.packages.urllib3.exceptions import InsecureRequestWarning
import six
import sys
@@ -44,6 +43,10 @@ from swiftclient.utils import (
EMPTY_ETAG, EXPIRES_ISO8601_FORMAT,
SHORT_EXPIRES_ISO8601_FORMAT, TIME_ERRMSG)
+try:
+ from requests.packages.urllib3.exceptions import InsecureRequestWarning
+except ImportError:
+ InsecureRequestWarning = None
if six.PY2:
BUILTIN_OPEN = '__builtin__.open'
@@ -114,6 +117,20 @@ def _make_cmd(cmd, opts, os_opts, use_env=False, flags=None, cmd_args=None):
return args, env
+@contextlib.contextmanager
+def patch_disable_warnings():
+ if InsecureRequestWarning is None:
+ # If InsecureRequestWarning isn't available, disbale_warnings won't
+ # be either; they both came in with
+ # https://github.com/requests/requests/commit/811ee4e and left again
+ # in https://github.com/requests/requests/commit/8e17600
+ yield None
+ else:
+ with mock.patch('requests.packages.urllib3.disable_warnings') \
+ as patched:
+ yield patched
+
+
@mock.patch.dict(os.environ, mocked_os_environ)
class TestShell(unittest.TestCase):
def setUp(self):
@@ -2529,8 +2546,7 @@ class TestKeystoneOptions(MockHttpTest):
_make_fake_import_keystone_client(fake_ks)), \
mock.patch('swiftclient.client.http_connection', fake_conn), \
mock.patch.dict(os.environ, env, clear=True), \
- mock.patch('requests.packages.urllib3.disable_warnings') as \
- mock_disable_warnings:
+ patch_disable_warnings() as mock_disable_warnings:
try:
swiftclient.shell.main(args)
except SystemExit as e:
@@ -2538,11 +2554,12 @@ class TestKeystoneOptions(MockHttpTest):
except SwiftError as err:
self.fail('Unexpected SwiftError: %s' % err)
- if 'insecure' in flags:
- self.assertEqual([mock.call(InsecureRequestWarning)],
- mock_disable_warnings.mock_calls)
- else:
- self.assertEqual([], mock_disable_warnings.mock_calls)
+ if InsecureRequestWarning is not None:
+ if 'insecure' in flags:
+ self.assertEqual([mock.call(InsecureRequestWarning)],
+ mock_disable_warnings.mock_calls)
+ else:
+ self.assertEqual([], mock_disable_warnings.mock_calls)
if no_auth:
# check that keystone client was not used and terminate tests
diff --git a/tests/unit/test_swiftclient.py b/tests/unit/test_swiftclient.py
index b6d6856..3303372 100644
--- a/tests/unit/test_swiftclient.py
+++ b/tests/unit/test_swiftclient.py
@@ -1896,6 +1896,57 @@ class TestHTTPConnection(MockHttpTest):
self.assertFalse(resp.read())
self.assertTrue(resp.closed)
+ @unittest.skipIf(six.PY3, 'python2 specific test')
+ def test_response_python2_headers(self):
+ '''Test utf-8 headers in Python 2.
+ '''
+ _, conn = c.http_connection(u'http://www.test.com/')
+ conn.resp = MockHttpResponse(
+ status=200,
+ headers={
+ '\xd8\xaa-unicode': '\xd8\xaa-value',
+ 'empty-header': ''
+ }
+ )
+
+ resp = conn.getresponse()
+ self.assertEqual(
+ '\xd8\xaa-value', resp.getheader('\xd8\xaa-unicode'))
+ self.assertEqual(
+ '\xd8\xaa-value', resp.getheader('\xd8\xaa-UNICODE'))
+ self.assertEqual('', resp.getheader('empty-header'))
+ self.assertEqual(
+ dict([('\xd8\xaa-unicode', '\xd8\xaa-value'),
+ ('empty-header', ''),
+ ('etag', '"%s"' % EMPTY_ETAG)]),
+ dict(resp.getheaders()))
+
+ @unittest.skipIf(six.PY2, 'python3 specific test')
+ def test_response_python3_headers(self):
+ '''Test latin1-encoded headers in Python 3.
+ '''
+ _, conn = c.http_connection(u'http://www.test.com/')
+ conn.resp = MockHttpResponse(
+ status=200,
+ headers={
+ b'\xd8\xaa-unicode'.decode('iso-8859-1'):
+ b'\xd8\xaa-value'.decode('iso-8859-1'),
+ 'empty-header': ''
+ }
+ )
+
+ resp = conn.getresponse()
+ self.assertEqual(
+ '\u062a-value', resp.getheader('\u062a-unicode'))
+ self.assertEqual(
+ '\u062a-value', resp.getheader('\u062a-UNICODE'))
+ self.assertEqual('', resp.getheader('empty-header'))
+ self.assertEqual(
+ dict([('\u062a-unicode', '\u062a-value'),
+ ('empty-header', ''),
+ ('etag', ('"%s"' % EMPTY_ETAG))]),
+ dict(resp.getheaders()))
+
class TestConnection(MockHttpTest):
@@ -2839,6 +2890,16 @@ class TestLogging(MockHttpTest):
self.assertIn('X-Storage-Token', output)
self.assertIn(unicode_token_value, output)
+ @mock.patch('swiftclient.client.logger.debug')
+ def test_unicode_path(self, mock_log):
+ path = u'http://swift/v1/AUTH_account-\u062a'.encode('utf-8')
+ c.http_log(['GET', path], {},
+ MockHttpResponse(status=200, headers=[]), '')
+ request_log_line = mock_log.mock_calls[0]
+ self.assertEqual('REQ: %s', request_log_line[1][0])
+ self.assertEqual(u'curl -i -X GET %s' % path.decode('utf-8'),
+ request_log_line[1][1])
+
class TestCloseConnection(MockHttpTest):