oslo.utils-4.1.1/0000775000175000017500000000000013643050530013651 5ustar zuulzuul00000000000000oslo.utils-4.1.1/.zuul.yaml0000664000175000017500000000041313643050415015612 0ustar zuulzuul00000000000000- project: templates: - check-requirements - lib-forward-testing-python3 - openstack-lower-constraints-jobs - openstack-python3-ussuri-jobs - periodic-stable-jobs - publish-openstack-docs-pti - release-notes-jobs-python3 oslo.utils-4.1.1/requirements.txt0000664000175000017500000000131413643050415017136 0ustar zuulzuul00000000000000# 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. # NOTE(dhellmann): Because oslo.utils is used by the client libraries, # we do not want to add a lot of dependencies to it. If you find that # adding a new feature to oslo.utils means adding a new dependency, # that is a likely indicator that the feature belongs somewhere else. pbr!=2.1.0,>=2.0.0 # Apache-2.0 six>=1.10.0 # MIT iso8601>=0.1.11 # MIT oslo.i18n>=3.15.3 # Apache-2.0 pytz>=2013.6 # MIT netaddr>=0.7.18 # BSD netifaces>=0.10.4 # MIT debtcollector>=1.2.0 # Apache-2.0 pyparsing>=2.1.0 # MIT oslo.utils-4.1.1/setup.py0000664000175000017500000000127113643050415015366 0ustar zuulzuul00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # 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 setuptools setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) oslo.utils-4.1.1/oslo_utils/0000775000175000017500000000000013643050530016045 5ustar zuulzuul00000000000000oslo.utils-4.1.1/oslo_utils/reflection.py0000664000175000017500000001676613643050415020573 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2012-2013 Yahoo! Inc. All Rights Reserved. # # 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. """ Reflection module. .. versionadded:: 1.1 """ import inspect import types import six try: _TYPE_TYPE = types.TypeType except AttributeError: _TYPE_TYPE = type # See: https://docs.python.org/2/library/__builtin__.html#module-__builtin__ # and see https://docs.python.org/2/reference/executionmodel.html (and likely # others)... _BUILTIN_MODULES = ('builtins', '__builtin__', '__builtins__', 'exceptions') if six.PY3: Parameter = inspect.Parameter Signature = inspect.Signature get_signature = inspect.signature else: # Provide an equivalent but use funcsigs instead... import funcsigs Parameter = funcsigs.Parameter Signature = funcsigs.Signature get_signature = funcsigs.signature def get_members(obj, exclude_hidden=True): """Yields the members of an object, filtering by hidden/not hidden. .. versionadded:: 2.3 """ for (name, value) in inspect.getmembers(obj): if name.startswith("_") and exclude_hidden: continue yield (name, value) def get_member_names(obj, exclude_hidden=True): """Get all the member names for a object.""" return [name for (name, _obj) in get_members(obj, exclude_hidden=exclude_hidden)] def get_class_name(obj, fully_qualified=True, truncate_builtins=True): """Get class name for object. If object is a type, returns name of the type. If object is a bound method or a class method, returns its ``self`` object's class name. If object is an instance of class, returns instance's class name. Else, name of the type of the object is returned. If fully_qualified is True, returns fully qualified name of the type. For builtin types, just name is returned. TypeError is raised if can't get class name from object. """ if inspect.isfunction(obj): raise TypeError("Can't get class name.") if inspect.ismethod(obj): obj = get_method_self(obj) if not isinstance(obj, six.class_types): obj = type(obj) if truncate_builtins: try: built_in = obj.__module__ in _BUILTIN_MODULES except AttributeError: # nosec pass else: if built_in: return obj.__name__ if fully_qualified and hasattr(obj, '__module__'): return '%s.%s' % (obj.__module__, obj.__name__) else: return obj.__name__ def get_all_class_names(obj, up_to=object, fully_qualified=True, truncate_builtins=True): """Get class names of object parent classes. Iterate over all class names object is instance or subclass of, in order of method resolution (mro). If up_to parameter is provided, only name of classes that are sublcasses to that class are returned. """ if not isinstance(obj, six.class_types): obj = type(obj) for cls in obj.mro(): if issubclass(cls, up_to): yield get_class_name(cls, fully_qualified=fully_qualified, truncate_builtins=truncate_builtins) def get_callable_name(function): """Generate a name from callable. Tries to do the best to guess fully qualified callable name. """ method_self = get_method_self(function) if method_self is not None: # This is a bound method. if isinstance(method_self, six.class_types): # This is a bound class method. im_class = method_self else: im_class = type(method_self) try: parts = (im_class.__module__, function.__qualname__) except AttributeError: parts = (im_class.__module__, im_class.__name__, function.__name__) elif inspect.ismethod(function) or inspect.isfunction(function): # This could be a function, a static method, a unbound method... try: parts = (function.__module__, function.__qualname__) except AttributeError: if hasattr(function, 'im_class'): # This is a unbound method, which exists only in python 2.x im_class = function.im_class parts = (im_class.__module__, im_class.__name__, function.__name__) else: parts = (function.__module__, function.__name__) else: im_class = type(function) if im_class is _TYPE_TYPE: im_class = function try: parts = (im_class.__module__, im_class.__qualname__) except AttributeError: parts = (im_class.__module__, im_class.__name__) return '.'.join(parts) def get_method_self(method): """Gets the ``self`` object attached to this method (or none).""" if not inspect.ismethod(method): return None try: return six.get_method_self(method) except AttributeError: return None def is_same_callback(callback1, callback2, strict=True): """Returns if the two callbacks are the same.""" if callback1 is callback2: # This happens when plain methods are given (or static/non-bound # methods). return True if callback1 == callback2: if not strict: return True # Two bound methods are equal if functions themselves are equal and # objects they are applied to are equal. This means that a bound # method could be the same bound method on another object if the # objects have __eq__ methods that return true (when in fact it is a # different bound method). Python u so crazy! try: self1 = six.get_method_self(callback1) self2 = six.get_method_self(callback2) return self1 is self2 except AttributeError: # nosec pass return False def is_bound_method(method): """Returns if the given method is bound to an object.""" return get_method_self(method) is not None def is_subclass(obj, cls): """Returns if the object is class and it is subclass of a given class.""" return inspect.isclass(obj) and issubclass(obj, cls) def get_callable_args(function, required_only=False): """Get names of callable arguments. Special arguments (like ``*args`` and ``**kwargs``) are not included into output. If required_only is True, optional arguments (with default values) are not included into output. """ sig = get_signature(function) function_args = list(six.iterkeys(sig.parameters)) for param_name, p in six.iteritems(sig.parameters): if (p.kind in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD) or (required_only and p.default is not Parameter.empty)): function_args.remove(param_name) return function_args def accepts_kwargs(function): """Returns ``True`` if function accepts kwargs otherwise ``False``.""" sig = get_signature(function) return any(p.kind == Parameter.VAR_KEYWORD for p in six.itervalues(sig.parameters)) oslo.utils-4.1.1/oslo_utils/versionutils.py0000664000175000017500000000615413643050415021175 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # 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. """ Helpers for comparing version strings. .. versionadded:: 1.6 """ import pkg_resources import six from oslo_utils._i18n import _ def is_compatible(requested_version, current_version, same_major=True): """Determine whether `requested_version` is satisfied by `current_version`; in other words, `current_version` is >= `requested_version`. :param requested_version: version to check for compatibility :param current_version: version to check against :param same_major: if True, the major version must be identical between `requested_version` and `current_version`. This is used when a major-version difference indicates incompatibility between the two versions. Since this is the common-case in practice, the default is True. :returns: True if compatible, False if not """ requested_parts = pkg_resources.parse_version(requested_version) current_parts = pkg_resources.parse_version(current_version) if same_major: # NOTE(jlvillal) pkg_resources issues a warning if we try to access # portions of the version, for example requested_parts[0] will issue a # warning message. So get the major_version from the string instead. if requested_version.split('.')[0] != current_version.split('.')[0]: return False return current_parts >= requested_parts def convert_version_to_int(version): """Convert a version to an integer. *version* must be a string with dots or a tuple of integers. .. versionadded:: 2.0 """ try: if isinstance(version, six.string_types): version = convert_version_to_tuple(version) if isinstance(version, tuple): return six.moves.reduce(lambda x, y: (x * 1000) + y, version) except Exception as ex: msg = _("Version %s is invalid.") % version six.raise_from(ValueError(msg), ex) def convert_version_to_str(version_int): """Convert a version integer to a string with dots. .. versionadded:: 2.0 """ version_numbers = [] factor = 1000 while version_int != 0: version_number = version_int - (version_int // factor * factor) version_numbers.insert(0, six.text_type(version_number)) version_int = version_int // factor return '.'.join(map(str, version_numbers)) def convert_version_to_tuple(version_str): """Convert a version string with dots to a tuple. .. versionadded:: 2.0 """ return tuple(int(part) for part in version_str.split('.')) oslo.utils-4.1.1/oslo_utils/_i18n.py0000664000175000017500000000152713643050415017344 0ustar zuulzuul00000000000000# Copyright 2014 IBM Corp. # # 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. """oslo.i18n integration module. See https://docs.openstack.org/oslo.i18n/latest/user/index.html . """ import oslo_i18n _translators = oslo_i18n.TranslatorFactory(domain='oslo_utils') # The primary translation function using the well-known name "_" _ = _translators.primary oslo.utils-4.1.1/oslo_utils/eventletutils.py0000664000175000017500000001566213643050415021342 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2015 Yahoo! Inc. All Rights Reserved. # # 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. """ Eventlet utils helper module. .. versionadded:: 1.3 """ import threading import warnings from oslo_utils import importutils from oslo_utils import timeutils # These may or may not exist; so carefully import them if we can... _eventlet = importutils.try_import('eventlet') _patcher = importutils.try_import('eventlet.patcher') # Attribute that can be used by others to see if eventlet is even currently # useable (can be used in unittests to skip test cases or test classes that # require eventlet to work). EVENTLET_AVAILABLE = all((_eventlet, _patcher)) # Taken from eventlet.py (v0.16.1) patcher code (it's not a accessible set # for some reason...) _ALL_PATCH = frozenset(['__builtin__', 'MySQLdb', 'os', 'psycopg', 'select', 'socket', 'thread', 'time']) def fetch_current_thread_functor(): """Get the current thread. If eventlet is used to monkey-patch the threading module, return the current eventlet greenthread. Otherwise, return the current Python thread. .. versionadded:: 1.5 """ # Until https://github.com/eventlet/eventlet/issues/172 is resolved # or addressed we have to use complicated workaround to get a object # that will not be recycled; the usage of threading.current_thread() # doesn't appear to currently be monkey patched and therefore isn't # reliable to use (and breaks badly when used as all threads share # the same current_thread() object)... if not EVENTLET_AVAILABLE: return threading.current_thread else: green_threaded = _patcher.is_monkey_patched('thread') if green_threaded: return _eventlet.getcurrent else: return threading.current_thread def warn_eventlet_not_patched(expected_patched_modules=None, what='this library'): """Warns if eventlet is being used without patching provided modules. :param expected_patched_modules: list of modules to check to ensure that they are patched (and to warn if they are not); these names should correspond to the names passed into the eventlet monkey_patch() routine. If not provided then *all* the modules that could be patched are checked. The currently valid selection is one or multiple of ['MySQLdb', '__builtin__', 'all', 'os', 'psycopg', 'select', 'socket', 'thread', 'time'] (where 'all' has an inherent special meaning). :type expected_patched_modules: list/tuple/iterable :param what: string to merge into the warnings message to identify what is being checked (used in forming the emitted warnings message). :type what: string """ if not expected_patched_modules: expanded_patched_modules = _ALL_PATCH.copy() else: expanded_patched_modules = set() for m in expected_patched_modules: if m == 'all': expanded_patched_modules.update(_ALL_PATCH) else: if m not in _ALL_PATCH: raise ValueError("Unknown module '%s' requested to check" " if patched" % m) else: expanded_patched_modules.add(m) if EVENTLET_AVAILABLE: try: # The patcher code stores a dictionary here of all modules # names -> whether it was patched... # # Example: # # >>> _patcher.monkey_patch(os=True) # >>> print(_patcher.already_patched) # {'os': True} maybe_patched = bool(_patcher.already_patched) except AttributeError: # Assume it is patched (the attribute used here doesn't appear # to be a public documented API so we will assume that everything # is patched when that attribute isn't there to be safe...) maybe_patched = True if maybe_patched: not_patched = [] for m in sorted(expanded_patched_modules): if not _patcher.is_monkey_patched(m): not_patched.append(m) if not_patched: warnings.warn("It is highly recommended that when eventlet" " is used that the %s modules are monkey" " patched when using %s (to avoid" " spurious or unexpected lock-ups" " and/or hangs)" % (not_patched, what), RuntimeWarning, stacklevel=3) def is_monkey_patched(module): """Determines safely is eventlet patching for module enabled or not :param module: String, module name :return Bool: True if module is patched, False otherwise """ if _patcher is None: return False return _patcher.is_monkey_patched(module) class EventletEvent(object): """A class that provides consistent eventlet/threading Event API. This wraps the eventlet.event.Event class to have the same API as the standard threading.Event object. """ def __init__(self, *args, **kwargs): super(EventletEvent, self).__init__() self.clear() def clear(self): if getattr(self, '_set', True): self._set = False self._event = _eventlet.event.Event() def is_set(self): return self._set isSet = is_set def set(self): if not self._set: self._set = True self._event.send(True) def wait(self, timeout=None): with timeutils.StopWatch(timeout) as sw: while True: event = self._event with _eventlet.timeout.Timeout(sw.leftover(return_none=True), False): event.wait() if event is not self._event: continue return self.is_set() def Event(): if is_monkey_patched("thread"): return EventletEvent() else: return threading.Event() oslo.utils-4.1.1/oslo_utils/fileutils.py0000664000175000017500000001131113643050415020416 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # 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. """ File utilities. .. versionadded:: 1.8 """ import contextlib import errno import hashlib import os import stat import tempfile from oslo_utils import excutils _DEFAULT_MODE = stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO def ensure_tree(path, mode=_DEFAULT_MODE): """Create a directory (and any ancestor directories required) :param path: Directory to create :param mode: Directory creation permissions """ try: os.makedirs(path, mode) except OSError as exc: if exc.errno == errno.EEXIST: if not os.path.isdir(path): raise else: raise def delete_if_exists(path, remove=os.unlink): """Delete a file, but ignore file not found error. :param path: File to delete :param remove: Optional function to remove passed path """ try: remove(path) except OSError as e: if e.errno != errno.ENOENT: raise @contextlib.contextmanager def remove_path_on_error(path, remove=delete_if_exists): """Protect code that wants to operate on PATH atomically. Any exception will cause PATH to be removed. :param path: File to work with :param remove: Optional function to remove passed path """ try: yield except Exception: with excutils.save_and_reraise_exception(): remove(path) def write_to_tempfile(content, path=None, suffix='', prefix='tmp'): """Create a temporary file containing data. Create a temporary file containing specified content, with a specified filename suffix and prefix. The tempfile will be created in a default location, or in the directory `path`, if it is not None. `path` and its parent directories will be created if they don't exist. :param content: bytestring to write to the file :param path: same as parameter 'dir' for mkstemp :param suffix: same as parameter 'suffix' for mkstemp :param prefix: same as parameter 'prefix' for mkstemp For example: it can be used in database tests for creating configuration files. .. versionadded:: 1.9 """ if path: ensure_tree(path) (fd, path) = tempfile.mkstemp(suffix=suffix, dir=path, prefix=prefix) try: os.write(fd, content) finally: os.close(fd) return path def compute_file_checksum(path, read_chunksize=65536, algorithm='sha256'): """Compute checksum of a file's contents. :param path: Path to the file :param read_chunksize: Maximum number of bytes to be read from the file at once. Default is 65536 bytes or 64KB :param algorithm: The hash algorithm name to use. For example, 'md5', 'sha256', 'sha512' and so on. Default is 'sha256'. Refer to hashlib.algorithms_available for available algorithms :return: Hex digest string of the checksum .. versionadded:: 3.31.0 """ checksum = hashlib.new(algorithm) # Raises appropriate exceptions. with open(path, 'rb') as f: for chunk in iter(lambda: f.read(read_chunksize), b''): checksum.update(chunk) return checksum.hexdigest() def last_bytes(path, num): """Return num bytes from the end of the file and unread byte count. Returns a tuple containing some content from the file and the number of bytes that appear in the file before the point at which reading started. The content will be at most ``num`` bytes, taken from the end of the file. If the file is smaller than ``num`` bytes the entire content of the file is returned. :param path: The file path to read :param num: The number of bytes to return :returns: (data, unread_bytes) """ with open(path, 'rb') as fp: try: fp.seek(-num, os.SEEK_END) except IOError as e: # seek() fails with EINVAL when trying to go before the start of # the file. It means that num is larger than the file size, so # just go to the start. if e.errno == errno.EINVAL: fp.seek(0, os.SEEK_SET) else: raise unread_bytes = fp.tell() return (fp.read(), unread_bytes) oslo.utils-4.1.1/oslo_utils/secretutils.py0000664000175000017500000000261613643050415020774 0ustar zuulzuul00000000000000# All Rights Reserved. # # 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. """ Secret utilities. .. versionadded:: 3.5 """ import hmac def _constant_time_compare(first, second): """Return True if both string or binary inputs are equal, otherwise False. This function should take a constant amount of time regardless of how many characters in the strings match. This function uses an approach designed to prevent timing analysis by avoiding content-based short circuiting behaviour, making it appropriate for cryptography. """ first = str(first) second = str(second) if len(first) != len(second): return False result = 0 for x, y in zip(first, second): result |= ord(x) ^ ord(y) return result == 0 try: constant_time_compare = hmac.compare_digest except AttributeError: constant_time_compare = _constant_time_compare oslo.utils-4.1.1/oslo_utils/timeutils.py0000664000175000017500000004262313643050415020447 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # 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. """ Time related utilities and helper functions. """ import calendar import datetime import logging import time from debtcollector import removals import iso8601 import pytz import six from oslo_utils import reflection if hasattr(time, 'monotonic'): now = time.monotonic else: from monotonic import monotonic as now # noqa # ISO 8601 extended time format with microseconds _ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f' _ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S' PERFECT_TIME_FORMAT = _ISO8601_TIME_FORMAT_SUBSECOND _MAX_DATETIME_SEC = 59 @removals.remove( message="use datetime.datetime.isoformat()", version="1.6", removal_version="?", ) def isotime(at=None, subsecond=False): """Stringify time in ISO 8601 format. .. deprecated:: 1.5.0 Use :func:`utcnow` and :func:`datetime.datetime.isoformat` instead. """ if not at: at = utcnow() st = at.strftime(_ISO8601_TIME_FORMAT if not subsecond else _ISO8601_TIME_FORMAT_SUBSECOND) tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC' # Need to handle either iso8601 or python UTC format st += ('Z' if tz in ('UTC', 'UTC+00:00') else tz) return st def parse_isotime(timestr): """Parse time from ISO 8601 format.""" try: return iso8601.parse_date(timestr) except iso8601.ParseError as e: raise ValueError(six.text_type(e)) except TypeError as e: raise ValueError(six.text_type(e)) @removals.remove( message="use either datetime.datetime.isoformat() " "or datetime.datetime.strftime() instead", version="1.6", removal_version="?", ) def strtime(at=None, fmt=PERFECT_TIME_FORMAT): """Returns formatted utcnow. .. deprecated:: 1.5.0 Use :func:`utcnow()`, :func:`datetime.datetime.isoformat` or :func:`datetime.strftime` instead: * ``strtime()`` => ``utcnow().isoformat()`` * ``strtime(fmt=...)`` => ``utcnow().strftime(fmt)`` * ``strtime(at)`` => ``at.isoformat()`` * ``strtime(at, fmt)`` => ``at.strftime(fmt)`` """ if not at: at = utcnow() return at.strftime(fmt) def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT): """Turn a formatted time back into a datetime.""" return datetime.datetime.strptime(timestr, fmt) def normalize_time(timestamp): """Normalize time in arbitrary timezone to UTC naive object.""" offset = timestamp.utcoffset() if offset is None: return timestamp return timestamp.replace(tzinfo=None) - offset def is_older_than(before, seconds): """Return True if before is older than seconds. .. versionchanged:: 1.7 Accept datetime string with timezone information. Fix comparison with timezone aware datetime. """ if isinstance(before, six.string_types): before = parse_isotime(before) before = normalize_time(before) return utcnow() - before > datetime.timedelta(seconds=seconds) def is_newer_than(after, seconds): """Return True if after is newer than seconds. .. versionchanged:: 1.7 Accept datetime string with timezone information. Fix comparison with timezone aware datetime. """ if isinstance(after, six.string_types): after = parse_isotime(after) after = normalize_time(after) return after - utcnow() > datetime.timedelta(seconds=seconds) def utcnow_ts(microsecond=False): """Timestamp version of our utcnow function. See :py:class:`oslo_utils.fixture.TimeFixture`. .. versionchanged:: 1.3 Added optional *microsecond* parameter. """ if utcnow.override_time is None: # NOTE(kgriffs): This is several times faster # than going through calendar.timegm(...) timestamp = time.time() if not microsecond: timestamp = int(timestamp) return timestamp now = utcnow() timestamp = calendar.timegm(now.timetuple()) if microsecond: timestamp += float(now.microsecond) / 1000000 return timestamp def utcnow(with_timezone=False): """Overridable version of utils.utcnow that can return a TZ-aware datetime. See :py:class:`oslo_utils.fixture.TimeFixture`. .. versionchanged:: 1.6 Added *with_timezone* parameter. """ if utcnow.override_time: try: return utcnow.override_time.pop(0) except AttributeError: return utcnow.override_time if with_timezone: return datetime.datetime.now(tz=iso8601.iso8601.UTC) return datetime.datetime.utcnow() @removals.remove( message="use datetime.datetime.utcfromtimestamp().isoformat()", version="1.6", removal_version="?", ) def iso8601_from_timestamp(timestamp, microsecond=False): """Returns an iso8601 formatted date from timestamp. .. versionchanged:: 1.3 Added optional *microsecond* parameter. .. deprecated:: 1.5.0 Use :func:`datetime.datetime.utcfromtimestamp` and :func:`datetime.datetime.isoformat` instead. """ return isotime(datetime.datetime.utcfromtimestamp(timestamp), microsecond) utcnow.override_time = None def set_time_override(override_time=None): """Overrides utils.utcnow. Make it return a constant time or a list thereof, one at a time. See :py:class:`oslo_utils.fixture.TimeFixture`. :param override_time: datetime instance or list thereof. If not given, defaults to the current UTC time. """ utcnow.override_time = override_time or datetime.datetime.utcnow() def advance_time_delta(timedelta): """Advance overridden time using a datetime.timedelta. See :py:class:`oslo_utils.fixture.TimeFixture`. """ assert utcnow.override_time is not None # nosec try: for dt in utcnow.override_time: dt += timedelta except TypeError: utcnow.override_time += timedelta def advance_time_seconds(seconds): """Advance overridden time by seconds. See :py:class:`oslo_utils.fixture.TimeFixture`. """ advance_time_delta(datetime.timedelta(0, seconds)) def clear_time_override(): """Remove the overridden time. See :py:class:`oslo_utils.fixture.TimeFixture`. """ utcnow.override_time = None def marshall_now(now=None): """Make an rpc-safe datetime with microseconds. .. versionchanged:: 1.6 Timezone information is now serialized instead of being stripped. """ if not now: now = utcnow() d = dict(day=now.day, month=now.month, year=now.year, hour=now.hour, minute=now.minute, second=now.second, microsecond=now.microsecond) if now.tzinfo: # Need to handle either iso8601 or python UTC format tzname = now.tzinfo.tzname(None) d['tzname'] = 'UTC' if tzname == 'UTC+00:00' else tzname return d def unmarshall_time(tyme): """Unmarshall a datetime dict. .. versionchanged:: 1.5 Drop leap second. .. versionchanged:: 1.6 Added support for timezone information. """ # NOTE(ihrachys): datetime does not support leap seconds, # so the best thing we can do for now is dropping them # http://bugs.python.org/issue23574 second = min(tyme['second'], _MAX_DATETIME_SEC) dt = datetime.datetime(day=tyme['day'], month=tyme['month'], year=tyme['year'], hour=tyme['hour'], minute=tyme['minute'], second=second, microsecond=tyme['microsecond']) tzname = tyme.get('tzname') if tzname: # Need to handle either iso8601 or python UTC format tzname = 'UTC' if tzname == 'UTC+00:00' else tzname tzinfo = pytz.timezone(tzname) dt = tzinfo.localize(dt) return dt def delta_seconds(before, after): """Return the difference between two timing objects. Compute the difference in seconds between two date, time, or datetime objects (as a float, to microsecond resolution). """ delta = after - before return delta.total_seconds() def is_soon(dt, window): """Determines if time is going to happen in the next window seconds. :param dt: the time :param window: minimum seconds to remain to consider the time not soon :return: True if expiration is within the given duration """ soon = (utcnow() + datetime.timedelta(seconds=window)) return normalize_time(dt) <= soon class Split(object): """A *immutable* stopwatch split. See: http://en.wikipedia.org/wiki/Stopwatch for what this is/represents. .. versionadded:: 1.4 """ __slots__ = ['_elapsed', '_length'] def __init__(self, elapsed, length): self._elapsed = elapsed self._length = length @property def elapsed(self): """Duration from stopwatch start.""" return self._elapsed @property def length(self): """Seconds from last split (or the elapsed time if no prior split).""" return self._length def __repr__(self): r = reflection.get_class_name(self, fully_qualified=False) r += "(elapsed=%s, length=%s)" % (self._elapsed, self._length) return r def time_it(logger, log_level=logging.DEBUG, message="It took %(seconds).02f seconds to" " run function '%(func_name)s'", enabled=True, min_duration=0.01): """Decorator that will log how long its decorated function takes to run. This does **not** output a log if the decorated function fails with an exception. :param logger: logger instance to use when logging elapsed time :param log_level: logger logging level to use when logging elapsed time :param message: customized message to use when logging elapsed time, the message may use automatically provide values ``%(seconds)`` and ``%(func_name)`` if it finds those values useful to record :param enabled: whether to enable or disable this decorator (useful to decorate a function with this decorator, and then easily be able to switch that decoration off by some config or other value) :param min_duration: argument that determines if logging is triggered or not, it is by default set to 0.01 seconds to avoid logging when durations and/or elapsed function call times are less than 0.01 seconds, to disable any ``min_duration`` checks this value should be set to less than or equal to zero or set to none """ def decorator(func): if not enabled: return func @six.wraps(func) def wrapper(*args, **kwargs): with StopWatch() as w: result = func(*args, **kwargs) time_taken = w.elapsed() if min_duration is None or time_taken >= min_duration: logger.log(log_level, message, {'seconds': time_taken, 'func_name': reflection.get_callable_name(func)}) return result return wrapper return decorator class StopWatch(object): """A simple timer/stopwatch helper class. Inspired by: apache-commons-lang java stopwatch. Not thread-safe (when a single watch is mutated by multiple threads at the same time). Thread-safe when used by a single thread (not shared) or when operations are performed in a thread-safe manner on these objects by wrapping those operations with locks. It will use the `monotonic`_ pypi library to find an appropriate monotonically increasing time providing function (which typically varies depending on operating system and python version). .. _monotonic: https://pypi.org/project/monotonic/ .. versionadded:: 1.4 """ _STARTED = 'STARTED' _STOPPED = 'STOPPED' def __init__(self, duration=None): if duration is not None and duration < 0: raise ValueError("Duration must be greater or equal to" " zero and not %s" % duration) self._duration = duration self._started_at = None self._stopped_at = None self._state = None self._splits = () def start(self): """Starts the watch (if not already started). NOTE(harlowja): resets any splits previously captured (if any). """ if self._state == self._STARTED: return self self._started_at = now() self._stopped_at = None self._state = self._STARTED self._splits = () return self @property def splits(self): """Accessor to all/any splits that have been captured.""" return self._splits def split(self): """Captures a split/elapsed since start time (and doesn't stop).""" if self._state == self._STARTED: elapsed = self.elapsed() if self._splits: length = self._delta_seconds(self._splits[-1].elapsed, elapsed) else: length = elapsed self._splits = self._splits + (Split(elapsed, length),) return self._splits[-1] else: raise RuntimeError("Can not create a split time of a stopwatch" " if it has not been started or if it has been" " stopped") def restart(self): """Restarts the watch from a started/stopped state.""" if self._state == self._STARTED: self.stop() self.start() return self @staticmethod def _delta_seconds(earlier, later): # Uses max to avoid the delta/time going backwards (and thus negative). return max(0.0, later - earlier) def elapsed(self, maximum=None): """Returns how many seconds have elapsed.""" if self._state not in (self._STARTED, self._STOPPED): raise RuntimeError("Can not get the elapsed time of a stopwatch" " if it has not been started/stopped") if self._state == self._STOPPED: elapsed = self._delta_seconds(self._started_at, self._stopped_at) else: elapsed = self._delta_seconds(self._started_at, now()) if maximum is not None and elapsed > maximum: elapsed = max(0.0, maximum) return elapsed def __enter__(self): """Starts the watch.""" self.start() return self def __exit__(self, type, value, traceback): """Stops the watch (ignoring errors if stop fails).""" try: self.stop() except RuntimeError: # nosec: errors are meant to be ignored pass def leftover(self, return_none=False): """Returns how many seconds are left until the watch expires. :param return_none: when ``True`` instead of raising a ``RuntimeError`` when no duration has been set this call will return ``None`` instead. :type return_none: boolean """ if self._state != self._STARTED: raise RuntimeError("Can not get the leftover time of a stopwatch" " that has not been started") if self._duration is None: if not return_none: raise RuntimeError("Can not get the leftover time of a watch" " that has no duration") return None return max(0.0, self._duration - self.elapsed()) def expired(self): """Returns if the watch has expired (ie, duration provided elapsed).""" if self._state not in (self._STARTED, self._STOPPED): raise RuntimeError("Can not check if a stopwatch has expired" " if it has not been started/stopped") if self._duration is None: return False return self.elapsed() > self._duration def has_started(self): """Returns True if the watch is in a started state.""" return self._state == self._STARTED def has_stopped(self): """Returns True if the watch is in a stopped state.""" return self._state == self._STOPPED def resume(self): """Resumes the watch from a stopped state.""" if self._state == self._STOPPED: self._state = self._STARTED return self else: raise RuntimeError("Can not resume a stopwatch that has not been" " stopped") def stop(self): """Stops the watch.""" if self._state == self._STOPPED: return self if self._state != self._STARTED: raise RuntimeError("Can not stop a stopwatch that has not been" " started") self._stopped_at = now() self._state = self._STOPPED return self oslo.utils-4.1.1/oslo_utils/strutils.py0000664000175000017500000005032213643050415020314 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # 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. """ System-level utilities and helper functions. """ import collections import math import re import unicodedata import pyparsing as pp import six from six.moves import urllib from oslo_utils._i18n import _ from oslo_utils import encodeutils UNIT_PREFIX_EXPONENT = { 'k': 1, 'K': 1, 'Ki': 1, 'M': 2, 'Mi': 2, 'G': 3, 'Gi': 3, 'T': 4, 'Ti': 4, } UNIT_SYSTEM_INFO = { 'IEC': (1024, re.compile(r'(^[-+]?\d*\.?\d+)([KMGT]i?)?(b|bit|B)$')), 'SI': (1000, re.compile(r'(^[-+]?\d*\.?\d+)([kMGT])?(b|bit|B)$')), 'mixed': (None, re.compile(r'(^[-+]?\d*\.?\d+)([kKMGT]i?)?(b|bit|B)$')), } TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes') FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no') SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]") SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+") # NOTE(flaper87): The following globals are used by `mask_password` and # `mask_dict_password`. They must all be lowercase. _SANITIZE_KEYS = ['adminpass', 'admin_pass', 'password', 'admin_password', 'auth_token', 'new_pass', 'auth_password', 'secret_uuid', 'secret', 'sys_pswd', 'token', 'configdrive', 'chappassword', 'encrypted_key', 'private_key', 'encryption_key_id', 'fernetkey', 'sslkey', 'passphrase', 'cephclusterfsid', 'octaviaheartbeatkey', 'rabbitcookie', 'cephmanilaclientkey', 'pacemakerremoteauthkey', 'designaterndckey', 'cephadminkey', 'heatauthencryptionkey', 'cephclientkey', 'keystonecredential', 'barbicansimplecryptokek', 'cephrgwkey', 'swifthashsuffix', 'migrationsshkey', 'cephmdskey', 'cephmonkey'] # NOTE(ldbragst): Let's build a list of regex objects using the list of # _SANITIZE_KEYS we already have. This way, we only have to add the new key # to the list of _SANITIZE_KEYS and we can generate regular expressions # for XML and JSON automatically. _SANITIZE_PATTERNS_2 = {} _SANITIZE_PATTERNS_1 = {} # NOTE(amrith): Some regular expressions have only one parameter, some # have two parameters. Use different lists of patterns here. _FORMAT_PATTERNS_1 = [r'(%(key)s[0-9]*\s*[=]\s*)[^\s^\'^\"]+'] _FORMAT_PATTERNS_2 = [r'(%(key)s[0-9]*\s*[=]\s*[\"\'])[^\"\']*([\"\'])', r'(%(key)s[0-9]*\s+[\"\'])[^\"\']*([\"\'])', r'([-]{2}%(key)s[0-9]*\s+)[^\'^\"^=^\s]+([\s]*)', r'(<%(key)s[0-9]*>)[^<]*()', r'([\"\']%(key)s[0-9]*[\"\']\s*:\s*[\"\'])[^\"\']*' r'([\"\'])', r'([\'"][^"\']*%(key)s[0-9]*[\'"]\s*:\s*u?[\'"])[^\"\']*' r'([\'"])', r'([\'"][^\'"]*%(key)s[0-9]*[\'"]\s*,\s*\'--?[A-z]+' r'\'\s*,\s*u?[\'"])[^\"\']*([\'"])', r'(%(key)s[0-9]*\s*--?[A-z]+\s*)\S+(\s*)'] # NOTE(dhellmann): Keep a separate list of patterns by key so we only # need to apply the substitutions for keys we find using a quick "in" # test. for key in _SANITIZE_KEYS: _SANITIZE_PATTERNS_1[key] = [] _SANITIZE_PATTERNS_2[key] = [] for pattern in _FORMAT_PATTERNS_2: reg_ex = re.compile(pattern % {'key': key}, re.DOTALL | re.IGNORECASE) _SANITIZE_PATTERNS_2[key].append(reg_ex) for pattern in _FORMAT_PATTERNS_1: reg_ex = re.compile(pattern % {'key': key}, re.DOTALL | re.IGNORECASE) _SANITIZE_PATTERNS_1[key].append(reg_ex) def int_from_bool_as_string(subject): """Interpret a string as a boolean and return either 1 or 0. Any string value in: ('True', 'true', 'On', 'on', '1') is interpreted as a boolean True. Useful for JSON-decoded stuff and config file parsing """ return int(bool_from_string(subject)) def bool_from_string(subject, strict=False, default=False): """Interpret a subject as a boolean. A subject can be a boolean, a string or an integer. Boolean type value will be returned directly, otherwise the subject will be converted to a string. A case-insensitive match is performed such that strings matching 't','true', 'on', 'y', 'yes', or '1' are considered True and, when `strict=False`, anything else returns the value specified by 'default'. Useful for JSON-decoded stuff and config file parsing. If `strict=True`, unrecognized values, including None, will raise a ValueError which is useful when parsing values passed in from an API call. Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'. """ if isinstance(subject, bool): return subject if not isinstance(subject, six.string_types): subject = six.text_type(subject) lowered = subject.strip().lower() if lowered in TRUE_STRINGS: return True elif lowered in FALSE_STRINGS: return False elif strict: acceptable = ', '.join( "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS)) msg = _("Unrecognized value '%(val)s', acceptable values are:" " %(acceptable)s") % {'val': subject, 'acceptable': acceptable} raise ValueError(msg) else: return default def is_valid_boolstr(value): """Check if the provided string is a valid bool string or not. :param value: value to verify :type value: string :returns: true if value is boolean string, false otherwise .. versionadded:: 3.17 """ boolstrs = TRUE_STRINGS + FALSE_STRINGS return str(value).lower() in boolstrs def string_to_bytes(text, unit_system='IEC', return_int=False): """Converts a string into an float representation of bytes. The units supported for IEC / mixed:: Kb(it), Kib(it), Mb(it), Mib(it), Gb(it), Gib(it), Tb(it), Tib(it) KB, KiB, MB, MiB, GB, GiB, TB, TiB The units supported for SI :: kb(it), Mb(it), Gb(it), Tb(it) kB, MB, GB, TB SI units are interpreted as power-of-ten (e.g. 1kb = 1000b). Note that the SI unit system does not support capital letter 'K' IEC units are interpreted as power-of-two (e.g. 1MiB = 1MB = 1024b) Mixed units interpret the "i" to mean IEC, and no "i" to mean SI (e.g. 1kb = 1000b, 1kib == 1024b). Additionaly, mixed units interpret 'K' as power-of-ten. This mode is not particuarly useful for new code, but can help with compatability for parsers such as GNU parted. :param text: String input for bytes size conversion. :param unit_system: Unit system for byte size conversion. :param return_int: If True, returns integer representation of text in bytes. (default: decimal) :returns: Numerical representation of text in bytes. :raises ValueError: If text has an invalid value. """ try: base, reg_ex = UNIT_SYSTEM_INFO[unit_system] except KeyError: msg = _('Invalid unit system: "%s"') % unit_system raise ValueError(msg) match = reg_ex.match(text) if match: magnitude = float(match.group(1)) unit_prefix = match.group(2) if match.group(3) in ['b', 'bit']: magnitude /= 8 # In the mixed matcher, IEC units (with a trailing 'i') are # interpreted as power-of-two, others as power-of-ten if unit_system == 'mixed': if unit_prefix and not unit_prefix.endswith('i'): # For maximum compatability in mixed mode, we understand # "K" (which is not strict SI) as "k" if unit_prefix.startswith == 'K': unit_prefix = 'k' base = 1000 else: base = 1024 else: msg = _('Invalid string format: %s') % text raise ValueError(msg) if not unit_prefix: res = magnitude else: res = magnitude * pow(base, UNIT_PREFIX_EXPONENT[unit_prefix]) if return_int: return int(math.ceil(res)) return res def to_slug(value, incoming=None, errors="strict"): """Normalize string. Convert to lowercase, remove non-word characters, and convert spaces to hyphens. Inspired by Django's `slugify` filter. :param value: Text to slugify :param incoming: Text's current encoding :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: slugified unicode representation of `value` :raises TypeError: If text is not an instance of str """ value = encodeutils.safe_decode(value, incoming, errors) # NOTE(aababilov): no need to use safe_(encode|decode) here: # encodings are always "ascii", error handling is always "ignore" # and types are always known (first: unicode; second: str) value = unicodedata.normalize("NFKD", value).encode( "ascii", "ignore").decode("ascii") value = SLUGIFY_STRIP_RE.sub("", value).strip().lower() return SLUGIFY_HYPHENATE_RE.sub("-", value) # NOTE(dhellmann): Before submitting a patch to add a new argument to # this function to allow the caller to pass in "extra" or "additional" # or "replacement" patterns to be masked out, please note that we have # discussed that feature many times and always rejected it based on # the desire to have Oslo functions behave consistently across all # projects and *especially* to have security features work the same # way no matter where they are used. If every project adopted its own # set patterns for secret values, it would be very difficult to audit # the logging to ensure that everything is properly masked. So, please # either add your pattern to the module-level variables at the top of # this file or, even better, pick an existing pattern or key to use in # your application to ensure that the value is masked by this # function. def mask_password(message, secret="***"): # nosec """Replace password with *secret* in message. :param message: The string which includes security information. :param secret: value with which to replace passwords. :returns: The unicode value of message with the password fields masked. For example: >>> mask_password("'adminPass' : 'aaaaa'") "'adminPass' : '***'" >>> mask_password("'admin_pass' : 'aaaaa'") "'admin_pass' : '***'" >>> mask_password('"password" : "aaaaa"') '"password" : "***"' >>> mask_password("'original_password' : 'aaaaa'") "'original_password' : '***'" >>> mask_password("u'original_password' : u'aaaaa'") "u'original_password' : u'***'" .. versionadded:: 0.2 .. versionchanged:: 1.1 Replace also ``'auth_token'``, ``'new_pass'`` and ``'auth_password'`` keys. .. versionchanged:: 1.1.1 Replace also ``'secret_uuid'`` key. .. versionchanged:: 1.5 Replace also ``'sys_pswd'`` key. .. versionchanged:: 2.6 Replace also ``'token'`` key. .. versionchanged:: 2.7 Replace also ``'secret'`` key. .. versionchanged:: 3.4 Replace also ``'configdrive'`` key. .. versionchanged:: 3.8 Replace also ``'CHAPPASSWORD'`` key. """ try: message = six.text_type(message) except UnicodeDecodeError: # nosec # NOTE(jecarey): Temporary fix to handle cases where message is a # byte string. A better solution will be provided in Kilo. pass substitute1 = r'\g<1>' + secret substitute2 = r'\g<1>' + secret + r'\g<2>' # NOTE(ldbragst): Check to see if anything in message contains any key # specified in _SANITIZE_KEYS, if not then just return the message since # we don't have to mask any passwords. for key in _SANITIZE_KEYS: if key in message.lower(): for pattern in _SANITIZE_PATTERNS_2[key]: message = re.sub(pattern, substitute2, message) for pattern in _SANITIZE_PATTERNS_1[key]: message = re.sub(pattern, substitute1, message) return message def mask_dict_password(dictionary, secret="***"): # nosec """Replace password with *secret* in a dictionary recursively. :param dictionary: The dictionary which includes secret information. :param secret: value with which to replace secret information. :returns: The dictionary with string substitutions. A dictionary (which may contain nested dictionaries) contains information (such as passwords) which should not be revealed, and this function helps detect and replace those with the 'secret' provided (or `***` if none is provided). Substitution is performed in one of three situations: If the key is something that is considered to be indicative of a secret, then the corresponding value is replaced with the secret provided (or `***` if none is provided). If a value in the dictionary is a string, then it is masked using the ``mask_password()`` function. Finally, if a value is a dictionary, this function will recursively mask that dictionary as well. For example: >>> mask_dict_password({'password': 'd81juxmEW_', >>> 'user': 'admin', >>> 'home-dir': '/home/admin'}, >>> '???') {'password': '???', 'user': 'admin', 'home-dir': '/home/admin'} For example (the value is masked using mask_password()) >>> mask_dict_password({'password': '--password d81juxmEW_', >>> 'user': 'admin', >>> 'home-dir': '/home/admin'}, >>> '???') {'password': '--password ???', 'user': 'admin', 'home-dir': '/home/admin'} For example (a nested dictionary is masked): >>> mask_dict_password({"nested": {'password': 'd81juxmEW_', >>> 'user': 'admin', >>> 'home': '/home/admin'}}, >>> '???') {"nested": {'password': '???', 'user': 'admin', 'home': '/home/admin'}} .. versionadded:: 3.4 """ if not isinstance(dictionary, collections.Mapping): raise TypeError("Expected a Mapping, got %s instead." % type(dictionary)) out = {} for k, v in dictionary.items(): if isinstance(v, collections.Mapping): out[k] = mask_dict_password(v, secret=secret) continue # NOTE(jlvillal): Check to see if anything in the dictionary 'key' # contains any key specified in _SANITIZE_KEYS. k_matched = False if isinstance(k, six.string_types): for sani_key in _SANITIZE_KEYS: if sani_key in k.lower(): out[k] = secret k_matched = True break if not k_matched: # We did not find a match for the key name in the # _SANITIZE_KEYS, so we fall through to here if isinstance(v, six.string_types): out[k] = mask_password(v, secret=secret) else: # Just leave it alone. out[k] = v return out def is_int_like(val): """Check if a value looks like an integer with base 10. :param val: Value to verify :type val: string :returns: bool .. versionadded:: 1.1 """ try: return six.text_type(int(val)) == six.text_type(val) except (TypeError, ValueError): return False def check_string_length(value, name=None, min_length=0, max_length=None): """Check the length of specified string. :param value: the value of the string :param name: the name of the string :param min_length: the min_length of the string :param max_length: the max_length of the string :raises TypeError, ValueError: For any invalid input. .. versionadded:: 3.7 """ if name is None: name = value if not isinstance(value, six.string_types): msg = _("%s is not a string or unicode") % name raise TypeError(msg) length = len(value) if length < min_length: msg = _("%(name)s has %(length)s characters, less than " "%(min_length)s.") % {'name': name, 'length': length, 'min_length': min_length} raise ValueError(msg) if max_length and length > max_length: msg = _("%(name)s has %(length)s characters, more than " "%(max_length)s.") % {'name': name, 'length': length, 'max_length': max_length} raise ValueError(msg) def validate_integer(value, name, min_value=None, max_value=None): """Make sure that value is a valid integer, potentially within range. :param value: value of the integer :param name: name of the integer :param min_value: min_value of the integer :param max_value: max_value of the integer :returns: integer :raises: ValueError if value is an invalid integer .. versionadded:: 3.33 """ try: value = int(str(value)) except (ValueError, UnicodeEncodeError): msg = _('%(value_name)s must be an integer' ) % {'value_name': name} raise ValueError(msg) if min_value is not None and value < min_value: msg = _('%(value_name)s must be >= %(min_value)d' ) % {'value_name': name, 'min_value': min_value} raise ValueError(msg) if max_value is not None and value > max_value: msg = _('%(value_name)s must be <= %(max_value)d' ) % {'value_name': name, 'max_value': max_value} raise ValueError(msg) return value def split_path(path, minsegs=1, maxsegs=None, rest_with_last=False): """Validate and split the given HTTP request path. **Examples**:: ['a'] = _split_path('/a') ['a', None] = _split_path('/a', 1, 2) ['a', 'c'] = _split_path('/a/c', 1, 2) ['a', 'c', 'o/r'] = _split_path('/a/c/o/r', 1, 3, True) :param path: HTTP Request path to be split :param minsegs: Minimum number of segments to be extracted :param maxsegs: Maximum number of segments to be extracted :param rest_with_last: If True, trailing data will be returned as part of last segment. If False, and there is trailing data, raises ValueError. :returns: list of segments with a length of maxsegs (non-existent segments will return as None) :raises: ValueError if given an invalid path .. versionadded:: 3.11 """ if not maxsegs: maxsegs = minsegs if minsegs > maxsegs: raise ValueError(_('minsegs > maxsegs: %(min)d > %(max)d)') % {'min': minsegs, 'max': maxsegs}) if rest_with_last: segs = path.split('/', maxsegs) minsegs += 1 maxsegs += 1 count = len(segs) if (segs[0] or count < minsegs or count > maxsegs or '' in segs[1:minsegs]): raise ValueError(_('Invalid path: %s') % urllib.parse.quote(path)) else: minsegs += 1 maxsegs += 1 segs = path.split('/', maxsegs) count = len(segs) if (segs[0] or count < minsegs or count > maxsegs + 1 or '' in segs[1:minsegs] or (count == maxsegs + 1 and segs[maxsegs])): raise ValueError(_('Invalid path: %s') % urllib.parse.quote(path)) segs = segs[1:maxsegs] segs.extend([None] * (maxsegs - 1 - len(segs))) return segs def split_by_commas(value): """Split values by commas and quotes according to api-wg :param value: value to be split .. versionadded:: 3.17 """ word = (pp.QuotedString(quoteChar='"', escChar='\\') | pp.Word(pp.printables, excludeChars='",')) grammar = pp.stringStart + pp.delimitedList(word) + pp.stringEnd try: return list(grammar.parseString(value)) except pp.ParseException: raise ValueError("Invalid value: %s" % value) oslo.utils-4.1.1/oslo_utils/locale/0000775000175000017500000000000013643050530017304 5ustar zuulzuul00000000000000oslo.utils-4.1.1/oslo_utils/locale/en_GB/0000775000175000017500000000000013643050530020256 5ustar zuulzuul00000000000000oslo.utils-4.1.1/oslo_utils/locale/en_GB/LC_MESSAGES/0000775000175000017500000000000013643050530022043 5ustar zuulzuul00000000000000oslo.utils-4.1.1/oslo_utils/locale/en_GB/LC_MESSAGES/oslo_utils.po0000664000175000017500000000572113643050415024606 0ustar zuulzuul00000000000000# Translations template for oslo.utils. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the oslo.utils project. # # Translators: # Andi Chandler , 2014-2015 # OpenStack Infra , 2015. #zanata # Andi Chandler , 2016. #zanata # Andreas Jaeger , 2016. #zanata # Andi Chandler , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: oslo.utils VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-02-09 13:01+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-02-06 11:26+0000\n" "Last-Translator: Andi Chandler \n" "Language: en_GB\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 4.3.3\n" "Language-Team: English (United Kingdom)\n" #, python-format msgid "%(name)s has %(length)s characters, less than %(min_length)s." msgstr "%(name)s has %(length)s characters, less than %(min_length)s." #, python-format msgid "%(name)s has %(length)s characters, more than %(max_length)s." msgstr "%(name)s has %(length)s characters, more than %(max_length)s." #, python-format msgid "%(value_name)s must be <= %(max_value)d" msgstr "%(value_name)s must be <= %(max_value)d" #, python-format msgid "%(value_name)s must be >= %(min_value)d" msgstr "%(value_name)s must be >= %(min_value)d" #, python-format msgid "%(value_name)s must be an integer" msgstr "%(value_name)s must be an integer" #, python-format msgid "%s is not a string or unicode" msgstr "%s is not a string or unicode" #, python-format msgid "" "Bad prefix or mac format for generating IPv6 address by EUI-64: %(prefix)s, " "%(mac)s:" msgstr "" "Bad prefix or mac format for generating IPv6 address by EUI-64: %(prefix)s, " "%(mac)s:" #, python-format msgid "Bad prefix type for generating IPv6 address by EUI-64: %s" msgstr "Bad prefix type for generating IPv6 address by EUI-64: %s" #, python-format msgid "Invalid input value \"%s\"." msgstr "Invalid input value \"%s\"." #, python-format msgid "Invalid path: %s" msgstr "Invalid path: %s" #, python-format msgid "Invalid string format: %s" msgstr "Invalid string format: %s" #, python-format msgid "Invalid unit system: \"%s\"" msgstr "Invalid unit system: \"%s\"" msgid "Snapshot list encountered but no header found!" msgstr "Snapshot list encountered but no header found!" msgid "Unable to generate IP address by EUI64 for IPv4 prefix" msgstr "Unable to generate IP address by EUI64 for IPv4 prefix" #, python-format msgid "Unrecognized value '%(val)s', acceptable values are: %(acceptable)s" msgstr "Unrecognised value '%(val)s', acceptable values are: %(acceptable)s" #, python-format msgid "Version %s is invalid." msgstr "Version %s is invalid." #, python-format msgid "minsegs > maxsegs: %(min)d > %(max)d)" msgstr "minsegs > maxsegs: %(min)d > %(max)d)" oslo.utils-4.1.1/oslo_utils/locale/fr/0000775000175000017500000000000013643050530017713 5ustar zuulzuul00000000000000oslo.utils-4.1.1/oslo_utils/locale/fr/LC_MESSAGES/0000775000175000017500000000000013643050530021500 5ustar zuulzuul00000000000000oslo.utils-4.1.1/oslo_utils/locale/fr/LC_MESSAGES/oslo_utils.po0000664000175000017500000000434513643050415024244 0ustar zuulzuul00000000000000# Translations template for oslo.utils. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the oslo.utils project. # # Translators: # Maxime COQUEREL , 2014-2015 # OpenStack Infra , 2015. #zanata # Tom Cocozzello , 2015. #zanata # Andreas Jaeger , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: oslo.utils 3.7.1.dev13\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2016-05-02 20:03+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2015-07-27 10:55+0000\n" "Last-Translator: Maxime COQUEREL \n" "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 3.7.3\n" "Language-Team: French\n" #, python-format msgid "%s is not a string or unicode" msgstr "%s n'est pas une chaîne ou unicode" #, python-format msgid "" "Bad prefix or mac format for generating IPv6 address by EUI-64: %(prefix)s, " "%(mac)s:" msgstr "" "Mauvais type de préfixe ou mauvais format d'adresse mac pour générer une " "adresse IPv6 par EUI-64: %(prefix)s, %(mac)s:" #, python-format msgid "Bad prefix type for generating IPv6 address by EUI-64: %s" msgstr "Mauvais type de préfixe pour générer adresse IPv6 par EUI-64: %s" #, python-format msgid "Invalid input value \"%s\"." msgstr "Valeur en entrée \"%s\" non valide." #, python-format msgid "Invalid string format: %s" msgstr "Format de chaine de caractère non valide: %s" #, python-format msgid "Invalid unit system: \"%s\"" msgstr "Unit système non valide: \"%s\"" msgid "Snapshot list encountered but no header found!" msgstr "Liste d'instantanés trouvée mais aucun en-tête trouvé !" msgid "Unable to generate IP address by EUI64 for IPv4 prefix" msgstr "Impossible de générer l'adresse IP par EUI64 pour le préfixe IPv4" #, python-format msgid "Unrecognized value '%(val)s', acceptable values are: %(acceptable)s" msgstr "" "Valeur non reconnue '%(val)s', les valeurs acceptables sont: %(acceptable)s" #, python-format msgid "Version %s is invalid." msgstr "La version %s est invalide." oslo.utils-4.1.1/oslo_utils/locale/de/0000775000175000017500000000000013643050530017674 5ustar zuulzuul00000000000000oslo.utils-4.1.1/oslo_utils/locale/de/LC_MESSAGES/0000775000175000017500000000000013643050530021461 5ustar zuulzuul00000000000000oslo.utils-4.1.1/oslo_utils/locale/de/LC_MESSAGES/oslo_utils.po0000664000175000017500000000420713643050415024222 0ustar zuulzuul00000000000000# Translations template for oslo.utils. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the oslo.utils project. # # Translators: # Andreas Jaeger , 2014 # Ettore Atalan , 2014 # Andreas Jaeger , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: oslo.utils 3.7.1.dev13\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2016-05-02 20:03+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-01-31 08:10+0000\n" "Last-Translator: Andreas Jaeger \n" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "Generated-By: Babel 2.0\n" "X-Generator: Zanata 3.7.3\n" "Language-Team: German\n" #, python-format msgid "%s is not a string or unicode" msgstr "%s ist keine Zeichenkette oder Unicode" #, python-format msgid "" "Bad prefix or mac format for generating IPv6 address by EUI-64: %(prefix)s, " "%(mac)s:" msgstr "" "Falsches Präfix- oder MAC-Format für das Generieren der IPv6-Adresse durch " "EUI-64: %(prefix)s, %(mac)s:" #, python-format msgid "Bad prefix type for generating IPv6 address by EUI-64: %s" msgstr "" "Falscher Präfixtyp für das Generieren der IPv6-Adresse durch EUI-64: %s" #, python-format msgid "Invalid input value \"%s\"." msgstr "Ungültiger Eingabewert \"%s\"." #, python-format msgid "Invalid string format: %s" msgstr "Ungültiges Stringformat: %s" #, python-format msgid "Invalid unit system: \"%s\"" msgstr "Ungültiges Einheitensystem: \"%s\"" msgid "Snapshot list encountered but no header found!" msgstr "Momentaufnahmenliste gefunden, aber kein Header gefunden!" msgid "Unable to generate IP address by EUI64 for IPv4 prefix" msgstr "" "IP-Adresse kann nicht mithilfe von EUI64 mit dem IPv4-Präfix generiert werden" #, python-format msgid "Unrecognized value '%(val)s', acceptable values are: %(acceptable)s" msgstr "Nicht erkannter Wert '%(val)s', zulässige Werte are: %(acceptable)s" #, python-format msgid "Version %s is invalid." msgstr "Version %s ist ungültig." oslo.utils-4.1.1/oslo_utils/netutils.py0000664000175000017500000003374213643050415020301 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation. # All Rights Reserved. # # 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. """ Network-related utilities and helper functions. """ import logging import os import re import socket import netaddr import netifaces import six from six.moves.urllib import parse from oslo_utils._i18n import _ LOG = logging.getLogger(__name__) _IS_IPV6_ENABLED = None def parse_host_port(address, default_port=None): """Interpret a string as a host:port pair. An IPv6 address MUST be escaped if accompanied by a port, because otherwise ambiguity ensues: 2001:db8:85a3::8a2e:370:7334 means both [2001:db8:85a3::8a2e:370:7334] and [2001:db8:85a3::8a2e:370]:7334. >>> parse_host_port('server01:80') ('server01', 80) >>> parse_host_port('server01') ('server01', None) >>> parse_host_port('server01', default_port=1234) ('server01', 1234) >>> parse_host_port('[::1]:80') ('::1', 80) >>> parse_host_port('[::1]') ('::1', None) >>> parse_host_port('[::1]', default_port=1234) ('::1', 1234) >>> parse_host_port('2001:db8:85a3::8a2e:370:7334', default_port=1234) ('2001:db8:85a3::8a2e:370:7334', 1234) >>> parse_host_port(None) (None, None) """ if not address: return (None, None) if address[0] == '[': # Escaped ipv6 _host, _port = address[1:].split(']') host = _host if ':' in _port: port = _port.split(':')[1] else: port = default_port else: if address.count(':') == 1: host, port = address.split(':') else: # 0 means ipv4, >1 means ipv6. # We prohibit unescaped ipv6 addresses with port. host = address port = default_port return (host, None if port is None else int(port)) def is_valid_ipv4(address): """Verify that address represents a valid IPv4 address. :param address: Value to verify :type address: string :returns: bool .. versionadded:: 1.1 """ try: return netaddr.valid_ipv4(address) except netaddr.AddrFormatError: return False def is_valid_ipv6(address): """Verify that address represents a valid IPv6 address. :param address: Value to verify :type address: string :returns: bool .. versionadded:: 1.1 """ if not address: return False parts = address.rsplit("%", 1) address = parts[0] scope = parts[1] if len(parts) > 1 else None if scope is not None and (len(scope) < 1 or len(scope) > 15): return False try: return netaddr.valid_ipv6(address, netaddr.core.INET_PTON) except netaddr.AddrFormatError: return False def is_valid_cidr(address): """Verify that address represents a valid CIDR address. :param address: Value to verify :type address: string :returns: bool .. versionadded:: 3.8 """ try: # Validate the correct CIDR Address netaddr.IPNetwork(address) except (TypeError, netaddr.AddrFormatError): return False # Prior validation partially verify /xx part # Verify it here ip_segment = address.split('/') if (len(ip_segment) <= 1 or ip_segment[1] == ''): return False return True def is_valid_ipv6_cidr(address): """Verify that address represents a valid IPv6 CIDR address. :param address: address to verify :type address: string :returns: true if address is valid, false otherwise .. versionadded:: 3.17 """ try: netaddr.IPNetwork(address, version=6).cidr return True except (TypeError, netaddr.AddrFormatError): return False def get_ipv6_addr_by_EUI64(prefix, mac): """Calculate IPv6 address using EUI-64 specification. This method calculates the IPv6 address using the EUI-64 addressing scheme as explained in rfc2373. :param prefix: IPv6 prefix. :param mac: IEEE 802 48-bit MAC address. :returns: IPv6 address on success. :raises ValueError, TypeError: For any invalid input. .. versionadded:: 1.4 """ # Check if the prefix is an IPv4 address if is_valid_ipv4(prefix): msg = _("Unable to generate IP address by EUI64 for IPv4 prefix") raise ValueError(msg) try: eui64 = int(netaddr.EUI(mac).eui64()) prefix = netaddr.IPNetwork(prefix) return netaddr.IPAddress(prefix.first + eui64 ^ (1 << 57)) except (ValueError, netaddr.AddrFormatError): raise ValueError(_('Bad prefix or mac format for generating IPv6 ' 'address by EUI-64: %(prefix)s, %(mac)s:') % {'prefix': prefix, 'mac': mac}) except TypeError: raise TypeError(_('Bad prefix type for generating IPv6 address by ' 'EUI-64: %s') % prefix) def is_ipv6_enabled(): """Check if IPv6 support is enabled on the platform. This api will look into the proc entries of the platform to figure out the status of IPv6 support on the platform. :returns: True if the platform has IPv6 support, False otherwise. .. versionadded:: 1.4 """ global _IS_IPV6_ENABLED if _IS_IPV6_ENABLED is None: disabled_ipv6_path = "/proc/sys/net/ipv6/conf/default/disable_ipv6" if os.path.exists(disabled_ipv6_path): with open(disabled_ipv6_path, 'r') as f: disabled = f.read().strip() _IS_IPV6_ENABLED = disabled == "0" else: _IS_IPV6_ENABLED = False return _IS_IPV6_ENABLED def escape_ipv6(address): """Escape an IP address in square brackets if IPv6 :param address: address to optionaly escape :type address: string :returns: string .. versionadded:: 3.29.0 """ if is_valid_ipv6(address): return "[%s]" % address return address def is_valid_ip(address): """Verify that address represents a valid IP address. :param address: Value to verify :type address: string :returns: bool .. versionadded:: 1.1 """ return is_valid_ipv4(address) or is_valid_ipv6(address) def is_valid_mac(address): """Verify the format of a MAC address. Check if a MAC address is valid and contains six octets. Accepts colon-separated format only. :param address: MAC address to be validated. :returns: True if valid. False if not. .. versionadded:: 3.17 """ m = "[0-9a-f]{2}(:[0-9a-f]{2}){5}$" return (isinstance(address, six.string_types) and re.match(m, address.lower())) def _is_int_in_range(value, start, end): """Try to convert value to int and check if it lies within range 'start' to 'end'. :param value: value to verify :param start: start number of range :param end: end number of range :returns: bool """ try: val = int(value) except (ValueError, TypeError): return False return (start <= val <= end) def is_valid_port(port): """Verify that port represents a valid port number. Port can be valid integer having a value of 0 up to and including 65535. .. versionadded:: 1.1.1 """ return _is_int_in_range(port, 0, 65535) def is_valid_icmp_type(type): """Verify if ICMP type is valid. :param type: ICMP *type* field can only be a valid integer :returns: bool ICMP *type* field can be valid integer having a value of 0 up to and including 255. """ return _is_int_in_range(type, 0, 255) def is_valid_icmp_code(code): """Verify if ICMP code is valid. :param code: ICMP *code* field can be valid integer or None :returns: bool ICMP *code* field can be either None or valid integer having a value of 0 up to and including 255. """ if code is None: return True return _is_int_in_range(code, 0, 255) def get_my_ipv4(): """Returns the actual ipv4 of the local machine. This code figures out what source address would be used if some traffic were to be sent out to some well known address on the Internet. In this case, IP from RFC5737 is used, but the specific address does not matter much. No traffic is actually sent. .. versionadded:: 1.1 .. versionchanged:: 1.2.1 Return ``'127.0.0.1'`` if there is no default interface. """ try: csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock.connect(('192.0.2.0', 80)) (addr, port) = csock.getsockname() csock.close() return addr except socket.error: return _get_my_ipv4_address() def _get_my_ipv4_address(): """Figure out the best ipv4 """ LOCALHOST = '127.0.0.1' gtw = netifaces.gateways() try: interface = gtw['default'][netifaces.AF_INET][1] except (KeyError, IndexError): LOG.info('Could not determine default network interface, ' 'using 127.0.0.1 for IPv4 address') return LOCALHOST try: return netifaces.ifaddresses(interface)[netifaces.AF_INET][0]['addr'] except (KeyError, IndexError): LOG.info('Could not determine IPv4 address for interface %s, ' 'using 127.0.0.1', interface) except Exception as e: LOG.info('Could not determine IPv4 address for ' 'interface %(interface)s: %(error)s', {'interface': interface, 'error': e}) return LOCALHOST class _ModifiedSplitResult(parse.SplitResult): """Split results class for urlsplit.""" def params(self, collapse=True): """Extracts the query parameters from the split urls components. This method will provide back as a dictionary the query parameter names and values that were provided in the url. :param collapse: Boolean, turn on or off collapsing of query values with the same name. Since a url can contain the same query parameter name with different values it may or may not be useful for users to care that this has happened. This parameter when True uses the last value that was given for a given name, while if False it will retain all values provided by associating the query parameter name with a list of values instead of a single (non-list) value. """ if self.query: if collapse: return dict(parse.parse_qsl(self.query)) else: params = {} for (key, value) in parse.parse_qsl(self.query): if key in params: if isinstance(params[key], list): params[key].append(value) else: params[key] = [params[key], value] else: params[key] = value return params else: return {} def urlsplit(url, scheme='', allow_fragments=True): """Parse a URL using urlparse.urlsplit(), splitting query and fragments. This function papers over Python issue9374_ when needed. .. _issue9374: http://bugs.python.org/issue9374 The parameters are the same as urlparse.urlsplit. """ scheme, netloc, path, query, fragment = parse.urlsplit( url, scheme, allow_fragments) if allow_fragments and '#' in path: path, fragment = path.split('#', 1) if '?' in path: path, query = path.split('?', 1) return _ModifiedSplitResult(scheme, netloc, path, query, fragment) def set_tcp_keepalive(sock, tcp_keepalive=True, tcp_keepidle=None, tcp_keepalive_interval=None, tcp_keepalive_count=None): """Set values for tcp keepalive parameters This function configures tcp keepalive parameters if users wish to do so. :param tcp_keepalive: Boolean, turn on or off tcp_keepalive. If users are not sure, this should be True, and default values will be used. :param tcp_keepidle: time to wait before starting to send keepalive probes :param tcp_keepalive_interval: time between successive probes, once the initial wait time is over :param tcp_keepalive_count: number of probes to send before the connection is killed """ # NOTE(praneshp): Despite keepalive being a tcp concept, the level is # still SOL_SOCKET. This is a quirk. if isinstance(tcp_keepalive, bool): sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, tcp_keepalive) else: raise TypeError("tcp_keepalive must be a boolean") if not tcp_keepalive: return # These options aren't available in the OS X version of eventlet, # Idle + Count * Interval effectively gives you the total timeout. if tcp_keepidle is not None: if hasattr(socket, 'TCP_KEEPIDLE'): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, tcp_keepidle) else: LOG.warning('tcp_keepidle not available on your system') if tcp_keepalive_interval is not None: if hasattr(socket, 'TCP_KEEPINTVL'): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, tcp_keepalive_interval) else: LOG.warning('tcp_keepintvl not available on your system') if tcp_keepalive_count is not None: if hasattr(socket, 'TCP_KEEPCNT'): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, tcp_keepalive_count) else: LOG.warning('tcp_keepcnt not available on your system') oslo.utils-4.1.1/oslo_utils/fixture.py0000664000175000017500000000556313643050415020120 0ustar zuulzuul00000000000000 # Copyright 2015 OpenStack Foundation # All Rights Reserved. # # 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. """ Test fixtures. .. versionadded:: 1.3 """ import threading import fixtures from oslo_utils import timeutils from oslo_utils import uuidutils class TimeFixture(fixtures.Fixture): """A fixture for overriding the time returned by timeutils.utcnow(). :param override_time: datetime instance or list thereof. If not given, defaults to the current UTC time. """ def __init__(self, override_time=None): super(TimeFixture, self).__init__() self._override_time = override_time def setUp(self): super(TimeFixture, self).setUp() timeutils.set_time_override(self._override_time) self.addCleanup(timeutils.clear_time_override) def advance_time_delta(self, timedelta): """Advance overridden time using a datetime.timedelta.""" timeutils.advance_time_delta(timedelta) def advance_time_seconds(self, seconds): """Advance overridden time by seconds.""" timeutils.advance_time_seconds(seconds) class _UUIDSentinels(object): """Registry of dynamically created, named, random UUID strings. An instance of this class will dynamically generate attributes as they are referenced, associating a random UUID string with each. Thereafter, referring to the same attribute will give the same UUID for the life of the instance. Plan accordingly. Usage:: from oslo_utils.fixture import uuidsentinel as uuids ... foo = uuids.foo do_a_thing(foo) # Referencing the same sentinel again gives the same value assert foo == uuids.foo # But a different one will be different assert foo != uuids.bar """ def __init__(self): self._sentinels = {} self._lock = threading.Lock() def __getattr__(self, name): if name.startswith('_'): raise ValueError('Sentinels must not start with _') with self._lock: if name not in self._sentinels: self._sentinels[name] = uuidutils.generate_uuid() return self._sentinels[name] # Singleton sentinel instance. Caveat emptor: using this multiple times in the # same process (including across multiple modules) will result in the same # values uuidsentinel = _UUIDSentinels() oslo.utils-4.1.1/oslo_utils/excutils.py0000664000175000017500000003322513643050415020266 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # Copyright 2012, 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. """ Exception related utilities. """ import functools import logging import os import sys import time import traceback import six from oslo_utils import encodeutils from oslo_utils import reflection from oslo_utils import timeutils class CausedByException(Exception): """Base class for exceptions which have associated causes. NOTE(harlowja): in later versions of python we can likely remove the need to have a ``cause`` here as PY3+ have implemented :pep:`3134` which handles chaining in a much more elegant manner. :param message: the exception message, typically some string that is useful for consumers to view when debugging or analyzing failures. :param cause: the cause of the exception being raised, when provided this should itself be an exception instance, this is useful for creating a chain of exceptions for versions of python where this is not yet implemented/supported natively. .. versionadded:: 2.4 """ def __init__(self, message, cause=None): super(CausedByException, self).__init__(message) self.cause = cause def __bytes__(self): return self.pformat().encode("utf8") def __str__(self): return self.pformat() def _get_message(self): # We must *not* call into the ``__str__`` method as that will # reactivate the pformat method, which will end up badly (and doesn't # look pretty at all); so be careful... return self.args[0] def pformat(self, indent=2, indent_text=" ", show_root_class=False): """Pretty formats a caused exception + any connected causes.""" if indent < 0: raise ValueError("Provided 'indent' must be greater than" " or equal to zero instead of %s" % indent) buf = six.StringIO() if show_root_class: buf.write(reflection.get_class_name(self, fully_qualified=False)) buf.write(": ") buf.write(self._get_message()) active_indent = indent next_up = self.cause seen = [] while next_up is not None and next_up not in seen: seen.append(next_up) buf.write(os.linesep) if isinstance(next_up, CausedByException): buf.write(indent_text * active_indent) buf.write(reflection.get_class_name(next_up, fully_qualified=False)) buf.write(": ") buf.write(next_up._get_message()) else: lines = traceback.format_exception_only(type(next_up), next_up) for i, line in enumerate(lines): buf.write(indent_text * active_indent) if line.endswith("\n"): # We'll add our own newlines on... line = line[0:-1] buf.write(line) if i + 1 != len(lines): buf.write(os.linesep) # Don't go deeper into non-caused-by exceptions... as we # don't know if there exception 'cause' attributes are even # useable objects... break active_indent += indent next_up = getattr(next_up, 'cause', None) return buf.getvalue() def raise_with_cause(exc_cls, message, *args, **kwargs): """Helper to raise + chain exceptions (when able) and associate a *cause*. NOTE(harlowja): Since in py3.x exceptions can be chained (due to :pep:`3134`) we should try to raise the desired exception with the given *cause* (or extract a *cause* from the current stack if able) so that the exception formats nicely in old and new versions of python. Since py2.x does **not** support exception chaining (or formatting) the exception class provided should take a ``cause`` keyword argument (which it may discard if it wants) to its constructor which can then be inspected/retained on py2.x to get *similar* information as would be automatically included/obtainable in py3.x. :param exc_cls: the exception class to raise (typically one derived from :py:class:`.CausedByException` or equivalent). :param message: the text/str message that will be passed to the exceptions constructor as its first positional argument. :param args: any additional positional arguments to pass to the exceptions constructor. :param kwargs: any additional keyword arguments to pass to the exceptions constructor. .. versionadded:: 1.6 """ if 'cause' not in kwargs: exc_type, exc, exc_tb = sys.exc_info() try: if exc is not None: kwargs['cause'] = exc finally: # Leave no references around (especially with regards to # tracebacks and any variables that it retains internally). del(exc_type, exc, exc_tb) six.raise_from(exc_cls(message, *args, **kwargs), kwargs.get('cause')) class save_and_reraise_exception(object): """Save current exception, run some code and then re-raise. In some cases the exception context can be cleared, resulting in None being attempted to be re-raised after an exception handler is run. This can happen when eventlet switches greenthreads or when running an exception handler, code raises and catches an exception. In both cases the exception context will be cleared. To work around this, we save the exception state, run handler code, and then re-raise the original exception. If another exception occurs, the saved exception is logged and the new exception is re-raised. In some cases the caller may not want to re-raise the exception, and for those circumstances this context provides a reraise flag that can be used to suppress the exception. For example:: except Exception: with save_and_reraise_exception() as ctxt: decide_if_need_reraise() if not should_be_reraised: ctxt.reraise = False If another exception occurs and reraise flag is False, the saved exception will not be logged. If the caller wants to raise new exception during exception handling he/she sets reraise to False initially with an ability to set it back to True if needed:: except Exception: with save_and_reraise_exception(reraise=False) as ctxt: [if statements to determine whether to raise a new exception] # Not raising a new exception, so reraise ctxt.reraise = True .. versionchanged:: 1.4 Added *logger* optional parameter. """ def __init__(self, reraise=True, logger=None): self.reraise = reraise if logger is None: logger = logging.getLogger() self.logger = logger self.type_, self.value, self.tb = (None, None, None) def force_reraise(self): if self.type_ is None and self.value is None: raise RuntimeError("There is no (currently) captured exception" " to force the reraising of") six.reraise(self.type_, self.value, self.tb) def capture(self, check=True): (type_, value, tb) = sys.exc_info() if check and type_ is None and value is None: raise RuntimeError("There is no active exception to capture") self.type_, self.value, self.tb = (type_, value, tb) return self def __enter__(self): # TODO(harlowja): perhaps someday in the future turn check here # to true, because that is likely the desired intention, and doing # so ensures that people are actually using this correctly. return self.capture(check=False) def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is not None: if self.reraise: self.logger.error('Original exception being dropped: %s', traceback.format_exception(self.type_, self.value, self.tb)) return False if self.reraise: self.force_reraise() def forever_retry_uncaught_exceptions(*args, **kwargs): """Decorates provided function with infinite retry behavior. The function retry delay is **always** one second unless keyword argument ``retry_delay`` is passed that defines a value different than 1.0 (less than zero values are automatically changed to be 0.0). If repeated exceptions with the same message occur, logging will only output/get triggered for those equivalent messages every 60.0 seconds, this can be altered by keyword argument ``same_log_delay`` to be a value different than 60.0 seconds (exceptions that change the message are always logged no matter what this delay is set to). As in the ``retry_delay`` case if this is less than zero, it will be automatically changed to be 0.0. """ def decorator(infunc): retry_delay = max(0.0, float(kwargs.get('retry_delay', 1.0))) same_log_delay = max(0.0, float(kwargs.get('same_log_delay', 60.0))) @six.wraps(infunc) def wrapper(*args, **kwargs): last_exc_message = None same_failure_count = 0 watch = timeutils.StopWatch(duration=same_log_delay) while True: try: return infunc(*args, **kwargs) except Exception as exc: this_exc_message = encodeutils.exception_to_unicode(exc) if this_exc_message == last_exc_message: same_failure_count += 1 else: same_failure_count = 1 if this_exc_message != last_exc_message or watch.expired(): # The watch has expired or the exception message # changed, so time to log it again... logging.exception( 'Unexpected exception occurred %d time(s)... ' 'retrying.' % same_failure_count) if not watch.has_started(): watch.start() else: watch.restart() same_failure_count = 0 last_exc_message = this_exc_message time.sleep(retry_delay) return wrapper # This is needed to handle when the decorator has args or the decorator # doesn't have args, python is rather weird here... if kwargs or not args: return decorator else: if len(args) == 1: return decorator(args[0]) else: return decorator class exception_filter(object): """A context manager that prevents some exceptions from being raised. Use this class as a decorator for a function that returns whether a given exception should be ignored, in cases where complex logic beyond subclass matching is required. e.g. >>> @exception_filter >>> def ignore_test_assertions(ex): ... return isinstance(ex, AssertionError) and 'test' in str(ex) The filter matching function can then be used as a context manager: >>> with ignore_test_assertions: ... assert False, 'This is a test' or called directly: >>> try: ... assert False, 'This is a test' ... except Exception as ex: ... ignore_test_assertions(ex) Any non-matching exception will be re-raised. When the filter is used as a context manager, the traceback for re-raised exceptions is always preserved. When the filter is called as a function, the traceback is preserved provided that no other exceptions have been raised in the intervening time. The context manager method is preferred for this reason except in cases where the ignored exception affects control flow. """ def __init__(self, should_ignore_ex): self._should_ignore_ex = should_ignore_ex if all(hasattr(should_ignore_ex, a) for a in functools.WRAPPER_ASSIGNMENTS): functools.update_wrapper(self, should_ignore_ex) def __get__(self, obj, owner): return type(self)(self._should_ignore_ex.__get__(obj, owner)) def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_val is not None: return self._should_ignore_ex(exc_val) def __call__(self, ex): """Re-raise any exception value not being filtered out. If the exception was the last to be raised, it will be re-raised with its original traceback. """ exc_type, exc_val, traceback = sys.exc_info() try: if not self._should_ignore_ex(ex): if exc_val is ex: six.reraise(exc_type, exc_val, traceback) else: raise ex finally: del exc_type, exc_val, traceback oslo.utils-4.1.1/oslo_utils/fnmatch.py0000664000175000017500000000505013643050415020041 0ustar zuulzuul00000000000000# 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. """Thread safe fnmatch re-implementation. Standard library fnmatch in Python versions <= 2.7.9 has thread safe issue, this module is created for such case. see: https://bugs.python.org/issue23191 .. versionadded:: 3.3 """ from __future__ import absolute_import import fnmatch as standard_fnmatch import os import posixpath import re import sys if sys.version_info > (2, 7, 9): fnmatch = standard_fnmatch.fnmatch fnmatchcase = standard_fnmatch.fnmatchcase filter = standard_fnmatch.filter translate = standard_fnmatch.translate else: _MATCH_CACHE = {} _MATCH_CACHE_MAX = 100 translate = standard_fnmatch.translate def _get_cached_pattern(pattern): cached_pattern = _MATCH_CACHE.get(pattern) if cached_pattern is None: translated_pattern = translate(pattern) cached_pattern = re.compile(translated_pattern) if len(_MATCH_CACHE) >= _MATCH_CACHE_MAX: _MATCH_CACHE.clear() _MATCH_CACHE[pattern] = cached_pattern return cached_pattern def fnmatchcase(filename, pattern): cached_pattern = _get_cached_pattern(pattern) return cached_pattern.match(filename) is not None def fnmatch(filename, pattern): filename = os.path.normcase(filename) pattern = os.path.normcase(pattern) return fnmatchcase(filename, pattern) def filter(filenames, pattern): filtered_filenames = [] pattern = os.path.normcase(pattern) cached_pattern = _get_cached_pattern(pattern) if os.path is posixpath: # normcase on posix is NOP. Optimize it away from the loop. for filename in filenames: if cached_pattern.match(filename): filtered_filenames.append(filename) else: for filename in filenames: norm_name = os.path.normcase(filename) if cached_pattern.match(norm_name): filtered_filenames.append(filename) return filtered_filenames oslo.utils-4.1.1/oslo_utils/specs_matcher.py0000664000175000017500000001353313643050415021246 0ustar zuulzuul00000000000000# Copyright (c) 2011 OpenStack Foundation # All Rights Reserved. # # 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 ast import operator import pyparsing def _all_in(x, *y): x = ast.literal_eval(x) if not isinstance(x, list): raise TypeError(" must compare with a list literal" " string, EG \"%s\"" % (['aes', 'mmx'],)) return all(val in x for val in y) op_methods = { # This one is special/odd, # TODO(harlowja): fix it so that it's not greater than or # equal, see here for the original @ https://review.openstack.org/#/c/8089/ '=': lambda x, y: float(x) >= float(y), # More sane ops/methods # Numerical methods '!=': lambda x, y: float(x) != float(y), '<=': lambda x, y: float(x) <= float(y), '<': lambda x, y: float(x) < float(y), '==': lambda x, y: float(x) == float(y), '>=': lambda x, y: float(x) >= float(y), '>': lambda x, y: float(x) > float(y), # String methods 's!=': operator.ne, 's<': operator.lt, 's<=': operator.le, 's==': operator.eq, 's>': operator.gt, 's>=': operator.ge, # Other '': _all_in, '': lambda x, y: y in x, '': lambda x, *y: any(x == a for a in y), } def make_grammar(): """Creates the grammar to be used by a spec matcher. The grammar created supports the following operations. Numerical values: * ``= :`` equal to or greater than. This is equivalent to ``>=`` and is supported for `legacy reasons `_ * ``!= :`` Float/integer value not equal * ``<= :`` Float/integer value less than or equal * ``< :`` Float/integer value less than * ``== :`` Float/integer value equal * ``>= :`` Float/integer value greater than or equal * ``> :`` Float/integer value greater String operations: * ``s!= :`` Not equal * ``s< :`` Less than * ``s<= :`` Less than or equal * ``s== :`` Equal * ``s> :`` Greater than * ``s>= :`` Greater than or equal Other operations: * `` :`` All items 'in' value * `` :`` Item 'in' value, like a substring in a string. * `` :`` Logical 'or' If no operator is specified the default is ``s==`` (string equality comparison) Example operations: * ``">= 60"`` Is the numerical value greater than or equal to 60 * ``" spam eggs"`` Does the value contain ``spam`` or ``eggs`` * ``"s== 2.1.0"`` Is the string value equal to ``2.1.0`` * ``" gcc"`` Is the string ``gcc`` contained in the value string * ``" aes mmx"`` Are both ``aes`` and ``mmx`` in the value :returns: A pyparsing.MatchFirst object. See https://pythonhosted.org/pyparsing/ for details on pyparsing. """ # This is apparently how pyparsing recommends to be used, # as http://pyparsing.wikispaces.com/share/view/644825 states that # it is not thread-safe to use a parser across threads. unary_ops = ( # Order matters here (so that '=' doesn't match before '==') pyparsing.Literal("==") | pyparsing.Literal("=") | pyparsing.Literal("!=") | pyparsing.Literal("") | pyparsing.Literal(">=") | pyparsing.Literal("<=") | pyparsing.Literal(">") | pyparsing.Literal("<") | pyparsing.Literal("s==") | pyparsing.Literal("s!=") | # Order matters here (so that '<' doesn't match before '<=') pyparsing.Literal("s<=") | pyparsing.Literal("s<") | # Order matters here (so that '>' doesn't match before '>=') pyparsing.Literal("s>=") | pyparsing.Literal("s>")) all_in_nary_op = pyparsing.Literal("") or_ = pyparsing.Literal("") # An atom is anything not an keyword followed by anything but whitespace atom = ~(unary_ops | all_in_nary_op | or_) + pyparsing.Regex(r"\S+") unary = unary_ops + atom nary = all_in_nary_op + pyparsing.OneOrMore(atom) disjunction = pyparsing.OneOrMore(or_ + atom) # Even-numbered tokens will be '', so we drop them disjunction.setParseAction(lambda _s, _l, t: [""] + t[1::2]) expr = disjunction | nary | unary | atom return expr def match(cmp_value, spec): """Match a given value to a given spec DSL. This uses the grammar defined by make_grammar() :param cmp_value: Value to be checked for match. :param spec: The comparison specification string, for example ``">= 70"`` or ``"s== string_value"``. See ``make_grammar()`` for examples of a specification string. :returns: True if cmp_value is a match for spec. False otherwise. """ expr = make_grammar() try: # As of 2018-01-29 documentation on parseString() # https://pythonhosted.org/pyparsing/pyparsing.ParserElement-class.html#parseString # # parseString() will take our specification string, for example "< 6" # and convert it into a list of ['<', "6"] tree = expr.parseString(spec) except pyparsing.ParseException: # If an exception then we will just check if the value matches the spec tree = [spec] if len(tree) == 1: return tree[0] == cmp_value # tree[0] will contain a string representation of a comparison operation # such as '>=', we then convert that string to a comparison function compare_func = op_methods[tree[0]] return compare_func(cmp_value, *tree[1:]) oslo.utils-4.1.1/oslo_utils/units.py0000664000175000017500000000232413643050415017564 0ustar zuulzuul00000000000000# Copyright 2013 IBM Corp # All Rights Reserved. # # 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. """ Unit constants """ # Binary unit constants. Ki = 1024 "Binary kilo unit" Mi = 1024 ** 2 "Binary mega unit" Gi = 1024 ** 3 "Binary giga unit" Ti = 1024 ** 4 "Binary tera unit" Pi = 1024 ** 5 "Binary peta unit" Ei = 1024 ** 6 "Binary exa unit" Zi = 1024 ** 7 "Binary zetta unit" Yi = 1024 ** 8 "Binary yotta unit" # Decimal unit constants. k = 1000 "Decimal kilo unit" M = 1000 ** 2 "Decimal mega unit" G = 1000 ** 3 "Decimal giga unit" T = 1000 ** 4 "Decimal tera unit" P = 1000 ** 5 "Decimal peta unit" E = 1000 ** 6 "Decimal exa unit" Z = 1000 ** 7 "Decimal zetta unit" Y = 1000 ** 8 "Decimal yotta unit" oslo.utils-4.1.1/oslo_utils/tests/0000775000175000017500000000000013643050530017207 5ustar zuulzuul00000000000000oslo.utils-4.1.1/oslo_utils/tests/test_fnmatch.py0000664000175000017500000000411013643050415022236 0ustar zuulzuul00000000000000# 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. from __future__ import absolute_import import fnmatch as standard_fnmatch import ntpath import posixpath import sys from unittest import mock from oslotest import base import six fnmatch = None class TestFnmatch(base.BaseTestCase): def _test_fnmatch(self): self.assertFalse(fnmatch.fnmatch("tesX", "Test")) self.assertTrue(fnmatch.fnmatch("test", "test")) self.assertFalse(fnmatch.fnmatchcase("test", "Test")) self.assertTrue(fnmatch.fnmatchcase("test", "test")) self.assertTrue(fnmatch.fnmatch("testX", "test*")) self.assertEqual(["Test"], fnmatch.filter(["Test", "TestX"], "Test")) def _test_fnmatch_posix_nt(self): with mock.patch("os.path", new=posixpath): self.assertFalse(fnmatch.fnmatch("test", "Test")) self._test_fnmatch() with mock.patch("os.path", new=ntpath): self._test_fnmatch() self.assertTrue(fnmatch.fnmatch("test", "Test")) self.assertEqual(["Test"], fnmatch.filter(["Test", "TestX"], "test")) def test_fnmatch(self): global fnmatch fnmatch = standard_fnmatch self._test_fnmatch_posix_nt() with mock.patch.object(sys, 'version_info', new=(2, 7, 11)): from oslo_utils import fnmatch as oslo_fnmatch fnmatch = oslo_fnmatch self._test_fnmatch_posix_nt() with mock.patch.object(sys, 'version_info', new=(2, 7, 0)): six.moves.reload_module(oslo_fnmatch) self._test_fnmatch_posix_nt() oslo.utils-4.1.1/oslo_utils/tests/test_eventletutils.py0000664000175000017500000002070513643050415023535 0ustar zuulzuul00000000000000# Copyright 2012, 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 threading from unittest import mock import warnings import eventlet from eventlet import greenthread from oslotest import base as test_base import six from oslo_utils import eventletutils class EventletUtilsTest(test_base.BaseTestCase): def setUp(self): super(EventletUtilsTest, self).setUp() self._old_avail = eventletutils.EVENTLET_AVAILABLE eventletutils.EVENTLET_AVAILABLE = True def tearDown(self): super(EventletUtilsTest, self).tearDown() eventletutils.EVENTLET_AVAILABLE = self._old_avail @mock.patch("oslo_utils.eventletutils._patcher") def test_warning_not_patched(self, mock_patcher): mock_patcher.already_patched = True mock_patcher.is_monkey_patched.return_value = False with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") eventletutils.warn_eventlet_not_patched(['os']) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(RuntimeWarning, w.category) self.assertIn('os', six.text_type(w.message)) @mock.patch("oslo_utils.eventletutils._patcher") def test_warning_not_patched_none_provided(self, mock_patcher): mock_patcher.already_patched = True mock_patcher.is_monkey_patched.return_value = False with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") eventletutils.warn_eventlet_not_patched() self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(RuntimeWarning, w.category) for m in eventletutils._ALL_PATCH: self.assertIn(m, six.text_type(w.message)) @mock.patch("oslo_utils.eventletutils._patcher") def test_warning_not_patched_all(self, mock_patcher): mock_patcher.already_patched = True mock_patcher.is_monkey_patched.return_value = False with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") eventletutils.warn_eventlet_not_patched(['all']) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(RuntimeWarning, w.category) for m in eventletutils._ALL_PATCH: self.assertIn(m, six.text_type(w.message)) @mock.patch("oslo_utils.eventletutils._patcher") def test_no_warning(self, mock_patcher): mock_patcher.already_patched = True mock_patcher.is_monkey_patched.return_value = True with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") eventletutils.warn_eventlet_not_patched(['os']) self.assertEqual(0, len(capture)) @mock.patch("oslo_utils.eventletutils._patcher") def test_eventlet_is_patched(self, mock_patcher): mock_patcher.is_monkey_patched.return_value = True self.assertTrue(eventletutils.is_monkey_patched('os')) mock_patcher.is_monkey_patched.return_value = False self.assertFalse(eventletutils.is_monkey_patched('os')) @mock.patch("oslo_utils.eventletutils._patcher", None) def test_eventlet_no_patcher(self): self.assertFalse(eventletutils.is_monkey_patched('os')) @mock.patch("oslo_utils.eventletutils._patcher") def test_partially_patched_warning(self, mock_patcher): is_patched = set() mock_patcher.already_patched = True mock_patcher.is_monkey_patched.side_effect = lambda m: m in is_patched with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") eventletutils.warn_eventlet_not_patched(['os']) self.assertEqual(1, len(capture)) is_patched.add('os') with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") eventletutils.warn_eventlet_not_patched(['os']) self.assertEqual(0, len(capture)) is_patched.add('thread') with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") eventletutils.warn_eventlet_not_patched(['os', 'thread']) self.assertEqual(0, len(capture)) with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") eventletutils.warn_eventlet_not_patched(['all']) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(RuntimeWarning, w.category) for m in ['os', 'thread']: self.assertNotIn(m, six.text_type(w.message)) def test_invalid_patch_check(self): self.assertRaises(ValueError, eventletutils.warn_eventlet_not_patched, ['blah.blah']) @mock.patch('oslo_utils.eventletutils._eventlet') def test_event_api_compat(self, mock_eventlet): with mock.patch('oslo_utils.eventletutils.is_monkey_patched', return_value=True): e_event = eventletutils.Event() self.assertIsInstance(e_event, eventletutils.EventletEvent) t_event = eventletutils.Event() if six.PY3: t_event_cls = threading.Event else: t_event_cls = threading._Event self.assertIsInstance(t_event, t_event_cls) public_methods = [m for m in dir(t_event) if not m.startswith("_") and callable(getattr(t_event, m))] for method in public_methods: self.assertTrue(hasattr(e_event, method)) # Ensure set() allows multiple invocations, same as in # threading implementation. e_event.set() self.assertTrue(e_event.isSet()) e_event.set() self.assertTrue(e_event.isSet()) def test_event_no_timeout(self): event = eventletutils.EventletEvent() def thread_a(): self.assertTrue(event.wait()) a = greenthread.spawn(thread_a) with eventlet.timeout.Timeout(0.5, False): a.wait() self.fail('wait() timed out') def test_event_race(self): event = eventletutils.EventletEvent() def thread_a(): self.assertTrue(event.wait(2)) a = greenthread.spawn(thread_a) def thread_b(): eventlet.sleep(0.1) event.clear() event.set() a.wait() b = greenthread.spawn(thread_b) with eventlet.timeout.Timeout(0.5): b.wait() def test_event_clear_timeout(self): event = eventletutils.EventletEvent() def thread_a(): self.assertFalse(event.wait(0.5)) a = greenthread.spawn(thread_a) def thread_b(): eventlet.sleep(0.1) event.clear() eventlet.sleep(0.1) event.clear() a.wait() b = greenthread.spawn(thread_b) with eventlet.timeout.Timeout(0.7): b.wait() def test_event_set_clear_timeout(self): event = eventletutils.EventletEvent() wakes = [] def thread_func(): result = event.wait(0.2) wakes.append(result) if len(wakes) == 1: self.assertTrue(result) event.clear() else: self.assertFalse(result) a = greenthread.spawn(thread_func) b = greenthread.spawn(thread_func) eventlet.sleep(0) # start threads event.set() with eventlet.timeout.Timeout(0.3): a.wait() b.wait() self.assertFalse(event.is_set()) self.assertEqual([True, False], wakes) @mock.patch('oslo_utils.eventletutils._eventlet.event.Event') def test_event_clear_already_sent(self, mock_event): old_event = mock.Mock() new_event = mock.Mock() mock_event.side_effect = [old_event, new_event] event = eventletutils.EventletEvent() event.set() event.clear() self.assertEqual(1, old_event.send.call_count) oslo.utils-4.1.1/oslo_utils/tests/test_netutils.py0000664000175000017500000004260613643050415022501 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation. # All Rights Reserved. # # 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 contextlib import socket from unittest import mock import netifaces from oslotest import base as test_base import six from oslo_utils import netutils class NetworkUtilsTest(test_base.BaseTestCase): def test_no_host(self): result = netutils.urlsplit('http://') self.assertEqual('', result.netloc) self.assertIsNone(result.port) self.assertIsNone(result.hostname) self.assertEqual('http', result.scheme) def test_parse_host_port(self): self.assertEqual(('server01', 80), netutils.parse_host_port('server01:80')) self.assertEqual(('server01', None), netutils.parse_host_port('server01')) self.assertEqual(('server01', 1234), netutils.parse_host_port('server01', default_port=1234)) self.assertEqual(('::1', 80), netutils.parse_host_port('[::1]:80')) self.assertEqual(('::1', None), netutils.parse_host_port('[::1]')) self.assertEqual(('::1', 1234), netutils.parse_host_port('[::1]', default_port=1234)) self.assertEqual(('2001:db8:85a3::8a2e:370:7334', 1234), netutils.parse_host_port( '2001:db8:85a3::8a2e:370:7334', default_port=1234)) def test_urlsplit(self): result = netutils.urlsplit('rpc://myhost?someparam#somefragment') self.assertEqual(result.scheme, 'rpc') self.assertEqual(result.netloc, 'myhost') self.assertEqual(result.path, '') self.assertEqual(result.query, 'someparam') self.assertEqual(result.fragment, 'somefragment') result = netutils.urlsplit( 'rpc://myhost/mypath?someparam#somefragment', allow_fragments=False) self.assertEqual(result.scheme, 'rpc') self.assertEqual(result.netloc, 'myhost') self.assertEqual(result.path, '/mypath') self.assertEqual(result.query, 'someparam#somefragment') self.assertEqual(result.fragment, '') result = netutils.urlsplit( 'rpc://user:pass@myhost/mypath?someparam#somefragment', allow_fragments=False) self.assertEqual(result.scheme, 'rpc') self.assertEqual(result.netloc, 'user:pass@myhost') self.assertEqual(result.path, '/mypath') self.assertEqual(result.query, 'someparam#somefragment') self.assertEqual(result.fragment, '') def test_urlsplit_ipv6(self): ipv6_url = 'http://[::1]:443/v2.0/' result = netutils.urlsplit(ipv6_url) self.assertEqual(result.scheme, 'http') self.assertEqual(result.netloc, '[::1]:443') self.assertEqual(result.path, '/v2.0/') self.assertEqual(result.hostname, '::1') self.assertEqual(result.port, 443) ipv6_url = 'http://user:pass@[::1]/v2.0/' result = netutils.urlsplit(ipv6_url) self.assertEqual(result.scheme, 'http') self.assertEqual(result.netloc, 'user:pass@[::1]') self.assertEqual(result.path, '/v2.0/') self.assertEqual(result.hostname, '::1') self.assertIsNone(result.port) ipv6_url = 'https://[2001:db8:85a3::8a2e:370:7334]:1234/v2.0/xy?ab#12' result = netutils.urlsplit(ipv6_url) self.assertEqual(result.scheme, 'https') self.assertEqual(result.netloc, '[2001:db8:85a3::8a2e:370:7334]:1234') self.assertEqual(result.path, '/v2.0/xy') self.assertEqual(result.hostname, '2001:db8:85a3::8a2e:370:7334') self.assertEqual(result.port, 1234) self.assertEqual(result.query, 'ab') self.assertEqual(result.fragment, '12') def test_urlsplit_params(self): test_url = "http://localhost/?a=b&c=d" result = netutils.urlsplit(test_url) self.assertEqual({'a': 'b', 'c': 'd'}, result.params()) self.assertEqual({'a': 'b', 'c': 'd'}, result.params(collapse=False)) test_url = "http://localhost/?a=b&a=c&a=d" result = netutils.urlsplit(test_url) self.assertEqual({'a': 'd'}, result.params()) self.assertEqual({'a': ['b', 'c', 'd']}, result.params(collapse=False)) test_url = "http://localhost" result = netutils.urlsplit(test_url) self.assertEqual({}, result.params()) test_url = "http://localhost?" result = netutils.urlsplit(test_url) self.assertEqual({}, result.params()) def test_set_tcp_keepalive(self): mock_sock = mock.Mock() netutils.set_tcp_keepalive(mock_sock, True, 100, 10, 5) calls = [ mock.call.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, True), ] if hasattr(socket, 'TCP_KEEPIDLE'): calls += [ mock.call.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 100) ] if hasattr(socket, 'TCP_KEEPINTVL'): calls += [ mock.call.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10), ] if hasattr(socket, 'TCP_KEEPCNT'): calls += [ mock.call.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5) ] mock_sock.assert_has_calls(calls) mock_sock.reset_mock() netutils.set_tcp_keepalive(mock_sock, False) self.assertEqual(1, len(mock_sock.mock_calls)) def test_is_valid_ipv4(self): self.assertTrue(netutils.is_valid_ipv4('42.42.42.42')) self.assertFalse(netutils.is_valid_ipv4('-1.11.11.11')) self.assertFalse(netutils.is_valid_ipv4('')) def test_is_valid_ipv6(self): self.assertTrue(netutils.is_valid_ipv6('::1')) self.assertTrue(netutils.is_valid_ipv6('fe80::1%eth0')) self.assertFalse(netutils.is_valid_ip('fe%80::1%eth0')) self.assertFalse(netutils.is_valid_ipv6( '1fff::a88:85a3::172.31.128.1')) self.assertFalse(netutils.is_valid_ipv6('')) def test_escape_ipv6(self): self.assertEqual('[1234::1234]', netutils.escape_ipv6('1234::1234')) self.assertEqual('127.0.0.1', netutils.escape_ipv6('127.0.0.1')) def test_is_valid_ip(self): self.assertTrue(netutils.is_valid_ip('127.0.0.1')) self.assertTrue(netutils.is_valid_ip('2001:db8::ff00:42:8329')) self.assertTrue(netutils.is_valid_ip('fe80::1%eth0')) self.assertFalse(netutils.is_valid_ip('256.0.0.0')) self.assertFalse(netutils.is_valid_ip('::1.2.3.')) self.assertFalse(netutils.is_valid_ip('')) self.assertFalse(netutils.is_valid_ip(None)) def test_is_valid_mac(self): self.assertTrue(netutils.is_valid_mac("52:54:00:cf:2d:31")) self.assertTrue(netutils.is_valid_mac(u"52:54:00:cf:2d:31")) self.assertFalse(netutils.is_valid_mac("127.0.0.1")) self.assertFalse(netutils.is_valid_mac("not:a:mac:address")) self.assertFalse(netutils.is_valid_mac("52-54-00-cf-2d-31")) self.assertFalse(netutils.is_valid_mac("aa bb cc dd ee ff")) self.assertTrue(netutils.is_valid_mac("AA:BB:CC:DD:EE:FF")) self.assertFalse(netutils.is_valid_mac("AA BB CC DD EE FF")) self.assertFalse(netutils.is_valid_mac("AA-BB-CC-DD-EE-FF")) def test_is_valid_cidr(self): self.assertTrue(netutils.is_valid_cidr('10.0.0.0/24')) self.assertTrue(netutils.is_valid_cidr('10.0.0.1/32')) self.assertTrue(netutils.is_valid_cidr('0.0.0.0/0')) self.assertTrue(netutils.is_valid_cidr('2600::/64')) self.assertTrue(netutils.is_valid_cidr( '0000:0000:0000:0000:0000:0000:0000:0001/32')) self.assertFalse(netutils.is_valid_cidr('10.0.0.1')) self.assertFalse(netutils.is_valid_cidr('10.0.0.1/33')) self.assertFalse(netutils.is_valid_cidr(10)) def test_is_valid_ipv6_cidr(self): self.assertTrue(netutils.is_valid_ipv6_cidr("2600::/64")) self.assertTrue(netutils.is_valid_ipv6_cidr( "abcd:ef01:2345:6789:abcd:ef01:192.168.254.254/48")) self.assertTrue(netutils.is_valid_ipv6_cidr( "0000:0000:0000:0000:0000:0000:0000:0001/32")) self.assertTrue(netutils.is_valid_ipv6_cidr( "0000:0000:0000:0000:0000:0000:0000:0001")) self.assertFalse(netutils.is_valid_ipv6_cidr("foo")) self.assertFalse(netutils.is_valid_ipv6_cidr("127.0.0.1")) def test_valid_port(self): valid_inputs = [0, '0', 1, '1', 2, '3', '5', 8, 13, 21, '80', '3246', '65535'] for input_str in valid_inputs: self.assertTrue(netutils.is_valid_port(input_str)) def test_valid_port_fail(self): invalid_inputs = ['-32768', '65536', 528491, '528491', '528.491', 'thirty-seven', None] for input_str in invalid_inputs: self.assertFalse(netutils.is_valid_port(input_str)) def test_get_my_ip(self): sock_attrs = { 'return_value.getsockname.return_value': ['1.2.3.4', '']} with mock.patch('socket.socket', **sock_attrs): addr = netutils.get_my_ipv4() self.assertEqual(addr, '1.2.3.4') def test_is_int_in_range(self): valid_inputs = [(1, -100, 100), ('1', -100, 100), (100, -100, 100), ('100', -100, 100), (-100, -100, 100), ('-100', -100, 100)] for input_value in valid_inputs: self.assertTrue(netutils._is_int_in_range(*input_value)) def test_is_int_not_in_range(self): invalid_inputs = [(None, 1, 100), ('ten', 1, 100), (-1, 0, 255), ('None', 1, 100)] for input_value in invalid_inputs: self.assertFalse(netutils._is_int_in_range(*input_value)) def test_valid_icmp_type(self): valid_inputs = [1, '1', 0, '0', 255, '255'] for input_value in valid_inputs: self.assertTrue(netutils.is_valid_icmp_type(input_value)) def test_invalid_icmp_type(self): invalid_inputs = [-1, '-1', 256, '256', None, 'None', 'five'] for input_value in invalid_inputs: self.assertFalse(netutils.is_valid_icmp_type(input_value)) def test_valid_icmp_code(self): valid_inputs = [1, '1', 0, '0', 255, '255', None] for input_value in valid_inputs: self.assertTrue(netutils.is_valid_icmp_code(input_value)) def test_invalid_icmp_code(self): invalid_inputs = [-1, '-1', 256, '256', 'None', 'zero'] for input_value in invalid_inputs: self.assertFalse(netutils.is_valid_icmp_code(input_value)) @mock.patch('socket.socket') @mock.patch('oslo_utils.netutils._get_my_ipv4_address') def test_get_my_ip_socket_error(self, ip, mock_socket): mock_socket.side_effect = socket.error ip.return_value = '1.2.3.4' addr = netutils.get_my_ipv4() self.assertEqual(addr, '1.2.3.4') @mock.patch('netifaces.gateways') @mock.patch('netifaces.ifaddresses') def test_get_my_ipv4_address_with_default_route( self, ifaddr, gateways): with mock.patch.dict(netifaces.__dict__, {'AF_INET': '0'}): ifaddr.return_value = {'0': [{'addr': '172.18.204.1'}]} addr = netutils._get_my_ipv4_address() self.assertEqual('172.18.204.1', addr) @mock.patch('netifaces.gateways') @mock.patch('netifaces.ifaddresses') def test_get_my_ipv4_address_without_default_route( self, ifaddr, gateways): with mock.patch.dict(netifaces.__dict__, {'AF_INET': '0'}): ifaddr.return_value = {} addr = netutils._get_my_ipv4_address() self.assertEqual('127.0.0.1', addr) @mock.patch('netifaces.gateways') @mock.patch('netifaces.ifaddresses') def test_get_my_ipv4_address_without_default_interface( self, ifaddr, gateways): gateways.return_value = {} addr = netutils._get_my_ipv4_address() self.assertEqual('127.0.0.1', addr) self.assertFalse(ifaddr.called) class IPv6byEUI64TestCase(test_base.BaseTestCase): """Unit tests to generate IPv6 by EUI-64 operations.""" def test_generate_IPv6_by_EUI64(self): addr = netutils.get_ipv6_addr_by_EUI64('2001:db8::', '00:16:3e:33:44:55') self.assertEqual('2001:db8::216:3eff:fe33:4455', addr.format()) def test_generate_IPv6_with_IPv4_prefix(self): ipv4_prefix = '10.0.8' mac = '00:16:3e:33:44:55' self.assertRaises(ValueError, lambda: netutils.get_ipv6_addr_by_EUI64(ipv4_prefix, mac)) def test_generate_IPv6_with_bad_mac(self): bad_mac = '00:16:3e:33:44:5Z' prefix = '2001:db8::' self.assertRaises(ValueError, lambda: netutils.get_ipv6_addr_by_EUI64(prefix, bad_mac)) def test_generate_IPv6_with_bad_prefix(self): mac = '00:16:3e:33:44:55' bad_prefix = 'bb' self.assertRaises(ValueError, lambda: netutils.get_ipv6_addr_by_EUI64(bad_prefix, mac)) def test_generate_IPv6_with_error_prefix_type(self): mac = '00:16:3e:33:44:55' prefix = 123 self.assertRaises(TypeError, lambda: netutils.get_ipv6_addr_by_EUI64(prefix, mac)) def test_generate_IPv6_with_empty_prefix(self): mac = '00:16:3e:33:44:55' prefix = '' self.assertRaises(ValueError, lambda: netutils.get_ipv6_addr_by_EUI64(prefix, mac)) @contextlib.contextmanager def mock_file_content(content): # Allows StringIO to act like a context manager-enabled file. yield six.StringIO(content) class TestIsIPv6Enabled(test_base.BaseTestCase): def setUp(self): super(TestIsIPv6Enabled, self).setUp() def reset_detection_flag(): netutils._IS_IPV6_ENABLED = None reset_detection_flag() self.addCleanup(reset_detection_flag) @mock.patch('os.path.exists', return_value=True) @mock.patch('six.moves.builtins.open', return_value=mock_file_content('0')) def test_enabled(self, mock_open, exists): enabled = netutils.is_ipv6_enabled() self.assertTrue(enabled) @mock.patch('os.path.exists', return_value=True) @mock.patch('six.moves.builtins.open', return_value=mock_file_content('1')) def test_disabled(self, mock_open, exists): enabled = netutils.is_ipv6_enabled() self.assertFalse(enabled) @mock.patch('os.path.exists', return_value=False) @mock.patch('six.moves.builtins.open', side_effect=AssertionError('should not read')) def test_disabled_non_exists(self, mock_open, exists): enabled = netutils.is_ipv6_enabled() self.assertFalse(enabled) @mock.patch('os.path.exists', return_value=True) def test_memoize_enabled(self, exists): # Reset the flag to appear that we haven't looked for it yet. netutils._IS_IPV6_ENABLED = None with mock.patch('six.moves.builtins.open', return_value=mock_file_content('0')) as mock_open: enabled = netutils.is_ipv6_enabled() self.assertTrue(mock_open.called) self.assertTrue(netutils._IS_IPV6_ENABLED) self.assertTrue(enabled) # The second call should not use open again with mock.patch('six.moves.builtins.open', side_effect=AssertionError('should not be called')): enabled = netutils.is_ipv6_enabled() self.assertTrue(enabled) @mock.patch('os.path.exists', return_value=True) def test_memoize_disabled(self, exists): # Reset the flag to appear that we haven't looked for it yet. netutils._IS_IPV6_ENABLED = None with mock.patch('six.moves.builtins.open', return_value=mock_file_content('1')): enabled = netutils.is_ipv6_enabled() self.assertFalse(enabled) # The second call should not use open again with mock.patch('six.moves.builtins.open', side_effect=AssertionError('should not be called')): enabled = netutils.is_ipv6_enabled() self.assertFalse(enabled) @mock.patch('os.path.exists', return_value=False) @mock.patch('six.moves.builtins.open', side_effect=AssertionError('should not read')) def test_memoize_not_exists(self, mock_open, exists): # Reset the flag to appear that we haven't looked for it yet. netutils._IS_IPV6_ENABLED = None enabled = netutils.is_ipv6_enabled() self.assertFalse(enabled) enabled = netutils.is_ipv6_enabled() self.assertFalse(enabled) oslo.utils-4.1.1/oslo_utils/tests/test_excutils.py0000664000175000017500000005541513643050415022474 0ustar zuulzuul00000000000000# Copyright 2012, 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 logging from unittest import mock import fixtures from oslotest import base as test_base from oslo_utils import excutils from oslo_utils import timeutils class Fail1(excutils.CausedByException): pass class Fail2(excutils.CausedByException): pass class CausedByTest(test_base.BaseTestCase): def test_caused_by_explicit(self): e = self.assertRaises(Fail1, excutils.raise_with_cause, Fail1, "I was broken", cause=Fail2("I have been broken")) self.assertIsInstance(e.cause, Fail2) e_p = e.pformat() self.assertIn("I have been broken", e_p) self.assertIn("Fail2", e_p) def test_caused_by_implicit(self): def raises_chained(): try: raise Fail2("I have been broken") except Fail2: excutils.raise_with_cause(Fail1, "I was broken") e = self.assertRaises(Fail1, raises_chained) self.assertIsInstance(e.cause, Fail2) e_p = e.pformat() self.assertIn("I have been broken", e_p) self.assertIn("Fail2", e_p) class SaveAndReraiseTest(test_base.BaseTestCase): def test_save_and_reraise_exception_forced(self): def _force_reraise(): try: raise IOError("I broke") except Exception: with excutils.save_and_reraise_exception() as e: e.reraise = False e.force_reraise() self.assertRaises(IOError, _force_reraise) def test_save_and_reraise_exception_capture_reraise(self): def _force_reraise(): try: raise IOError("I broke") except Exception: excutils.save_and_reraise_exception().capture().force_reraise() self.assertRaises(IOError, _force_reraise) def test_save_and_reraise_exception_capture_not_active(self): e = excutils.save_and_reraise_exception() self.assertRaises(RuntimeError, e.capture, check=True) def test_save_and_reraise_exception_forced_not_active(self): e = excutils.save_and_reraise_exception() self.assertRaises(RuntimeError, e.force_reraise) e = excutils.save_and_reraise_exception() e.capture(check=False) self.assertRaises(RuntimeError, e.force_reraise) def test_save_and_reraise_exception(self): e = None msg = 'foo' try: try: raise Exception(msg) except Exception: with excutils.save_and_reraise_exception(): pass except Exception as _e: e = _e self.assertEqual(str(e), msg) @mock.patch('logging.getLogger') def test_save_and_reraise_exception_dropped(self, get_logger_mock): logger = get_logger_mock() e = None msg = 'second exception' try: try: raise Exception('dropped') except Exception: with excutils.save_and_reraise_exception(): raise Exception(msg) except Exception as _e: e = _e self.assertEqual(str(e), msg) self.assertTrue(logger.error.called) def test_save_and_reraise_exception_no_reraise(self): """Test that suppressing the reraise works.""" try: raise Exception('foo') except Exception: with excutils.save_and_reraise_exception() as ctxt: ctxt.reraise = False @mock.patch('logging.getLogger') def test_save_and_reraise_exception_dropped_no_reraise(self, get_logger_mock): logger = get_logger_mock() e = None msg = 'second exception' try: try: raise Exception('dropped') except Exception: with excutils.save_and_reraise_exception(reraise=False): raise Exception(msg) except Exception as _e: e = _e self.assertEqual(str(e), msg) self.assertFalse(logger.error.called) def test_save_and_reraise_exception_provided_logger(self): fake_logger = mock.MagicMock() try: try: raise Exception('foo') except Exception: with excutils.save_and_reraise_exception(logger=fake_logger): raise Exception('second exception') except Exception: pass self.assertTrue(fake_logger.error.called) class ForeverRetryUncaughtExceptionsTest(test_base.BaseTestCase): def setUp(self): super(ForeverRetryUncaughtExceptionsTest, self).setUp() self._exceptions = [] self.useFixture(fixtures.MockPatch('time.sleep', return_value=None)) @excutils.forever_retry_uncaught_exceptions def exception_generator(self): while self._exceptions: raise self._exceptions.pop(0) @mock.patch.object(logging, 'exception') @mock.patch.object(timeutils, 'now') def test_exc_retrier_1exc_gives_1log(self, mock_now, mock_log): self._exceptions = [ Exception('unexpected %d' % 1), ] mock_now.side_effect = [0] self.exception_generator() self.assertEqual([], self._exceptions) # log should only be called once mock_log.assert_called_once_with( 'Unexpected exception occurred %d time(s)... retrying.' % 1 ) mock_now.assert_has_calls([ mock.call(), ]) @mock.patch.object(logging, 'exception') @mock.patch.object(timeutils, 'now') def test_exc_retrier_same_10exc_1min_gives_1log(self, mock_now, mock_log): self._exceptions = [ Exception('unexpected 1'), ] # Timestamp calls that happen after the logging is possibly triggered. mock_now_side_effect = [0] # By design, the following exceptions won't get logged because they # are within the same minute. for i in range(2, 11): self._exceptions.append(Exception('unexpected 1')) # Timestamp calls that happen before the logging is possibly # triggered. mock_now_side_effect.append(i) mock_now.side_effect = mock_now_side_effect self.exception_generator() self.assertEqual([], self._exceptions) self.assertEqual(10, len(mock_now.mock_calls)) self.assertEqual(1, len(mock_log.mock_calls)) mock_log.assert_has_calls([ mock.call('Unexpected exception occurred 1 time(s)... retrying.'), ]) @mock.patch.object(logging, 'exception') @mock.patch.object(timeutils, 'now') def test_exc_retrier_same_2exc_2min_gives_2logs(self, mock_now, mock_log): self._exceptions = [ Exception('unexpected 1'), Exception('unexpected 1'), ] mock_now.side_effect = [ # Timestamp calls that happen after the logging is possibly # triggered 0, # Timestamp calls that happen before the logging is possibly # triggered 65, # Timestamp calls that happen after the logging is possibly # triggered. 65, 66, ] self.exception_generator() self.assertEqual([], self._exceptions) self.assertEqual(4, len(mock_now.mock_calls)) self.assertEqual(2, len(mock_log.mock_calls)) mock_log.assert_has_calls([ mock.call('Unexpected exception occurred 1 time(s)... retrying.'), mock.call('Unexpected exception occurred 1 time(s)... retrying.'), ]) @mock.patch.object(logging, 'exception') @mock.patch.object(timeutils, 'now') def test_exc_retrier_same_10exc_2min_gives_2logs(self, mock_now, mock_log): self._exceptions = [ Exception('unexpected 1'), ] # Timestamp calls that happen after the logging is possibly triggered. mock_now_side_effect = [ 0, ] for ts in [12, 23, 34, 45]: self._exceptions.append(Exception('unexpected 1')) # Timestamp calls that happen before the logging is possibly # triggered. mock_now_side_effect.append(ts) # The previous 4 exceptions are counted here self._exceptions.append(Exception('unexpected 1')) # Timestamp calls that happen before the logging is possibly triggered. mock_now_side_effect.append(106) for ts in [106, 107]: # Timestamp calls that happen after the logging is possibly # triggered. mock_now_side_effect.append(ts) # Again, the following are not logged due to being within # the same minute for ts in [117, 128, 139, 150]: self._exceptions.append(Exception('unexpected 1')) # Timestamp calls that happen before the logging is possibly # triggered. mock_now_side_effect.append(ts) mock_now.side_effect = mock_now_side_effect self.exception_generator() self.assertEqual([], self._exceptions) self.assertEqual(12, len(mock_now.mock_calls)) self.assertEqual(2, len(mock_log.mock_calls)) mock_log.assert_has_calls([ mock.call('Unexpected exception occurred 1 time(s)... retrying.'), mock.call('Unexpected exception occurred 5 time(s)... retrying.'), ]) @mock.patch.object(logging, 'exception') @mock.patch.object(timeutils, 'now') def test_exc_retrier_mixed_4exc_1min_gives_2logs(self, mock_now, mock_log): # The stop watch will be started, which will consume one timestamp # call. self._exceptions = [ Exception('unexpected 1'), ] # Timestamp calls that happen after the logging is possibly # triggered. mock_now_side_effect = [0] # By design, this second 'unexpected 1' exception is not counted. This # is likely a rare thing and is a sacrifice for code simplicity. self._exceptions.append(Exception('unexpected 1')) # Timestamp calls that happen before the logging is possibly triggered. # Since the exception will be the same the expiry method will be # called, which uses up a timestamp call. mock_now_side_effect.append(5) self._exceptions.append(Exception('unexpected 2')) # Timestamp calls that happen after the logging is possibly triggered. # The watch should get reset, which uses up two timestamp calls. mock_now_side_effect.extend([10, 20]) self._exceptions.append(Exception('unexpected 2')) # Timestamp calls that happen before the logging is possibly triggered. # Since the exception will be the same the expiry method will be # called, which uses up a timestamp call. mock_now_side_effect.append(25) mock_now.side_effect = mock_now_side_effect self.exception_generator() self.assertEqual([], self._exceptions) self.assertEqual(5, len(mock_now.mock_calls)) self.assertEqual(2, len(mock_log.mock_calls)) mock_log.assert_has_calls([ mock.call('Unexpected exception occurred 1 time(s)... retrying.'), mock.call('Unexpected exception occurred 1 time(s)... retrying.'), ]) @mock.patch.object(logging, 'exception') @mock.patch.object(timeutils, 'now') def test_exc_retrier_mixed_4exc_2min_gives_2logs(self, mock_now, mock_log): self._exceptions = [ Exception('unexpected 1'), ] # Timestamp calls that happen after the logging is possibly triggered. mock_now_side_effect = [0] # Again, this second exception of the same type is not counted # for the sake of code simplicity. self._exceptions.append(Exception('unexpected 1')) # Timestamp calls that happen before the logging is possibly triggered. mock_now_side_effect.append(10) # The difference between this and the previous case is the log # is also triggered by more than a minute expiring. self._exceptions.append(Exception('unexpected 2')) # Timestamp calls that happen after the logging is possibly triggered. mock_now_side_effect.extend([100, 105]) self._exceptions.append(Exception('unexpected 2')) # Timestamp calls that happen before the logging is possibly triggered. mock_now_side_effect.append(110) mock_now.side_effect = mock_now_side_effect self.exception_generator() self.assertEqual([], self._exceptions) self.assertEqual(5, len(mock_now.mock_calls)) self.assertEqual(2, len(mock_log.mock_calls)) mock_log.assert_has_calls([ mock.call('Unexpected exception occurred 1 time(s)... retrying.'), mock.call('Unexpected exception occurred 1 time(s)... retrying.'), ]) @mock.patch.object(logging, 'exception') @mock.patch.object(timeutils, 'now') def test_exc_retrier_mixed_4exc_2min_gives_3logs(self, mock_now, mock_log): self._exceptions = [ Exception('unexpected 1'), ] # Timestamp calls that happen after the logging is possibly triggered. mock_now_side_effect = [0] # This time the second 'unexpected 1' exception is counted due # to the same exception occurring same when the minute expires. self._exceptions.append(Exception('unexpected 1')) # Timestamp calls that happen before the logging is possibly triggered. mock_now_side_effect.append(10) self._exceptions.append(Exception('unexpected 1')) # Timestamp calls that happen before the logging is possibly triggered. mock_now_side_effect.extend([100, 100, 105]) self._exceptions.append(Exception('unexpected 2')) # Timestamp calls that happen after the logging is possibly triggered. mock_now_side_effect.extend([110, 111]) mock_now.side_effect = mock_now_side_effect self.exception_generator() self.assertEqual([], self._exceptions) self.assertEqual(7, len(mock_now.mock_calls)) self.assertEqual(3, len(mock_log.mock_calls)) mock_log.assert_has_calls([ mock.call('Unexpected exception occurred 1 time(s)... retrying.'), mock.call('Unexpected exception occurred 2 time(s)... retrying.'), mock.call('Unexpected exception occurred 1 time(s)... retrying.'), ]) class ExceptionFilterTest(test_base.BaseTestCase): def _make_filter_func(self, ignore_classes=AssertionError): @excutils.exception_filter def ignore_exceptions(ex): '''Ignore some exceptions F.''' return isinstance(ex, ignore_classes) return ignore_exceptions def _make_filter_method(self, ignore_classes=AssertionError): class ExceptionIgnorer(object): def __init__(self, ignore): self.ignore = ignore @excutils.exception_filter def ignore_exceptions(self, ex): '''Ignore some exceptions M.''' return isinstance(ex, self.ignore) return ExceptionIgnorer(ignore_classes).ignore_exceptions def _make_filter_classmethod(self, ignore_classes=AssertionError): class ExceptionIgnorer(object): ignore = ignore_classes @excutils.exception_filter @classmethod def ignore_exceptions(cls, ex): '''Ignore some exceptions C.''' return isinstance(ex, cls.ignore) return ExceptionIgnorer.ignore_exceptions def _make_filter_staticmethod(self, ignore_classes=AssertionError): class ExceptionIgnorer(object): @excutils.exception_filter @staticmethod def ignore_exceptions(ex): '''Ignore some exceptions S.''' return isinstance(ex, ignore_classes) return ExceptionIgnorer.ignore_exceptions def test_filter_func_call(self): ignore_assertion_error = self._make_filter_func() try: assert False, "This is a test" except Exception as exc: ignore_assertion_error(exc) def test_raise_func_call(self): ignore_assertion_error = self._make_filter_func() try: raise RuntimeError except Exception as exc: self.assertRaises(RuntimeError, ignore_assertion_error, exc) def test_raise_previous_func_call(self): ignore_assertion_error = self._make_filter_func() try: raise RuntimeError except Exception as exc1: try: raise RuntimeError except Exception as exc2: self.assertIsNot(exc1, exc2) raised = self.assertRaises(RuntimeError, ignore_assertion_error, exc1) self.assertIs(exc1, raised) def test_raise_previous_after_filtered_func_call(self): ignore_assertion_error = self._make_filter_func() try: raise RuntimeError except Exception as exc1: try: assert False, "This is a test" except Exception: pass self.assertRaises(RuntimeError, ignore_assertion_error, exc1) def test_raise_other_func_call(self): @excutils.exception_filter def translate_exceptions(ex): raise RuntimeError try: assert False, "This is a test" except Exception as exc: self.assertRaises(RuntimeError, translate_exceptions, exc) def test_filter_func_context_manager(self): ignore_assertion_error = self._make_filter_func() with ignore_assertion_error: assert False, "This is a test" def test_raise_func_context_manager(self): ignore_assertion_error = self._make_filter_func() def try_runtime_err(): with ignore_assertion_error: raise RuntimeError self.assertRaises(RuntimeError, try_runtime_err) def test_raise_other_func_context_manager(self): @excutils.exception_filter def translate_exceptions(ex): raise RuntimeError def try_assertion(): with translate_exceptions: assert False, "This is a test" self.assertRaises(RuntimeError, try_assertion) def test_noexc_func_context_manager(self): ignore_assertion_error = self._make_filter_func() with ignore_assertion_error: pass def test_noexc_nocall_func_context_manager(self): @excutils.exception_filter def translate_exceptions(ex): raise RuntimeError with translate_exceptions: pass def test_func_docstring(self): ignore_func = self._make_filter_func() self.assertEqual('Ignore some exceptions F.', ignore_func.__doc__) def test_filter_method_call(self): ignore_assertion_error = self._make_filter_method() try: assert False, "This is a test" except Exception as exc: ignore_assertion_error(exc) def test_raise_method_call(self): ignore_assertion_error = self._make_filter_method() try: raise RuntimeError except Exception as exc: self.assertRaises(RuntimeError, ignore_assertion_error, exc) def test_filter_method_context_manager(self): ignore_assertion_error = self._make_filter_method() with ignore_assertion_error: assert False, "This is a test" def test_raise_method_context_manager(self): ignore_assertion_error = self._make_filter_method() def try_runtime_err(): with ignore_assertion_error: raise RuntimeError self.assertRaises(RuntimeError, try_runtime_err) def test_method_docstring(self): ignore_func = self._make_filter_method() self.assertEqual('Ignore some exceptions M.', ignore_func.__doc__) def test_filter_classmethod_call(self): ignore_assertion_error = self._make_filter_classmethod() try: assert False, "This is a test" except Exception as exc: ignore_assertion_error(exc) def test_raise_classmethod_call(self): ignore_assertion_error = self._make_filter_classmethod() try: raise RuntimeError except Exception as exc: self.assertRaises(RuntimeError, ignore_assertion_error, exc) def test_filter_classmethod_context_manager(self): ignore_assertion_error = self._make_filter_classmethod() with ignore_assertion_error: assert False, "This is a test" def test_raise_classmethod_context_manager(self): ignore_assertion_error = self._make_filter_classmethod() def try_runtime_err(): with ignore_assertion_error: raise RuntimeError self.assertRaises(RuntimeError, try_runtime_err) def test_classmethod_docstring(self): ignore_func = self._make_filter_classmethod() self.assertEqual('Ignore some exceptions C.', ignore_func.__doc__) def test_filter_staticmethod_call(self): ignore_assertion_error = self._make_filter_staticmethod() try: assert False, "This is a test" except Exception as exc: ignore_assertion_error(exc) def test_raise_staticmethod_call(self): ignore_assertion_error = self._make_filter_staticmethod() try: raise RuntimeError except Exception as exc: self.assertRaises(RuntimeError, ignore_assertion_error, exc) def test_filter_staticmethod_context_manager(self): ignore_assertion_error = self._make_filter_staticmethod() with ignore_assertion_error: assert False, "This is a test" def test_raise_staticmethod_context_manager(self): ignore_assertion_error = self._make_filter_staticmethod() def try_runtime_err(): with ignore_assertion_error: raise RuntimeError self.assertRaises(RuntimeError, try_runtime_err) def test_staticmethod_docstring(self): ignore_func = self._make_filter_staticmethod() self.assertEqual('Ignore some exceptions S.', ignore_func.__doc__) oslo.utils-4.1.1/oslo_utils/tests/test_versionutils.py0000664000175000017500000000722713643050415023400 0ustar zuulzuul00000000000000# Copyright (c) 2013 OpenStack Foundation # All Rights Reserved. # # 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. from oslotest import base as test_base from oslo_utils import versionutils class IsCompatibleTestCase(test_base.BaseTestCase): def test_same_version(self): self.assertTrue(versionutils.is_compatible('1', '1')) self.assertTrue(versionutils.is_compatible('1.0', '1.0')) self.assertTrue(versionutils.is_compatible('1.0.0', '1.0.0')) def test_requested_minor_greater(self): self.assertFalse(versionutils.is_compatible('1.1', '1.0')) def test_requested_minor_less_than(self): self.assertTrue(versionutils.is_compatible('1.0', '1.1')) def test_requested_patch_greater(self): self.assertFalse(versionutils.is_compatible('1.0.1', '1.0.0')) def test_requested_patch_less_than(self): self.assertTrue(versionutils.is_compatible('1.0.0', '1.0.1')) def test_requested_patch_not_present_same(self): self.assertTrue(versionutils.is_compatible('1.0', '1.0.0')) def test_requested_patch_not_present_less_than(self): self.assertTrue(versionutils.is_compatible('1.0', '1.0.1')) def test_current_patch_not_present_same(self): self.assertTrue(versionutils.is_compatible('1.0.0', '1.0')) def test_current_patch_not_present_less_than(self): self.assertFalse(versionutils.is_compatible('1.0.1', '1.0')) def test_same_major_true(self): """Even though the current version is 2.0, since `same_major` defaults to `True`, 1.0 is deemed incompatible. """ self.assertFalse(versionutils.is_compatible('2.0', '1.0')) self.assertTrue(versionutils.is_compatible('1.0', '1.0')) self.assertFalse(versionutils.is_compatible('1.0', '2.0')) def test_same_major_false(self): """With `same_major` set to False, then major version compatibiity rule is not enforced, so a current version of 2.0 is deemed to satisfy a requirement of 1.0. """ self.assertFalse(versionutils.is_compatible('2.0', '1.0', same_major=False)) self.assertTrue(versionutils.is_compatible('1.0', '1.0', same_major=False)) self.assertTrue(versionutils.is_compatible('1.0', '2.0', same_major=False)) def test_convert_version_to_int(self): self.assertEqual(6002000, versionutils.convert_version_to_int('6.2.0')) self.assertEqual(6004003, versionutils.convert_version_to_int((6, 4, 3))) self.assertEqual(5, versionutils.convert_version_to_int((5, ))) self.assertRaises(ValueError, versionutils.convert_version_to_int, '5a.6b') def test_convert_version_to_string(self): self.assertEqual('6.7.0', versionutils.convert_version_to_str(6007000)) self.assertEqual('4', versionutils.convert_version_to_str(4)) def test_convert_version_to_tuple(self): self.assertEqual((6, 7, 0), versionutils.convert_version_to_tuple('6.7.0')) oslo.utils-4.1.1/oslo_utils/tests/test_strutils.py0000664000175000017500000012276113643050415022524 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # 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 collections import copy import math from unittest import mock import ddt from oslotest import base as test_base import six import testscenarios from oslo_utils import strutils from oslo_utils import units load_tests = testscenarios.load_tests_apply_scenarios class StrUtilsTest(test_base.BaseTestCase): @mock.patch("six.text_type") def test_bool_bool_from_string_no_text(self, mock_text): self.assertTrue(strutils.bool_from_string(True)) self.assertFalse(strutils.bool_from_string(False)) self.assertEqual(0, mock_text.call_count) def test_bool_bool_from_string(self): self.assertTrue(strutils.bool_from_string(True)) self.assertFalse(strutils.bool_from_string(False)) def test_bool_bool_from_string_default(self): self.assertTrue(strutils.bool_from_string('', default=True)) self.assertFalse(strutils.bool_from_string('wibble', default=False)) 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.assertTrue(strutils.bool_from_string(c('T'))) self.assertTrue(strutils.bool_from_string(c('t'))) self.assertTrue(strutils.bool_from_string(c('Y'))) self.assertTrue(strutils.bool_from_string(c('y'))) 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'))) self.assertFalse(strutils.bool_from_string(c('F'))) self.assertFalse(strutils.bool_from_string(c('f'))) self.assertFalse(strutils.bool_from_string(c('N'))) self.assertFalse(strutils.bool_from_string(c('n'))) # Whitespace should be stripped self.assertTrue(strutils.bool_from_string(c(' 1 '))) self.assertTrue(strutils.bool_from_string(c(' true '))) self.assertFalse(strutils.bool_from_string(c(' 0 '))) 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._test_bool_from_string(six.text_type) self.assertFalse(strutils.bool_from_string(u'使用', strict=False)) exc = self.assertRaises(ValueError, strutils.bool_from_string, u'使用', strict=True) expected_msg = (u"Unrecognized value '使用', acceptable values are:" u" '0', '1', 'f', 'false', 'n', 'no', 'off', 'on'," u" 't', 'true', 'y', 'yes'") self.assertEqual(expected_msg, six.text_type(exc)) 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_strict_bool_from_string(self): # None isn't allowed in strict mode exc = self.assertRaises(ValueError, strutils.bool_from_string, None, strict=True) expected_msg = ("Unrecognized value 'None', acceptable values are:" " '0', '1', 'f', 'false', 'n', 'no', 'off', 'on'," " 't', 'true', 'y', 'yes'") self.assertEqual(expected_msg, str(exc)) # Unrecognized strings aren't allowed self.assertFalse(strutils.bool_from_string('Other', strict=False)) exc = self.assertRaises(ValueError, strutils.bool_from_string, 'Other', strict=True) expected_msg = ("Unrecognized value 'Other', acceptable values are:" " '0', '1', 'f', 'false', 'n', 'no', 'off', 'on'," " 't', 'true', 'y', 'yes'") self.assertEqual(expected_msg, str(exc)) # Unrecognized numbers aren't allowed exc = self.assertRaises(ValueError, strutils.bool_from_string, 2, strict=True) expected_msg = ("Unrecognized value '2', acceptable values are:" " '0', '1', 'f', 'false', 'n', 'no', 'off', 'on'," " 't', 'true', 'y', 'yes'") self.assertEqual(expected_msg, str(exc)) # False-like values are allowed self.assertFalse(strutils.bool_from_string('f', strict=True)) self.assertFalse(strutils.bool_from_string('false', strict=True)) self.assertFalse(strutils.bool_from_string('off', strict=True)) self.assertFalse(strutils.bool_from_string('n', strict=True)) self.assertFalse(strutils.bool_from_string('no', strict=True)) self.assertFalse(strutils.bool_from_string('0', strict=True)) self.assertTrue(strutils.bool_from_string('1', strict=True)) # Avoid font-similarity issues (one looks like lowercase-el, zero like # oh, etc...) for char in ('O', 'o', 'L', 'l', 'I', 'i'): self.assertRaises(ValueError, strutils.bool_from_string, char, strict=True) 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)) def test_is_valid_boolstr(self): self.assertTrue(strutils.is_valid_boolstr('true')) self.assertTrue(strutils.is_valid_boolstr('false')) self.assertTrue(strutils.is_valid_boolstr('yes')) self.assertTrue(strutils.is_valid_boolstr('no')) self.assertTrue(strutils.is_valid_boolstr('y')) self.assertTrue(strutils.is_valid_boolstr('n')) self.assertTrue(strutils.is_valid_boolstr('1')) self.assertTrue(strutils.is_valid_boolstr('0')) self.assertTrue(strutils.is_valid_boolstr(1)) self.assertTrue(strutils.is_valid_boolstr(0)) self.assertFalse(strutils.is_valid_boolstr('maybe')) self.assertFalse(strutils.is_valid_boolstr('only on tuesdays')) def test_slugify(self): to_slug = strutils.to_slug self.assertRaises(TypeError, to_slug, True) self.assertEqual(six.u("hello"), to_slug("hello")) self.assertEqual(six.u("two-words"), to_slug("Two Words")) self.assertEqual(six.u("ma-any-spa-ce-es"), to_slug("Ma-any\t spa--ce- es")) self.assertEqual(six.u("excamation"), to_slug("exc!amation!")) self.assertEqual(six.u("ampserand"), to_slug("&ser$and")) self.assertEqual(six.u("ju5tnum8er"), to_slug("ju5tnum8er")) self.assertEqual(six.u("strip-"), to_slug(" strip - ")) self.assertEqual(six.u("perche"), to_slug(six.b("perch\xc3\xa9"))) self.assertEqual(six.u("strange"), to_slug("\x80strange", errors="ignore")) class StringToBytesTest(test_base.BaseTestCase): _unit_system = [ ('si', dict(unit_system='SI')), ('iec', dict(unit_system='IEC')), ('mixed', dict(unit_system='mixed')), ('invalid_unit_system', dict(unit_system='KKK', assert_error=True)), ] _sign = [ ('no_sign', dict(sign='')), ('positive', dict(sign='+')), ('negative', dict(sign='-')), ('invalid_sign', dict(sign='~', assert_error=True)), ] _magnitude = [ ('integer', dict(magnitude='79')), ('decimal', dict(magnitude='7.9')), ('decimal_point_start', dict(magnitude='.9')), ('decimal_point_end', dict(magnitude='79.', assert_error=True)), ('invalid_literal', dict(magnitude='7.9.9', assert_error=True)), ('garbage_value', dict(magnitude='asdf', assert_error=True)), ] _unit_prefix = [ ('no_unit_prefix', dict(unit_prefix='')), ('k', dict(unit_prefix='k')), ('K', dict(unit_prefix='K')), ('M', dict(unit_prefix='M')), ('G', dict(unit_prefix='G')), ('T', dict(unit_prefix='T')), ('Ki', dict(unit_prefix='Ki')), ('Mi', dict(unit_prefix='Mi')), ('Gi', dict(unit_prefix='Gi')), ('Ti', dict(unit_prefix='Ti')), ('invalid_unit_prefix', dict(unit_prefix='B', assert_error=True)), ] _unit_suffix = [ ('b', dict(unit_suffix='b')), ('bit', dict(unit_suffix='bit')), ('B', dict(unit_suffix='B')), ('invalid_unit_suffix', dict(unit_suffix='Kg', assert_error=True)), ] _return_int = [ ('return_dec', dict(return_int=False)), ('return_int', dict(return_int=True)), ] @classmethod def generate_scenarios(cls): cls.scenarios = testscenarios.multiply_scenarios(cls._unit_system, cls._sign, cls._magnitude, cls._unit_prefix, cls._unit_suffix, cls._return_int) def test_string_to_bytes(self): def _get_quantity(sign, magnitude, unit_suffix): res = float('%s%s' % (sign, magnitude)) if unit_suffix in ['b', 'bit']: res /= 8 return res def _get_constant(unit_prefix, unit_system): if not unit_prefix: return 1 elif unit_system == 'SI': res = getattr(units, unit_prefix) elif unit_system == 'IEC': if unit_prefix.endswith('i'): res = getattr(units, unit_prefix) else: res = getattr(units, '%si' % unit_prefix) elif unit_system == 'mixed': # Note: this will return 'i' units as power-of-two, # and other units as power-of-ten. Additionally, for # compatability a "K" is interpreted as "k" in mixed # mode if unit_prefix == 'K': unit_prefix = 'k' res = getattr(units, unit_prefix) return res text = ''.join([self.sign, self.magnitude, self.unit_prefix, self.unit_suffix]) err_si = self.unit_system == 'SI' and (self.unit_prefix == 'K' or self.unit_prefix.endswith('i')) err_iec = self.unit_system == 'IEC' and self.unit_prefix == 'k' if getattr(self, 'assert_error', False) or err_si or err_iec: self.assertRaises(ValueError, strutils.string_to_bytes, text, unit_system=self.unit_system, return_int=self.return_int) return quantity = _get_quantity(self.sign, self.magnitude, self.unit_suffix) constant = _get_constant(self.unit_prefix, self.unit_system) expected = quantity * constant actual = strutils.string_to_bytes(text, unit_system=self.unit_system, return_int=self.return_int) if self.return_int: self.assertEqual(actual, int(math.ceil(expected))) else: self.assertAlmostEqual(actual, expected) StringToBytesTest.generate_scenarios() class MaskPasswordTestCase(test_base.BaseTestCase): def test_sanitize_keys(self): lowered = [k.lower() for k in strutils._SANITIZE_KEYS] message = "The _SANITIZE_KEYS must all be lowercase." self.assertEqual(strutils._SANITIZE_KEYS, lowered, message) def test_json(self): # Test 'adminPass' w/o spaces payload = """{'adminPass':'TL0EfN33'}""" expected = """{'adminPass':'***'}""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'adminPass' with spaces payload = """{ 'adminPass' : 'TL0EfN33' }""" expected = """{ 'adminPass' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'admin_pass' w/o spaces payload = """{'admin_pass':'TL0EfN33'}""" expected = """{'admin_pass':'***'}""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'admin_pass' with spaces payload = """{ 'admin_pass' : 'TL0EfN33' }""" expected = """{ 'admin_pass' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'admin_password' w/o spaces payload = """{'admin_password':'TL0EfN33'}""" expected = """{'admin_password':'***'}""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'admin_password' with spaces payload = """{ 'admin_password' : 'TL0EfN33' }""" expected = """{ 'admin_password' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'password' w/o spaces payload = """{'password':'TL0EfN33'}""" expected = """{'password':'***'}""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'password' with spaces payload = """{ 'password' : 'TL0EfN33' }""" expected = """{ 'password' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'auth_password' w/o spaces payload = """{'auth_password':'TL0EfN33'}""" expected = """{'auth_password':'***'}""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'auth_password' with spaces payload = """{ 'auth_password' : 'TL0EfN33' }""" expected = """{ 'auth_password' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'secret_uuid' w/o spaces payload = """{'secret_uuid':'myuuid'}""" expected = """{'secret_uuid':'***'}""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'secret_uuid' with spaces payload = """{ 'secret_uuid' : 'myuuid' }""" expected = """{ 'secret_uuid' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'token' w/o spaces payload = """{'token':'token'}""" expected = """{'token':'***'}""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'token' with spaces payload = """{ 'token' : 'token' }""" expected = """{ 'token' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'fernetkey' payload = """{ 'fernetkey' : 'token' }""" expected = """{ 'fernetkey' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'FernetKey' payload = """{ 'FernetKey' : 'token' }""" expected = """{ 'FernetKey' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'sslkey' payload = """{ 'sslkey' : 'token' }""" expected = """{ 'sslkey' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'SslKey' payload = """{ 'SslKey' : 'token' }""" expected = """{ 'SslKey' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'passphrase' payload = """{ 'passphrase' : 'token' }""" expected = """{ 'passphrase' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'PassPhrase' payload = """{ 'PassPhrase' : 'token' }""" expected = """{ 'PassPhrase' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Some real-life cases # Test 'KeystoneFernetKey1' payload = """{ 'KeystoneFernetKey1' : 'token' }""" expected = """{ 'KeystoneFernetKey1' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'OctaviaCaKeyPassword' payload = """{ 'OctaviaCaKeyPassword' : 'token' }""" expected = """{ 'OctaviaCaKeyPassword' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'OctaviaCaKeyPassphrase' payload = """{ 'OctaviaCaKeyPassphrase' : 'token' }""" expected = """{ 'OctaviaCaKeyPassphrase' : '***' }""" self.assertEqual(expected, strutils.mask_password(payload)) def test_xml(self): # Test 'adminPass' w/o spaces payload = """TL0EfN33""" expected = """***""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'adminPass' with spaces payload = """ TL0EfN33 """ expected = """***""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'admin_pass' w/o spaces payload = """TL0EfN33""" expected = """***""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'admin_pass' with spaces payload = """ TL0EfN33 """ expected = """***""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'admin_password' w/o spaces payload = """TL0EfN33""" expected = """***""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'admin_password' with spaces payload = """ TL0EfN33 """ expected = """***""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'password' w/o spaces payload = """TL0EfN33""" expected = """***""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'password' with spaces payload = """ TL0EfN33 """ expected = """***""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'Password1' - case-insensitive + number payload = """TL0EfN33""" expected = """***""" self.assertEqual(expected, strutils.mask_password(payload)) def test_xml_attribute(self): # Test 'adminPass' w/o spaces payload = """adminPass='TL0EfN33'""" expected = """adminPass='***'""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'adminPass' with spaces payload = """adminPass = 'TL0EfN33'""" expected = """adminPass = '***'""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'adminPass' with double quotes payload = """adminPass = "TL0EfN33\"""" expected = """adminPass = "***\"""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'admin_pass' w/o spaces payload = """admin_pass='TL0EfN33'""" expected = """admin_pass='***'""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'admin_pass' with spaces payload = """admin_pass = 'TL0EfN33'""" expected = """admin_pass = '***'""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'admin_pass' with double quotes payload = """admin_pass = "TL0EfN33\"""" expected = """admin_pass = "***\"""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'admin_password' w/o spaces payload = """admin_password='TL0EfN33'""" expected = """admin_password='***'""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'admin_password' with spaces payload = """admin_password = 'TL0EfN33'""" expected = """admin_password = '***'""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'admin_password' with double quotes payload = """admin_password = "TL0EfN33\"""" expected = """admin_password = "***\"""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'password' w/o spaces payload = """password='TL0EfN33'""" expected = """password='***'""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'password' with spaces payload = """password = 'TL0EfN33'""" expected = """password = '***'""" self.assertEqual(expected, strutils.mask_password(payload)) # Test 'password' with double quotes payload = """password = "TL0EfN33\"""" expected = """password = "***\"""" self.assertEqual(expected, strutils.mask_password(payload)) def test_json_message(self): payload = """body: {"changePassword": {"adminPass": "1234567"}}""" expected = """body: {"changePassword": {"adminPass": "***"}}""" self.assertEqual(expected, strutils.mask_password(payload)) payload = """body: {"rescue": {"admin_pass": "1234567"}}""" expected = """body: {"rescue": {"admin_pass": "***"}}""" self.assertEqual(expected, strutils.mask_password(payload)) payload = """body: {"rescue": {"admin_password": "1234567"}}""" expected = """body: {"rescue": {"admin_password": "***"}}""" self.assertEqual(expected, strutils.mask_password(payload)) payload = """body: {"rescue": {"password": "1234567"}}""" expected = """body: {"rescue": {"password": "***"}}""" self.assertEqual(expected, strutils.mask_password(payload)) payload = """body: {"rescue": {"encryption_key_id": "1234567"}}""" expected = """body: {"rescue": {"encryption_key_id": "***"}}""" self.assertEqual(expected, strutils.mask_password(payload)) def test_xml_message(self): payload = """ Apache1 """ expected = """ Apache1 """ self.assertEqual(expected, strutils.mask_password(payload)) payload = """ """ expected = """ """ self.assertEqual(expected, strutils.mask_password(payload)) payload = """ """ expected = """ """ self.assertEqual(expected, strutils.mask_password(payload)) payload = """ """ expected = """ """ self.assertEqual(expected, strutils.mask_password(payload)) def test_mask_password(self): payload = "test = 'password' : 'aaaaaa'" expected = "test = 'password' : '111'" self.assertEqual(expected, strutils.mask_password(payload, secret='111')) payload = 'mysqld --password "aaaaaa"' expected = 'mysqld --password "****"' self.assertEqual(expected, strutils.mask_password(payload, secret='****')) payload = 'mysqld --password aaaaaa' expected = 'mysqld --password ???' self.assertEqual(expected, strutils.mask_password(payload, secret='???')) payload = 'mysqld --password = "aaaaaa"' expected = 'mysqld --password = "****"' self.assertEqual(expected, strutils.mask_password(payload, secret='****')) payload = "mysqld --password = 'aaaaaa'" expected = "mysqld --password = '****'" self.assertEqual(expected, strutils.mask_password(payload, secret='****')) payload = "mysqld --password = aaaaaa" expected = "mysqld --password = ****" self.assertEqual(expected, strutils.mask_password(payload, secret='****')) payload = "test = password = aaaaaa" expected = "test = password = 111" self.assertEqual(expected, strutils.mask_password(payload, secret='111')) payload = "test = password= aaaaaa" expected = "test = password= 111" self.assertEqual(expected, strutils.mask_password(payload, secret='111')) payload = "test = password =aaaaaa" expected = "test = password =111" self.assertEqual(expected, strutils.mask_password(payload, secret='111')) payload = "test = password=aaaaaa" expected = "test = password=111" self.assertEqual(expected, strutils.mask_password(payload, secret='111')) payload = 'test = "original_password" : "aaaaaaaaa"' expected = 'test = "original_password" : "***"' self.assertEqual(expected, strutils.mask_password(payload)) payload = 'test = "param1" : "value"' expected = 'test = "param1" : "value"' self.assertEqual(expected, strutils.mask_password(payload)) payload = """{'adminPass':'TL0EfN33'}""" payload = six.text_type(payload) expected = """{'adminPass':'***'}""" self.assertEqual(expected, strutils.mask_password(payload)) payload = """{'token':'mytoken'}""" payload = six.text_type(payload) expected = """{'token':'***'}""" self.assertEqual(expected, strutils.mask_password(payload)) payload = ("test = 'node.session.auth.password','-v','TL0EfN33'," "'nomask'") expected = ("test = 'node.session.auth.password','-v','***'," "'nomask'") self.assertEqual(expected, strutils.mask_password(payload)) payload = ("test = 'node.session.auth.password', '--password', " "'TL0EfN33', 'nomask'") expected = ("test = 'node.session.auth.password', '--password', " "'***', 'nomask'") self.assertEqual(expected, strutils.mask_password(payload)) payload = ("test = 'node.session.auth.password', '--password', " "'TL0EfN33'") expected = ("test = 'node.session.auth.password', '--password', " "'***'") self.assertEqual(expected, strutils.mask_password(payload)) payload = "test = node.session.auth.password -v TL0EfN33 nomask" expected = "test = node.session.auth.password -v *** nomask" self.assertEqual(expected, strutils.mask_password(payload)) payload = ("test = node.session.auth.password --password TL0EfN33 " "nomask") expected = ("test = node.session.auth.password --password *** " "nomask") self.assertEqual(expected, strutils.mask_password(payload)) payload = ("test = node.session.auth.password --password TL0EfN33") expected = ("test = node.session.auth.password --password ***") self.assertEqual(expected, strutils.mask_password(payload)) payload = "test = cmd --password my\xe9\x80\x80pass" expected = ("test = cmd --password ***") self.assertEqual(expected, strutils.mask_password(payload)) class TestMapping(collections.Mapping): """Test class for non-dict mappings""" def __init__(self): super(TestMapping, self).__init__() self.data = {'password': 'shhh', 'foo': 'bar', } def __getitem__(self, key): return self.data[key] def __iter__(self): return self.data.__iter__() def __len__(self): return len(self.data) class NestedMapping(TestMapping): """Test class that contains an instance of TestMapping""" def __init__(self): super(NestedMapping, self).__init__() self.data = {'nested': TestMapping()} class MaskDictionaryPasswordTestCase(test_base.BaseTestCase): def test_dictionary(self): payload = {'password': 'TL0EfN33'} expected = {'password': '***'} self.assertEqual(expected, strutils.mask_dict_password(payload)) payload = {'user': 'admin', 'password': 'TL0EfN33'} expected = {'user': 'admin', 'password': '***'} self.assertEqual(expected, strutils.mask_dict_password(payload)) payload = {'strval': 'somestring', 'dictval': {'user': 'admin', 'password': 'TL0EfN33'}} expected = {'strval': 'somestring', 'dictval': {'user': 'admin', 'password': '***'}} self.assertEqual(expected, strutils.mask_dict_password(payload)) payload = {'strval': '--password abc', 'dont_change': 'this is fine', 'dictval': {'user': 'admin', 'password': b'TL0EfN33'}} expected = {'strval': '--password ***', 'dont_change': 'this is fine', 'dictval': {'user': 'admin', 'password': '***'}} self.assertEqual(expected, strutils.mask_dict_password(payload)) payload = {'ipmi_password': 'KeDrahishvowphyecMornEm0or('} expected = {'ipmi_password': '***'} self.assertEqual(expected, strutils.mask_dict_password(payload)) payload = {'passwords': {'KeystoneFernetKey1': 'c5FijjS'}} expected = {'passwords': {'KeystoneFernetKey1': '***'}} self.assertEqual(expected, strutils.mask_dict_password(payload)) payload = {'passwords': {'keystonecredential0': 'c5FijjS'}} expected = {'passwords': {'keystonecredential0': '***'}} self.assertEqual(expected, strutils.mask_dict_password(payload)) def test_do_no_harm(self): payload = {} expected = {} self.assertEqual(expected, strutils.mask_dict_password(payload)) payload = {'somekey': 'somevalue', 'anotherkey': 'anothervalue'} expected = {'somekey': 'somevalue', 'anotherkey': 'anothervalue'} self.assertEqual(expected, strutils.mask_dict_password(payload)) def test_do_an_int(self): payload = {} payload[1] = 2 expected = payload.copy() self.assertEqual(expected, strutils.mask_dict_password(payload)) def test_mask_values(self): payload = {'somekey': 'test = cmd --password my\xe9\x80\x80pass'} expected = {'somekey': 'test = cmd --password ***'} self.assertEqual(expected, strutils.mask_dict_password(payload)) def test_other_non_str_values(self): payload = {'password': 'DK0PK1AK3', 'bool': True, 'dict': {'cat': 'meow', 'password': "*aa38skdjf"}, 'float': 0.1, 'int': 123, 'list': [1, 2], 'none': None, 'str': 'foo'} expected = {'password': '***', 'bool': True, 'dict': {'cat': 'meow', 'password': '***'}, 'float': 0.1, 'int': 123, 'list': [1, 2], 'none': None, 'str': 'foo'} self.assertEqual(expected, strutils.mask_dict_password(payload)) def test_argument_untouched(self): """Make sure that the argument passed in is not modified""" payload = {'password': 'DK0PK1AK3', 'bool': True, 'dict': {'cat': 'meow', 'password': "*aa38skdjf"}, 'float': 0.1, 'int': 123, 'list': [1, 2], 'none': None, 'str': 'foo'} pristine = copy.deepcopy(payload) # Send the payload into the function, to see if it gets modified strutils.mask_dict_password(payload) self.assertEqual(pristine, payload) def test_non_dict(self): expected = {'password': '***', 'foo': 'bar', } payload = TestMapping() self.assertEqual(expected, strutils.mask_dict_password(payload)) def test_nested_non_dict(self): expected = {'nested': {'password': '***', 'foo': 'bar', } } payload = NestedMapping() self.assertEqual(expected, strutils.mask_dict_password(payload)) class IsIntLikeTestCase(test_base.BaseTestCase): def test_is_int_like_true(self): self.assertTrue(strutils.is_int_like(1)) self.assertTrue(strutils.is_int_like("1")) self.assertTrue(strutils.is_int_like("514")) self.assertTrue(strutils.is_int_like("0")) def test_is_int_like_false(self): self.assertFalse(strutils.is_int_like(1.1)) self.assertFalse(strutils.is_int_like("1.1")) self.assertFalse(strutils.is_int_like("1.1.1")) self.assertFalse(strutils.is_int_like(None)) self.assertFalse(strutils.is_int_like("0.")) self.assertFalse(strutils.is_int_like("aaaaaa")) self.assertFalse(strutils.is_int_like("....")) self.assertFalse(strutils.is_int_like("1g")) self.assertFalse( strutils.is_int_like("0cc3346e-9fef-4445-abe6-5d2b2690ec64")) self.assertFalse(strutils.is_int_like("a1")) # NOTE(viktors): 12e3 - is a float number self.assertFalse(strutils.is_int_like("12e3")) # NOTE(viktors): Check integer numbers with base not 10 self.assertFalse(strutils.is_int_like("0o51")) self.assertFalse(strutils.is_int_like("0xDEADBEEF")) class StringLengthTestCase(test_base.BaseTestCase): def test_check_string_length(self): self.assertIsNone(strutils.check_string_length( 'test', 'name', max_length=255)) self.assertRaises(ValueError, strutils.check_string_length, '', 'name', min_length=1) self.assertRaises(ValueError, strutils.check_string_length, 'a' * 256, 'name', max_length=255) self.assertRaises(TypeError, strutils.check_string_length, 11, 'name', max_length=255) self.assertRaises(TypeError, strutils.check_string_length, dict(), 'name', max_length=255) def test_check_string_length_noname(self): self.assertIsNone(strutils.check_string_length( 'test', max_length=255)) self.assertRaises(ValueError, strutils.check_string_length, '', min_length=1) self.assertRaises(ValueError, strutils.check_string_length, 'a' * 256, max_length=255) self.assertRaises(TypeError, strutils.check_string_length, 11, max_length=255) self.assertRaises(TypeError, strutils.check_string_length, dict(), max_length=255) class SplitPathTestCase(test_base.BaseTestCase): def test_split_path_failed(self): self.assertRaises(ValueError, strutils.split_path, '') self.assertRaises(ValueError, strutils.split_path, '/') self.assertRaises(ValueError, strutils.split_path, '//') self.assertRaises(ValueError, strutils.split_path, '//a') self.assertRaises(ValueError, strutils.split_path, '/a/c') self.assertRaises(ValueError, strutils.split_path, '//c') self.assertRaises(ValueError, strutils.split_path, '/a/c/') self.assertRaises(ValueError, strutils.split_path, '/a//') self.assertRaises(ValueError, strutils.split_path, '/a', 2) self.assertRaises(ValueError, strutils.split_path, '/a', 2, 3) self.assertRaises(ValueError, strutils.split_path, '/a', 2, 3, True) self.assertRaises(ValueError, strutils.split_path, '/a/c/o/r', 3, 3) self.assertRaises(ValueError, strutils.split_path, '/a', 5, 4) def test_split_path_success(self): self.assertEqual(strutils.split_path('/a'), ['a']) self.assertEqual(strutils.split_path('/a/'), ['a']) self.assertEqual(strutils.split_path('/a/c', 2), ['a', 'c']) self.assertEqual(strutils.split_path('/a/c/o', 3), ['a', 'c', 'o']) self.assertEqual(strutils.split_path('/a/c/o/r', 3, 3, True), ['a', 'c', 'o/r']) self.assertEqual(strutils.split_path('/a/c', 2, 3, True), ['a', 'c', None]) self.assertEqual(strutils.split_path('/a/c/', 2), ['a', 'c']) self.assertEqual(strutils.split_path('/a/c/', 2, 3), ['a', 'c', '']) def test_split_path_invalid_path(self): try: strutils.split_path('o\nn e', 2) except ValueError as err: self.assertEqual(str(err), 'Invalid path: o%0An%20e') try: strutils.split_path('o\nn e', 2, 3, True) except ValueError as err: self.assertEqual(str(err), 'Invalid path: o%0An%20e') class SplitByCommas(test_base.BaseTestCase): def test_not_closed_quotes(self): self.assertRaises(ValueError, strutils.split_by_commas, '"ab","b""') def test_no_comma_before_opening_quotes(self): self.assertRaises(ValueError, strutils.split_by_commas, '"ab""b"') def test_quote_inside_unquoted(self): self.assertRaises(ValueError, strutils.split_by_commas, 'a"b,cd') def check(self, expect, input): self.assertEqual(expect, strutils.split_by_commas(input)) def test_plain(self): self.check(["a,b", "ac"], '"a,b",ac') def test_with_backslash_inside_quoted(self): self.check(['abc"', 'de', 'fg,h', 'klm\\', '"nop'], r'"abc\"","de","fg,h","klm\\","\"nop"') def test_with_backslash_inside_unquoted(self): self.check([r'a\bc', 'de'], r'a\bc,de') def test_with_escaped_quotes_in_row_inside_quoted(self): self.check(['a"b""c', 'd'], r'"a\"b\"\"c",d') @ddt.ddt class ValidateIntegerTestCase(test_base.BaseTestCase): @ddt.unpack @ddt.data({"value": 42, "name": "answer", "output": 42}, {"value": "42", "name": "answer", "output": 42}, {"value": "7", "name": "lucky", "output": 7, "min_value": 7, "max_value": 8}, {"value": 7, "name": "lucky", "output": 7, "min_value": 6, "max_value": 7}, {"value": 300, "name": "Spartaaa!!!", "output": 300, "min_value": 300}, {"value": "300", "name": "Spartaaa!!!", "output": 300, "max_value": 300}) def test_valid_inputs(self, output, value, name, **kwargs): self.assertEqual(strutils.validate_integer(value, name, **kwargs), output) @ddt.unpack @ddt.data({"value": "im-not-an-int", "name": ''}, {"value": 3.14, "name": "Pie"}, {"value": "299", "name": "Sparta no-show", "min_value": 300, "max_value": 300}, {"value": 55, "name": "doing 55 in a 54", "max_value": 54}, {"value": six.unichr(129), "name": "UnicodeError", "max_value": 1000}) def test_invalid_inputs(self, value, name, **kwargs): self.assertRaises(ValueError, strutils.validate_integer, value, name, **kwargs) oslo.utils-4.1.1/oslo_utils/tests/base.py0000664000175000017500000000367013643050415020503 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # Copyright 2010-2011 OpenStack Foundation # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # 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 fixtures import testtools _TRUE_VALUES = ('true', '1', 'yes') # FIXME(dhellmann) Update this to use oslo.test library class TestCase(testtools.TestCase): """Test case base class for all unit tests.""" def setUp(self): """Run before each test method to initialize test environment.""" super(TestCase, self).setUp() test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0) try: test_timeout = int(test_timeout) except ValueError: # If timeout value is invalid do not set a timeout. test_timeout = 0 if test_timeout > 0: self.useFixture(fixtures.Timeout(test_timeout, gentle=True)) self.useFixture(fixtures.NestedTempfile()) self.useFixture(fixtures.TempHomeDir()) if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES: stdout = self.useFixture(fixtures.StringStream('stdout')).stream self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES: stderr = self.useFixture(fixtures.StringStream('stderr')).stream self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) self.log_fixture = self.useFixture(fixtures.FakeLogger()) oslo.utils-4.1.1/oslo_utils/tests/tests_encodeutils.py0000664000175000017500000002427613643050415023336 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # Copyright 2014 Red Hat, Inc. # All Rights Reserved. # # 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. from unittest import mock from oslo_i18n import fixture as oslo_i18n_fixture from oslotest import base as test_base import six import testtools from oslo_utils import encodeutils class EncodeUtilsTest(test_base.BaseTestCase): def test_safe_decode(self): safe_decode = encodeutils.safe_decode self.assertRaises(TypeError, safe_decode, True) self.assertEqual(six.u('ni\xf1o'), safe_decode(six.b("ni\xc3\xb1o"), incoming="utf-8")) if six.PY2: # In Python 3, bytes.decode() doesn't support anymore # bytes => bytes encodings like base64 self.assertEqual(six.u("test"), safe_decode("dGVzdA==", incoming='base64')) self.assertEqual(six.u("strange"), safe_decode(six.b('\x80strange'), errors='ignore')) self.assertEqual(six.u('\xc0'), safe_decode(six.b('\xc0'), incoming='iso-8859-1')) # Forcing incoming to ascii so it falls back to utf-8 self.assertEqual(six.u('ni\xf1o'), safe_decode(six.b('ni\xc3\xb1o'), incoming='ascii')) self.assertEqual(six.u('foo'), safe_decode(b'foo')) def test_safe_encode_none_instead_of_text(self): self.assertRaises(TypeError, encodeutils.safe_encode, None) def test_safe_encode_bool_instead_of_text(self): self.assertRaises(TypeError, encodeutils.safe_encode, True) def test_safe_encode_int_instead_of_text(self): self.assertRaises(TypeError, encodeutils.safe_encode, 1) def test_safe_encode_list_instead_of_text(self): self.assertRaises(TypeError, encodeutils.safe_encode, []) def test_safe_encode_dict_instead_of_text(self): self.assertRaises(TypeError, encodeutils.safe_encode, {}) def test_safe_encode_tuple_instead_of_text(self): self.assertRaises(TypeError, encodeutils.safe_encode, ('foo', 'bar', )) def test_safe_encode_py2(self): if six.PY2: # In Python 3, str.encode() doesn't support anymore # text => text encodings like base64 self.assertEqual( six.b("dGVzdA==\n"), encodeutils.safe_encode("test", encoding='base64'), ) else: self.skipTest("Requires py2.x") def test_safe_encode_force_incoming_utf8_to_ascii(self): # Forcing incoming to ascii so it falls back to utf-8 self.assertEqual( six.b('ni\xc3\xb1o'), encodeutils.safe_encode(six.b('ni\xc3\xb1o'), incoming='ascii'), ) def test_safe_encode_same_encoding_different_cases(self): with mock.patch.object(encodeutils, 'safe_decode', mock.Mock()): utf8 = encodeutils.safe_encode( six.u('foo\xf1bar'), encoding='utf-8') self.assertEqual( encodeutils.safe_encode(utf8, 'UTF-8', 'utf-8'), encodeutils.safe_encode(utf8, 'utf-8', 'UTF-8'), ) self.assertEqual( encodeutils.safe_encode(utf8, 'UTF-8', 'utf-8'), encodeutils.safe_encode(utf8, 'utf-8', 'utf-8'), ) encodeutils.safe_decode.assert_has_calls([]) def test_safe_encode_different_encodings(self): text = six.u('foo\xc3\xb1bar') result = encodeutils.safe_encode( text=text, incoming='utf-8', encoding='iso-8859-1') self.assertNotEqual(text, result) self.assertNotEqual(six.b("foo\xf1bar"), result) def test_to_utf8(self): self.assertEqual(encodeutils.to_utf8(b'a\xe9\xff'), # bytes b'a\xe9\xff') self.assertEqual(encodeutils.to_utf8(u'a\xe9\xff\u20ac'), # Unicode b'a\xc3\xa9\xc3\xbf\xe2\x82\xac') self.assertRaises(TypeError, encodeutils.to_utf8, 123) # invalid # oslo.i18n Message objects should also be accepted for convenience. # It works because Message is a subclass of six.text_type. Use the # lazy translation to get a Message instance of oslo_i18n. msg = oslo_i18n_fixture.Translation().lazy("test") self.assertEqual(encodeutils.to_utf8(msg), b'test') class ExceptionToUnicodeTest(test_base.BaseTestCase): def test_str_exception(self): # The regular Exception class cannot be used directly: # Exception(u'\xe9').__str__() raises an UnicodeEncodeError # on Python 2 class StrException(Exception): def __init__(self, value): Exception.__init__(self) self.value = value def __str__(self): return self.value # On Python 3, an exception which returns bytes with is __str__() # method (like StrException(bytes)) is probably a bug, but it was not # harder to support this silly case in exception_to_unicode(). # Decode from ASCII exc = StrException(b'bytes ascii') self.assertEqual(encodeutils.exception_to_unicode(exc), u'bytes ascii') # Decode from UTF-8 exc = StrException(b'utf-8 \xc3\xa9\xe2\x82\xac') self.assertEqual(encodeutils.exception_to_unicode(exc), u'utf-8 \xe9\u20ac') # Force the locale encoding to ASCII to test the fallback with mock.patch.object(encodeutils, '_getfilesystemencoding', return_value='ascii'): # Fallback: decode from ISO-8859-1 exc = StrException(b'rawbytes \x80\xff') self.assertEqual(encodeutils.exception_to_unicode(exc), u'rawbytes \x80\xff') # No conversion needed exc = StrException(u'unicode ascii') self.assertEqual(encodeutils.exception_to_unicode(exc), u'unicode ascii') # No conversion needed exc = StrException(u'unicode \xe9\u20ac') self.assertEqual(encodeutils.exception_to_unicode(exc), u'unicode \xe9\u20ac') # Test the locale encoding with mock.patch.object(encodeutils, '_getfilesystemencoding', return_value='koi8_r'): exc = StrException(b'\xf2\xd5\xd3\xd3\xcb\xc9\xca') # Decode from the locale encoding # (the message cannot be decoded from ASCII nor UTF-8) self.assertEqual(encodeutils.exception_to_unicode(exc), u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439') @testtools.skipIf(six.PY3, 'test specific to Python 2') def test_unicode_exception(self): # Exception with a __unicode__() method, but no __str__() class UnicodeException(Exception): def __init__(self, value): Exception.__init__(self) self.value = value def __unicode__(self): return self.value # __unicode__() returns unicode exc = UnicodeException(u'unicode \xe9\u20ac') self.assertEqual(encodeutils.exception_to_unicode(exc), u'unicode \xe9\u20ac') # __unicode__() returns bytes (does this case really happen in the # wild?) exc = UnicodeException(b'utf-8 \xc3\xa9\xe2\x82\xac') self.assertEqual(encodeutils.exception_to_unicode(exc), u'utf-8 \xe9\u20ac') @testtools.skipIf(six.PY3, 'test specific to Python 2') def test_unicode_or_str_exception(self): # Exception with __str__() and __unicode__() methods class UnicodeOrStrException(Exception): def __init__(self, unicode_value, str_value): Exception.__init__(self) self.unicode_value = unicode_value self.str_value = str_value def __unicode__(self): return self.unicode_value def __str__(self): return self.str_value # __unicode__() returns unicode exc = UnicodeOrStrException(u'unicode \xe9\u20ac', b'str') self.assertEqual(encodeutils.exception_to_unicode(exc), u'unicode \xe9\u20ac') # __unicode__() returns bytes (does this case really happen in the # wild?) exc = UnicodeOrStrException(b'utf-8 \xc3\xa9\xe2\x82\xac', b'str') self.assertEqual(encodeutils.exception_to_unicode(exc), u'utf-8 \xe9\u20ac') @testtools.skipIf(six.PY3, 'test specific to Python 2') def test_unicode_only_exception(self): # Exception with a __unicode__() method and a __str__() which # raises an exception (similar to the Message class of oslo_i18n) class UnicodeOnlyException(Exception): def __init__(self, value): Exception.__init__(self) self.value = value def __unicode__(self): return self.value def __str__(self): raise UnicodeError("use unicode()") # __unicode__() returns unicode exc = UnicodeOnlyException(u'unicode \xe9\u20ac') self.assertEqual(encodeutils.exception_to_unicode(exc), u'unicode \xe9\u20ac') # __unicode__() returns bytes exc = UnicodeOnlyException(b'utf-8 \xc3\xa9\xe2\x82\xac') self.assertEqual(encodeutils.exception_to_unicode(exc), u'utf-8 \xe9\u20ac') def test_oslo_i18n_message(self): # use the lazy translation to get a Message instance of oslo_i18n exc = oslo_i18n_fixture.Translation().lazy("test") self.assertEqual(encodeutils.exception_to_unicode(exc), u"test") oslo.utils-4.1.1/oslo_utils/tests/test_uuidutils.py0000664000175000017500000000447713643050415022665 0ustar zuulzuul00000000000000# Copyright (c) 2012 Intel Corporation. # All Rights Reserved. # # 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 uuid from oslotest import base as test_base from oslo_utils import uuidutils class UUIDUtilsTest(test_base.BaseTestCase): def test_generate_uuid(self): uuid_string = uuidutils.generate_uuid() self.assertIsInstance(uuid_string, str) self.assertEqual(len(uuid_string), 36) # make sure there are 4 dashes self.assertEqual(len(uuid_string.replace('-', '')), 32) def test_generate_uuid_dashed_false(self): uuid_string = uuidutils.generate_uuid(dashed=False) self.assertIsInstance(uuid_string, str) self.assertEqual(len(uuid_string), 32) self.assertNotIn('-', uuid_string) def test_is_uuid_like(self): self.assertTrue(uuidutils.is_uuid_like(str(uuid.uuid4()))) self.assertTrue(uuidutils.is_uuid_like( '{12345678-1234-5678-1234-567812345678}')) self.assertTrue(uuidutils.is_uuid_like( '12345678123456781234567812345678')) self.assertTrue(uuidutils.is_uuid_like( 'urn:uuid:12345678-1234-5678-1234-567812345678')) self.assertTrue(uuidutils.is_uuid_like( 'urn:bbbaaaaa-aaaa-aaaa-aabb-bbbbbbbbbbbb')) self.assertTrue(uuidutils.is_uuid_like( 'uuid:bbbaaaaa-aaaa-aaaa-aabb-bbbbbbbbbbbb')) self.assertTrue(uuidutils.is_uuid_like( '{}---bbb---aaa--aaa--aaa-----aaa---aaa--bbb-bbb---bbb-bbb-bb-{}')) def test_is_uuid_like_insensitive(self): self.assertTrue(uuidutils.is_uuid_like(str(uuid.uuid4()).upper())) def test_id_is_uuid_like(self): self.assertFalse(uuidutils.is_uuid_like(1234567)) def test_name_is_uuid_like(self): self.assertFalse(uuidutils.is_uuid_like('zhongyueluo')) oslo.utils-4.1.1/oslo_utils/tests/test_specs_matcher.py0000664000175000017500000003021613643050415023444 0ustar zuulzuul00000000000000# 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. from oslotest import base as test_base from oslo_utils import specs_matcher class SpecsMatcherTestCase(test_base.BaseTestCase): def _do_specs_matcher_test(self, value, req, matches): assertion = self.assertTrue if matches else self.assertFalse assertion(specs_matcher.match(value, req)) def test_specs_matches_simple(self): self._do_specs_matcher_test( value='1', req='1', matches=True) def test_specs_fails_string_vs_int(self): # With no operator specified it is a string comparison test, therefore # '1' does not equal '01' self._do_specs_matcher_test( value='01', req='1', matches=False) def test_specs_match_int_leading_zero(self): # Show that numerical comparison works with leading zero self._do_specs_matcher_test( value='01', req='== 1', matches=True) def test_specs_fails_simple(self): self._do_specs_matcher_test( value='', req='1', matches=False) def test_specs_fails_simple2(self): self._do_specs_matcher_test( value='3', req='1', matches=False) def test_specs_fails_simple3(self): self._do_specs_matcher_test( value='222', req='2', matches=False) def test_specs_fails_with_bogus_ops(self): self._do_specs_matcher_test( value='4', req='! 2', matches=False) def test_specs_matches_with_op_eq(self): self._do_specs_matcher_test( value='123', req='= 123', matches=True) def test_specs_matches_with_op_eq2(self): self._do_specs_matcher_test( value='124', req='= 123', matches=True) def test_specs_fails_with_op_eq(self): self._do_specs_matcher_test( value='34', req='= 234', matches=False) def test_specs_fails_with_op_eq3(self): self._do_specs_matcher_test( value='34', req='=', matches=False) def test_specs_matches_with_op_seq(self): self._do_specs_matcher_test( value='123', req='s== 123', matches=True) def test_specs_fails_with_op_seq(self): self._do_specs_matcher_test( value='1234', req='s== 123', matches=False) def test_specs_matches_with_op_sneq(self): self._do_specs_matcher_test( value='1234', req='s!= 123', matches=True) def test_specs_fails_with_op_sneq(self): self._do_specs_matcher_test( value='123', req='s!= 123', matches=False) def test_specs_matches_with_op_sge(self): self._do_specs_matcher_test( value='234', req='s>= 1000', matches=True) def test_specs_matches_with_op_sge2(self): self._do_specs_matcher_test( value='234', req='s>= 234', matches=True) def test_specs_fails_with_op_sge(self): self._do_specs_matcher_test( value='1000', req='s>= 234', matches=False) def test_specs_matches_with_op_sle(self): self._do_specs_matcher_test( value='1000', req='s<= 1234', matches=True) def test_specs_matches_with_op_sle2(self): self._do_specs_matcher_test( value='1234', req='s<= 1234', matches=True) def test_specs_fails_with_op_sle(self): self._do_specs_matcher_test( value='1234', req='s<= 1000', matches=False) def test_specs_matches_with_op_sl(self): self._do_specs_matcher_test( value='12', req='s< 2', matches=True) def test_specs_fails_with_op_sl(self): self._do_specs_matcher_test( value='2', req='s< 12', matches=False) def test_specs_fails_with_op_sl2(self): self._do_specs_matcher_test( value='12', req='s< 12', matches=False) def test_specs_matches_with_op_sg(self): self._do_specs_matcher_test( value='2', req='s> 12', matches=True) def test_specs_fails_with_op_sg(self): self._do_specs_matcher_test( value='12', req='s> 2', matches=False) def test_specs_fails_with_op_sg2(self): self._do_specs_matcher_test( value='12', req='s> 12', matches=False) def test_specs_matches_with_op_in(self): self._do_specs_matcher_test( value='12311321', req=' 11', matches=True) def test_specs_matches_with_op_in2(self): self._do_specs_matcher_test( value='12311321', req=' 12311321', matches=True) def test_specs_matches_with_op_in3(self): self._do_specs_matcher_test( value='12311321', req=' 12311321 ', matches=True) def test_specs_fails_with_op_in(self): self._do_specs_matcher_test( value='12310321', req=' 11', matches=False) def test_specs_fails_with_op_in2(self): self._do_specs_matcher_test( value='12310321', req=' 11 ', matches=False) def test_specs_matches_with_op_or(self): self._do_specs_matcher_test( value='12', req=' 11 12', matches=True) def test_specs_matches_with_op_or2(self): self._do_specs_matcher_test( value='12', req=' 11 12 ', matches=True) def test_specs_matches_with_op_or3(self): self._do_specs_matcher_test( value='12', req=' 12', matches=True) def test_specs_fails_with_op_or(self): self._do_specs_matcher_test( value='13', req=' 11 12', matches=False) def test_specs_fails_with_op_or2(self): self._do_specs_matcher_test( value='13', req=' 11 12 ', matches=False) def test_specs_fails_with_op_or3(self): self._do_specs_matcher_test( value='13', req=' 11', matches=False) def test_specs_matches_with_op_le(self): self._do_specs_matcher_test( value='2', req='<= 10', matches=True) def test_specs_matches_with_op_le2(self): self._do_specs_matcher_test( value='10', req='<= 10', matches=True) def test_specs_fails_with_op_le(self): self._do_specs_matcher_test( value='3', req='<= 2', matches=False) def test_specs_matches_with_op_ge(self): self._do_specs_matcher_test( value='3', req='>= 1', matches=True) def test_specs_matches_with_op_ge2(self): self._do_specs_matcher_test( value='3.0', req='>= 3', matches=True) def test_specs_matches_with_op_g(self): self._do_specs_matcher_test( value='3', req='> 1', matches=True) def test_specs_matches_with_op_g2(self): self._do_specs_matcher_test( value='3', req='> 3', matches=False) def test_specs_matches_with_op_g3(self): self._do_specs_matcher_test( value='3.0', req='> 2', matches=True) def test_specs_matches_with_op_l(self): self._do_specs_matcher_test( value='3', req='< 5', matches=True) def test_specs_matches_with_op_l2(self): self._do_specs_matcher_test( value='3', req='< 3', matches=False) def test_specs_matches_with_op_l3(self): self._do_specs_matcher_test( value='1.0', req='< 6', matches=True) def test_specs_fails_with_op_ge(self): self._do_specs_matcher_test( value='2', req='>= 3', matches=False) def test_specs_matches_with_op_ne(self): self._do_specs_matcher_test( value='3.2', req='!= 3.1', matches=True) def test_specs_fails_with_op_ne(self): self._do_specs_matcher_test( value='3.2', req='!= 3.2', matches=False) def test_specs_matches_with_op_eqeq(self): self._do_specs_matcher_test( value='3', req='== 3', matches=True) def test_specs_matches_with_op_eqeq2(self): self._do_specs_matcher_test( value='3.0', req='== 3', matches=True) def test_specs_fails_with_op_eqeq(self): self._do_specs_matcher_test( value='3.0', req='== 3.1', matches=False) def test_specs_matches_all_with_op_allin(self): self._do_specs_matcher_test( value=str(['aes', 'mmx', 'aux']), req=' aes mmx', matches=True) def test_specs_matches_one_with_op_allin(self): self._do_specs_matcher_test( value=str(['aes', 'mmx', 'aux']), req=' mmx', matches=True) def test_specs_fails_with_op_allin(self): self._do_specs_matcher_test( value=str(['aes', 'mmx', 'aux']), req=' txt', matches=False) def test_specs_fails_all_with_op_allin(self): self._do_specs_matcher_test( value=str(['aes', 'mmx', 'aux']), req=' txt 3dnow', matches=False) def test_specs_fails_match_one_with_op_allin(self): self._do_specs_matcher_test( value=str(['aes', 'mmx', 'aux']), req=' txt aes', matches=False) def test_specs_fails_match_substr_single(self): self._do_specs_matcher_test( value=str(['X_X']), req=' _', matches=False) def test_specs_fails_match_substr(self): self._do_specs_matcher_test( value=str(['X___X']), req=' ___', matches=False) def test_specs_fails_match_substr_reversed(self): self._do_specs_matcher_test( value=str(['aes', 'mmx', 'aux']), req=' XaesX', matches=False) def test_specs_fails_onechar_with_op_allin(self): self.assertRaises( TypeError, specs_matcher.match, value=str(['aes', 'mmx', 'aux']), req=' e') def test_specs_errors_list_with_op_allin(self): self.assertRaises( TypeError, specs_matcher.match, value=['aes', 'mmx', 'aux'], req=' aes') def test_specs_errors_str_with_op_allin(self): self.assertRaises( TypeError, specs_matcher.match, value='aes', req=' aes') def test_specs_errors_dict_literal_with_op_allin(self): self.assertRaises( TypeError, specs_matcher.match, value=str({'aes': 1}), req=' aes') def test_specs_errors_bad_literal_with_op_allin(self): self.assertRaises( TypeError, specs_matcher.match, value="^&*($", req=' aes') oslo.utils-4.1.1/oslo_utils/tests/test_reflection.py0000664000175000017500000002565213643050415022766 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2012 Yahoo! Inc. All Rights Reserved. # # 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. from oslotest import base as test_base import six import testtools from oslo_utils import reflection if six.PY3: RUNTIME_ERROR_CLASSES = ['RuntimeError', 'Exception', 'BaseException', 'object'] else: RUNTIME_ERROR_CLASSES = ['RuntimeError', 'StandardError', 'Exception', 'BaseException', 'object'] def dummy_decorator(f): @six.wraps(f) def wrapper(*args, **kwargs): return f(*args, **kwargs) return wrapper def mere_function(a, b): pass def function_with_defs(a, b, optional=None): pass def function_with_kwargs(a, b, **kwargs): pass class TestObject(object): def _hello(self): pass def hi(self): pass class Class(object): def method(self, c, d): pass @staticmethod def static_method(e, f): pass @classmethod def class_method(cls, g, h): pass class BadClass(object): def do_something(self): pass def __nonzero__(self): return False class CallableClass(object): def __call__(self, i, j): pass class ClassWithInit(object): def __init__(self, k, l): pass class MemberGetTest(test_base.BaseTestCase): def test_get_members_exclude_hidden(self): obj = TestObject() members = list(reflection.get_members(obj, exclude_hidden=True)) self.assertEqual(1, len(members)) def test_get_members_no_exclude_hidden(self): obj = TestObject() members = list(reflection.get_members(obj, exclude_hidden=False)) self.assertGreater(len(members), 1) def test_get_members_names_exclude_hidden(self): obj = TestObject() members = list(reflection.get_member_names(obj, exclude_hidden=True)) self.assertEqual(["hi"], members) def test_get_members_names_no_exclude_hidden(self): obj = TestObject() members = list(reflection.get_member_names(obj, exclude_hidden=False)) members = [member for member in members if not member.startswith("__")] self.assertEqual(["_hello", "hi"], sorted(members)) class CallbackEqualityTest(test_base.BaseTestCase): def test_different_simple_callbacks(self): def a(): pass def b(): pass self.assertFalse(reflection.is_same_callback(a, b)) def test_static_instance_callbacks(self): class A(object): @staticmethod def b(a, b, c): pass a = A() b = A() self.assertTrue(reflection.is_same_callback(a.b, b.b)) def test_different_instance_callbacks(self): class A(object): def b(self): pass def __eq__(self, other): return True def __ne__(self, other): return not self.__eq__(other) b = A() c = A() self.assertFalse(reflection.is_same_callback(b.b, c.b)) self.assertTrue(reflection.is_same_callback(b.b, c.b, strict=False)) class BoundMethodTest(test_base.BaseTestCase): def test_baddy(self): b = BadClass() self.assertTrue(reflection.is_bound_method(b.do_something)) def test_static_method(self): self.assertFalse(reflection.is_bound_method(Class.static_method)) class GetCallableNameTest(test_base.BaseTestCase): def test_mere_function(self): name = reflection.get_callable_name(mere_function) self.assertEqual('.'.join((__name__, 'mere_function')), name) def test_method(self): name = reflection.get_callable_name(Class.method) self.assertEqual('.'.join((__name__, 'Class', 'method')), name) def test_instance_method(self): name = reflection.get_callable_name(Class().method) self.assertEqual('.'.join((__name__, 'Class', 'method')), name) def test_static_method(self): name = reflection.get_callable_name(Class.static_method) if six.PY3: self.assertEqual('.'.join((__name__, 'Class', 'static_method')), name) else: # NOTE(imelnikov): static method are just functions, class name # is not recorded anywhere in them. self.assertEqual('.'.join((__name__, 'static_method')), name) def test_class_method(self): name = reflection.get_callable_name(Class.class_method) self.assertEqual('.'.join((__name__, 'Class', 'class_method')), name) def test_constructor(self): name = reflection.get_callable_name(Class) self.assertEqual('.'.join((__name__, 'Class')), name) def test_callable_class(self): name = reflection.get_callable_name(CallableClass()) self.assertEqual('.'.join((__name__, 'CallableClass')), name) def test_callable_class_call(self): name = reflection.get_callable_name(CallableClass().__call__) self.assertEqual('.'.join((__name__, 'CallableClass', '__call__')), name) # These extended/special case tests only work on python 3, due to python 2 # being broken/incorrect with regard to these special cases... @testtools.skipIf(not six.PY3, 'python 3.x is not currently available') class GetCallableNameTestExtended(test_base.BaseTestCase): # Tests items in http://legacy.python.org/dev/peps/pep-3155/ class InnerCallableClass(object): def __call__(self): pass def test_inner_callable_class(self): obj = self.InnerCallableClass() name = reflection.get_callable_name(obj.__call__) expected_name = '.'.join((__name__, 'GetCallableNameTestExtended', 'InnerCallableClass', '__call__')) self.assertEqual(expected_name, name) def test_inner_callable_function(self): def a(): def b(): pass return b name = reflection.get_callable_name(a()) expected_name = '.'.join((__name__, 'GetCallableNameTestExtended', 'test_inner_callable_function', '', 'a', '', 'b')) self.assertEqual(expected_name, name) def test_inner_class(self): obj = self.InnerCallableClass() name = reflection.get_callable_name(obj) expected_name = '.'.join((__name__, 'GetCallableNameTestExtended', 'InnerCallableClass')) self.assertEqual(expected_name, name) class GetCallableArgsTest(test_base.BaseTestCase): def test_mere_function(self): result = reflection.get_callable_args(mere_function) self.assertEqual(['a', 'b'], result) def test_function_with_defaults(self): result = reflection.get_callable_args(function_with_defs) self.assertEqual(['a', 'b', 'optional'], result) def test_required_only(self): result = reflection.get_callable_args(function_with_defs, required_only=True) self.assertEqual(['a', 'b'], result) def test_method(self): result = reflection.get_callable_args(Class.method) self.assertEqual(['self', 'c', 'd'], result) def test_instance_method(self): result = reflection.get_callable_args(Class().method) self.assertEqual(['c', 'd'], result) def test_class_method(self): result = reflection.get_callable_args(Class.class_method) self.assertEqual(['g', 'h'], result) def test_class_constructor(self): result = reflection.get_callable_args(ClassWithInit) self.assertEqual(['k', 'l'], result) def test_class_with_call(self): result = reflection.get_callable_args(CallableClass()) self.assertEqual(['i', 'j'], result) def test_decorators_work(self): @dummy_decorator def special_fun(x, y): pass result = reflection.get_callable_args(special_fun) self.assertEqual(['x', 'y'], result) class AcceptsKwargsTest(test_base.BaseTestCase): def test_no_kwargs(self): self.assertEqual(False, reflection.accepts_kwargs(mere_function)) def test_with_kwargs(self): self.assertEqual(True, reflection.accepts_kwargs(function_with_kwargs)) class GetClassNameTest(test_base.BaseTestCase): def test_std_exception(self): name = reflection.get_class_name(RuntimeError) self.assertEqual('RuntimeError', name) def test_class(self): name = reflection.get_class_name(Class) self.assertEqual('.'.join((__name__, 'Class')), name) def test_qualified_class(self): class QualifiedClass(object): pass name = reflection.get_class_name(QualifiedClass) self.assertEqual('.'.join((__name__, 'QualifiedClass')), name) def test_instance(self): name = reflection.get_class_name(Class()) self.assertEqual('.'.join((__name__, 'Class')), name) def test_int(self): name = reflection.get_class_name(42) self.assertEqual('int', name) def test_class_method(self): name = reflection.get_class_name(Class.class_method) self.assertEqual('%s.Class' % __name__, name) # test with fully_qualified=False name = reflection.get_class_name(Class.class_method, fully_qualified=False) self.assertEqual('Class', name) def test_static_method(self): self.assertRaises(TypeError, reflection.get_class_name, Class.static_method) def test_unbound_method(self): self.assertRaises(TypeError, reflection.get_class_name, mere_function) def test_bound_method(self): c = Class() name = reflection.get_class_name(c.method) self.assertEqual('%s.Class' % __name__, name) # test with fully_qualified=False name = reflection.get_class_name(c.method, fully_qualified=False) self.assertEqual('Class', name) class GetAllClassNamesTest(test_base.BaseTestCase): def test_std_class(self): names = list(reflection.get_all_class_names(RuntimeError)) self.assertEqual(RUNTIME_ERROR_CLASSES, names) def test_std_class_up_to(self): names = list(reflection.get_all_class_names(RuntimeError, up_to=Exception)) self.assertEqual(RUNTIME_ERROR_CLASSES[:-2], names) oslo.utils-4.1.1/oslo_utils/tests/test_secretutils.py0000664000175000017500000000511613643050415023173 0ustar zuulzuul00000000000000# All Rights Reserved. # # 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 hashlib import hmac from oslotest import base as test_base import testscenarios from oslo_utils import secretutils class SecretUtilsTest(testscenarios.TestWithScenarios, test_base.BaseTestCase): _gen_digest = lambda text: hmac.new(b'foo', text.encode('utf-8'), digestmod=hashlib.sha1).digest() scenarios = [ ('binary', {'converter': _gen_digest}), ('unicode', {'converter': lambda text: text}), ] def test_constant_time_compare(self): # make sure it works as a compare, the "constant time" aspect # isn't appropriate to test in unittests # Make sure the unittests are applied to our function instead of # the built-in function, otherwise that is in vain. ctc = secretutils._constant_time_compare self.assertTrue(ctc(self.converter(u'abcd'), self.converter(u'abcd'))) self.assertTrue(ctc(self.converter(u''), self.converter(u''))) self.assertTrue(ctc('abcd', 'abcd')) self.assertFalse(ctc(self.converter(u'abcd'), self.converter(u'efgh'))) self.assertFalse(ctc(self.converter(u'abc'), self.converter(u'abcd'))) self.assertFalse(ctc(self.converter(u'abc'), self.converter(u'abc\x00'))) self.assertFalse(ctc(self.converter(u''), self.converter(u'abc'))) self.assertTrue(ctc(self.converter(u'abcd1234'), self.converter(u'abcd1234'))) self.assertFalse(ctc(self.converter(u'abcd1234'), self.converter(u'ABCD234'))) self.assertFalse(ctc(self.converter(u'abcd1234'), self.converter(u'a'))) self.assertFalse(ctc(self.converter(u'abcd1234'), self.converter(u'1234abcd'))) self.assertFalse(ctc('abcd1234', '1234abcd')) oslo.utils-4.1.1/oslo_utils/tests/fake/0000775000175000017500000000000013643050530020115 5ustar zuulzuul00000000000000oslo.utils-4.1.1/oslo_utils/tests/fake/v2/0000775000175000017500000000000013643050530020444 5ustar zuulzuul00000000000000oslo.utils-4.1.1/oslo_utils/tests/fake/v2/__init__.py0000664000175000017500000000000013643050415022545 0ustar zuulzuul00000000000000oslo.utils-4.1.1/oslo_utils/tests/fake/v2/dummpy.py0000664000175000017500000000164113643050415022335 0ustar zuulzuul00000000000000# Copyright 2016, EasyStack, 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. class V2FakeDriver(object): def __init__(self, first_arg=True): self.first_arg = first_arg class V2FakeDriver2(object): def __init__(self, first_arg): self.first_arg = first_arg class V2FakeDriver3(object): def __init__(self): raise ImportError("ImportError occurs in __init__") oslo.utils-4.1.1/oslo_utils/tests/fake/__init__.py0000664000175000017500000000160213643050415022227 0ustar zuulzuul00000000000000# Copyright 2012 IBM Corp. # # 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. class FakeDriver(): def __init__(self, first_arg=True): self.first_arg = first_arg class FakeDriver2(): def __init__(self, first_arg): self.first_arg = first_arg class FakeDriver3(): def __init__(self): raise ImportError("ImportError occurs in __init__") oslo.utils-4.1.1/oslo_utils/tests/test_dictutils.py0000664000175000017500000000271613643050415022634 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2016 EasyStack Inc. # All Rights Reserved. # # 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. from oslotest import base as test_base from oslo_utils import dictutils as du class DictUtilsTestCase(test_base.BaseTestCase): def test_flatten_dict_to_keypairs(self): data = {'a': 'A', 'b': 'B', 'nested': {'a': 'A', 'b': 'B'}} pairs = list(du.flatten_dict_to_keypairs(data)) self.assertEqual([('a', 'A'), ('b', 'B'), ('nested:a', 'A'), ('nested:b', 'B')], pairs) def test_flatten_dict_to_keypairs_with_separator(self): data = {'a': 'A', 'b': 'B', 'nested': {'a': 'A', 'b': 'B'}} pairs = list(du.flatten_dict_to_keypairs(data, separator='.')) self.assertEqual([('a', 'A'), ('b', 'B'), ('nested.a', 'A'), ('nested.b', 'B')], pairs) oslo.utils-4.1.1/oslo_utils/tests/test_fixture.py0000664000175000017500000000623713643050415022320 0ustar zuulzuul00000000000000 # Copyright 2015 OpenStack Foundation # All Rights Reserved. # # 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 datetime from oslotest import base as test_base import six from oslo_utils import fixture from oslo_utils.fixture import uuidsentinel as uuids from oslo_utils import timeutils from oslo_utils import uuidutils class TimeFixtureTest(test_base.BaseTestCase): def test_set_time_override_using_default(self): # When the fixture is used with its default constructor, the # override_time is set to the current timestamp. # Also, when the fixture is cleaned up, the override_time is reset. self.assertIsNone(timeutils.utcnow.override_time) with fixture.TimeFixture(): self.assertIsNotNone(timeutils.utcnow.override_time) self.assertIsNone(timeutils.utcnow.override_time) def test_set_time_override(self): # When the fixture is used to set a time, utcnow returns that time. new_time = datetime.datetime(2015, 1, 2, 3, 4, 6, 7) self.useFixture(fixture.TimeFixture(new_time)) self.assertEqual(new_time, timeutils.utcnow()) # Call again to make sure it keeps returning the same time. self.assertEqual(new_time, timeutils.utcnow()) def test_advance_time_delta(self): # advance_time_delta() advances the overridden time by some timedelta. new_time = datetime.datetime(2015, 1, 2, 3, 4, 6, 7) time_fixture = self.useFixture(fixture.TimeFixture(new_time)) time_fixture.advance_time_delta(datetime.timedelta(seconds=1)) expected_time = datetime.datetime(2015, 1, 2, 3, 4, 7, 7) self.assertEqual(expected_time, timeutils.utcnow()) def test_advance_time_seconds(self): # advance_time_seconds() advances the overridden time by some number of # seconds. new_time = datetime.datetime(2015, 1, 2, 3, 4, 6, 7) time_fixture = self.useFixture(fixture.TimeFixture(new_time)) time_fixture.advance_time_seconds(2) expected_time = datetime.datetime(2015, 1, 2, 3, 4, 8, 7) self.assertEqual(expected_time, timeutils.utcnow()) class UUIDSentinelsTest(test_base.BaseTestCase): def test_different_sentinel(self): uuid1 = uuids.foobar uuid2 = uuids.barfoo self.assertNotEqual(uuid1, uuid2) def test_returns_uuid(self): self.assertTrue(uuidutils.is_uuid_like(uuids.foo)) def test_returns_string(self): self.assertIsInstance(uuids.foo, str) def test_with_underline_prefix(self): ex = self.assertRaises(ValueError, getattr, uuids, '_foo') self.assertIn("Sentinels must not start with _", six.text_type(ex)) oslo.utils-4.1.1/oslo_utils/tests/test_importutils.py0000664000175000017500000001511013643050415023213 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # 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 datetime import sys from oslotest import base as test_base from oslo_utils import importutils class ImportUtilsTest(test_base.BaseTestCase): # NOTE(jkoelker) There has GOT to be a way to test this. But mocking # __import__ is the devil. Right now we just make # sure we can import something from the stdlib def test_import_class(self): dt = importutils.import_class('datetime.datetime') self.assertEqual(sys.modules['datetime'].datetime, dt) def test_import_bad_class(self): self.assertRaises(ImportError, importutils.import_class, 'lol.u_mad.brah') def test_import_module(self): dt = importutils.import_module('datetime') self.assertEqual(sys.modules['datetime'], dt) def test_import_object_optional_arg_not_present(self): obj = importutils.import_object('oslo_utils.tests.fake.FakeDriver') self.assertEqual(obj.__class__.__name__, 'FakeDriver') def test_import_object_optional_arg_present(self): obj = importutils.import_object('oslo_utils.tests.fake.FakeDriver', first_arg=False) self.assertEqual(obj.__class__.__name__, 'FakeDriver') def test_import_object_required_arg_not_present(self): # arg 1 isn't optional here self.assertRaises(TypeError, importutils.import_object, 'oslo_utils.tests.fake.FakeDriver2') def test_import_object_required_arg_present(self): obj = importutils.import_object('oslo_utils.tests.fake.FakeDriver2', first_arg=False) self.assertEqual(obj.__class__.__name__, 'FakeDriver2') # namespace tests def test_import_object_ns_optional_arg_not_present(self): obj = importutils.import_object_ns('oslo_utils', 'tests.fake.FakeDriver') self.assertEqual(obj.__class__.__name__, 'FakeDriver') def test_import_object_ns_optional_arg_present(self): obj = importutils.import_object_ns('oslo_utils', 'tests.fake.FakeDriver', first_arg=False) self.assertEqual(obj.__class__.__name__, 'FakeDriver') def test_import_object_ns_required_arg_not_present(self): # arg 1 isn't optional here self.assertRaises(TypeError, importutils.import_object_ns, 'oslo_utils', 'tests.fake.FakeDriver2') def test_import_object_ns_required_arg_present(self): obj = importutils.import_object_ns('oslo_utils', 'tests.fake.FakeDriver2', first_arg=False) self.assertEqual(obj.__class__.__name__, 'FakeDriver2') # namespace tests def test_import_object_ns_full_optional_arg_not_present(self): obj = importutils.import_object_ns('tests2', 'oslo_utils.tests.fake.FakeDriver') self.assertEqual(obj.__class__.__name__, 'FakeDriver') def test_import_object_ns_full_optional_arg_present(self): obj = importutils.import_object_ns('tests2', 'oslo_utils.tests.fake.FakeDriver', first_arg=False) self.assertEqual(obj.__class__.__name__, 'FakeDriver') def test_import_object_ns_full_required_arg_not_present(self): # arg 1 isn't optional here self.assertRaises(TypeError, importutils.import_object_ns, 'tests2', 'oslo_utils.tests.fake.FakeDriver2') def test_import_object_ns_full_required_arg_present(self): obj = importutils.import_object_ns('tests2', 'oslo_utils.tests.fake.FakeDriver2', first_arg=False) self.assertEqual(obj.__class__.__name__, 'FakeDriver2') def test_import_object_ns_raise_import_error_in_init(self): self.assertRaises(ImportError, importutils.import_object_ns, 'tests2', 'oslo_utils.tests.fake.FakeDriver3') def test_import_object(self): dt = importutils.import_object('datetime.time') self.assertIsInstance(dt, sys.modules['datetime'].time) def test_import_object_with_args(self): dt = importutils.import_object('datetime.datetime', 2012, 4, 5) self.assertIsInstance(dt, sys.modules['datetime'].datetime) self.assertEqual(dt, datetime.datetime(2012, 4, 5)) def test_import_versioned_module(self): v2 = importutils.import_versioned_module('oslo_utils.tests.fake', 2) self.assertEqual(sys.modules['oslo_utils.tests.fake.v2'], v2) dummpy = importutils.import_versioned_module('oslo_utils.tests.fake', 2, 'dummpy') self.assertEqual(sys.modules['oslo_utils.tests.fake.v2.dummpy'], dummpy) def test_import_versioned_module_wrong_version_parameter(self): self.assertRaises(ValueError, importutils.import_versioned_module, 'oslo_utils.tests.fake', "2.0", 'fake') def test_import_versioned_module_error(self): self.assertRaises(ImportError, importutils.import_versioned_module, 'oslo_utils.tests.fake', 2, 'fake') def test_try_import(self): dt = importutils.try_import('datetime') self.assertEqual(sys.modules['datetime'], dt) def test_try_import_returns_default(self): foo = importutils.try_import('foo.bar') self.assertIsNone(foo) def test_import_any_none_found(self): self.assertRaises(ImportError, importutils.import_any, 'foo.bar', 'foo.foo.bar') def test_import_any_found(self): dt = importutils.import_any('foo.bar', 'datetime') self.assertEqual(sys.modules['datetime'], dt) oslo.utils-4.1.1/oslo_utils/tests/test_fileutils.py0000664000175000017500000002155213643050415022627 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # 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 errno import hashlib import os import shutil import stat import tempfile import uuid from oslotest import base as test_base import six from oslo_utils import fileutils TEST_PERMISSIONS = stat.S_IRWXU class EnsureTree(test_base.BaseTestCase): def test_ensure_tree(self): tmpdir = tempfile.mkdtemp() try: testdir = '%s/foo/bar/baz' % (tmpdir,) fileutils.ensure_tree(testdir, TEST_PERMISSIONS) self.assertTrue(os.path.isdir(testdir)) self.assertEqual(os.stat(testdir).st_mode, TEST_PERMISSIONS | stat.S_IFDIR) finally: if os.path.exists(tmpdir): shutil.rmtree(tmpdir) class DeleteIfExists(test_base.BaseTestCase): def test_file_present(self): tmpfile = tempfile.mktemp() open(tmpfile, 'w') fileutils.delete_if_exists(tmpfile) self.assertFalse(os.path.exists(tmpfile)) def test_file_absent(self): tmpfile = tempfile.mktemp() fileutils.delete_if_exists(tmpfile) self.assertFalse(os.path.exists(tmpfile)) def test_dir_present(self): tmpdir = tempfile.mktemp() os.mkdir(tmpdir) fileutils.delete_if_exists(tmpdir, remove=os.rmdir) self.assertFalse(os.path.exists(tmpdir)) def test_file_error(self): def errm(path): raise OSError(errno.EINVAL, '') tmpfile = tempfile.mktemp() open(tmpfile, 'w') self.assertRaises(OSError, fileutils.delete_if_exists, tmpfile, errm) os.unlink(tmpfile) class RemovePathOnError(test_base.BaseTestCase): def test_error(self): tmpfile = tempfile.mktemp() open(tmpfile, 'w') try: with fileutils.remove_path_on_error(tmpfile): raise Exception except Exception: self.assertFalse(os.path.exists(tmpfile)) def test_no_error(self): tmpfile = tempfile.mktemp() open(tmpfile, 'w') with fileutils.remove_path_on_error(tmpfile): pass self.assertTrue(os.path.exists(tmpfile)) os.unlink(tmpfile) def test_remove(self): tmpfile = tempfile.mktemp() open(tmpfile, 'w') try: with fileutils.remove_path_on_error(tmpfile, remove=lambda x: x): raise Exception except Exception: self.assertTrue(os.path.exists(tmpfile)) os.unlink(tmpfile) def test_remove_dir(self): tmpdir = tempfile.mktemp() os.mkdir(tmpdir) try: with fileutils.remove_path_on_error( tmpdir, lambda path: fileutils.delete_if_exists(path, os.rmdir)): raise Exception except Exception: self.assertFalse(os.path.exists(tmpdir)) class WriteToTempfileTestCase(test_base.BaseTestCase): def setUp(self): super(WriteToTempfileTestCase, self).setUp() self.content = 'testing123'.encode('ascii') def check_file_content(self, path): with open(path, 'r') as fd: ans = fd.read() self.assertEqual(self.content, six.b(ans)) def test_file_without_path_and_suffix(self): res = fileutils.write_to_tempfile(self.content) self.assertTrue(os.path.exists(res)) (basepath, tmpfile) = os.path.split(res) self.assertTrue(basepath.startswith(tempfile.gettempdir())) self.assertTrue(tmpfile.startswith('tmp')) self.check_file_content(res) def test_file_with_not_existing_path(self): random_dir = uuid.uuid4().hex path = '/tmp/%s/test1' % random_dir res = fileutils.write_to_tempfile(self.content, path=path) self.assertTrue(os.path.exists(res)) (basepath, tmpfile) = os.path.split(res) self.assertEqual(basepath, path) self.assertTrue(tmpfile.startswith('tmp')) self.check_file_content(res) shutil.rmtree('/tmp/' + random_dir) def test_file_with_not_default_suffix(self): suffix = '.conf' res = fileutils.write_to_tempfile(self.content, suffix=suffix) self.assertTrue(os.path.exists(res)) (basepath, tmpfile) = os.path.split(res) self.assertTrue(basepath.startswith(tempfile.gettempdir())) self.assertTrue(tmpfile.startswith('tmp')) self.assertTrue(tmpfile.endswith('.conf')) self.check_file_content(res) def test_file_with_not_existing_path_and_not_default_suffix(self): suffix = '.txt' random_dir = uuid.uuid4().hex path = '/tmp/%s/test2' % random_dir res = fileutils.write_to_tempfile(self.content, path=path, suffix=suffix) self.assertTrue(os.path.exists(res)) (basepath, tmpfile) = os.path.split(res) self.assertTrue(tmpfile.startswith('tmp')) self.assertEqual(basepath, path) self.assertTrue(tmpfile.endswith(suffix)) self.check_file_content(res) shutil.rmtree('/tmp/' + random_dir) def test_file_with_not_default_prefix(self): prefix = 'test' res = fileutils.write_to_tempfile(self.content, prefix=prefix) self.assertTrue(os.path.exists(res)) (basepath, tmpfile) = os.path.split(res) self.assertTrue(tmpfile.startswith(prefix)) self.assertTrue(basepath.startswith(tempfile.gettempdir())) self.check_file_content(res) class TestComputeFileChecksum(test_base.BaseTestCase): def setUp(self): super(TestComputeFileChecksum, self).setUp() self.content = 'fake_content'.encode('ascii') def check_file_content(self, content, path): with open(path, 'r') as fd: ans = fd.read() self.assertEqual(content, six.b(ans)) def test_compute_checksum_default_algorithm(self): path = fileutils.write_to_tempfile(self.content) self.assertTrue(os.path.exists(path)) self.check_file_content(self.content, path) expected_checksum = hashlib.sha256() expected_checksum.update(self.content) actual_checksum = fileutils.compute_file_checksum(path) self.assertEqual(expected_checksum.hexdigest(), actual_checksum) def test_compute_checksum_named_algorithm(self): path = fileutils.write_to_tempfile(self.content) self.assertTrue(os.path.exists(path)) self.check_file_content(self.content, path) expected_checksum = hashlib.sha512() expected_checksum.update(self.content) actual_checksum = fileutils.compute_file_checksum(path, algorithm='sha512') self.assertEqual(expected_checksum.hexdigest(), actual_checksum) def test_compute_checksum_invalid_algorithm(self): path = fileutils.write_to_tempfile(self.content) self.assertTrue(os.path.exists(path)) self.check_file_content(self.content, path) self.assertRaises(ValueError, fileutils.compute_file_checksum, path, algorithm='foo') def test_file_does_not_exist(self): random_file_name = uuid.uuid4().hex path = os.path.join('/tmp', random_file_name) self.assertRaises(IOError, fileutils.compute_file_checksum, path) def test_generic_io_error(self): tempdir = tempfile.mkdtemp() self.assertRaises(IOError, fileutils.compute_file_checksum, tempdir) class LastBytesTestCase(test_base.BaseTestCase): """Test the last_bytes() utility method.""" def setUp(self): super(LastBytesTestCase, self).setUp() self.content = b'1234567890' def test_truncated(self): res = fileutils.write_to_tempfile(self.content) self.assertTrue(os.path.exists(res)) out, unread_bytes = fileutils.last_bytes(res, 5) self.assertEqual(b'67890', out) self.assertGreater(unread_bytes, 0) def test_read_all(self): res = fileutils.write_to_tempfile(self.content) self.assertTrue(os.path.exists(res)) out, unread_bytes = fileutils.last_bytes(res, 1000) self.assertEqual(b'1234567890', out) self.assertEqual(0, unread_bytes) def test_non_exist_file(self): self.assertRaises(IOError, fileutils.last_bytes, 'non_exist_file', 1000) oslo.utils-4.1.1/oslo_utils/tests/__init__.py0000664000175000017500000000107213643050415021322 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # 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. oslo.utils-4.1.1/oslo_utils/tests/test_imageutils.py0000664000175000017500000002361713643050415022776 0ustar zuulzuul00000000000000# Copyright (C) 2012 Yahoo! Inc. # All Rights Reserved. # # 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. from oslotest import base as test_base import testscenarios from oslo_utils import imageutils load_tests = testscenarios.load_tests_apply_scenarios class ImageUtilsRawTestCase(test_base.BaseTestCase): _image_name = [ ('disk_config', dict(image_name='disk.config')), ] _file_format = [ ('raw', dict(file_format='raw')), ] _virtual_size = [ ('64M', dict(virtual_size='64M', exp_virtual_size=67108864)), ('64M_with_byte_hint', dict(virtual_size='64M (67108844 bytes)', exp_virtual_size=67108844)), ('64M_byte', dict(virtual_size='67108844', exp_virtual_size=67108844)), ('64_MiB_with_byte_hint', dict(virtual_size='64 MiB (67108844 bytes)', exp_virtual_size=67108844)), ('4.4M', dict(virtual_size='4.4M', exp_virtual_size=4613735)), ('4.4M_with_byte_hint', dict(virtual_size='4.4M (4592640 bytes)', exp_virtual_size=4592640)), ('4.4_MiB_with_byte_hint', dict(virtual_size='4.4 MiB (4592640 bytes)', exp_virtual_size=4592640)), ('2K', dict(virtual_size='2K', exp_virtual_size=2048)), ('2K_with_byte_hint', dict(virtual_size='2K (2048 bytes)', exp_virtual_size=2048)), ('2_KiB_with_byte_hint', dict(virtual_size='2 KiB (2048 bytes)', exp_virtual_size=2048)), ('1e+03_MiB', dict(virtual_size='1e+03 MiB', exp_virtual_size=1048576000)), ] _disk_size = [ ('96K', dict(disk_size='96K', exp_disk_size=98304)), ('96_KiB', dict(disk_size='96 KiB', exp_disk_size=98304)), ('96K_byte', dict(disk_size='98304', exp_disk_size=98304)), ('98304_B', dict(disk_size='98304 B', exp_disk_size=98304)), ('3.1G', dict(disk_size='3.1G', exp_disk_size=3328599655)), ('3.1_GiB', dict(disk_size='3.1 GiB', exp_disk_size=3328599655)), ('unavailable', dict(disk_size='unavailable', exp_disk_size=0)), ('1e+03_MiB', dict(disk_size='1e+03 MiB', exp_disk_size=1048576000)), ] _garbage_before_snapshot = [ ('no_garbage', dict(garbage_before_snapshot=None)), ('garbage_before_snapshot_list', dict(garbage_before_snapshot=False)), ('garbage_after_snapshot_list', dict(garbage_before_snapshot=True)), ] _snapshot_count = [ ('no_snapshots', dict(snapshot_count=None)), ('one_snapshots', dict(snapshot_count=1)), ('three_snapshots', dict(snapshot_count=3)), ] @classmethod def generate_scenarios(cls): cls.scenarios = testscenarios.multiply_scenarios( cls._image_name, cls._file_format, cls._virtual_size, cls._disk_size, cls._garbage_before_snapshot, cls._snapshot_count) def _initialize_img_info(self): return ('image: %s' % self.image_name, 'file_format: %s' % self.file_format, 'virtual_size: %s' % self.virtual_size, 'disk_size: %s' % self.disk_size) def _insert_snapshots(self, img_info): img_info = img_info + ('Snapshot list:',) img_info = img_info + ('ID ' 'TAG ' 'VM SIZE ' 'DATE ' 'VM CLOCK',) for i in range(self.snapshot_count): img_info = img_info + ('%d ' 'd9a9784a500742a7bb95627bb3aace38 ' '0 2012-08-20 10:52:46 ' '00:00:00.000' % (i + 1),) return img_info def _base_validation(self, image_info): self.assertEqual(image_info.image, self.image_name) self.assertEqual(image_info.file_format, self.file_format) self.assertEqual(image_info.virtual_size, self.exp_virtual_size) self.assertEqual(image_info.disk_size, self.exp_disk_size) if self.snapshot_count is not None: self.assertEqual(len(image_info.snapshots), self.snapshot_count) def test_qemu_img_info(self): img_info = self._initialize_img_info() if self.garbage_before_snapshot is True: img_info = img_info + ('blah BLAH: bb',) if self.snapshot_count is not None: img_info = self._insert_snapshots(img_info) if self.garbage_before_snapshot is False: img_info = img_info + ('junk stuff: bbb',) example_output = '\n'.join(img_info) image_info = imageutils.QemuImgInfo(example_output) self._base_validation(image_info) ImageUtilsRawTestCase.generate_scenarios() class ImageUtilsQemuTestCase(ImageUtilsRawTestCase): _file_format = [ ('qcow2', dict(file_format='qcow2')), ] _qcow2_cluster_size = [ ('65536', dict(cluster_size='65536', exp_cluster_size=65536)), ] _qcow2_encrypted = [ ('no_encryption', dict(encrypted=None)), ('encrypted', dict(encrypted='yes')), ] _qcow2_backing_file = [ ('no_backing_file', dict(backing_file=None)), ('backing_file_path', dict(backing_file='/var/lib/nova/a328c7998805951a_2', exp_backing_file='/var/lib/nova/a328c7998805951a_2')), ('backing_file_path_with_actual_path', dict(backing_file='/var/lib/nova/a328c7998805951a_2 ' '(actual path: /b/3a988059e51a_2)', exp_backing_file='/b/3a988059e51a_2')), ] @classmethod def generate_scenarios(cls): cls.scenarios = testscenarios.multiply_scenarios( cls._image_name, cls._file_format, cls._virtual_size, cls._disk_size, cls._garbage_before_snapshot, cls._snapshot_count, cls._qcow2_cluster_size, cls._qcow2_encrypted, cls._qcow2_backing_file) def test_qemu_img_info(self): img_info = self._initialize_img_info() img_info = img_info + ('cluster_size: %s' % self.cluster_size,) if self.backing_file is not None: img_info = img_info + ('backing file: %s' % self.backing_file,) if self.encrypted is not None: img_info = img_info + ('encrypted: %s' % self.encrypted,) if self.garbage_before_snapshot is True: img_info = img_info + ('blah BLAH: bb',) if self.snapshot_count is not None: img_info = self._insert_snapshots(img_info) if self.garbage_before_snapshot is False: img_info = img_info + ('junk stuff: bbb',) example_output = '\n'.join(img_info) image_info = imageutils.QemuImgInfo(example_output) self._base_validation(image_info) self.assertEqual(image_info.cluster_size, self.exp_cluster_size) if self.backing_file is not None: self.assertEqual(image_info.backing_file, self.exp_backing_file) if self.encrypted is not None: self.assertEqual(image_info.encrypted, self.encrypted) ImageUtilsQemuTestCase.generate_scenarios() class ImageUtilsBlankTestCase(test_base.BaseTestCase): def test_qemu_img_info_blank(self): example_output = '\n'.join(['image: None', 'file_format: None', 'virtual_size: None', 'disk_size: None', 'cluster_size: None', 'backing_file: None']) image_info = imageutils.QemuImgInfo() self.assertEqual(str(image_info), example_output) self.assertEqual(len(image_info.snapshots), 0) class ImageUtilsJSONTestCase(test_base.BaseTestCase): def test_qemu_img_info_json_format(self): img_output = '''{ "virtual-size": 41126400, "filename": "fake_img", "cluster-size": 65536, "format": "qcow2", "actual-size": 13168640, "format-specific": {"data": {"foo": "bar"}} }''' image_info = imageutils.QemuImgInfo(img_output, format='json') self.assertEqual(41126400, image_info.virtual_size) self.assertEqual('fake_img', image_info.image) self.assertEqual(65536, image_info.cluster_size) self.assertEqual('qcow2', image_info.file_format) self.assertEqual(13168640, image_info.disk_size) self.assertEqual("bar", image_info.format_specific["data"]["foo"]) def test_qemu_img_info_json_format_blank(self): img_output = '{}' image_info = imageutils.QemuImgInfo(img_output, format='json') self.assertIsNone(image_info.virtual_size) self.assertIsNone(image_info.image) self.assertIsNone(image_info.cluster_size) self.assertIsNone(image_info.file_format) self.assertIsNone(image_info.disk_size) self.assertIsNone(image_info.format_specific) oslo.utils-4.1.1/oslo_utils/tests/test_timeutils.py0000664000175000017500000005726113643050415022654 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # 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 calendar import datetime import logging import time from unittest import mock import iso8601 from oslotest import base as test_base from testtools import matchers from oslo_utils import timeutils def monotonic_iter(start=0, incr=0.05): while True: yield start start += incr class TimeUtilsTest(test_base.BaseTestCase): def setUp(self): super(TimeUtilsTest, self).setUp() self.skynet_self_aware_time_str = '1997-08-29T06:14:00Z' self.skynet_self_aware_time_ms_str = '1997-08-29T06:14:00.000123Z' self.skynet_self_aware_time = datetime.datetime(1997, 8, 29, 6, 14, 0) self.skynet_self_aware_ms_time = datetime.datetime(1997, 8, 29, 6, 14, 0, 123) self.one_minute_before = datetime.datetime(1997, 8, 29, 6, 13, 0) self.one_minute_after = datetime.datetime(1997, 8, 29, 6, 15, 0) self.skynet_self_aware_time_perfect_str = '1997-08-29T06:14:00.000000' self.skynet_self_aware_time_perfect = datetime.datetime(1997, 8, 29, 6, 14, 0) self.addCleanup(timeutils.clear_time_override) def test_isotime(self): with mock.patch('datetime.datetime') as datetime_mock: datetime_mock.utcnow.return_value = self.skynet_self_aware_time dt = timeutils.isotime() self.assertEqual(dt, self.skynet_self_aware_time_str) def test_isotimei_micro_second_precision(self): with mock.patch('datetime.datetime') as datetime_mock: datetime_mock.utcnow.return_value = self.skynet_self_aware_ms_time dt = timeutils.isotime(subsecond=True) self.assertEqual(dt, self.skynet_self_aware_time_ms_str) def test_parse_isotime(self): expect = timeutils.parse_isotime(self.skynet_self_aware_time_str) skynet_self_aware_time_utc = self.skynet_self_aware_time.replace( tzinfo=iso8601.iso8601.UTC) self.assertEqual(skynet_self_aware_time_utc, expect) def test_parse_isotime_micro_second_precision(self): expect = timeutils.parse_isotime(self.skynet_self_aware_time_ms_str) skynet_self_aware_time_ms_utc = self.skynet_self_aware_ms_time.replace( tzinfo=iso8601.iso8601.UTC) self.assertEqual(skynet_self_aware_time_ms_utc, expect) def test_strtime(self): expect = timeutils.strtime(self.skynet_self_aware_time_perfect) self.assertEqual(self.skynet_self_aware_time_perfect_str, expect) def test_parse_strtime(self): perfect_time_format = self.skynet_self_aware_time_perfect_str expect = timeutils.parse_strtime(perfect_time_format) self.assertEqual(self.skynet_self_aware_time_perfect, expect) def test_strtime_and_back(self): orig_t = datetime.datetime(1997, 8, 29, 6, 14, 0) s = timeutils.strtime(orig_t) t = timeutils.parse_strtime(s) self.assertEqual(orig_t, t) @mock.patch('datetime.datetime', wraps=datetime.datetime) def _test_is_older_than(self, fn, datetime_mock): datetime_mock.utcnow.return_value = self.skynet_self_aware_time expect_true = timeutils.is_older_than(fn(self.one_minute_before), 59) self.assertTrue(expect_true) expect_false = timeutils.is_older_than(fn(self.one_minute_before), 60) self.assertFalse(expect_false) expect_false = timeutils.is_older_than(fn(self.one_minute_before), 61) self.assertFalse(expect_false) def test_is_older_than_datetime(self): self._test_is_older_than(lambda x: x) def test_is_older_than_str(self): self._test_is_older_than(timeutils.strtime) def test_is_older_than_aware(self): """Tests sending is_older_than an 'aware' datetime.""" self._test_is_older_than(lambda x: x.replace( tzinfo=iso8601.iso8601.UTC)) def test_is_older_than_aware_no_utc(self): self._test_is_older_than(lambda x: x.replace( tzinfo=iso8601.iso8601.FixedOffset(1, 0, 'foo')).replace( hour=7)) @mock.patch('datetime.datetime', wraps=datetime.datetime) def _test_is_newer_than(self, fn, datetime_mock): datetime_mock.utcnow.return_value = self.skynet_self_aware_time expect_true = timeutils.is_newer_than(fn(self.one_minute_after), 59) self.assertTrue(expect_true) expect_false = timeutils.is_newer_than(fn(self.one_minute_after), 60) self.assertFalse(expect_false) expect_false = timeutils.is_newer_than(fn(self.one_minute_after), 61) self.assertFalse(expect_false) def test_is_newer_than_datetime(self): self._test_is_newer_than(lambda x: x) def test_is_newer_than_str(self): self._test_is_newer_than(timeutils.strtime) def test_is_newer_than_aware(self): """Tests sending is_newer_than an 'aware' datetime.""" self._test_is_newer_than(lambda x: x.replace( tzinfo=iso8601.iso8601.UTC)) def test_is_newer_than_aware_no_utc(self): self._test_is_newer_than(lambda x: x.replace( tzinfo=iso8601.iso8601.FixedOffset(1, 0, 'foo')).replace( hour=7)) def test_set_time_override_using_default(self): now = timeutils.utcnow_ts() # NOTE(kgriffs): Normally it's bad form to sleep in a unit test, # but this is the only way to test that set_time_override defaults # to setting the override to the current time. time.sleep(1) timeutils.set_time_override() overriden_now = timeutils.utcnow_ts() self.assertThat(now, matchers.LessThan(overriden_now)) def test_utcnow_ts(self): skynet_self_aware_ts = 872835240 skynet_dt = datetime.datetime.utcfromtimestamp(skynet_self_aware_ts) self.assertEqual(self.skynet_self_aware_time, skynet_dt) # NOTE(kgriffs): timeutils.utcnow_ts() uses time.time() # IFF time override is not set. with mock.patch('time.time') as time_mock: time_mock.return_value = skynet_self_aware_ts ts = timeutils.utcnow_ts() self.assertEqual(ts, skynet_self_aware_ts) timeutils.set_time_override(skynet_dt) ts = timeutils.utcnow_ts() self.assertEqual(ts, skynet_self_aware_ts) def test_utcnow(self): timeutils.set_time_override(mock.sentinel.utcnow) self.assertEqual(timeutils.utcnow(), mock.sentinel.utcnow) timeutils.clear_time_override() self.assertFalse(timeutils.utcnow() == mock.sentinel.utcnow) self.assertTrue(timeutils.utcnow()) def test_advance_time_delta(self): timeutils.set_time_override(self.one_minute_before) timeutils.advance_time_delta(datetime.timedelta(seconds=60)) self.assertEqual(timeutils.utcnow(), self.skynet_self_aware_time) def test_advance_time_seconds(self): timeutils.set_time_override(self.one_minute_before) timeutils.advance_time_seconds(60) self.assertEqual(timeutils.utcnow(), self.skynet_self_aware_time) def test_marshall_time(self): now = timeutils.utcnow() binary = timeutils.marshall_now(now) backagain = timeutils.unmarshall_time(binary) self.assertEqual(now, backagain) def test_marshall_time_with_tz(self): now = timeutils.utcnow() now = now.replace(tzinfo=iso8601.iso8601.UTC) binary = timeutils.marshall_now(now) self.assertEqual("UTC", binary['tzname']) backagain = timeutils.unmarshall_time(binary) self.assertEqual(now, backagain) self.assertIsNotNone(backagain.tzinfo) self.assertEqual(now.utcoffset(), backagain.utcoffset()) def test_unmarshall_time_leap_second(self): leap_dict = dict(day=30, month=6, year=2015, hour=23, minute=59, second=timeutils._MAX_DATETIME_SEC + 1, microsecond=0) leap_time = timeutils.unmarshall_time(leap_dict) leap_dict.update(second=timeutils._MAX_DATETIME_SEC) expected = timeutils.unmarshall_time(leap_dict) self.assertEqual(expected, leap_time) def test_delta_seconds(self): before = timeutils.utcnow() after = before + datetime.timedelta(days=7, seconds=59, microseconds=123456) self.assertAlmostEquals(604859.123456, timeutils.delta_seconds(before, after)) def test_iso8601_from_timestamp(self): utcnow = timeutils.utcnow() iso = timeutils.isotime(utcnow) ts = calendar.timegm(utcnow.timetuple()) self.assertEqual(iso, timeutils.iso8601_from_timestamp(ts)) def test_iso8601_from_timestamp_ms(self): ts = timeutils.utcnow_ts(microsecond=True) utcnow = datetime.datetime.utcfromtimestamp(ts) iso = timeutils.isotime(utcnow, subsecond=True) self.assertEqual(iso, timeutils.iso8601_from_timestamp(ts, True)) def test_is_soon(self): expires = timeutils.utcnow() + datetime.timedelta(minutes=5) self.assertFalse(timeutils.is_soon(expires, 120)) self.assertTrue(timeutils.is_soon(expires, 300)) self.assertTrue(timeutils.is_soon(expires, 600)) with mock.patch('datetime.datetime') as datetime_mock: datetime_mock.utcnow.return_value = self.skynet_self_aware_time expires = timeutils.utcnow() self.assertTrue(timeutils.is_soon(expires, 0)) class TestIso8601Time(test_base.BaseTestCase): def _instaneous(self, timestamp, yr, mon, day, hr, minute, sec, micro): self.assertEqual(timestamp.year, yr) self.assertEqual(timestamp.month, mon) self.assertEqual(timestamp.day, day) self.assertEqual(timestamp.hour, hr) self.assertEqual(timestamp.minute, minute) self.assertEqual(timestamp.second, sec) self.assertEqual(timestamp.microsecond, micro) def _do_test(self, time_str, yr, mon, day, hr, minute, sec, micro, shift): DAY_SECONDS = 24 * 60 * 60 timestamp = timeutils.parse_isotime(time_str) self._instaneous(timestamp, yr, mon, day, hr, minute, sec, micro) offset = timestamp.tzinfo.utcoffset(None) self.assertEqual(offset.seconds + offset.days * DAY_SECONDS, shift) def test_zulu(self): time_str = '2012-02-14T20:53:07Z' self._do_test(time_str, 2012, 2, 14, 20, 53, 7, 0, 0) def test_zulu_micros(self): time_str = '2012-02-14T20:53:07.123Z' self._do_test(time_str, 2012, 2, 14, 20, 53, 7, 123000, 0) def test_offset_east(self): time_str = '2012-02-14T20:53:07+04:30' offset = 4.5 * 60 * 60 self._do_test(time_str, 2012, 2, 14, 20, 53, 7, 0, offset) def test_offset_east_micros(self): time_str = '2012-02-14T20:53:07.42+04:30' offset = 4.5 * 60 * 60 self._do_test(time_str, 2012, 2, 14, 20, 53, 7, 420000, offset) def test_offset_west(self): time_str = '2012-02-14T20:53:07-05:30' offset = -5.5 * 60 * 60 self._do_test(time_str, 2012, 2, 14, 20, 53, 7, 0, offset) def test_offset_west_micros(self): time_str = '2012-02-14T20:53:07.654321-05:30' offset = -5.5 * 60 * 60 self._do_test(time_str, 2012, 2, 14, 20, 53, 7, 654321, offset) def test_compare(self): zulu = timeutils.parse_isotime('2012-02-14T20:53:07') east = timeutils.parse_isotime('2012-02-14T20:53:07-01:00') west = timeutils.parse_isotime('2012-02-14T20:53:07+01:00') self.assertTrue(east > west) self.assertTrue(east > zulu) self.assertTrue(zulu > west) def test_compare_micros(self): zulu = timeutils.parse_isotime('2012-02-14T20:53:07.6544') east = timeutils.parse_isotime('2012-02-14T19:53:07.654321-01:00') west = timeutils.parse_isotime('2012-02-14T21:53:07.655+01:00') self.assertTrue(east < west) self.assertTrue(east < zulu) self.assertTrue(zulu < west) def test_zulu_roundtrip(self): time_str = '2012-02-14T20:53:07Z' zulu = timeutils.parse_isotime(time_str) self.assertEqual(zulu.tzinfo, iso8601.iso8601.UTC) self.assertEqual(timeutils.isotime(zulu), time_str) def test_east_roundtrip(self): time_str = '2012-02-14T20:53:07-07:00' east = timeutils.parse_isotime(time_str) self.assertEqual(east.tzinfo.tzname(None), '-07:00') self.assertEqual(timeutils.isotime(east), time_str) def test_west_roundtrip(self): time_str = '2012-02-14T20:53:07+11:30' west = timeutils.parse_isotime(time_str) self.assertEqual(west.tzinfo.tzname(None), '+11:30') self.assertEqual(timeutils.isotime(west), time_str) def test_now_roundtrip(self): time_str = timeutils.isotime() now = timeutils.parse_isotime(time_str) self.assertEqual(now.tzinfo, iso8601.iso8601.UTC) self.assertEqual(timeutils.isotime(now), time_str) def test_zulu_normalize(self): time_str = '2012-02-14T20:53:07Z' zulu = timeutils.parse_isotime(time_str) normed = timeutils.normalize_time(zulu) self._instaneous(normed, 2012, 2, 14, 20, 53, 7, 0) def test_east_normalize(self): time_str = '2012-02-14T20:53:07-07:00' east = timeutils.parse_isotime(time_str) normed = timeutils.normalize_time(east) self._instaneous(normed, 2012, 2, 15, 3, 53, 7, 0) def test_west_normalize(self): time_str = '2012-02-14T20:53:07+21:00' west = timeutils.parse_isotime(time_str) normed = timeutils.normalize_time(west) 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, 7) time_str = '2011-02-14T20:53:07+21:00' aware = timeutils.parse_isotime(time_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, 7) time_str = '2011-02-14T19:53:07Z' aware = timeutils.parse_isotime(time_str) naive = timeutils.normalize_time(aware) self.assertTrue(naive < dt) def test_normalize_naive(self): 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) class TimeItTest(test_base.BaseTestCase): @mock.patch('time.sleep') @mock.patch('oslo_utils.timeutils.now') def test_timed(self, mock_now, mock_sleep): mock_now.side_effect = monotonic_iter(incr=0.1) fake_logger = mock.MagicMock(logging.getLogger(), autospec=True) @timeutils.time_it(fake_logger) def slow_function(): time.sleep(0.1) slow_function() self.assertTrue(mock_now.called) self.assertTrue(mock_sleep.called) self.assertTrue(fake_logger.log.called) fake_logger.log.assert_called_with(logging.DEBUG, mock.ANY, mock.ANY) @mock.patch('time.sleep') @mock.patch('oslo_utils.timeutils.now') def test_no_timed_disabled(self, mock_now, mock_sleep): mock_now.side_effect = monotonic_iter(incr=0.1) fake_logger = mock.MagicMock(logging.getLogger(), autospec=True) @timeutils.time_it(fake_logger, enabled=False) def slow_function(): time.sleep(0.1) slow_function() self.assertFalse(mock_now.called) self.assertFalse(fake_logger.log.called) @mock.patch('time.sleep') @mock.patch('oslo_utils.timeutils.now') def test_no_timed_to_fast(self, mock_now, mock_sleep): mock_now.side_effect = monotonic_iter(incr=0.1) fake_logger = mock.MagicMock(logging.getLogger(), autospec=True) @timeutils.time_it(fake_logger, min_duration=10) def fast_function(): pass fast_function() self.assertFalse(fake_logger.log.called) @mock.patch('time.sleep') @mock.patch('oslo_utils.timeutils.now') def test_no_timed_exception(self, mock_now, mock_sleep): mock_now.side_effect = monotonic_iter(incr=0.1) fake_logger = mock.MagicMock(logging.getLogger(), autospec=True) @timeutils.time_it(fake_logger) def broken_function(): raise IOError("Broken") self.assertRaises(IOError, broken_function) self.assertFalse(fake_logger.log.called) @mock.patch('time.sleep') @mock.patch('oslo_utils.timeutils.now') def test_timed_custom_message(self, mock_now, mock_sleep): mock_now.side_effect = monotonic_iter(incr=0.1) fake_logger = mock.MagicMock(logging.getLogger(), autospec=True) @timeutils.time_it(fake_logger, message="That took a long time") def slow_function(): time.sleep(0.1) slow_function() self.assertTrue(mock_now.called) self.assertTrue(mock_sleep.called) self.assertTrue(fake_logger.log.called) fake_logger.log.assert_called_with(logging.DEBUG, "That took a long time", mock.ANY) @mock.patch('time.sleep') @mock.patch('oslo_utils.timeutils.now') def test_timed_custom_level(self, mock_now, mock_sleep): mock_now.side_effect = monotonic_iter(incr=0.1) fake_logger = mock.MagicMock(logging.getLogger(), autospec=True) @timeutils.time_it(fake_logger, log_level=logging.INFO) def slow_function(): time.sleep(0.1) slow_function() self.assertTrue(mock_now.called) self.assertTrue(mock_sleep.called) self.assertTrue(fake_logger.log.called) fake_logger.log.assert_called_with(logging.INFO, mock.ANY, mock.ANY) class StopWatchTest(test_base.BaseTestCase): def test_leftover_no_duration(self): watch = timeutils.StopWatch() watch.start() self.assertRaises(RuntimeError, watch.leftover) self.assertRaises(RuntimeError, watch.leftover, return_none=False) self.assertIsNone(watch.leftover(return_none=True)) def test_no_states(self): watch = timeutils.StopWatch() self.assertRaises(RuntimeError, watch.stop) self.assertRaises(RuntimeError, watch.resume) def test_bad_expiry(self): self.assertRaises(ValueError, timeutils.StopWatch, -1) @mock.patch('oslo_utils.timeutils.now') def test_backwards(self, mock_now): mock_now.side_effect = [0, 0.5, -1.0, -1.0] watch = timeutils.StopWatch(0.1) watch.start() self.assertTrue(watch.expired()) self.assertFalse(watch.expired()) self.assertEqual(0.0, watch.elapsed()) @mock.patch('oslo_utils.timeutils.now') def test_expiry(self, mock_now): mock_now.side_effect = monotonic_iter(incr=0.2) watch = timeutils.StopWatch(0.1) watch.start() self.assertTrue(watch.expired()) @mock.patch('oslo_utils.timeutils.now') def test_not_expired(self, mock_now): mock_now.side_effect = monotonic_iter() watch = timeutils.StopWatch(0.1) watch.start() self.assertFalse(watch.expired()) def test_has_started_stopped(self): watch = timeutils.StopWatch() self.assertFalse(watch.has_started()) self.assertFalse(watch.has_stopped()) watch.start() self.assertTrue(watch.has_started()) self.assertFalse(watch.has_stopped()) watch.stop() self.assertTrue(watch.has_stopped()) self.assertFalse(watch.has_started()) def test_no_expiry(self): watch = timeutils.StopWatch(0.1) self.assertRaises(RuntimeError, watch.expired) @mock.patch('oslo_utils.timeutils.now') def test_elapsed(self, mock_now): mock_now.side_effect = monotonic_iter(incr=0.2) watch = timeutils.StopWatch() watch.start() matcher = matchers.GreaterThan(0.19) self.assertThat(watch.elapsed(), matcher) def test_no_elapsed(self): watch = timeutils.StopWatch() self.assertRaises(RuntimeError, watch.elapsed) def test_no_leftover(self): watch = timeutils.StopWatch() self.assertRaises(RuntimeError, watch.leftover) watch = timeutils.StopWatch(1) self.assertRaises(RuntimeError, watch.leftover) @mock.patch('oslo_utils.timeutils.now') def test_pause_resume(self, mock_now): mock_now.side_effect = monotonic_iter() watch = timeutils.StopWatch() watch.start() watch.stop() elapsed = watch.elapsed() self.assertAlmostEqual(elapsed, watch.elapsed()) watch.resume() self.assertNotEqual(elapsed, watch.elapsed()) @mock.patch('oslo_utils.timeutils.now') def test_context_manager(self, mock_now): mock_now.side_effect = monotonic_iter() with timeutils.StopWatch() as watch: pass matcher = matchers.GreaterThan(0.04) self.assertThat(watch.elapsed(), matcher) @mock.patch('oslo_utils.timeutils.now') def test_context_manager_splits(self, mock_now): mock_now.side_effect = monotonic_iter() with timeutils.StopWatch() as watch: time.sleep(0.01) watch.split() self.assertRaises(RuntimeError, watch.split) self.assertEqual(1, len(watch.splits)) def test_splits_stopped(self): watch = timeutils.StopWatch() watch.start() watch.split() watch.stop() self.assertRaises(RuntimeError, watch.split) def test_splits_never_started(self): watch = timeutils.StopWatch() self.assertRaises(RuntimeError, watch.split) @mock.patch('oslo_utils.timeutils.now') def test_splits(self, mock_now): mock_now.side_effect = monotonic_iter() watch = timeutils.StopWatch() watch.start() self.assertEqual(0, len(watch.splits)) watch.split() self.assertEqual(1, len(watch.splits)) self.assertEqual(watch.splits[0].elapsed, watch.splits[0].length) watch.split() splits = watch.splits self.assertEqual(2, len(splits)) self.assertNotEqual(splits[0].elapsed, splits[1].elapsed) self.assertEqual(splits[1].length, splits[1].elapsed - splits[0].elapsed) watch.stop() self.assertEqual(2, len(watch.splits)) watch.start() self.assertEqual(0, len(watch.splits)) @mock.patch('oslo_utils.timeutils.now') def test_elapsed_maximum(self, mock_now): mock_now.side_effect = [0, 1] + ([11] * 4) watch = timeutils.StopWatch() watch.start() self.assertEqual(1, watch.elapsed()) self.assertEqual(11, watch.elapsed()) self.assertEqual(1, watch.elapsed(maximum=1)) watch.stop() self.assertEqual(11, watch.elapsed()) self.assertEqual(11, watch.elapsed()) self.assertEqual(0, watch.elapsed(maximum=-1)) oslo.utils-4.1.1/oslo_utils/dictutils.py0000664000175000017500000000227713643050415020435 0ustar zuulzuul00000000000000# Copyright (c) 2016 EasyStack Inc. # All Rights Reserved. # # 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 six def flatten_dict_to_keypairs(d, separator=':'): """Generator that produces sequence of keypairs for nested dictionaries. :param d: dictionaries which may be nested :param separator: symbol between names """ for name, value in sorted(six.iteritems(d)): if isinstance(value, dict): for subname, subvalue in flatten_dict_to_keypairs(value, separator): yield ('%s%s%s' % (name, separator, subname), subvalue) else: yield name, value oslo.utils-4.1.1/oslo_utils/encodeutils.py0000664000175000017500000001604113643050415020741 0ustar zuulzuul00000000000000# Copyright 2014 Red Hat, Inc. # All Rights Reserved. # # 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 sys import six # NOTE(blk-u): This provides a symbol that can be overridden just for this # module during testing. sys.getfilesystemencoding() is called by coverage so # mocking it globally caused the coverage job to fail. _getfilesystemencoding = sys.getfilesystemencoding def safe_decode(text, incoming=None, errors='strict'): """Decodes incoming text/bytes string using `incoming` if they're not already unicode. :param incoming: Text's current encoding :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: text or a unicode `incoming` encoded representation of it. :raises TypeError: If text is not an instance of str """ if not isinstance(text, (six.string_types, six.binary_type)): raise TypeError("%s can't be decoded" % type(text)) if isinstance(text, six.text_type): return text if not incoming: incoming = (getattr(sys.stdin, 'encoding', None) or sys.getdefaultencoding()) try: return text.decode(incoming, errors) except UnicodeDecodeError: # Note(flaper87) If we get here, it means that # sys.stdin.encoding / sys.getdefaultencoding # didn't return a suitable encoding to decode # text. This happens mostly when global LANG # var is not set correctly and there's no # default encoding. In this case, most likely # python will use ASCII or ANSI encoders as # default encodings but they won't be capable # of decoding non-ASCII characters. # # Also, UTF-8 is being used since it's an ASCII # extension. return text.decode('utf-8', errors) def safe_encode(text, incoming=None, encoding='utf-8', errors='strict'): """Encodes incoming text/bytes string using `encoding`. If incoming is not specified, text is expected to be encoded with current python's default encoding. (`sys.getdefaultencoding`) :param incoming: Text's current encoding :param encoding: Expected encoding for text (Default UTF-8) :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: text or a bytestring `encoding` encoded representation of it. :raises TypeError: If text is not an instance of str See also to_utf8() function which is simpler and don't depend on the locale encoding. """ if not isinstance(text, (six.string_types, six.binary_type)): raise TypeError("%s can't be encoded" % type(text)) if not incoming: incoming = (getattr(sys.stdin, 'encoding', None) or sys.getdefaultencoding()) # Avoid case issues in comparisons if hasattr(incoming, 'lower'): incoming = incoming.lower() if hasattr(encoding, 'lower'): encoding = encoding.lower() if isinstance(text, six.text_type): return text.encode(encoding, errors) elif text and encoding != incoming: # Decode text before encoding it with `encoding` text = safe_decode(text, incoming, errors) return text.encode(encoding, errors) else: return text def to_utf8(text): """Encode Unicode to UTF-8, return bytes unchanged. Raise TypeError if text is not a bytes string or a Unicode string. .. versionadded:: 3.5 """ if isinstance(text, six.binary_type): return text elif isinstance(text, six.text_type): return text.encode('utf-8') else: raise TypeError("bytes or Unicode expected, got %s" % type(text).__name__) def exception_to_unicode(exc): """Get the message of an exception as a Unicode string. On Python 3, the exception message is always a Unicode string. On Python 2, the exception message is a bytes string *most* of the time. If the exception message is a bytes strings, try to decode it from UTF-8 (superset of ASCII), from the locale encoding, or fallback to decoding it from ISO-8859-1 (which never fails). .. versionadded:: 1.6 """ msg = None if six.PY2: # First try by calling the unicode type constructor. We should try # unicode() before exc.__unicode__() because subclasses of unicode can # be easily casted to unicode, whereas they have no __unicode__() # method. try: msg = unicode(exc) # NOQA except UnicodeError: # unicode(exc) fail with UnicodeDecodeError on Python 2 if # exc.__unicode__() or exc.__str__() returns a bytes string not # decodable from the default encoding (ASCII) if hasattr(exc, '__unicode__'): # Call directly the __unicode__() method to avoid # the implicit decoding from the default encoding try: msg = exc.__unicode__() except UnicodeError: # nosec pass if msg is None: # Don't call directly str(exc), because it fails with # UnicodeEncodeError on Python 2 if exc.__str__() returns a Unicode # string not encodable to the default encoding (ASCII) msg = exc.__str__() if isinstance(msg, six.text_type): # This should be the default path on Python 3 and an *optional* path # on Python 2 (if for some reason the exception message was already # in unicode instead of the more typical bytes string); so avoid # further converting to unicode in both of these cases. return msg try: # Try to decode from UTF-8 (superset of ASCII). The decoder fails # if the string is not a valid UTF-8 string: the UTF-8 codec includes # a validation algorithm to ensure the consistency of the codec. return msg.decode('utf-8') except UnicodeDecodeError: # nosec pass # Try the locale encoding, most error messages are encoded to this encoding # (ex: os.strerror(errno)) encoding = _getfilesystemencoding() try: return msg.decode(encoding) except UnicodeDecodeError: # nosec pass # The encoding is not ASCII, not UTF-8, nor the locale encoding. Fallback # to the ISO-8859-1 encoding which never fails. It will produce mojibake # if the message is not encoded to ISO-8859-1, but we don't want a super # complex heuristic to get the encoding of an exception message. return msg.decode('latin1') oslo.utils-4.1.1/oslo_utils/importutils.py0000664000175000017500000000723713643050415021025 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # 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 related utilities and helper functions. """ import sys import traceback def import_class(import_str): """Returns a class from a string including module and class. .. versionadded:: 0.3 """ mod_str, _sep, class_str = import_str.rpartition('.') __import__(mod_str) try: return getattr(sys.modules[mod_str], class_str) except AttributeError: raise ImportError('Class %s cannot be found (%s)' % (class_str, traceback.format_exception(*sys.exc_info()))) def import_object(import_str, *args, **kwargs): """Import a class and return an instance of it. .. versionadded:: 0.3 """ return import_class(import_str)(*args, **kwargs) def import_object_ns(name_space, import_str, *args, **kwargs): """Tries to import object from default namespace. Imports a class and return an instance of it, first by trying to find the class in a default namespace, then failing back to a full path if not found in the default namespace. .. versionadded:: 0.3 .. versionchanged:: 2.6 Don't capture :exc:`ImportError` when instanciating the object, only when importing the object class. """ import_value = "%s.%s" % (name_space, import_str) try: cls = import_class(import_value) except ImportError: cls = import_class(import_str) return cls(*args, **kwargs) def import_module(import_str): """Import a module. .. versionadded:: 0.3 """ __import__(import_str) return sys.modules[import_str] def import_versioned_module(module, version, submodule=None): """Import a versioned module in format {module}.v{version][.{submodule}]. :param module: the module name. :param version: the version number. :param submodule: the submodule name. :raises ValueError: For any invalid input. .. versionadded:: 0.3 .. versionchanged:: 3.17 Added *module* parameter. """ # NOTE(gcb) Disallow parameter version include character '.' if '.' in '%s' % version: raise ValueError("Parameter version shouldn't include character '.'.") module_str = '%s.v%s' % (module, version) if submodule: module_str = '.'.join((module_str, submodule)) return import_module(module_str) def try_import(import_str, default=None): """Try to import a module and if it fails return default.""" try: return import_module(import_str) except ImportError: return default def import_any(module, *modules): """Try to import a module from a list of modules. :param modules: A list of modules to try and import :returns: The first module found that can be imported :raises ImportError: If no modules can be imported from list .. versionadded:: 3.8 """ for module_name in (module,) + modules: imported_module = try_import(module_name) if imported_module: return imported_module raise ImportError('Unable to import any modules from the list %s' % str(modules)) oslo.utils-4.1.1/oslo_utils/__init__.py0000664000175000017500000000000013643050415020146 0ustar zuulzuul00000000000000oslo.utils-4.1.1/oslo_utils/imageutils.py0000664000175000017500000001747613643050415020603 0ustar zuulzuul00000000000000# Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # Copyright (c) 2010 Citrix Systems, 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. """ Helper methods to deal with images. .. versionadded:: 3.1 .. versionchanged:: 3.14.0 add paramter format. """ import json import re from oslo_utils._i18n import _ from oslo_utils import strutils class QemuImgInfo(object): """Parse Qemu image information from command `qemu-img info`'s output. The instance of :class:`QemuImgInfo` has properties: `image`, `backing_file`, `file_format`, `virtual_size`, `cluster_size`, `disk_size`, `snapshots` and `encrypted`. The parameter format can be set to 'json' or 'human'. With 'json' format output, qemu image information will be parsed more easily and readable. """ BACKING_FILE_RE = re.compile((r"^(.*?)\s*\(actual\s+path\s*:" r"\s+(.*?)\)\s*$"), re.I) TOP_LEVEL_RE = re.compile(r"^([\w\d\s\_\-]+):(.*)$") SIZE_RE = re.compile(r"([0-9]+[eE][-+][0-9]+|\d*\.?\d+)" r"\s*(\w+)?(\s*\(\s*(\d+)\s+bytes\s*\))?", re.I) def __init__(self, cmd_output=None, format='human'): if format == 'json': details = json.loads(cmd_output or '{}') self.image = details.get('filename') self.backing_file = details.get('backing-filename') self.file_format = details.get('format') self.virtual_size = details.get('virtual-size') self.cluster_size = details.get('cluster-size') self.disk_size = details.get('actual-size') self.snapshots = details.get('snapshots', []) self.encrypted = details.get('encrypted') self.format_specific = details.get('format-specific') else: details = self._parse(cmd_output or '') self.image = details.get('image') self.backing_file = details.get('backing_file') self.file_format = details.get('file_format') self.virtual_size = details.get('virtual_size') self.cluster_size = details.get('cluster_size') self.disk_size = details.get('disk_size') self.snapshots = details.get('snapshot_list', []) self.encrypted = details.get('encrypted') self.format_specific = None def __str__(self): lines = [ 'image: %s' % self.image, 'file_format: %s' % self.file_format, 'virtual_size: %s' % self.virtual_size, 'disk_size: %s' % self.disk_size, 'cluster_size: %s' % self.cluster_size, 'backing_file: %s' % self.backing_file, ] if self.snapshots: lines.append("snapshots: %s" % self.snapshots) if self.encrypted: lines.append("encrypted: %s" % self.encrypted) if self.format_specific: lines.appened("format_specific: %s" % self.format_specific) return "\n".join(lines) def _canonicalize(self, field): # Standardize on underscores/lc/no dash and no spaces # since qemu seems to have mixed outputs here... and # this format allows for better integration with python # - i.e. for usage in kwargs and such... field = field.lower().strip() for c in (" ", "-"): field = field.replace(c, '_') return field def _extract_bytes(self, details): # Replace it with the byte amount real_size = self.SIZE_RE.search(details) if not real_size: raise ValueError(_('Invalid input value "%s".') % details) magnitude = real_size.group(1) if "e" in magnitude.lower(): magnitude = format(float(real_size.group(1)), '.0f') unit_of_measure = real_size.group(2) bytes_info = real_size.group(3) if bytes_info: return int(real_size.group(4)) elif not unit_of_measure: return int(magnitude) # Allow abbreviated unit such as K to mean KB for compatibility. if len(unit_of_measure) == 1 and unit_of_measure != 'B': unit_of_measure += 'B' return strutils.string_to_bytes('%s%s' % (magnitude, unit_of_measure), return_int=True) def _extract_details(self, root_cmd, root_details, lines_after): real_details = root_details if root_cmd == 'backing_file': # Replace it with the real backing file backing_match = self.BACKING_FILE_RE.match(root_details) if backing_match: real_details = backing_match.group(2).strip() elif root_cmd in ['virtual_size', 'cluster_size', 'disk_size']: # Replace it with the byte amount (if we can convert it) if root_details in ('None', 'unavailable'): real_details = 0 else: real_details = self._extract_bytes(root_details) elif root_cmd == 'file_format': real_details = real_details.strip().lower() elif root_cmd == 'snapshot_list': # Next line should be a header, starting with 'ID' if not lines_after or not lines_after.pop(0).startswith("ID"): msg = _("Snapshot list encountered but no header found!") raise ValueError(msg) real_details = [] # This is the sprintf pattern we will try to match # "%-10s%-20s%7s%20s%15s" # ID TAG VM SIZE DATE VM CLOCK (current header) while lines_after: line = lines_after[0] line_pieces = line.split() if len(line_pieces) != 6: break # Check against this pattern in the final position # "%02d:%02d:%02d.%03d" date_pieces = line_pieces[5].split(":") if len(date_pieces) != 3: break lines_after.pop(0) real_details.append({ 'id': line_pieces[0], 'tag': line_pieces[1], 'vm_size': line_pieces[2], 'date': line_pieces[3], 'vm_clock': line_pieces[4] + " " + line_pieces[5], }) return real_details def _parse(self, cmd_output): # Analysis done of qemu-img.c to figure out what is going on here # Find all points start with some chars and then a ':' then a newline # and then handle the results of those 'top level' items in a separate # function. # # TODO(harlowja): newer versions might have a json output format # we should switch to that whenever possible. # see: http://bit.ly/XLJXDX contents = {} lines = [x for x in cmd_output.splitlines() if x.strip()] while lines: line = lines.pop(0) top_level = self.TOP_LEVEL_RE.match(line) if top_level: root = self._canonicalize(top_level.group(1)) if not root: continue root_details = top_level.group(2).strip() details = self._extract_details(root, root_details, lines) contents[root] = details return contents oslo.utils-4.1.1/oslo_utils/uuidutils.py0000664000175000017500000000304413643050415020451 0ustar zuulzuul00000000000000# Copyright (c) 2012 Intel Corporation. # All Rights Reserved. # # 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. """ UUID related utilities and helper functions. .. versionadded:: 1.1 """ import uuid def generate_uuid(dashed=True): """Creates a random uuid string. :param dashed: Generate uuid with dashes or not :type dashed: bool :returns: string """ if dashed: return str(uuid.uuid4()) return uuid.uuid4().hex def _format_uuid_string(string): return (string.replace('urn:', '') .replace('uuid:', '') .strip('{}') .replace('-', '') .lower()) def is_uuid_like(val): """Returns validation of a value as a UUID. :param val: Value to verify :type val: string :returns: bool .. versionchanged:: 1.1.1 Support non-lowercase UUIDs. """ try: return str(uuid.UUID(val)).replace('-', '') == _format_uuid_string(val) except (TypeError, ValueError, AttributeError): return False oslo.utils-4.1.1/tools/0000775000175000017500000000000013643050530015011 5ustar zuulzuul00000000000000oslo.utils-4.1.1/tools/perf_test_mask_password.py0000664000175000017500000000277413643050415022327 0ustar zuulzuul00000000000000#!/usr/bin/env python # All Rights Reserved. # # 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. """Performance tests for mask_password. """ from __future__ import print_function import timeit from oslo_utils import strutils # A moderately sized input (~50K) string # http://paste.openstack.org/raw/155864/ # infile = '155864.txt' # Untruncated version of the above (~310K) # http://dl.sileht.net/public/payload.json.gz infile = 'large_json_payload.txt' with open(infile, 'r') as f: input_str = f.read() print('payload has %d bytes' % len(input_str)) for pattern in strutils._SANITIZE_PATTERNS_2['admin_pass']: print('\ntesting %s' % pattern.pattern) t = timeit.Timer( r"re.sub(pattern, r'\g<1>***\g<2>', payload)", """ import re payload = '''%s''' pattern = re.compile(r'''%s''') """ % (input_str, pattern.pattern)) print(t.timeit(1)) t = timeit.Timer( "strutils.mask_password('''" + input_str + "''')", "from oslo_utils import strutils", ) print(t.timeit(1)) oslo.utils-4.1.1/babel.cfg0000664000175000017500000000002013643050415015371 0ustar zuulzuul00000000000000[python: **.py] oslo.utils-4.1.1/LICENSE0000664000175000017500000002363613643050415014672 0ustar zuulzuul00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. oslo.utils-4.1.1/.coveragerc0000664000175000017500000000015713643050415015777 0ustar zuulzuul00000000000000[run] branch = True source = oslo_utils omit = oslo_utils/tests/* [report] ignore_errors = True precision = 2 oslo.utils-4.1.1/oslo.utils.egg-info/0000775000175000017500000000000013643050530017456 5ustar zuulzuul00000000000000oslo.utils-4.1.1/oslo.utils.egg-info/top_level.txt0000664000175000017500000000001313643050530022202 0ustar zuulzuul00000000000000oslo_utils oslo.utils-4.1.1/oslo.utils.egg-info/SOURCES.txt0000664000175000017500000000672313643050530021352 0ustar zuulzuul00000000000000.coveragerc .mailmap .stestr.conf .zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst babel.cfg bindep.txt lower-constraints.txt requirements.txt setup.cfg setup.py test-requirements.txt tox.ini doc/requirements.txt doc/source/conf.py doc/source/index.rst doc/source/contributor/index.rst doc/source/install/index.rst doc/source/reference/dictutils.rst doc/source/reference/encodeutils.rst doc/source/reference/eventletutils.rst doc/source/reference/excutils.rst doc/source/reference/fileutils.rst doc/source/reference/fixture.rst doc/source/reference/importutils.rst doc/source/reference/index.rst doc/source/reference/netutils.rst doc/source/reference/reflection.rst doc/source/reference/secretutils.rst doc/source/reference/specs_matcher.rst doc/source/reference/strutils.rst doc/source/reference/timeutils.rst doc/source/reference/units.rst doc/source/reference/uuidutils.rst doc/source/reference/versionutils.rst doc/source/user/history.rst doc/source/user/index.rst doc/source/user/timeutils.rst doc/source/user/usage.rst oslo.utils.egg-info/PKG-INFO oslo.utils.egg-info/SOURCES.txt oslo.utils.egg-info/dependency_links.txt oslo.utils.egg-info/not-zip-safe oslo.utils.egg-info/pbr.json oslo.utils.egg-info/requires.txt oslo.utils.egg-info/top_level.txt oslo_utils/__init__.py oslo_utils/_i18n.py oslo_utils/dictutils.py oslo_utils/encodeutils.py oslo_utils/eventletutils.py oslo_utils/excutils.py oslo_utils/fileutils.py oslo_utils/fixture.py oslo_utils/fnmatch.py oslo_utils/imageutils.py oslo_utils/importutils.py oslo_utils/netutils.py oslo_utils/reflection.py oslo_utils/secretutils.py oslo_utils/specs_matcher.py oslo_utils/strutils.py oslo_utils/timeutils.py oslo_utils/units.py oslo_utils/uuidutils.py oslo_utils/versionutils.py oslo_utils/locale/de/LC_MESSAGES/oslo_utils.po oslo_utils/locale/en_GB/LC_MESSAGES/oslo_utils.po oslo_utils/locale/fr/LC_MESSAGES/oslo_utils.po oslo_utils/tests/__init__.py oslo_utils/tests/base.py oslo_utils/tests/test_dictutils.py oslo_utils/tests/test_eventletutils.py oslo_utils/tests/test_excutils.py oslo_utils/tests/test_fileutils.py oslo_utils/tests/test_fixture.py oslo_utils/tests/test_fnmatch.py oslo_utils/tests/test_imageutils.py oslo_utils/tests/test_importutils.py oslo_utils/tests/test_netutils.py oslo_utils/tests/test_reflection.py oslo_utils/tests/test_secretutils.py oslo_utils/tests/test_specs_matcher.py oslo_utils/tests/test_strutils.py oslo_utils/tests/test_timeutils.py oslo_utils/tests/test_uuidutils.py oslo_utils/tests/test_versionutils.py oslo_utils/tests/tests_encodeutils.py oslo_utils/tests/fake/__init__.py oslo_utils/tests/fake/v2/__init__.py oslo_utils/tests/fake/v2/dummpy.py releasenotes/notes/add-reno-350f5f34f794fb5e.yaml releasenotes/notes/bump-up-port-range-f774a16336158339.yaml releasenotes/notes/drop-python27-support-f97f680651693b47.yaml releasenotes/notes/image-utils-handle-scientific-notation-6f65d46e9c8c8f8c.yaml releasenotes/notes/mask-dict-passwords-99357ffb7972fb0b.yaml releasenotes/notes/mask-password-patterns-f41524069b8ae488.yaml releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/newton.rst releasenotes/source/ocata.rst releasenotes/source/pike.rst releasenotes/source/queens.rst releasenotes/source/rocky.rst releasenotes/source/stein.rst releasenotes/source/train.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po tools/perf_test_mask_password.pyoslo.utils-4.1.1/oslo.utils.egg-info/not-zip-safe0000664000175000017500000000000113643050530021704 0ustar zuulzuul00000000000000 oslo.utils-4.1.1/oslo.utils.egg-info/PKG-INFO0000664000175000017500000000402413643050530020553 0ustar zuulzuul00000000000000Metadata-Version: 1.2 Name: oslo.utils Version: 4.1.1 Summary: Oslo Utility library Home-page: https://docs.openstack.org/oslo.utils/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/oslo.utils.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on ========== oslo.utils ========== .. image:: https://img.shields.io/pypi/v/oslo.utils.svg :target: https://pypi.org/project/oslo.utils/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/oslo.utils.svg :target: https://pypi.org/project/oslo.utils/ :alt: Downloads The oslo.utils library provides support for common utility type functions, such as encoding, exception handling, string manipulation, and time handling. * Free software: Apache license * Documentation: https://docs.openstack.org/oslo.utils/latest/ * Source: https://opendev.org/openstack/oslo.utils * Bugs: https://bugs.launchpad.net/oslo.utils * Release notes: https://docs.openstack.org/releasenotes/oslo.utils/ Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: Implementation :: CPython Requires-Python: >=3.6 oslo.utils-4.1.1/oslo.utils.egg-info/dependency_links.txt0000664000175000017500000000000113643050530023524 0ustar zuulzuul00000000000000 oslo.utils-4.1.1/oslo.utils.egg-info/requires.txt0000664000175000017500000000022613643050530022056 0ustar zuulzuul00000000000000pbr!=2.1.0,>=2.0.0 six>=1.10.0 iso8601>=0.1.11 oslo.i18n>=3.15.3 pytz>=2013.6 netaddr>=0.7.18 netifaces>=0.10.4 debtcollector>=1.2.0 pyparsing>=2.1.0 oslo.utils-4.1.1/oslo.utils.egg-info/pbr.json0000664000175000017500000000005613643050530021135 0ustar zuulzuul00000000000000{"git_version": "af89f70", "is_release": true}oslo.utils-4.1.1/HACKING.rst0000664000175000017500000000021613643050415015450 0ustar zuulzuul00000000000000oslo.utils Style Commandments ============================= Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ oslo.utils-4.1.1/PKG-INFO0000664000175000017500000000402413643050530014746 0ustar zuulzuul00000000000000Metadata-Version: 1.2 Name: oslo.utils Version: 4.1.1 Summary: Oslo Utility library Home-page: https://docs.openstack.org/oslo.utils/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/oslo.utils.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on ========== oslo.utils ========== .. image:: https://img.shields.io/pypi/v/oslo.utils.svg :target: https://pypi.org/project/oslo.utils/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/oslo.utils.svg :target: https://pypi.org/project/oslo.utils/ :alt: Downloads The oslo.utils library provides support for common utility type functions, such as encoding, exception handling, string manipulation, and time handling. * Free software: Apache license * Documentation: https://docs.openstack.org/oslo.utils/latest/ * Source: https://opendev.org/openstack/oslo.utils * Bugs: https://bugs.launchpad.net/oslo.utils * Release notes: https://docs.openstack.org/releasenotes/oslo.utils/ Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: Implementation :: CPython Requires-Python: >=3.6 oslo.utils-4.1.1/releasenotes/0000775000175000017500000000000013643050530016342 5ustar zuulzuul00000000000000oslo.utils-4.1.1/releasenotes/source/0000775000175000017500000000000013643050530017642 5ustar zuulzuul00000000000000oslo.utils-4.1.1/releasenotes/source/conf.py0000664000175000017500000002054213643050415021146 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # 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. # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'openstackdocstheme', 'reno.sphinxext', ] # openstackdocstheme options repository_name = 'openstack/oslo.utils' bug_project = 'oslo.utils' bug_tag = '' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. copyright = u'2016, oslo.utils Developers' # Release notes do not need a version in the title, they span # multiple versions. # The full version, including alpha/beta/rc tags. release = '' # The short X.Y version. version = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'openstackdocs' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'oslo.utilsReleaseNotesDoc' # -- Options for LaTeX output --------------------------------------------- # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'oslo.utilsReleaseNotes.tex', u'oslo.utils Release Notes Documentation', u'oslo.utils Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'oslo.utilsReleaseNotes', u'oslo.utils Release Notes Documentation', [u'oslo.utils Developers'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'oslo.utilsReleaseNotes', u'oslo.utils Release Notes Documentation', u'oslo.utils Developers', 'oslo.utilsReleaseNotes', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] oslo.utils-4.1.1/releasenotes/source/train.rst0000664000175000017500000000017613643050415021517 0ustar zuulzuul00000000000000========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train oslo.utils-4.1.1/releasenotes/source/rocky.rst0000664000175000017500000000022113643050415021520 0ustar zuulzuul00000000000000=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky oslo.utils-4.1.1/releasenotes/source/index.rst0000664000175000017500000000031113643050415021500 0ustar zuulzuul00000000000000=========================== oslo.utils Release Notes =========================== .. toctree:: :maxdepth: 1 unreleased train stein rocky queens pike ocata newton oslo.utils-4.1.1/releasenotes/source/locale/0000775000175000017500000000000013643050530021101 5ustar zuulzuul00000000000000oslo.utils-4.1.1/releasenotes/source/locale/en_GB/0000775000175000017500000000000013643050530022053 5ustar zuulzuul00000000000000oslo.utils-4.1.1/releasenotes/source/locale/en_GB/LC_MESSAGES/0000775000175000017500000000000013643050530023640 5ustar zuulzuul00000000000000oslo.utils-4.1.1/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po0000664000175000017500000000256313643050415026701 0ustar zuulzuul00000000000000# Andi Chandler , 2017. #zanata # Andi Chandler , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: oslo.utils Release Notes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-02-09 13:01+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-02-06 11:26+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en_GB\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "3.13.0" msgstr "3.13.0" msgid "3.18.0" msgstr "3.18.0" msgid "Bug Fixes" msgstr "Bug Fixes" msgid "Expanded range of allowed ports by adding 0 to valid number." msgstr "Expanded range of allowed ports by adding 0 to valid number." msgid "Introduce reno for deployer release notes." msgstr "Introduce Reno for developer release notes." msgid "Newton Series Release Notes" msgstr "Newton Series Release Notes" msgid "Ocata Series Release Notes" msgstr "Ocata Series Release Notes" msgid "Other Notes" msgstr "Other Notes" msgid "Pike Series Release Notes" msgstr "Pike Series Release Notes" msgid "Queens Series Release Notes" msgstr "Queens Series Release Notes" msgid "Unreleased Release Notes" msgstr "Unreleased Release Notes" msgid "oslo.utils Release Notes" msgstr "oslo.utils Release Notes" oslo.utils-4.1.1/releasenotes/source/ocata.rst0000664000175000017500000000023013643050415021460 0ustar zuulzuul00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata oslo.utils-4.1.1/releasenotes/source/newton.rst0000664000175000017500000000021613643050415021707 0ustar zuulzuul00000000000000============================= Newton Series Release Notes ============================= .. release-notes:: :branch: origin/stable/newton oslo.utils-4.1.1/releasenotes/source/_static/0000775000175000017500000000000013643050530021270 5ustar zuulzuul00000000000000oslo.utils-4.1.1/releasenotes/source/_static/.placeholder0000664000175000017500000000000013643050415023543 0ustar zuulzuul00000000000000oslo.utils-4.1.1/releasenotes/source/pike.rst0000664000175000017500000000021713643050415021326 0ustar zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike oslo.utils-4.1.1/releasenotes/source/queens.rst0000664000175000017500000000022313643050415021673 0ustar zuulzuul00000000000000=================================== Queens Series Release Notes =================================== .. release-notes:: :branch: stable/queens oslo.utils-4.1.1/releasenotes/source/stein.rst0000664000175000017500000000022113643050415021513 0ustar zuulzuul00000000000000=================================== Stein Series Release Notes =================================== .. release-notes:: :branch: stable/stein oslo.utils-4.1.1/releasenotes/source/_templates/0000775000175000017500000000000013643050530021777 5ustar zuulzuul00000000000000oslo.utils-4.1.1/releasenotes/source/_templates/.placeholder0000664000175000017500000000000013643050415024252 0ustar zuulzuul00000000000000oslo.utils-4.1.1/releasenotes/source/unreleased.rst0000664000175000017500000000014413643050415022524 0ustar zuulzuul00000000000000========================== Unreleased Release Notes ========================== .. release-notes:: oslo.utils-4.1.1/releasenotes/notes/0000775000175000017500000000000013643050530017472 5ustar zuulzuul00000000000000oslo.utils-4.1.1/releasenotes/notes/add-reno-350f5f34f794fb5e.yaml0000664000175000017500000000007213643050415024301 0ustar zuulzuul00000000000000--- other: - Introduce reno for deployer release notes. oslo.utils-4.1.1/releasenotes/notes/drop-python27-support-f97f680651693b47.yaml0000664000175000017500000000017713643050415026555 0ustar zuulzuul00000000000000--- upgrade: - | Support for Python 2.7 has been dropped. The minimum version of Python now supported is Python 3.6. oslo.utils-4.1.1/releasenotes/notes/image-utils-handle-scientific-notation-6f65d46e9c8c8f8c.yaml0000664000175000017500000000024013643050415032323 0ustar zuulzuul00000000000000--- fixes: - | qemu 4.1.0 output shifts to scientific notation at 1000mb, breaking oslo.utils. ``QemuImgInfo`` is now fixed to support this notation. oslo.utils-4.1.1/releasenotes/notes/mask-password-patterns-f41524069b8ae488.yaml0000664000175000017500000000047113643050415027101 0ustar zuulzuul00000000000000--- security: - This patch ensures we actually mask sensitive data, even if case doesn't match the static entry we have in the patterns. - It also ensures that some fancy names with a common base, but added number are actually taken care of. fixes: - https://bugs.launchpad.net/tripleo/+bug/1850843 oslo.utils-4.1.1/releasenotes/notes/mask-dict-passwords-99357ffb7972fb0b.yaml0000664000175000017500000000050513643050415026516 0ustar zuulzuul00000000000000--- security: - | This patch ensures that we mask sensitive data when masking dicts, even if the case doesn't match. This means the behaviour of mask_password and mask_dict_password is now the same. - | Additional password names were included from real world logs that contained sensitive information.oslo.utils-4.1.1/releasenotes/notes/bump-up-port-range-f774a16336158339.yaml0000664000175000017500000000011413643050415025743 0ustar zuulzuul00000000000000--- fixes: - Expanded range of allowed ports by adding 0 to valid number. oslo.utils-4.1.1/lower-constraints.txt0000664000175000017500000000122513643050415020111 0ustar zuulzuul00000000000000appdirs==1.3.0 Babel==2.3.4 bandit==1.4.0 coverage==4.0 ddt==1.0.1 debtcollector==1.2.0 eventlet==0.18.2 extras==1.0.0 fixtures==3.0.0 funcsigs==1.0.0 gitdb==0.6.4 GitPython==1.0.1 iso8601==0.1.11 keystoneauth1==3.4.0 linecache2==1.0.0 mccabe==0.2.1 netaddr==0.7.18 netifaces==0.10.4 os-client-config==1.28.0 oslo.config==5.2.0 oslo.i18n==3.15.3 oslotest==3.2.0 pbr==2.0.0 pyparsing==2.1.0 python-mimeparse==1.6.0 python-subunit==1.0.0 pytz==2013.6 PyYAML==3.12 requests==2.14.2 requestsexceptions==1.2.0 rfc3986==0.3.1 six==1.10.0 smmap==0.9.0 stestr==2.0.0 stevedore==1.20.0 testscenarios==0.4 testtools==2.2.0 traceback2==1.4.0 unittest2==1.1.0 wrapt==1.7.0 oslo.utils-4.1.1/setup.cfg0000664000175000017500000000212613643050530015473 0ustar zuulzuul00000000000000[metadata] name = oslo.utils summary = Oslo Utility library description-file = README.rst author = OpenStack author-email = openstack-discuss@lists.openstack.org home-page = https://docs.openstack.org/oslo.utils/latest/ python-requires = >=3.6 classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3 :: Only Programming Language :: Python :: Implementation :: CPython [files] packages = oslo_utils [compile_catalog] directory = oslo_utils/locale domain = oslo_utils [update_catalog] domain = oslo_utils output_dir = oslo_utils/locale input_file = oslo_utils/locale/oslo_utils.pot [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = oslo_utils/locale/oslo_utils.pot [egg_info] tag_build = tag_date = 0 oslo.utils-4.1.1/AUTHORS0000664000175000017500000001432213643050530014723 0ustar zuulzuul00000000000000Abhishek Chanda Abhishek Kekane Adam Harwell Akihiro Motoki Akihiro Motoki Albert White Alessio Ababilov Alex Gaynor Alexander Gorodnev Alexis Lee Alfredo Moralejo Alvaro Lopez Garcia Amaury Medeiros Amrith Kumar Amrith Kumar Andreas Jaeger Andreas Jaeger Angus Lees Ann Kamyshnikova Ben Nemec Ben Nemec Bin Zhou Brant Knudson BubaVV Cedric Brandily Chang Bo Guo ChangBo Guo(gcb) Christian Berendt Chuck Short Chuck Short Corey Bryant Costin Galan Cyril Roelandt Cyril Roelandt Cédric Jeanneret Dan Prince Dan Smith Dariusz Smigiel Davanum Srinivas Davanum Srinivas Davanum Srinivas David Stanek Derek Higgins Dina Belova Dirk Mueller Dmitry Mescheryakov Dolph Mathews Doug Hellmann Doug Hellmann Dougal Matthews Drew Varner Elena Ezhova Eoghan Glynn Eric Brown Eric Fried Eugene Kirpichov Flaper Fesp Flavio Percoco Gary Kotton Ghanshyam Mann Ghe Rivero Guang Yee Hanxi Liu Hervé Beraud Ian Wienand Ihar Hrachyshka Ildiko Ivan Kolodyazhny Jakub Libosvar James Carey Javeme Jay Pipes Jay S. Bryant Jeremy Stanley Jim Rollenhagen Joe Gordon Joe Gordon Johannes Erdfelt John Eckersberg John L. Villalovos John L. Villalovos Jordan Pittier Joshua Harlow Joshua Harlow Joshua Harlow Julien Danjou Kenneth Giusti Kevin Houdebert Lee Yarwood Lucas Alvares Gomes Mark Mielke Matthew Booth Mehdi Abaakouk Michael Wilson Monty Taylor Morgan Fainberg Nguyen Hung Phuong Noorul Islam K M Oleg Bondarev Oleksii Chuprykov Oleksii Zamiatin Omar Shykhkerimov OpenStack Release Bot Paul Belanger Radomir Dopieralski Rahul Nair Rajath Agasthya Raymond Pekowski Rick Harris Ronald Bradford RongzeZhu Ruby Loo Russell Bryant SandyWalsh Sean Dague Sean McGinnis Sean McGinnis Sergey Kraynev Sergey Lukjanov Sirisha Devineni Stanislav Kudriashev Stephen Finucane Steve Martinelli Steven Hardy Suraj Deshmukh Swapnil Kulkarni (coolsvap) Timur Sufiev Tony Breeds Tovin Seven Valeriy Ponomaryov Victor Sergeyev Victor Stinner Victor Stinner Vu Cong Tuan Wen Zhi Yu Yaguang Tang Young Yunhong, Jiang Zane Bitter ZhiQiang Fan ZhijunWei ZhongShengping Zhongyue Luo Zhongyue Luo avnish bhagyashris caoyuan changxun dharmendra ekudryashova gecong1973 hnyang howardlee jacky06 kgriffs lin-hua-cheng lingyongxu lvdongbing malei melissaml paul-carlton2 pengyuesheng pran1990 ricolin sridhargaddam sunyandi yenai zhangsong zhengyao1 oslo.utils-4.1.1/test-requirements.txt0000664000175000017500000000136513643050415020121 0ustar zuulzuul00000000000000# 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. hacking>=3.0,<3.1.0 # Apache-2.0 eventlet>=0.18.2,!=0.18.3,!=0.20.1,!=0.21.0,!=0.23.0 # MIT fixtures>=3.0.0 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD testtools>=2.2.0 # MIT oslotest>=3.2.0 # Apache-2.0 ddt>=1.0.1 # MIT stestr>=2.0.0 # Apache-2.0 # when we can require tox>= 1.4, this can go into tox.ini: # [testenv:cover] # deps = {[testenv]deps} coverage coverage!=4.4,>=4.0 # Apache-2.0 # used for oslotest cross-testing scripts oslo.config>=5.2.0 # Apache-2.0 # Bandit security code scanner bandit>=1.1.0,<1.6.0 # Apache-2.0 oslo.utils-4.1.1/.stestr.conf0000664000175000017500000000006313643050415016123 0ustar zuulzuul00000000000000[DEFAULT] test_path=./oslo_utils/tests top_path=./ oslo.utils-4.1.1/README.rst0000664000175000017500000000170713643050415015347 0ustar zuulzuul00000000000000======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/oslo.utils.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on ========== oslo.utils ========== .. image:: https://img.shields.io/pypi/v/oslo.utils.svg :target: https://pypi.org/project/oslo.utils/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/oslo.utils.svg :target: https://pypi.org/project/oslo.utils/ :alt: Downloads The oslo.utils library provides support for common utility type functions, such as encoding, exception handling, string manipulation, and time handling. * Free software: Apache license * Documentation: https://docs.openstack.org/oslo.utils/latest/ * Source: https://opendev.org/openstack/oslo.utils * Bugs: https://bugs.launchpad.net/oslo.utils * Release notes: https://docs.openstack.org/releasenotes/oslo.utils/ oslo.utils-4.1.1/bindep.txt0000664000175000017500000000054313643050415015657 0ustar zuulzuul00000000000000# This is a cross-platform list tracking distribution packages needed by tests; # see http://docs.openstack.org/infra/bindep/ for additional information. locales [platform:debian] python-dev [platform:dpkg] python-devel [platform:rpm] python3-all-dev [platform:ubuntu !platform:ubuntu-precise] python3-dev [platform:dpkg] python3-devel [platform:fedora] oslo.utils-4.1.1/ChangeLog0000664000175000017500000005622713643050530015437 0ustar zuulzuul00000000000000CHANGES ======= 4.1.1 ----- * Update hacking for Python3 * Use unittest.mock instead of third party mock * tox: Use upper-constraints for docs jobs 4.1.0 ----- * Add test to check scientific notation on disk virtual size * tests: Convert remaining tests to mock * Flatten test case * Fix regex to correctly recognize scientific notation with QemuImgInfo * imageutils: Report format specific details when using JSON output format 4.0.1 ----- * remove outdated header * Remove universal wheel configuration * reword releasenote for py27 support dropping 4.0.0 ----- * [ussuri][goal] Drop python 2.7 support and testing * tox: Trivial cleanup * Ignore releasenote cache within git untracked files * trivial: Move setup code into setUp helper 3.42.1 ------ * Verify the sanitize keys are lowered * Fix invalid escapes in regular expression strings * Ignore the .eggs directory * Make mask\_dict\_password case insensitive and add new patterns * Bump the openstackdocstheme extension to 1.20 3.42.0 ------ * Make mask\_password case insensitive, and add new patterns * tox: Keeping going with docs * Switch to Ussuri jobs * Update the constraints url * Support "qemu-img info" virtual size in QEMU 4.1 and later * Update master for stable/train 3.41.1 ------ * Add digestmod when using hmac * Add Python 3 Train unit tests * Cap Bandit below 1.6.0 and update Sphinx requirement * Replace git.openstack.org URLs with opendev.org URLs 3.41.0 ------ * OpenDev Migration Patch * Dropping the py35 testing * Update master for stable/stein 3.40.3 ------ * add python 3.7 unit test job * Update hacking version * Mask encryption\_key\_id * eventletutils: Optimise EventletEvent.clear() 3.40.2 ------ * Avoid double-setting event 3.40.1 ------ 3.40.0 ------ 3.39.1 ------ * Avoid calling eventlet.event.Event.reset() * Use template for lower-constraints 3.39.0 ------ * Fix race condition in eventletutils Event * Don't use monotonic on Python >=3.3 * Update mailinglist from dev to discuss * Support non-dict mappings in mask\_dict\_password 3.38.0 ------ * Expose eventlet Event wrapper class * Clean up .gitignore references to personal tools 3.37.1 ------ * Fix exception raise at rpdb session 3.37.0 ------ * Fix docstring formatting nit in uuidsentinel * UUID sentinel * Remove moxstubout usage * add lib-forward-testing-python3 test job * add python 3.6 unit test job * import zuul job settings from project-config * Update reno for stable/rocky * Remove extra copy.deepcopy 3.36.4 ------ * Handle non-string keys appropriately * Switch to stestr * Add release notes link to README 3.36.3 ------ * fix tox python3 overrides * Fix exception with secretutils 3.36.2 ------ * Add private\_key to the list of sanitized keys * Remove stale pip-missing-reqs tox test * Capitalize Oslo 3.36.1 ------ * Trivial: Update pypi url to new url * set default python to python3 * eventletutils: Fix behavior discrepency when reusing Events * Fix project name in user docs * add lower-constraints job * Clean old output before new doc builds * Remove sphinx settings from setup.cfg * Add bindep.txt file to prevent fallback to generic list * Updated from global requirements 3.36.0 ------ * Add -W for document build * Imported Translations from Zanata * Update links in README * Imported Translations from Zanata * Fix breaking unit tests due to iso8601 changes * Document specs\_matcher.py functions * Clean imports in code * Update reno for stable/queens * Updated from global requirements * Replace 'assertFalse(a in b)' with 'assertNotIn(a, b)' * Updated from global requirements * Updated from global requirements 3.35.0 ------ 3.34.0 ------ * Updated from global requirements * Cleanup test-requirements * improve docstring for last\_bytes() * Add method last\_bytes in fileutils * Follow the new PTI for document build * Add missing information in docstring of validate\_integer 3.33.0 ------ 3.32.0 ------ * Avoid tox\_install.sh for constraints support * Updated from global requirements * Remove setting of version/release from releasenotes * Updated from global requirements 3.31.0 ------ * Add method to compute a file's checksum to fileutils * Imported Translations from Zanata * Add method validate\_integer 3.30.0 ------ * Updated from global requirements * Use six.binary\_type to point to the right type * Add a mixed mode parser to string\_to\_bytes * Updated from global requirements * Fix some reST field lists in docstrings 3.29.0 ------ * Imported Translations from Zanata * Add method to escape ipv6 ip addresses * Updated from global requirements * Update reno for stable/pike * Updated from global requirements * Prevent deprecation error messages from pkg\_resources 3.28.0 ------ * Update URLs in documents according to document migration 3.27.0 ------ * rearrange existing documentation to fit the new standard layout * switch from oslosphinx to openstackdocstheme * Updated from global requirements * Updated from global requirements 3.26.0 ------ * Updated from global requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements 3.25.1 ------ * Updated from global requirements * Remove split conversion to tuple * Updated from global requirements 3.25.0 ------ * Remove log translations 3.24.0 ------ * Use Sphinx 1.5 warning-is-error * Updated from global requirements * Adding a check of string type for hmacs 3.23.0 ------ * Updated from global requirements * [Fix gate]Update test requirement * Add missing documentation for secretutils * Updated from global requirements * Updated from global requirements * Update reno for stable/ocata 3.22.0 ------ * Remove references to Python 3.4 * Added the token 'encrypted\_key' to mask\_password list * Add Constraints support 3.21.0 ------ * Allow 'get\_all\_class\_names' to pass kwargs 3.20.0 ------ * Add toggle 'dashed' to 'generate\_uuid' function * Show team and repo badges on README 3.19.0 ------ * Updated from global requirements * Improve eventlet check when selecting Event backend * Updated from global requirements 3.18.0 ------ * Add option to not truncate built-ins * Create dictutils and add 'flatten\_dict\_to\_keypairs' * Updated from global requirements * Add reno for release notes management * Allow scoped ipv6 addresses * Trivial fixes to the usage doc * Add threading<->eventlet compatible Event * Updated from global requirements * [TrivialFix] Replace 'assertEqual(None, ...)' with 'assertIsNone(...)' 3.17.0 ------ * Add method is\_valid\_mac * Add \_\_ne\_\_ built-in function * Make method import\_versioned\_module work * Change assertTrue(isinstance()) by optimal assert * doc: Fix docstring of method bool\_from\_string * Change assertTrue(isinstance()) by optimal assert * Add method is\_valid\_boolstr * Add method is\_valid\_ipv6\_cidr * Updated from global requirements * Updated from global requirements * Add missing specs\_matcher documentation * Update homepage with developer documentation page * Updated from global requirements * Updated from global requirements * Add utils for validating and splitting quotes * Updated from global requirements * Extend specs matcher to support ">" and "<" * Remove discover from test-requirements 3.16.0 ------ * Fix mask\_dict\_password for non string/dict type key in dict * Restore operator * More unit tests for specs matcher * Imported Translations from Zanata * Add Python 3.5 classifier and venv * Use an actual well defined parser for spec matching * Remove unused LOG to keep code clean * Updated from global requirements 3.15.0 ------ * Add basic docstrings to stopwatch has\_started/stopped methods * Make mask\_dict\_password consistent with mask\_password * Updated from global requirements * improve tests for mask\_password and mask\_dict\_password * Simplify boolean expression in executils.py 3.14.0 ------ * Support json format output from qemu command * Fix flake8 issues * Use is\_valid\_ipv4 in get\_ipv6\_addr\_by\_EUI64 * Imported Translations from Zanata 3.13.0 ------ * Allow assigning "0" to port 3.12.0 ------ * Updated from global requirements * Fix method split\_path's docstring 'versionadded' * Updated from global requirements * Updated from global requirements * Avoid catching generic exception * Remove method total\_seconds in timeuitls * Fix is\_valid\_cidr raises TypeError 3.11.0 ------ * Trivial: ignore openstack/common in flake8 exclude list * Move method split\_path into oslo.utils 3.10.0 ------ * Imported Translations from Zanata * Updated from global requirements * Move nova extra\_specs\_ops to oslo.utils 3.9.0 ----- * Imported Translations from Zanata * Provide single step check if eventlet is monkey\_patched * Add method is\_valid\_cidr to netutils * Updated from global requirements * Updated from global requirements * Add importutils.import\_any method * Add excutils.exception\_filter * Explicitly exclude tests from bandit scan * Add CHAPPASSWORD to list of sanitize keys * Enable bandit in gate * Updated from global requirements 3.7.0 ----- * Add method check\_string\_length * Add missing versionchanged for configdrive 3.6.0 ----- 3.5.0 ----- * Updated from global requirements * Imported Translations from Zanata * Remove bandit.yaml in favor of defaults * Updated from global requirements * Narrow mock for getfilesystemencoding * Update translation setup * Revert "Use assertTrue/False instead of assertEqual(T/F)" * Updated from global requirements * Updated from global requirements * Add excutils.save\_and\_reraise\_exception force\_reraise + capture * Add encodeutils.to\_utf8() function * Create secretutils and include 'constant\_time\_compare' function * Fix coverage * Imported Translations from Zanata * Updated from global requirements 3.4.0 ----- * Updated from global requirements * Use assertTrue/False instead of assertEqual(T/F) * Add a mechanism to mask passwords in dictionaries * Add "configdrive" to the list of keys used by mask\_password() * assertIsNone(val) instead of assertEqual(None,val) 3.3.0 ----- * Fix DeprecationWarning when call method delta\_seconds * fix fnmatch.filter in non-posix system * fix fileutils ut code random failure * Add missing doc index for imageutils and fnmatch * re-implement thread safe fnmatch * Fix the bug of can't get the desired image info 3.2.0 ----- * Revert "Move netifaces to extras" * Remove Babel from requirements 3.1.0 ----- * Remove duplicated profiles section from bandit.yaml * Allow get\_class\_name to accept bound method and class method * deprecate timeutils.total\_seconds() * Move imageutils from oslo-incubator to oslo.utils * add comment explaining why we don't want extra values passed to mask\_password * networkutils: drop python 2.6 support * Remove 'MANIFEST.in' * Move netifaces to extras 3.0.0 ----- * Add a bandit target to tox.ini * Updated from global requirements * Remove python 2.6 classifier * Fix wrong bug tracking link * Remove python 2.6 and cleanup tox.ini * Refactor Port number validation * Add useful 'time\_it' decorator 2.8.0 ----- * Fix get\_class\_name() on Python 3 * Added ICMP 'type' and 'code' checking capability to 'netutils' module * Updated from global requirements * Imported Translations from Zanata * comment in write\_to\_tempfile * Use versionadded and versionchanged in doc 2.7.0 ----- * Expose function signature fetching function * Allow 'forever\_retry\_uncaught\_exceptions' to take in different defaults * Write document for each unit of oslo\_utils.utils * Fix usage of "deprecated" markup in docstrings * Just use 'exception\_to\_unicode' to handle exception to string * Add 'secret' to sensitive keys 2.6.0 ----- * Fix coverage configuration and execution * Use a stopwatch in 'forever\_retry\_uncaught\_exceptions' * No need for Oslo Incubator Sync * Make forever\_retry\_uncaught\_exceptions handle its own failures * Ensure stopwatch \_\_enter\_\_, \_\_exit\_\_ are in docs * Add some timeutils stop watch examples * Imported Translations from Zanata * Move 'history' -> release notes section * Fix bad acting classes and 'is\_bound\_method' check * Change ignore-errors to ignore\_errors * Updated from global requirements * If 'bool\_from\_string' provided a boolean just return it * Imported Translations from Zanata * only capture the ImportError when importing * Add 'token' to list of fields to be santized by mask\_password 2.5.0 ----- * Updated from global requirements * Imported Translations from Transifex * Updated from global requirements * Updated from global requirements 2.4.0 ----- 2.3.0 ----- * Updated from global requirements * Update docstring on stop watch to reflect monotonic lib. usage * Updated from global requirements * flake8 - remove unused rules * Bump monotonic to 0.3 to remove exception catching on import * Provide a common exception caused by base class * Imported Translations from Transifex * Allow access to reflection 'get\_members' * Updated from global requirements 2.2.0 ----- * Imported Translations from Transifex * Updated from global requirements 2.1.0 ----- * Imported Translations from Transifex * Updated from global requirements * Adding checking around the monotonic import 2.0.0 ----- * Updated from global requirements * Updated from global requirements * Add oslo.config to test requirements * Remove oslo namespace package * Updated from global requirements 1.9.0 ----- * Updated from global requirements * versionutils: add version convert helper methods * Imported Translations from Transifex * Add write\_to\_tempfile back to fileutils * Use monotonic library to avoid finding monotonic time function * Fix exception\_to\_unicode() for oslo\_i18n Message 1.8.0 ----- * Add fileutils to oslo\_utils * Updated from global requirements * Add tox target to find missing requirements 1.7.0 ----- * Updated from global requirements * Updated from global requirements * Switch badges from 'pypip.in' to 'shields.io' * timeutils: fix newer/older comparison with TZ aware datetime * Replace parse\_strtime with parse\_isotime in older/newer 1.6.0 ----- * Add pytz to requirements * Deprecate strtime * Imported Translations from Transifex * timeutils: utcnow() can return a value with a timezone * Add 'raise\_with\_cause' chaining helper to excutils * timeutils: deprecate isotime() * timeutils: make marshall timezone aware * Advertise support for Python3.4 / Remove support for Python 3.3 * Updated from global requirements * Add exception\_to\_unicode() function * Remove run\_cross\_tests.sh * Imported Translations from Transifex * Move versionutils into place and remove deprecation tools * Denote monotonic import ordering + usage 1.5.0 ----- * Add liberty release name to versionutils * Expose opts entry point for version\_utils * Switch from oslo.config to oslo\_config * Remove oslo.log code and clean up versionutils API * Remove code that moved to oslo.i18n * Enhance versionutils.deprecated to work with classes * Add Kilo release name to versionutils * Allow deprecated decorator to specify no plan for removal * Uncap library requirements for liberty * Add JUNO as a target to versionutils module * Add missing reflection + uuidutils docs * timeutils: avoid passing leap second to datetime * Add pypi download + version badges * Cleanup README.rst and setup.cfg * pep8: fixed multiple violations * Use oslotest instead of common test module * Use hacking import\_exceptions for gettextutils.\_ * fixed typos * Fix violations of H302:import only modules * Adds decorator to deprecate functions and methods * Remove vim header * Add \`versionutils\` for version compatibility checks * Update hacking setting * Updated from global requirements * Imported Translations from Transifex * Clean up TestIsIPv6Enabled * Fix test\_netutils: stop patches * Add a new string to the list of masked patterns * Provide common \`fetch\_current\_thread\_functor\` function * Imported Translations from Transifex 1.4.0 ----- * Add a stopwatch + split for duration(s) * Allow providing a logger to save\_and\_reraise\_exception * Updated from global requirements * Utility API to generate EUI-64 IPv6 address 1.3.0 ----- * Add a eventlet utils helper module * Add microsecond support to iso8601\_from\_timestamp * add dependency warning to requirements.txt * Updated from global requirements * Update Oslo imports to remove namespace package * Add TimeFixture * Add microsecond support to timeutils.utcnow\_ts() * Make setup.cfg packages include oslo.utils 1.2.1 ----- * Return LOCALHOST if no default interface * fix link to bug tracker in README 1.2.0 ----- * Improve performance of strutils.mask\_password * Move files out of the namespace package * Add method is\_valid\_port in netutils * Support non-lowercase uuids in is\_uuid\_like * Add 'secret\_uuid' in \_SANITIZE\_KEYS for strutils * Imported Translations from Transifex * Workflow documentation is now in infra-manual 1.1.0 ----- * Improve error reporting in \_get\_my\_ipv4\_address() * Add get\_my\_ip() * Updated from global requirements * Add 'auth\_password' in \_SANITIZE\_KEYS for strutils * Updated from global requirements * Activate pep8 check that \_ is imported * Add uuidutils to oslo.utils * Add pbr to installation requirements * Updated from global requirements * Add is\_int\_like() function * Hide auth\_token and new\_pass * Imported Translations from Transifex * Add history/changelog to docs * Imported Translations from Transifex * Support building wheels (PEP-427) * Imported Translations from Transifex * Improve docstrings for IP verification functions * Imported Translations from Transifex * Add ip address validation * Fix how it appears we need to use mock\_anything to avoid 'self' errors * Updated from global requirements * Move over a reflection module that taskflow uses * Make safe\_encode func case-insensitive * Enable mask\_password to handle byte code strings * Updated from global requirements 1.0.0 ----- * Imported Translations from Transifex * Add the ability to extract the query params from a urlsplit * Work toward Python 3.4 support and testing * warn against sorting requirements * Remove unused dependency on oslo.config * Updated from global requirements * Just use int(BOOL) to convert to 1 or 0 * Re-enable \_import\* hidden methods in import\_utils 0.2.0 ----- * Make strutils.mask\_password more secure * New public API for mask\_password ported from incubator * Imported Translations from Transifex 0.1.1 ----- * Make return type from urlsplit private * Add API docs and clean up other docs * Make the i18n integration module private * Cleaning up index.rst file * export only try\_import in \_\_all\_\_ * Switch to oslo.i18n and remove any dependency on oslo-incubator * Move units into oslo.utils * Switch to standard python logging * Setup for translation * Split strutils into 2 different modules * Rename network\_utils into netutils * get pep8 working * Get the tox tests working * exported from oslo-incubator by graduate.sh * Fixed a new pep8 error and a small typo * Set pbr 'warnerrors' option for doc build * fixed typos found by RETF rules * Use moxstubout and mockpatch from oslotest * Remove ValueError when accessing sys.modules * Enable configuring tcp keepalive * Avoid raising index error when no host * Remove str() from LOG.\* and exceptions * Remove import workaround of SplitResult * Use oslotest instead of common test module * Partial fix of test\_strutils.py on Python 3 * Fix safe\_encode(): return bytes on Python 3 * urlsplit issues with IPv6 addresses in python26 * save\_and\_reraise\_exception: make logging respect the reraise parameter * strutils: Allow safe\_{encode,decode} to take bytes as input * Fix import order in test\_excutils * Update oslo log messages with translation domains * Implements SI/IEC unit system conversion to bytes * Add basic Python 3 tests * py3kcompat: remove * Deleted duplicated method in cliutils * strutils bool\_from\_string, allow specified default * Utilizes assertIsNone and assertIsNotNone * Fix spelling errors in comments * Use hacking import\_exceptions for gettextutils.\_ * Correct invalid docstrings * Fix a bug in safe\_encode where it returns a bytes object in py3 * Fix typo in parameter documentation (timeutils) * Avoid TypeError in is\_older\_than, is\_newer\_than * Remove vim header * Use py3kcompat urlutils functions instead of urlparse * Add helper method total\_seconds in timeutils.py * Do not name variables as builtins * Use six.text\_type instead of unicode function in tests * Fix typos in oslo * Adjust import order according to PEP8 imports rule * python3: use six.text\_types for unicode() * Don't shadow str * Fix timeutils.set\_override\_time not defaulting to current wall time * Fix misused assertTrue in unit tests * Optimize timeutils.utcnow\_ts() * excutils: replace unicode by six.u * excutils: use six.reraise to re-raise * Replace using tests.utils part2 * Bump hacking to 0.7.0 * Replace using tests.utils with openstack.common.test * BaseException.message is deprecated since Python 2.6 * Enable H302 hacking check * Add conditional exception reraise * python3: Add python3 compatibility * Make AMQP based RPC consumer threads more robust * Add network\_utils.urlsplit * Remove useless logging in networks\_utils * python3: Fix traceback while running python3 * Refactors to\_bytes * Add slugify to strutils * Enable hacking H404 test * Added common code into fileutils and strutils * Enable hacking H402 test * Enable hacking H403 test * Enable hacking H201 test * Add 't', 'y', and \`strict\` to \`bool\_from\_string\` * Handle ints passed to \`boolean\_from\_string\` * Removes leading zeros on integers in test\_timeutils * Convert unicode strings for python3 portability * Do not import openstack.common.log in strutils * Improve Python 3.x compatibility * Replaces standard logging with common logging * Removes unused imports in the tests module * Fix Copyright Headers - Rename LLC to Foundation * support ISO 8601 micro-second precision * Decode / Encode string utils for openstack * timeutils: considers that now is soon * Replace direct use of testtools BaseTestCase * Use testtools as test base class * Import timeutils.is\_soon from keystoneclient * UTC ISO8601 from timestamp * Implement importutils.try\_import * Use basestring instead of str for type check * Make time comparison functions accept strings * Fix timezone handling in timeutils tests * Rename utils.py to strutils.py * Provide i18n to those messages without \_() * Make project pyflakes clean * Account for tasks duration in LoopingCall delay * Convenience wrapper for datetime.timedelta.total\_seconds() * Added is\_newer\_than function * Extracted parse\_host\_port into network\_utils * Normalize\_time() always return naive object * Use pep8 v1.3.3 * Don't trap then re-raise ImportError * Fix spelling typos * Support for marshalling datetime while preserving microseconds * Remove unused imports * fix bug lp:1019348,update openstack-common to support pep8 1.3 * Use strtime() in to\_primitive() for datetime objs * Improve exception from importutils.import\_class() * Update common code to support pep 1.3. bug 1014216 * add import\_object\_ns function * add more realistic unit tests for importutils * Fix utcnow\_ts to return UTC timestamp * Add nova time util functions to timeutils * Replace datetime.utcnow with timeutils.utcnow * Remove common.exception from common.importutils * Add save\_and\_reraise\_exception() * Update exception from importutils.import\_class() * Change behavior in utils.import\_object() * Create openstack.common.timeutils * Initial skeleton project oslo.utils-4.1.1/tox.ini0000664000175000017500000000273213643050415015172 0ustar zuulzuul00000000000000[tox] minversion = 3.1 envlist = py37,pep8 ignore_basepython_conflict = true [testenv] basepython = python3 deps = -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt commands = stestr run --slowest {posargs} [testenv:pep8] commands = flake8 # Run security linter bandit -r oslo_utils -x tests -n5 [testenv:venv] commands = {posargs} [testenv:docs] whitelist_externals = rm deps = -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/doc/requirements.txt commands = rm -fr doc/build sphinx-build -W --keep-going -b html doc/source doc/build/html [testenv:cover] commands = python setup.py test --coverage --coverage-package-name=oslo_utils --testr-args='{posargs}' [testenv:bandit] commands = bandit -r oslo_utils -x tests -n5 [flake8] # E731 skipped as assign a lambda expression # W504 line break after binary operator ignore = E123,E731,H405,W504 show-source = True exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,__init__.py [testenv:releasenotes] whitelist_externals = rm deps = {[testenv:docs]deps} commands = rm -rf releasenotes/build sphinx-build -a -E -W -d releasenotes/build/doctrees --keep-going -b html releasenotes/source releasenotes/build/html [testenv:lower-constraints] deps = -c{toxinidir}/lower-constraints.txt -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt oslo.utils-4.1.1/.mailmap0000664000175000017500000000013013643050415015266 0ustar zuulzuul00000000000000# Format is: # # oslo.utils-4.1.1/CONTRIBUTING.rst0000664000175000017500000000103513643050415016313 0ustar zuulzuul00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps in this page: http://docs.openstack.org/infra/manual/developers.html Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: http://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad, not GitHub: https://bugs.launchpad.net/oslo.utils oslo.utils-4.1.1/doc/0000775000175000017500000000000013643050530014416 5ustar zuulzuul00000000000000oslo.utils-4.1.1/doc/requirements.txt0000664000175000017500000000057313643050415017711 0ustar zuulzuul00000000000000# 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. # this is required for the docs build jobs sphinx>=1.8.0,!=2.1.0 # BSD openstackdocstheme>=1.20.0 # Apache-2.0 reno>=2.5.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD oslo.utils-4.1.1/doc/source/0000775000175000017500000000000013643050530015716 5ustar zuulzuul00000000000000oslo.utils-4.1.1/doc/source/reference/0000775000175000017500000000000013643050530017654 5ustar zuulzuul00000000000000oslo.utils-4.1.1/doc/source/reference/secretutils.rst0000664000175000017500000000016413643050415022757 0ustar zuulzuul00000000000000============= secretutils ============= .. automodule:: oslo_utils.secretutils :members: constant_time_compare oslo.utils-4.1.1/doc/source/reference/eventletutils.rst0000664000175000017500000000014613643050415023320 0ustar zuulzuul00000000000000=============== eventletutils =============== .. automodule:: oslo_utils.eventletutils :members: oslo.utils-4.1.1/doc/source/reference/units.rst0000664000175000017500000000010613643050415021547 0ustar zuulzuul00000000000000======= units ======= .. automodule:: oslo_utils.units :members: oslo.utils-4.1.1/doc/source/reference/importutils.rst0000664000175000017500000000013613643050415023003 0ustar zuulzuul00000000000000============= importutils ============= .. automodule:: oslo_utils.importutils :members: oslo.utils-4.1.1/doc/source/reference/fixture.rst0000664000175000017500000000012613643050415022075 0ustar zuulzuul00000000000000============= fixture ============= .. automodule:: oslo_utils.fixture :members: oslo.utils-4.1.1/doc/source/reference/strutils.rst0000664000175000017500000000012213643050415022274 0ustar zuulzuul00000000000000========== strutils ========== .. automodule:: oslo_utils.strutils :members: oslo.utils-4.1.1/doc/source/reference/dictutils.rst0000664000175000017500000000012613643050415022413 0ustar zuulzuul00000000000000=========== dictutils =========== .. automodule:: oslo_utils.dictutils :members: oslo.utils-4.1.1/doc/source/reference/fileutils.rst0000664000175000017500000000013313643050415022405 0ustar zuulzuul00000000000000============= fileutils ============= .. automodule:: oslo_utils.fileutils :members: oslo.utils-4.1.1/doc/source/reference/versionutils.rst0000664000175000017500000000014213643050415023153 0ustar zuulzuul00000000000000============== versionutils ============== .. automodule:: oslo_utils.versionutils :members: oslo.utils-4.1.1/doc/source/reference/netutils.rst0000664000175000017500000000012213643050415022252 0ustar zuulzuul00000000000000========== netutils ========== .. automodule:: oslo_utils.netutils :members: oslo.utils-4.1.1/doc/source/reference/index.rst0000664000175000017500000000044213643050415021517 0ustar zuulzuul00000000000000============= API Reference ============= .. toctree:: :maxdepth: 2 dictutils encodeutils eventletutils excutils fileutils fixture importutils netutils reflection secretutils specs_matcher strutils timeutils units uuidutils versionutils oslo.utils-4.1.1/doc/source/reference/reflection.rst0000664000175000017500000000013213643050415022536 0ustar zuulzuul00000000000000============ reflection ============ .. automodule:: oslo_utils.reflection :members: oslo.utils-4.1.1/doc/source/reference/uuidutils.rst0000664000175000017500000000012613643050415022436 0ustar zuulzuul00000000000000=========== uuidutils =========== .. automodule:: oslo_utils.uuidutils :members: oslo.utils-4.1.1/doc/source/reference/timeutils.rst0000664000175000017500000000017713643050415022434 0ustar zuulzuul00000000000000=========== timeutils =========== .. automodule:: oslo_utils.timeutils :members: :special-members: __enter__, __exit__ oslo.utils-4.1.1/doc/source/reference/encodeutils.rst0000664000175000017500000000013613643050415022726 0ustar zuulzuul00000000000000============= encodeutils ============= .. automodule:: oslo_utils.encodeutils :members: oslo.utils-4.1.1/doc/source/reference/excutils.rst0000664000175000017500000000012213643050415022243 0ustar zuulzuul00000000000000========== excutils ========== .. automodule:: oslo_utils.excutils :members: oslo.utils-4.1.1/doc/source/reference/specs_matcher.rst0000664000175000017500000000014413643050415023227 0ustar zuulzuul00000000000000============== specs_matcher ============== .. automodule:: oslo_utils.specs_matcher :members: oslo.utils-4.1.1/doc/source/conf.py0000775000175000017500000000501713643050415017225 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # 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 sys.path.insert(0, os.path.abspath('../..')) # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'openstackdocstheme' ] # openstackdocstheme options repository_name = 'openstack/oslo.utils' bug_project = 'oslo.utils' bug_tag = '' # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'oslo.utils' copyright = u'2014, OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ["."] # html_theme = '_theme' # html_static_path = ['static'] html_theme = 'openstackdocs' # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', '%s.tex' % project, u'%s Documentation' % project, u'OpenStack Foundation', 'manual'), ] # Example configuration for intersphinx: refer to the Python standard library. #intersphinx_mapping = {'http://docs.python.org/': None} oslo.utils-4.1.1/doc/source/index.rst0000664000175000017500000000077513643050415017572 0ustar zuulzuul00000000000000====================================== Welcome to oslo.utils's documentation! ====================================== The `Oslo`_ utils library provides support for common utility type functions, such as encoding, exception handling, string manipulation, and time handling. .. toctree:: :maxdepth: 1 install/index user/index reference/index contributor/index .. rubric:: Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` .. _Oslo: https://wiki.openstack.org/wiki/Oslo oslo.utils-4.1.1/doc/source/install/0000775000175000017500000000000013643050530017364 5ustar zuulzuul00000000000000oslo.utils-4.1.1/doc/source/install/index.rst0000664000175000017500000000030613643050415021226 0ustar zuulzuul00000000000000============ Installation ============ At the command line:: $ pip install oslo.utils Or, if you have virtualenvwrapper installed:: $ mkvirtualenv oslo.utils $ pip install oslo.utilsoslo.utils-4.1.1/doc/source/contributor/0000775000175000017500000000000013643050530020270 5ustar zuulzuul00000000000000oslo.utils-4.1.1/doc/source/contributor/index.rst0000664000175000017500000000011713643050415022132 0ustar zuulzuul00000000000000============ Contributing ============ .. include:: ../../../CONTRIBUTING.rst oslo.utils-4.1.1/doc/source/user/0000775000175000017500000000000013643050530016674 5ustar zuulzuul00000000000000oslo.utils-4.1.1/doc/source/user/index.rst0000664000175000017500000000033213643050415020535 0ustar zuulzuul00000000000000================ Using oslo.utils ================ .. toctree:: :maxdepth: 2 usage timeutils .. history contains a lot of sections, toctree with maxdepth 1 is used. .. toctree:: :maxdepth: 1 history oslo.utils-4.1.1/doc/source/user/history.rst0000664000175000017500000000004013643050415021123 0ustar zuulzuul00000000000000.. include:: ../../../ChangeLog oslo.utils-4.1.1/doc/source/user/timeutils.rst0000664000175000017500000000467013643050415021456 0ustar zuulzuul00000000000000=========== timeutils =========== Using a stopwatch (as a context manager) ---------------------------------------- :: >>> from oslo_utils import timeutils >>> import time >>> >>> def slow_routine(delay): ... def i_am_slow(): ... time.sleep(delay) ... return i_am_slow ... >>> >>> half_sec_func = slow_routine(0.5) >>> with timeutils.StopWatch() as w: ... half_sec_func() ... >>> print(w.elapsed()) 0.500243999995 Manually using a stopwatch -------------------------- :: >>> from oslo_utils import timeutils >>> import time >>> w = timeutils.StopWatch() >>> w.start() >>> time.sleep(0.1) >>> time.sleep(0.1) >>> time.sleep(0.1) >>> time.sleep(0.1) >>> w.stop() >>> w.elapsed() 13.96467600017786 Tracking durations with a stopwatch ----------------------------------- :: >>> from oslo_utils import timeutils >>> w = timeutils.StopWatch(duration=10) >>> w.start() >>> w.elapsed() 2.023942000232637 >>> w.leftover() 4.648160999640822 >>> w.leftover() 3.5522090001031756 >>> w.leftover() 3.0481000002473593 >>> w.leftover() 2.1918740002438426 >>> w.leftover() 1.6966530000790954 >>> w.leftover() 1.1202940000221133 >>> w.leftover() 0.0 >>> w.expired() True Tracking and splitting with a stopwatch --------------------------------------- :: >>> from oslo_utils import timeutils >>> w = timeutils.StopWatch() >>> w.start() >>> w.split() Split(elapsed=3.02423300035, length=3.02423300035) >>> w.split() Split(elapsed=6.44820600003, length=3.42397299968) >>> w.split() Split(elapsed=7.9678720003, length=1.51966600027) >>> w.splits (Split(elapsed=3.02423300035, length=3.02423300035), Split(elapsed=6.44820600003, length=3.42397299968), Split(elapsed=7.9678720003, length=1.51966600027)) >>> w.stop() >>> w.elapsed() 16.799759999848902 >>> w.splits (Split(elapsed=3.02423300035, length=3.02423300035), Split(elapsed=6.44820600003, length=3.42397299968), Split(elapsed=7.9678720003, length=1.51966600027)) oslo.utils-4.1.1/doc/source/user/usage.rst0000664000175000017500000000027613643050415020541 0ustar zuulzuul00000000000000======= Usage ======= To use oslo.utils in a project, import the individual module you need. For example:: from oslo_utils import strutils slug = strutils.to_slug('input value')