diff options
-rw-r--r-- | MAINTAINERS | 15 | ||||
-rw-r--r-- | README | 11 | ||||
-rw-r--r-- | README.rst | 31 | ||||
-rw-r--r-- | contrib/redhat-eventlet.patch | 16 | ||||
-rw-r--r-- | doc/source/conf.py | 8 | ||||
-rw-r--r-- | openstack/common/lockutils.py | 29 | ||||
-rw-r--r-- | openstack/common/processutils.py | 66 | ||||
-rw-r--r-- | openstack/common/rootwrap/filters.py | 20 | ||||
-rw-r--r-- | openstack/common/rootwrap/wrapper.py | 8 | ||||
-rw-r--r-- | openstack/common/rpc/impl_qpid.py | 2 | ||||
-rw-r--r-- | openstack/common/strutils.py | 15 | ||||
-rw-r--r-- | tests/unit/rpc/test_common.py | 8 | ||||
-rw-r--r-- | tests/unit/test_lockutils.py | 16 | ||||
-rw-r--r-- | tests/unit/test_processutils.py | 85 | ||||
-rw-r--r-- | tests/unit/test_strutils.py | 94 | ||||
-rw-r--r-- | tests/unit/test_timeutils.py | 26 | ||||
-rw-r--r-- | tests/unit/test_wsgi.py | 3 | ||||
-rw-r--r-- | tools/patch_tox_venv.py | 38 | ||||
-rw-r--r-- | tox.ini | 4 | ||||
-rw-r--r-- | update.py | 7 |
20 files changed, 392 insertions, 110 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 83822b1..55bc962 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -205,6 +205,13 @@ M: Michael Still <mikal@stillhq.com> S: Maintained F: processutils.py +== redhat-eventlet.patch == + +M: Mark McLoughlin <markmc@redhat.com> +S: Maintained +F: contrib/redhat-eventlet.patch +F: tools/patch_tox_venv.py + == rootwrap == M: Thierry Carrez <thierry@openstack.org> @@ -250,14 +257,14 @@ F: threadgroup.py == timeutils == -M: -S: Orphan +M: Zhongyue Luo <zhongyue.nah@intel.com> +S: Maintained F: timeutils.py == uuidutils == -M: -S: Orphan +M: Zhongyue Luo <zhongyue.nah@intel.com> +S: Maintained F: uuidutils.py == wsgi == @@ -1,11 +0,0 @@ -Common code for Openstack Projects. - -To run tests in virtualenvs (preferred): - - sudo pip install tox - tox - -To run tests in the current environment: - - sudo pip install -r tools/pip-requires - nosetests diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..33b88ab --- /dev/null +++ b/README.rst @@ -0,0 +1,31 @@ +------------------ +The Oslo Incubator +------------------ + +The Oslo program produces a set of python libraries containing +infrastructure code shared by OpenStack projects. The APIs provided by +these libraries should be high quality, stable, consistent and +generally useful. + +The process of developing a new Oslo API usually begins by taking code +which is common to some OpenStack projects and moving it into this +repository. Incubation shouldn't be seen as a long term option for any +API - it is merely a stepping stone to inclusion into a published Oslo +library. + +For more information, see our wiki page: + + https://wiki.openstack.org/wiki/Oslo + +Running Tests +------------- + +To run tests in virtualenvs (preferred): + + sudo pip install tox + tox + +To run tests in the current environment: + + sudo pip install -r tools/pip-requires + nosetests diff --git a/contrib/redhat-eventlet.patch b/contrib/redhat-eventlet.patch new file mode 100644 index 0000000..cf2ff53 --- /dev/null +++ b/contrib/redhat-eventlet.patch @@ -0,0 +1,16 @@ +--- .nova-venv/lib/python2.6/site-packages/eventlet/green/subprocess.py.orig +2011-05-25 +23:31:34.597271402 +0000 ++++ .nova-venv/lib/python2.6/site-packages/eventlet/green/subprocess.py +2011-05-25 +23:33:24.055602468 +0000 +@@ -32,7 +32,7 @@ + setattr(self, attr, wrapped_pipe) + __init__.__doc__ = subprocess_orig.Popen.__init__.__doc__ + +- def wait(self, check_interval=0.01): ++ def wait(self, check_interval=0.01, timeout=None): + # Instead of a blocking OS call, this version of wait() uses logic + # borrowed from the eventlet 0.2 processes.Process.wait() method. + try: + diff --git a/doc/source/conf.py b/doc/source/conf.py index d126a23..9088cfe 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -24,8 +24,8 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'Oslo' -copyright = u'2012, OpenStack Foundation' +project = 'Oslo' +copyright = '2012, OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True @@ -57,8 +57,8 @@ html_last_updated_fmt = os.popen(git_cmd).read() latex_documents = [ ('index', '%s.tex' % project, - u'%s Documentation' % project, - u'OpenStack Foundation', 'manual'), + '%s Documentation' % project, + 'OpenStack Foundation', 'manual'), ] # Example configuration for intersphinx: refer to the Python standard library. diff --git a/openstack/common/lockutils.py b/openstack/common/lockutils.py index 892d821..79d1905 100644 --- a/openstack/common/lockutils.py +++ b/openstack/common/lockutils.py @@ -49,6 +49,10 @@ CONF = cfg.CONF CONF.register_opts(util_opts) +def set_defaults(lock_path): + cfg.set_defaults(util_opts, lock_path=lock_path) + + class _InterProcessLock(object): """Lock implementation which allows multiple locks, working around issues like bugs.debian.org/cgi-bin/bugreport.cgi?bug=632857 and does @@ -247,3 +251,28 @@ def synchronized(name, lock_file_prefix, external=False, lock_path=None): return retval return inner return wrap + + +def synchronized_with_prefix(lock_file_prefix): + """Partial object generator for the synchronization decorator. + + Redefine @synchronized in each project like so:: + + (in nova/utils.py) + from nova.openstack.common import lockutils + + synchronized = lockutils.synchronized_with_prefix('nova-') + + + (in nova/foo.py) + from nova import utils + + @utils.synchronized('mylock') + def bar(self, *args): + ... + + The lock_file_prefix argument is used to provide lock files on disk with a + meaningful prefix. The prefix should end with a hyphen ('-') if specified. + """ + + return functools.partial(synchronized, lock_file_prefix=lock_file_prefix) diff --git a/openstack/common/processutils.py b/openstack/common/processutils.py index 560055e..09baea3 100644 --- a/openstack/common/processutils.py +++ b/openstack/common/processutils.py @@ -34,6 +34,11 @@ from openstack.common import log as logging LOG = logging.getLogger(__name__) +class InvalidArgumentError(Exception): + def __init__(self, message=None): + super(InvalidArgumentError, self).__init__(message) + + class UnknownArgumentError(Exception): def __init__(self, message=None): super(UnknownArgumentError, self).__init__(message) @@ -179,3 +184,64 @@ def execute(*cmd, **kwargs): # call clean something up in between calls, without # it two execute calls in a row hangs the second one greenthread.sleep(0) + + +def trycmd(*args, **kwargs): + """ + A wrapper around execute() to more easily handle warnings and errors. + + Returns an (out, err) tuple of strings containing the output of + the command's stdout and stderr. If 'err' is not empty then the + command can be considered to have failed. + + :discard_warnings True | False. Defaults to False. If set to True, + then for succeeding commands, stderr is cleared + + """ + discard_warnings = kwargs.pop('discard_warnings', False) + + try: + out, err = execute(*args, **kwargs) + failed = False + except ProcessExecutionError, exn: + out, err = '', str(exn) + failed = True + + if not failed and discard_warnings and err: + # Handle commands that output to stderr but otherwise succeed + err = '' + + return out, err + + +def ssh_execute(ssh, cmd, process_input=None, + addl_env=None, check_exit_code=True): + LOG.debug(_('Running cmd (SSH): %s'), cmd) + if addl_env: + raise InvalidArgumentError(_('Environment not supported over SSH')) + + if process_input: + # This is (probably) fixable if we need it... + raise InvalidArgumentError(_('process_input not supported over SSH')) + + stdin_stream, stdout_stream, stderr_stream = ssh.exec_command(cmd) + channel = stdout_stream.channel + + # NOTE(justinsb): This seems suspicious... + # ...other SSH clients have buffering issues with this approach + stdout = stdout_stream.read() + stderr = stderr_stream.read() + stdin_stream.close() + + exit_status = channel.recv_exit_status() + + # exit_status == -1 if no exit code was returned + if exit_status != -1: + LOG.debug(_('Result was %s') % exit_status) + if check_exit_code and exit_status != 0: + raise ProcessExecutionError(exit_code=exit_status, + stdout=stdout, + stderr=stderr, + cmd=cmd) + + return (stdout, stderr) diff --git a/openstack/common/rootwrap/filters.py b/openstack/common/rootwrap/filters.py index d9618af..58121cb 100644 --- a/openstack/common/rootwrap/filters.py +++ b/openstack/common/rootwrap/filters.py @@ -20,7 +20,7 @@ import re class CommandFilter(object): - """Command filter only checking that the 1st argument matches exec_path""" + """Command filter only checking that the 1st argument matches exec_path.""" def __init__(self, exec_path, run_as, *args): self.name = '' @@ -30,7 +30,7 @@ class CommandFilter(object): self.real_exec = None def get_exec(self, exec_dirs=[]): - """Returns existing executable, or empty string if none found""" + """Returns existing executable, or empty string if none found.""" if self.real_exec is not None: return self.real_exec self.real_exec = "" @@ -46,10 +46,8 @@ class CommandFilter(object): return self.real_exec def match(self, userargs): - """Only check that the first argument (command) matches exec_path""" - if (os.path.basename(self.exec_path) == userargs[0]): - return True - return False + """Only check that the first argument (command) matches exec_path.""" + return os.path.basename(self.exec_path) == userargs[0] def get_command(self, userargs, exec_dirs=[]): """Returns command to execute (with sudo -u if run_as != root).""" @@ -60,12 +58,12 @@ class CommandFilter(object): return [to_exec] + userargs[1:] def get_environment(self, userargs): - """Returns specific environment to set, None if none""" + """Returns specific environment to set, None if none.""" return None class RegExpFilter(CommandFilter): - """Command filter doing regexp matching for every argument""" + """Command filter doing regexp matching for every argument.""" def match(self, userargs): # Early skip if command or number of args don't match @@ -135,7 +133,7 @@ class PathFilter(CommandFilter): class DnsmasqFilter(CommandFilter): - """Specific filter for the dnsmasq call (which includes env)""" + """Specific filter for the dnsmasq call (which includes env).""" CONFIG_FILE_ARG = 'CONFIG_FILE' @@ -160,7 +158,7 @@ class DnsmasqFilter(CommandFilter): class DeprecatedDnsmasqFilter(DnsmasqFilter): - """Variant of dnsmasq filter to support old-style FLAGFILE""" + """Variant of dnsmasq filter to support old-style FLAGFILE.""" CONFIG_FILE_ARG = 'FLAGFILE' @@ -210,7 +208,7 @@ class KillFilter(CommandFilter): class ReadFileFilter(CommandFilter): - """Specific filter for the utils.read_file_as_root call""" + """Specific filter for the utils.read_file_as_root call.""" def __init__(self, file_path, *args): self.file_path = file_path diff --git a/openstack/common/rootwrap/wrapper.py b/openstack/common/rootwrap/wrapper.py index e0ac9df..d488ddd 100644 --- a/openstack/common/rootwrap/wrapper.py +++ b/openstack/common/rootwrap/wrapper.py @@ -93,7 +93,7 @@ def setup_syslog(execname, facility, level): def build_filter(class_name, *args): - """Returns a filter object of class class_name""" + """Returns a filter object of class class_name.""" if not hasattr(filters, class_name): logging.warning("Skipping unknown filter class (%s) specified " "in filter definitions" % class_name) @@ -103,7 +103,7 @@ def build_filter(class_name, *args): def load_filters(filters_path): - """Load filters from a list of directories""" + """Load filters from a list of directories.""" filterlist = [] for filterdir in filters_path: if not os.path.isdir(filterdir): @@ -121,7 +121,7 @@ def load_filters(filters_path): return filterlist -def match_filter(filters, userargs, exec_dirs=[]): +def match_filter(filter_list, userargs, exec_dirs=[]): """ Checks user command and arguments through command filters and returns the first matching filter. @@ -131,7 +131,7 @@ def match_filter(filters, userargs, exec_dirs=[]): """ first_not_executable_filter = None - for f in filters: + for f in filter_list: if f.match(userargs): # Try other filters if executable is absent if not f.get_exec(exec_dirs=exec_dirs): diff --git a/openstack/common/rpc/impl_qpid.py b/openstack/common/rpc/impl_qpid.py index a7b4707..a03ebb2 100644 --- a/openstack/common/rpc/impl_qpid.py +++ b/openstack/common/rpc/impl_qpid.py @@ -375,7 +375,7 @@ class Connection(object): try: return method(*args, **kwargs) except (qpid_exceptions.Empty, - qpid_exceptions.ConnectionError), e: + qpid_exceptions.ConnectionError) as e: if error_callback: error_callback(e) self.reconnect() diff --git a/openstack/common/strutils.py b/openstack/common/strutils.py index fe8418e..d6dfb13 100644 --- a/openstack/common/strutils.py +++ b/openstack/common/strutils.py @@ -49,12 +49,15 @@ def bool_from_string(subject): Useful for JSON-decoded stuff and config file parsing """ - if isinstance(subject, bool): - return subject - if isinstance(subject, basestring): - if subject.strip().lower() in ('true', 'on', 'yes', '1'): - return True - return False + try: + # True or 1 or '1' -> True + # False or < 1 or > 1 or '0' -> False + return int(subject) == 1 + except TypeError: + # None -> False + return False + except ValueError: + return subject.strip().lower() in ('true', 'on', 'yes') def safe_decode(text, incoming=None, errors='strict'): diff --git a/tests/unit/rpc/test_common.py b/tests/unit/rpc/test_common.py index 18951df..1f07ff7 100644 --- a/tests/unit/rpc/test_common.py +++ b/tests/unit/rpc/test_common.py @@ -153,15 +153,15 @@ class RpcCommonTestCase(test_utils.BaseTestCase): 'class': 'FakeUserDefinedException', 'module': self.__class__.__module__, 'tb': ['raise FakeUserDefinedException'], - 'args': (u'fakearg',), - 'kwargs': {u'fakekwarg': u'fake'}, + 'args': ('fakearg',), + 'kwargs': {'fakekwarg': 'fake'}, } serialized = jsonutils.dumps(failure) after_exc = rpc_common.deserialize_remote_exception(FLAGS, serialized) self.assertTrue(isinstance(after_exc, FakeUserDefinedException)) - self.assertEqual(after_exc.args, (u'fakearg',)) - self.assertEqual(after_exc.kwargs, {u'fakekwarg': u'fake'}) + self.assertEqual(after_exc.args, ('fakearg',)) + self.assertEqual(after_exc.kwargs, {'fakekwarg': 'fake'}) def test_deserialize_remote_exception_cannot_recreate(self): """Ensure a RemoteError is returned on initialization failure. diff --git a/tests/unit/test_lockutils.py b/tests/unit/test_lockutils.py index 1230569..c37b030 100644 --- a/tests/unit/test_lockutils.py +++ b/tests/unit/test_lockutils.py @@ -195,3 +195,19 @@ class LockTestCase(utils.BaseTestCase): finally: if os.path.exists(lock_dir): shutil.rmtree(lock_dir, ignore_errors=True) + + def test_synchronized_with_prefix(self): + lock_name = 'mylock' + lock_pfix = 'mypfix-' + + foo = lockutils.synchronized_with_prefix(lock_pfix) + + @foo(lock_name, external=True) + def bar(dirpath, pfix, name): + filepath = os.path.join(dirpath, '%s%s' % (pfix, name)) + return os.path.isfile(filepath) + + lock_dir = tempfile.mkdtemp() + self.config(lock_path=lock_dir) + + self.assertTrue(bar(lock_dir, lock_pfix, lock_name)) diff --git a/tests/unit/test_processutils.py b/tests/unit/test_processutils.py index 2b99d24..e00a66e 100644 --- a/tests/unit/test_processutils.py +++ b/tests/unit/test_processutils.py @@ -17,7 +17,9 @@ from __future__ import print_function +import fixtures import os +import StringIO import tempfile from openstack.common import processutils @@ -164,3 +166,86 @@ grep foo finally: os.unlink(tmpfilename) os.unlink(tmpfilename2) + + +def fake_execute(*cmd, **kwargs): + return 'stdout', 'stderr' + + +def fake_execute_raises(*cmd, **kwargs): + raise processutils.ProcessExecutionError(exit_code=42, + stdout='stdout', + stderr='stderr', + cmd=['this', 'is', 'a', + 'command']) + + +class TryCmdTestCase(utils.BaseTestCase): + def test_keep_warnings(self): + self.useFixture(fixtures.MonkeyPatch( + 'openstack.common.processutils.execute', fake_execute)) + o, e = processutils.trycmd('this is a command'.split(' ')) + self.assertNotEqual('', o) + self.assertNotEqual('', e) + + def test_keep_warnings_from_raise(self): + self.useFixture(fixtures.MonkeyPatch( + 'openstack.common.processutils.execute', fake_execute_raises)) + o, e = processutils.trycmd('this is a command'.split(' '), + discard_warnings=True) + self.assertNotEqual(None, o) + self.assertNotEqual('', e) + + def test_discard_warnings(self): + self.useFixture(fixtures.MonkeyPatch( + 'openstack.common.processutils.execute', fake_execute)) + o, e = processutils.trycmd('this is a command'.split(' '), + discard_warnings=True) + self.assertNotEqual(None, o) + self.assertEqual('', e) + + +class FakeSshChannel(object): + def __init__(self, rc): + self.rc = rc + + def recv_exit_status(self): + return self.rc + + +class FakeSshStream(StringIO.StringIO): + def setup_channel(self, rc): + self.channel = FakeSshChannel(rc) + + +class FakeSshConnection(object): + def __init__(self, rc): + self.rc = rc + + def exec_command(self, cmd): + stdout = FakeSshStream('stdout') + stdout.setup_channel(self.rc) + return (StringIO.StringIO(), + stdout, + StringIO.StringIO('stderr')) + + +class SshExecuteTestCase(utils.BaseTestCase): + def test_invalid_addl_env(self): + self.assertRaises(processutils.InvalidArgumentError, + processutils.ssh_execute, + None, 'ls', addl_env='important') + + def test_invalid_process_input(self): + self.assertRaises(processutils.InvalidArgumentError, + processutils.ssh_execute, + None, 'ls', process_input='important') + + def test_works(self): + o, e = processutils.ssh_execute(FakeSshConnection(0), 'ls') + self.assertEqual('stdout', o) + self.assertEqual('stderr', e) + + def test_fails(self): + self.assertRaises(processutils.ProcessExecutionError, + processutils.ssh_execute, FakeSshConnection(1), 'ls') diff --git a/tests/unit/test_strutils.py b/tests/unit/test_strutils.py index e2c4622..b0af958 100644 --- a/tests/unit/test_strutils.py +++ b/tests/unit/test_strutils.py @@ -16,6 +16,7 @@ # under the License. import mock +import six from openstack.common import strutils from tests import utils @@ -27,48 +28,47 @@ class StrUtilsTest(utils.BaseTestCase): self.assertTrue(strutils.bool_from_string(True)) self.assertFalse(strutils.bool_from_string(False)) - def test_str_bool_from_string(self): - self.assertTrue(strutils.bool_from_string('true')) - self.assertTrue(strutils.bool_from_string('TRUE')) - self.assertTrue(strutils.bool_from_string('on')) - self.assertTrue(strutils.bool_from_string('On')) - self.assertTrue(strutils.bool_from_string('yes')) - self.assertTrue(strutils.bool_from_string('YES')) - self.assertTrue(strutils.bool_from_string('yEs')) - self.assertTrue(strutils.bool_from_string('1')) - - self.assertFalse(strutils.bool_from_string('false')) - self.assertFalse(strutils.bool_from_string('FALSE')) - self.assertFalse(strutils.bool_from_string('off')) - self.assertFalse(strutils.bool_from_string('OFF')) - self.assertFalse(strutils.bool_from_string('no')) - self.assertFalse(strutils.bool_from_string('0')) - self.assertFalse(strutils.bool_from_string('42')) - self.assertFalse(strutils.bool_from_string('This should not be True')) + def _test_bool_from_string(self, c): + self.assertTrue(strutils.bool_from_string(c('true'))) + self.assertTrue(strutils.bool_from_string(c('TRUE'))) + self.assertTrue(strutils.bool_from_string(c('on'))) + self.assertTrue(strutils.bool_from_string(c('On'))) + self.assertTrue(strutils.bool_from_string(c('yes'))) + self.assertTrue(strutils.bool_from_string(c('YES'))) + self.assertTrue(strutils.bool_from_string(c('yEs'))) + self.assertTrue(strutils.bool_from_string(c('1'))) + + self.assertFalse(strutils.bool_from_string(c('false'))) + self.assertFalse(strutils.bool_from_string(c('FALSE'))) + self.assertFalse(strutils.bool_from_string(c('off'))) + self.assertFalse(strutils.bool_from_string(c('OFF'))) + self.assertFalse(strutils.bool_from_string(c('no'))) + self.assertFalse(strutils.bool_from_string(c('0'))) + self.assertFalse(strutils.bool_from_string(c('42'))) + self.assertFalse(strutils.bool_from_string(c( + 'This should not be True'))) + + # Whitespace should be stripped + self.assertTrue(strutils.bool_from_string(c(' true '))) + self.assertFalse(strutils.bool_from_string(c(' false '))) + + def test_bool_from_string(self): + self._test_bool_from_string(lambda s: s) def test_unicode_bool_from_string(self): - self.assertTrue(strutils.bool_from_string(u'true')) - self.assertTrue(strutils.bool_from_string(u'TRUE')) - self.assertTrue(strutils.bool_from_string(u'on')) - self.assertTrue(strutils.bool_from_string(u'On')) - self.assertTrue(strutils.bool_from_string(u'yes')) - self.assertTrue(strutils.bool_from_string(u'YES')) - self.assertTrue(strutils.bool_from_string(u'yEs')) - self.assertTrue(strutils.bool_from_string(u'1')) - - self.assertFalse(strutils.bool_from_string(u'false')) - self.assertFalse(strutils.bool_from_string(u'FALSE')) - self.assertFalse(strutils.bool_from_string(u'off')) - self.assertFalse(strutils.bool_from_string(u'OFF')) - self.assertFalse(strutils.bool_from_string(u'no')) - self.assertFalse(strutils.bool_from_string(u'NO')) - self.assertFalse(strutils.bool_from_string(u'0')) - self.assertFalse(strutils.bool_from_string(u'42')) - self.assertFalse(strutils.bool_from_string(u'This should not be True')) + self._test_bool_from_string(six.text_type) def test_other_bool_from_string(self): + self.assertFalse(strutils.bool_from_string(None)) self.assertFalse(strutils.bool_from_string(mock.Mock())) + def test_int_bool_from_string(self): + self.assertTrue(strutils.bool_from_string(1)) + + self.assertFalse(strutils.bool_from_string(-1)) + self.assertFalse(strutils.bool_from_string(0)) + self.assertFalse(strutils.bool_from_string(2)) + def test_int_from_bool_as_string(self): self.assertEqual(1, strutils.int_from_bool_as_string(True)) self.assertEqual(0, strutils.int_from_bool_as_string(False)) @@ -76,25 +76,25 @@ class StrUtilsTest(utils.BaseTestCase): def test_safe_decode(self): safe_decode = strutils.safe_decode self.assertRaises(TypeError, safe_decode, True) - self.assertEqual(u'ni\xf1o', safe_decode("ni\xc3\xb1o", - incoming="utf-8")) - self.assertEqual(u"test", safe_decode("dGVzdA==", - incoming='base64')) + self.assertEqual(six.u('ni\xf1o'), safe_decode("ni\xc3\xb1o", + incoming="utf-8")) + self.assertEqual(six.u("test"), safe_decode("dGVzdA==", + incoming='base64')) - self.assertEqual(u"strange", safe_decode('\x80strange', - errors='ignore')) + self.assertEqual(six.u("strange"), safe_decode('\x80strange', + errors='ignore')) - self.assertEqual(u'\xc0', safe_decode('\xc0', - incoming='iso-8859-1')) + self.assertEqual(six.u('\xc0'), safe_decode('\xc0', + incoming='iso-8859-1')) # Forcing incoming to ascii so it falls back to utf-8 - self.assertEqual(u'ni\xf1o', safe_decode('ni\xc3\xb1o', - incoming='ascii')) + self.assertEqual(six.u('ni\xf1o'), safe_decode('ni\xc3\xb1o', + incoming='ascii')) def test_safe_encode(self): safe_encode = strutils.safe_encode self.assertRaises(TypeError, safe_encode, True) - self.assertEqual("ni\xc3\xb1o", safe_encode(u'ni\xf1o', + self.assertEqual("ni\xc3\xb1o", safe_encode(six.u('ni\xf1o'), encoding="utf-8")) self.assertEqual("dGVzdA==\n", safe_encode("test", encoding='base64')) diff --git a/tests/unit/test_timeutils.py b/tests/unit/test_timeutils.py index bcb6a69..bfab278 100644 --- a/tests/unit/test_timeutils.py +++ b/tests/unit/test_timeutils.py @@ -201,31 +201,31 @@ class TestIso8601Time(utils.BaseTestCase): def test_zulu(self): str = '2012-02-14T20:53:07Z' - self._do_test(str, 2012, 02, 14, 20, 53, 7, 0, 0) + self._do_test(str, 2012, 2, 14, 20, 53, 7, 0, 0) def test_zulu_micros(self): str = '2012-02-14T20:53:07.123Z' - self._do_test(str, 2012, 02, 14, 20, 53, 7, 123000, 0) + self._do_test(str, 2012, 2, 14, 20, 53, 7, 123000, 0) def test_offset_east(self): str = '2012-02-14T20:53:07+04:30' offset = 4.5 * 60 * 60 - self._do_test(str, 2012, 02, 14, 20, 53, 7, 0, offset) + self._do_test(str, 2012, 2, 14, 20, 53, 7, 0, offset) def test_offset_east_micros(self): str = '2012-02-14T20:53:07.42+04:30' offset = 4.5 * 60 * 60 - self._do_test(str, 2012, 02, 14, 20, 53, 7, 420000, offset) + self._do_test(str, 2012, 2, 14, 20, 53, 7, 420000, offset) def test_offset_west(self): str = '2012-02-14T20:53:07-05:30' offset = -5.5 * 60 * 60 - self._do_test(str, 2012, 02, 14, 20, 53, 7, 0, offset) + self._do_test(str, 2012, 2, 14, 20, 53, 7, 0, offset) def test_offset_west_micros(self): str = '2012-02-14T20:53:07.654321-05:30' offset = -5.5 * 60 * 60 - self._do_test(str, 2012, 02, 14, 20, 53, 7, 654321, offset) + self._do_test(str, 2012, 2, 14, 20, 53, 7, 654321, offset) def test_compare(self): zulu = timeutils.parse_isotime('2012-02-14T20:53:07') @@ -271,36 +271,36 @@ class TestIso8601Time(utils.BaseTestCase): str = '2012-02-14T20:53:07Z' zulu = timeutils.parse_isotime(str) normed = timeutils.normalize_time(zulu) - self._instaneous(normed, 2012, 2, 14, 20, 53, 07, 0) + self._instaneous(normed, 2012, 2, 14, 20, 53, 7, 0) def test_east_normalize(self): str = '2012-02-14T20:53:07-07:00' east = timeutils.parse_isotime(str) normed = timeutils.normalize_time(east) - self._instaneous(normed, 2012, 2, 15, 03, 53, 07, 0) + self._instaneous(normed, 2012, 2, 15, 3, 53, 7, 0) def test_west_normalize(self): str = '2012-02-14T20:53:07+21:00' west = timeutils.parse_isotime(str) normed = timeutils.normalize_time(west) - self._instaneous(normed, 2012, 2, 13, 23, 53, 07, 0) + self._instaneous(normed, 2012, 2, 13, 23, 53, 7, 0) def test_normalize_aware_to_naive(self): - dt = datetime.datetime(2011, 2, 14, 20, 53, 07) + dt = datetime.datetime(2011, 2, 14, 20, 53, 7) str = '2011-02-14T20:53:07+21:00' aware = timeutils.parse_isotime(str) naive = timeutils.normalize_time(aware) self.assertTrue(naive < dt) def test_normalize_zulu_aware_to_naive(self): - dt = datetime.datetime(2011, 2, 14, 20, 53, 07) + dt = datetime.datetime(2011, 2, 14, 20, 53, 7) str = '2011-02-14T19:53:07Z' aware = timeutils.parse_isotime(str) naive = timeutils.normalize_time(aware) self.assertTrue(naive < dt) def test_normalize_naive(self): - dt = datetime.datetime(2011, 2, 14, 20, 53, 07) - dtn = datetime.datetime(2011, 2, 14, 19, 53, 07) + dt = datetime.datetime(2011, 2, 14, 20, 53, 7) + dtn = datetime.datetime(2011, 2, 14, 19, 53, 7) naive = timeutils.normalize_time(dtn) self.assertTrue(naive < dt) diff --git a/tests/unit/test_wsgi.py b/tests/unit/test_wsgi.py index 94d8092..a3a4d32 100644 --- a/tests/unit/test_wsgi.py +++ b/tests/unit/test_wsgi.py @@ -21,6 +21,7 @@ import urllib2 import mock import routes +import six import webob from openstack.common import exception @@ -189,7 +190,7 @@ class JSONDictSerializerTest(utils.BaseTestCase): def test_object_unicode(self): class TestUnicode: def __unicode__(self): - return u'TestUnicode' + return six.u('TestUnicode') input_dict = dict(cls=TestUnicode()) expected_str = '{"cls":"TestUnicode"}' serializer = wsgi.JSONDictSerializer() diff --git a/tools/patch_tox_venv.py b/tools/patch_tox_venv.py new file mode 100644 index 0000000..e23cf74 --- /dev/null +++ b/tools/patch_tox_venv.py @@ -0,0 +1,38 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 Red Hat, Inc. +# +# 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 +import sys + +import install_venv_common as install_venv + + +def main(argv): + root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + + venv = os.environ['VIRTUAL_ENV'] + + pip_requires = os.path.join(root, 'tools', 'pip-requires') + test_requires = os.path.join(root, 'tools', 'test-requires') + py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) + project = 'oslo' + install = install_venv.InstallVenv(root, venv, pip_requires, test_requires, + py_version, project) + #NOTE(dprince): For Tox we only run post_process (which patches files, etc) + install.post_process() + +if __name__ == '__main__': + main(sys.argv) @@ -11,7 +11,9 @@ setenv = VIRTUAL_ENV={envdir} NOSE_OPENSTACK_STDOUT=1 deps = -r{toxinidir}/tools/pip-requires -r{toxinidir}/tools/test-requires -commands = nosetests --with-doctest --exclude-dir=tests/testmods {posargs} +commands = + python tools/patch_tox_venv.py + nosetests --with-doctest --exclude-dir=tests/testmods {posargs} [flake8] show-source = True @@ -173,8 +173,7 @@ def _copy_pyfile(path, base, dest_dir): def _copy_module(mod, base, dest_dir): - print(("Copying openstack.common.%s under the %s module in %s" % - (mod, base, dest_dir))) + print("Copying %s under the %s module in %s" % (mod, base, dest_dir)) copy_pyfile = functools.partial(_copy_pyfile, base=base, dest_dir=dest_dir) @@ -183,7 +182,8 @@ def _copy_module(mod, base, dest_dir): path = _mod_to_path('openstack.common') for d in mod.split('.')[:-1]: path = os.path.join(path, d) - copy_pyfile(os.path.join(path, '__init__.py')) + if os.path.isdir(path): + copy_pyfile(os.path.join(path, '__init__.py')) mod_path = _mod_to_path('openstack.common.%s' % mod) mod_file = '%s.py' % mod_path @@ -200,6 +200,7 @@ def _copy_module(mod, base, dest_dir): globs_to_copy = [ os.path.join('tools', mod + '*'), os.path.join('etc', 'oslo', mod + '*.conf'), + os.path.join('contrib', mod + '*'), ] for matches in [glob.glob(g) for g in globs_to_copy]: |