diff options
-rwxr-xr-x | .functests | 2 | ||||
-rw-r--r-- | .stestr.conf | 2 | ||||
-rwxr-xr-x | .unittests | 2 | ||||
-rw-r--r-- | .zuul.yaml | 4 | ||||
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | ChangeLog | 15 | ||||
-rw-r--r-- | doc/source/conf.py | 4 | ||||
-rw-r--r-- | lower-constraints.txt | 1 | ||||
-rw-r--r-- | releasenotes/notes/3_8_1_release-cb5648c3ae69bde1.yaml | 14 | ||||
-rw-r--r-- | releasenotes/source/index.rst | 1 | ||||
-rw-r--r-- | releasenotes/source/train.rst | 6 | ||||
-rw-r--r-- | setup.cfg | 1 | ||||
-rw-r--r-- | swiftclient/authv1.py | 12 | ||||
-rw-r--r-- | swiftclient/multithreading.py | 6 | ||||
-rwxr-xr-x | swiftclient/shell.py | 6 | ||||
-rw-r--r-- | test-requirements.txt | 1 | ||||
-rw-r--r-- | test/__init__.py (renamed from tests/__init__.py) | 0 | ||||
-rw-r--r-- | test/functional/__init__.py | 93 | ||||
-rw-r--r-- | test/functional/test_openstacksdk.py | 92 | ||||
-rw-r--r-- | test/functional/test_swiftclient.py (renamed from tests/functional/test_swiftclient.py) | 85 | ||||
-rw-r--r-- | test/sample.conf (renamed from tests/sample.conf) | 0 | ||||
-rw-r--r-- | test/unit/__init__.py (renamed from tests/functional/__init__.py) | 0 | ||||
-rw-r--r-- | test/unit/test_authv1.py (renamed from tests/unit/test_authv1.py) | 0 | ||||
-rw-r--r-- | test/unit/test_command_helpers.py (renamed from tests/unit/test_command_helpers.py) | 0 | ||||
-rw-r--r-- | test/unit/test_multithreading.py (renamed from tests/unit/test_multithreading.py) | 0 | ||||
-rw-r--r-- | test/unit/test_service.py (renamed from tests/unit/test_service.py) | 8 | ||||
-rw-r--r-- | test/unit/test_shell.py (renamed from tests/unit/test_shell.py) | 0 | ||||
-rw-r--r-- | test/unit/test_swiftclient.py (renamed from tests/unit/test_swiftclient.py) | 0 | ||||
-rw-r--r-- | test/unit/test_utils.py (renamed from tests/unit/test_utils.py) | 0 | ||||
-rw-r--r-- | test/unit/utils.py (renamed from tests/unit/utils.py) | 0 | ||||
-rw-r--r-- | tests/unit/__init__.py | 0 | ||||
-rw-r--r-- | tox.ini | 15 |
32 files changed, 289 insertions, 82 deletions
@@ -1,7 +1,7 @@ #!/bin/bash set -e -export OS_TEST_PATH='tests.functional' +export OS_TEST_PATH='test.functional' export PYTHON='coverage run --source swiftclient --parallel-mode' stestr run --concurrency=1 diff --git a/.stestr.conf b/.stestr.conf index 5228f20..90f5b81 100644 --- a/.stestr.conf +++ b/.stestr.conf @@ -1,4 +1,4 @@ [DEFAULT] -test_path=${OS_TEST_PATH:-./tests/unit} +test_path=${OS_TEST_PATH:-./test/unit} top_dir=./ @@ -1,7 +1,7 @@ #!/bin/bash set -e -python setup.py testr --coverage --testr-args="tests.unit" +python setup.py testr --coverage --testr-args="test.unit" RET=$? coverage report -m rm -f .coverage @@ -39,9 +39,7 @@ - openstack-lower-constraints-jobs - openstack-pypy-jobs-nonvoting - openstack-python-jobs - - openstack-python35-jobs - - openstack-python36-jobs - - openstack-python37-jobs + - openstack-python3-ussuri-jobs - publish-openstack-docs-pti - release-notes-jobs-python3 check: @@ -24,6 +24,7 @@ Clark Boylan (clark.boylan@gmail.com) Claudiu Belu (cbelu@cloudbasesolutions.com) Clay Gerrard (clay.gerrard@gmail.com) Clint Byrum (clint@fewbar.com) +Corey Bryant (corey.bryant@canonical.com) Dan Prince (dprince@redhat.com) Daniel Wakefield (daniel.wakefield@hp.com) Darrell Bishop (darrell@swiftstack.com) @@ -1,3 +1,18 @@ +3.8.1 +----- + +* Deleting or overwriting a symlink to an SLO or DLO will no longer attempt + to clean up the large object's segments. + +* Fixed an issue sending non-ASCII metadata keys on Python 3. + Note that receiving such metadata on py3 is still broken; + see https://bugs.python.org/issue37093 + +* Documentation can now be rendered as a PDF. + +* Dropped Python 3.5 testing. + + 3.8.0 ----- diff --git a/doc/source/conf.py b/doc/source/conf.py index 85dd81e..a8ad3ad 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -181,7 +181,7 @@ htmlhelp_basename = 'SwiftClientwebdoc' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]) latex_documents = [ - ('index', 'SwiftClient.tex', u'SwiftClient Documentation', + ('index', 'doc-python-swiftclient.tex', u'SwiftClient Documentation', u'OpenStack, LLC.', 'manual'), ] @@ -201,3 +201,5 @@ latex_documents = [ # If false, no module index is generated. # latex_use_modindex = True + +latex_use_xindy = False diff --git a/lower-constraints.txt b/lower-constraints.txt index 88d2865..ead0279 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -21,6 +21,7 @@ mccabe==0.2.1 mock==1.2.0 netaddr==0.7.10 openstackdocstheme==1.20.0 +openstacksdk==0.11.0 oslo.config==1.2.0 pbr==2.0.0 pep8==1.5.7 diff --git a/releasenotes/notes/3_8_1_release-cb5648c3ae69bde1.yaml b/releasenotes/notes/3_8_1_release-cb5648c3ae69bde1.yaml new file mode 100644 index 0000000..7985d37 --- /dev/null +++ b/releasenotes/notes/3_8_1_release-cb5648c3ae69bde1.yaml @@ -0,0 +1,14 @@ +--- +fixes: + - | + Deleting or overwriting a symlink to an SLO or DLO will no longer attempt + to clean up the large object's segments. + - | + Fixed an issue sending non-ASCII metadata keys on Python 3. + Note that *receiving* such metadata on py3 is `still broken + <https://bugs.python.org/issue37093>`__. +other: + - | + Documentation can now be rendered as a PDF. + - | + Dropped Python 3.5 testing. diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 27f675e..662c6f6 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 current + train stein rocky queens diff --git a/releasenotes/source/train.rst b/releasenotes/source/train.rst new file mode 100644 index 0000000..5839003 --- /dev/null +++ b/releasenotes/source/train.rst @@ -0,0 +1,6 @@ +========================== +Train Series Release Notes +========================== + +.. release-notes:: + :branch: stable/train @@ -17,7 +17,6 @@ classifier = Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 diff --git a/swiftclient/authv1.py b/swiftclient/authv1.py index 55469ac..d70acac 100644 --- a/swiftclient/authv1.py +++ b/swiftclient/authv1.py @@ -45,6 +45,7 @@ from six.moves.urllib.parse import urljoin # Note that while we import keystoneauth1 here, we *don't* need to add it to # requirements.txt -- this entire module only makes sense (and should only be # loaded) if keystoneauth is already installed. +from keystoneauth1 import discover from keystoneauth1 import plugin from keystoneauth1 import exceptions from keystoneauth1 import loading @@ -110,11 +111,20 @@ class ServiceCatalogV1(object): ] def url_for(self, **kwargs): + return self.endpoint_data_for(**kwargs).url + + def endpoint_data_for(self, **kwargs): kwargs.setdefault('interface', 'public') kwargs.setdefault('service_type', None) if kwargs['service_type'] == 'object-store': - return self.storage_url + return discover.EndpointData( + service_type='object-store', + service_name='swift', + interface=kwargs['interface'], + region_name='default', + catalog_url=self.storage_url, + ) # Although our "catalog" includes an identity entry, nothing that uses # url_for() (including `openstack endpoint list`) will know what to do diff --git a/swiftclient/multithreading.py b/swiftclient/multithreading.py index fcf0ed9..f128790 100644 --- a/swiftclient/multithreading.py +++ b/swiftclient/multithreading.py @@ -168,6 +168,12 @@ class ConnectionThreadPoolExecutor(ThreadPoolExecutor): We will only create as many connections as are required concurrently. """ def __init__(self, create_connection, max_workers): + """ + Initializes a new ThreadPoolExecutor instance. + + :param create_connection: callable to use to create new connections + :param max_workers: the maximum number of threads that can be used + """ self._connections = PriorityQueue() self._create_connection = create_connection for p in range(0, max_workers): diff --git a/swiftclient/shell.py b/swiftclient/shell.py index cc4f325..5e23bc4 100755 --- a/swiftclient/shell.py +++ b/swiftclient/shell.py @@ -169,7 +169,8 @@ def st_delete(parser, args, output_manager, return_parser=False): for r in del_iter: c = r.get('container', '') o = r.get('object', '') - a = r.get('attempts') + a = (' [after {0} attempts]'.format(r.get('attempts')) + if r.get('attempts') > 1 else '') if r['action'] == 'bulk_delete': if r['success']: @@ -202,9 +203,6 @@ def st_delete(parser, args, output_manager, return_parser=False): else: if r['success']: if options['verbose']: - a = (' [after {0} attempts]'.format(a) - if a > 1 else '') - if r['action'] == 'delete_object': if options['yes_all']: p = '{0}/{1}'.format(c, o) diff --git a/test-requirements.txt b/test-requirements.txt index b3ca5f8..1373253 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,3 +4,4 @@ coverage!=4.4,>=4.0 # Apache-2.0 keystoneauth1>=3.4.0 # Apache-2.0 mock>=1.2.0 # BSD stestr>=2.0.0 # Apache-2.0 +openstacksdk>=0.11.0 # Apache-2.0 diff --git a/tests/__init__.py b/test/__init__.py index e69de29..e69de29 100644 --- a/tests/__init__.py +++ b/test/__init__.py diff --git a/test/functional/__init__.py b/test/functional/__init__.py new file mode 100644 index 0000000..248875a --- /dev/null +++ b/test/functional/__init__.py @@ -0,0 +1,93 @@ +# Copyright (c) 2014 Christian Schwede <christian.schwede@enovance.com> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from six.moves import configparser + +TEST_CONFIG = None + + +def _load_config(force_reload=False): + global TEST_CONFIG + if not force_reload and TEST_CONFIG is not None: + return TEST_CONFIG + + config_file = os.environ.get('SWIFT_TEST_CONFIG_FILE', + '/etc/swift/test.conf') + parser = configparser.ConfigParser({'auth_version': '1'}) + parser.read(config_file) + conf = {} + if parser.has_section('func_test'): + if parser.has_option('func_test', 'auth_uri'): + conf['auth_url'] = parser.get('func_test', 'auth_uri') + try: + conf['auth_version'] = parser.get('func_test', 'auth_version') + except configparser.NoOptionError: + last_piece = conf['auth_url'].rstrip('/').rsplit('/', 1)[1] + if last_piece.endswith('.0'): + last_piece = last_piece[:-2] + if last_piece in ('1', '2', '3'): + conf['auth_version'] = last_piece + else: + raise + else: + auth_host = parser.get('func_test', 'auth_host') + auth_port = parser.getint('func_test', 'auth_port') + auth_ssl = parser.getboolean('func_test', 'auth_ssl') + auth_prefix = parser.get('func_test', 'auth_prefix') + conf['auth_version'] = parser.get('func_test', 'auth_version') + if auth_ssl: + auth_url = "https://" + else: + auth_url = "http://" + auth_url += "%s:%s%s" % (auth_host, auth_port, auth_prefix) + if conf['auth_version'] == "1": + auth_url += 'v1.0' + conf['auth_url'] = auth_url + + try: + conf['account_username'] = parser.get('func_test', + 'account_username') + except configparser.NoOptionError: + conf['account'] = parser.get('func_test', 'account') + conf['username'] = parser.get('func_test', 'username') + conf['account_username'] = "%s:%s" % (conf['account'], + conf['username']) + else: + # Still try to get separate account/usernames for keystone tests + try: + conf['account'] = parser.get('func_test', 'account') + conf['username'] = parser.get('func_test', 'username') + except configparser.NoOptionError: + pass + + conf['password'] = parser.get('func_test', 'password') + + # For keystone v3 + try: + conf['account4'] = parser.get('func_test', 'account4') + conf['username4'] = parser.get('func_test', 'username4') + conf['domain4'] = parser.get('func_test', 'domain4') + conf['password4'] = parser.get('func_test', 'password4') + except configparser.NoOptionError: + pass + + TEST_CONFIG = conf + + +try: + _load_config() +except configparser.NoOptionError: + TEST_CONFIG = None # sentinel used in test setup diff --git a/test/functional/test_openstacksdk.py b/test/functional/test_openstacksdk.py new file mode 100644 index 0000000..cee7f4e --- /dev/null +++ b/test/functional/test_openstacksdk.py @@ -0,0 +1,92 @@ +# Copyright (c) 2019 Tim Burke <tim@swiftstack.com> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import uuid + +import openstack + +from . import TEST_CONFIG + +PREFIX = 'test-swiftclient-' + + +class TestOpenStackSDK(unittest.TestCase): + @classmethod + def setUpClass(cls): + # NB: Only runs for v1 auth, to exercise our keystoneauth plugin + cls.skip_tests = (TEST_CONFIG is None or + TEST_CONFIG['auth_version'] != '1') + if not cls.skip_tests: + cls.conn = openstack.connect( + auth_type='v1password', + auth_url=TEST_CONFIG['auth_url'], + username=TEST_CONFIG['account_username'], + password=TEST_CONFIG['password'], + ) + cls.object_store = cls.conn.object_store + + def setUp(self): + if self.skip_tests: + raise unittest.SkipTest('SKIPPING V1-AUTH TESTS') + + def tearDown(self): + if self.skip_tests: + return + for c in self.object_store.containers(): + if c.name.startswith(PREFIX): + for o in self.object_store.objects(c.name): + self.object_store.delete_object( + o.name, container=c.name) + self.object_store.delete_container(c.name) + + def test_containers(self): + meta = self.object_store.get_account_metadata() + count_before = meta.account_container_count + containers = sorted(PREFIX + str(uuid.uuid4()) + for _ in range(10)) + for c in containers: + self.object_store.create_container(c) + self.assertEqual([ + c.name for c in self.object_store.containers() + if c.name.startswith(PREFIX) + ], containers) + meta = self.object_store.get_account_metadata() + self.assertEqual(count_before + len(containers), + meta.account_container_count) + + def test_objects(self): + container = PREFIX + str(uuid.uuid4()) + self.object_store.create_container(container) + objects = sorted(str(uuid.uuid4()) for _ in range(10)) + for o in objects: + self.object_store.create_object(container, o, data=b'x') + self.assertEqual([ + o.name for o in self.object_store.objects(container) + ], objects) + meta = self.object_store.get_container_metadata(container) + self.assertEqual(len(objects), meta.object_count) + + def test_object_metadata(self): + container = PREFIX + str(uuid.uuid4()) + self.object_store.create_container(container) + obj = str(uuid.uuid4()) + obj_meta = {str(uuid.uuid4()): str(uuid.uuid4()) for _ in range(10)} + # NB: as of 0.36.0, create_object() doesn't play well with passing + # both data and metadata, so we do a PUT then POST + self.object_store.create_object(container, obj, data=b'x') + self.object_store.set_object_metadata(obj, container, **obj_meta) + meta = self.object_store.get_object_metadata(obj, container) + self.assertEqual(obj_meta, meta.metadata) diff --git a/tests/functional/test_swiftclient.py b/test/functional/test_swiftclient.py index 9a74c63..54c514d 100644 --- a/tests/functional/test_swiftclient.py +++ b/test/functional/test_swiftclient.py @@ -13,23 +13,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os import unittest import time from io import BytesIO import six -from six.moves import configparser import swiftclient +from . import TEST_CONFIG class TestFunctional(unittest.TestCase): def __init__(self, *args, **kwargs): super(TestFunctional, self).__init__(*args, **kwargs) - self.skip_tests = False - self._get_config() + self.skip_tests = (TEST_CONFIG is None) + if not self.skip_tests: + self._get_config() self.test_data = b'42' * 10 self.etag = '2704306ec982238d85d4b235c925d58e' @@ -41,50 +41,10 @@ class TestFunctional(unittest.TestCase): self.objectname_2 = self.objectname + '_second' def _get_config(self): - config_file = os.environ.get('SWIFT_TEST_CONFIG_FILE', - '/etc/swift/test.conf') - config = configparser.ConfigParser({'auth_version': '1'}) - config.read(config_file) - self.config = config - if config.has_section('func_test'): - if config.has_option('func_test', 'auth_uri'): - self.auth_url = config.get('func_test', 'auth_uri') - try: - self.auth_version = config.get('func_test', 'auth_version') - except configparser.NoOptionError: - last_piece = self.auth_url.rstrip('/').rsplit('/', 1)[1] - if last_piece.endswith('.0'): - last_piece = last_piece[:-2] - if last_piece in ('1', '2', '3'): - self.auth_version = last_piece - else: - raise - else: - auth_host = config.get('func_test', 'auth_host') - auth_port = config.getint('func_test', 'auth_port') - auth_ssl = config.getboolean('func_test', 'auth_ssl') - auth_prefix = config.get('func_test', 'auth_prefix') - self.auth_version = config.get('func_test', 'auth_version') - self.auth_url = "" - if auth_ssl: - self.auth_url += "https://" - else: - self.auth_url += "http://" - self.auth_url += "%s:%s%s" % ( - auth_host, auth_port, auth_prefix) - if self.auth_version == "1": - self.auth_url += 'v1.0' - - try: - self.account_username = config.get('func_test', - 'account_username') - except configparser.NoOptionError: - account = config.get('func_test', 'account') - username = config.get('func_test', 'username') - self.account_username = "%s:%s" % (account, username) - self.password = config.get('func_test', 'password') - else: - self.skip_tests = True + self.auth_url = TEST_CONFIG['auth_url'] + self.auth_version = TEST_CONFIG['auth_version'] + self.account_username = TEST_CONFIG['account_username'] + self.password = TEST_CONFIG['password'] def _get_connection(self): """ @@ -514,20 +474,20 @@ class TestUsingKeystone(TestFunctional): """ def _get_connection(self): - account = username = password = None + account = username = None if self.auth_version not in ('2', '3'): self.skipTest('SKIPPING KEYSTONE-SPECIFIC FUNCTIONAL TESTS') try: - account = self.config.get('func_test', 'account') - username = self.config.get('func_test', 'username') - password = self.config.get('func_test', 'password') - except Exception: + account = TEST_CONFIG['account'] + username = TEST_CONFIG['username'] + except KeyError: self.skipTest('SKIPPING KEYSTONE-SPECIFIC FUNCTIONAL TESTS' + ' - NO CONFIG') - os_options = {'tenant_name': account} + return swiftclient.Connection( - self.auth_url, username, password, auth_version=self.auth_version, - os_options=os_options) + self.auth_url, username, self.password, + auth_version=self.auth_version, + os_options={'tenant_name': account}) class TestUsingKeystoneV3(TestFunctional): @@ -539,13 +499,14 @@ class TestUsingKeystoneV3(TestFunctional): account = username = password = project_domain = user_domain = None if self.auth_version != '3': self.skipTest('SKIPPING KEYSTONE-V3-SPECIFIC FUNCTIONAL TESTS') + try: - account = self.config.get('func_test', 'account4') - username = self.config.get('func_test', 'username4') - user_domain = self.config.get('func_test', 'domain4') - project_domain = self.config.get('func_test', 'domain4') - password = self.config.get('func_test', 'password4') - except Exception: + account = TEST_CONFIG['account4'] + username = TEST_CONFIG['username4'] + user_domain = TEST_CONFIG['domain4'] + project_domain = TEST_CONFIG['domain4'] + password = TEST_CONFIG['password4'] + except KeyError: self.skipTest('SKIPPING KEYSTONE-V3-SPECIFIC FUNCTIONAL TESTS' + ' - NO CONFIG') diff --git a/tests/sample.conf b/test/sample.conf index 95c1a47..95c1a47 100644 --- a/tests/sample.conf +++ b/test/sample.conf diff --git a/tests/functional/__init__.py b/test/unit/__init__.py index e69de29..e69de29 100644 --- a/tests/functional/__init__.py +++ b/test/unit/__init__.py diff --git a/tests/unit/test_authv1.py b/test/unit/test_authv1.py index 2ddf24b..2ddf24b 100644 --- a/tests/unit/test_authv1.py +++ b/test/unit/test_authv1.py diff --git a/tests/unit/test_command_helpers.py b/test/unit/test_command_helpers.py index 24684ae..24684ae 100644 --- a/tests/unit/test_command_helpers.py +++ b/test/unit/test_command_helpers.py diff --git a/tests/unit/test_multithreading.py b/test/unit/test_multithreading.py index 8944d48..8944d48 100644 --- a/tests/unit/test_multithreading.py +++ b/test/unit/test_multithreading.py diff --git a/tests/unit/test_service.py b/test/unit/test_service.py index b760352..ed3a2d6 100644 --- a/tests/unit/test_service.py +++ b/test/unit/test_service.py @@ -36,7 +36,7 @@ from swiftclient.service import ( SwiftService, SwiftError, SwiftUploadObject ) -from tests.unit import utils as test_utils +from test.unit import utils as test_utils clean_os_environ = {} @@ -1060,11 +1060,11 @@ class TestService(unittest.TestCase): @mock.patch('swiftclient.service.getsize', return_value=4) def test_upload_with_relative_path(self, *args, **kwargs): service = SwiftService({}) - objects = [{'path': "./test", + objects = [{'path': "./testobj", 'strt_indx': 2}, - {'path': os.path.join(os.getcwd(), "test"), + {'path': os.path.join(os.getcwd(), "testobj"), 'strt_indx': 1}, - {'path': ".\\test", + {'path': ".\\testobj", 'strt_indx': 2}] for obj in objects: with mock.patch('swiftclient.service.Connection') as mock_conn, \ diff --git a/tests/unit/test_shell.py b/test/unit/test_shell.py index c972281..c972281 100644 --- a/tests/unit/test_shell.py +++ b/test/unit/test_shell.py diff --git a/tests/unit/test_swiftclient.py b/test/unit/test_swiftclient.py index 2d45deb..2d45deb 100644 --- a/tests/unit/test_swiftclient.py +++ b/test/unit/test_swiftclient.py diff --git a/tests/unit/test_utils.py b/test/unit/test_utils.py index 97abc44..97abc44 100644 --- a/tests/unit/test_utils.py +++ b/test/unit/test_utils.py diff --git a/tests/unit/utils.py b/test/unit/utils.py index 025a234..025a234 100644 --- a/tests/unit/utils.py +++ b/test/unit/utils.py diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/tests/unit/__init__.py +++ /dev/null @@ -1,5 +1,5 @@ [tox] -envlist = py37,py36,py35,py27,pypy,pep8 +envlist = py27,py37,pypy,pep8 minversion = 2.0 skipsdist = True @@ -24,7 +24,7 @@ passenv = SWIFT_* *_proxy [testenv:pep8] basepython = python3 commands = - python -m flake8 swiftclient tests + python -m flake8 swiftclient test [testenv:venv] basepython = python3 @@ -44,7 +44,7 @@ commands = [testenv:func] basepython = python3 setenv = - OS_TEST_PATH=tests.functional + OS_TEST_PATH=test.functional PYTHON=coverage run --source swiftclient --parallel-mode whitelist_externals = coverage @@ -109,3 +109,12 @@ deps = -c{toxinidir}/lower-constraints.txt -r{toxinidir}/test-requirements.txt .[keystone] + +[testenv:pdf-docs] +basepython = python3 +deps = {[testenv:docs]deps} +whitelist_externals = + make +commands = + sphinx-build -W -b latex doc/source doc/build/pdf + make -C doc/build/pdf |