Use versionadded and versionchanged in doc

Document in which version new types and functions were added using
".. versionadded:: x.y". Document changes using
".. versionchanged:: x.y."

For new modules, add the versionadded tag in the module top
docstring, not on each type/function.

Add fileutils to documentation. The doc part was forgotten during the
graduation.

Add docstrings to convert versions of versionutils.

I used "git blame" + "git tag --contains=SHA1" to find these version,
and then I checked manually each version.

Change-Id: Ia2f00aa29eb36410a49fc1d350896a569a7737a1
This commit is contained in:
Victor Stinner 2015-10-15 14:57:35 +02:00
parent 6aa24675e8
commit 7f57de5bb2
14 changed files with 172 additions and 10 deletions

View File

@ -0,0 +1,7 @@
=============
fileutils
=============
.. automodule:: oslo_utils.fileutils
:members:

View File

@ -21,6 +21,7 @@ API Documentation
api/encodeutils api/encodeutils
api/eventletutils api/eventletutils
api/excutils api/excutils
api/fileutils
api/fixture api/fixture
api/importutils api/importutils
api/netutils api/netutils

View File

@ -104,6 +104,8 @@ def exception_to_unicode(exc):
If the exception message is a bytes strings, try to decode it from UTF-8 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 (superset of ASCII), from the locale encoding, or fallback to decoding it
from ISO-8859-1 (which never fails). from ISO-8859-1 (which never fails).
.. versionadded:: 1.6
""" """
msg = None msg = None
if six.PY2: if six.PY2:

View File

@ -14,6 +14,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
"""
Eventlet utils helper module.
.. versionadded:: 1.3
"""
import threading import threading
import warnings import warnings
@ -35,6 +41,13 @@ _ALL_PATCH = frozenset(['__builtin__', 'MySQLdb', 'os',
def fetch_current_thread_functor(): 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 # Until https://github.com/eventlet/eventlet/issues/172 is resolved
# or addressed we have to use complicated workaround to get a object # or addressed we have to use complicated workaround to get a object
# that will not be recycled; the usage of threading.current_thread() # that will not be recycled; the usage of threading.current_thread()

View File

@ -45,6 +45,8 @@ class CausedByException(Exception):
should itself be an exception instance, this is useful for should itself be an exception instance, this is useful for
creating a chain of exceptions for versions of python where creating a chain of exceptions for versions of python where
this is not yet implemented/supported natively. this is not yet implemented/supported natively.
.. versionadded:: 2.4
""" """
def __init__(self, message, cause=None): def __init__(self, message, cause=None):
super(CausedByException, self).__init__(message) super(CausedByException, self).__init__(message)
@ -126,6 +128,8 @@ def raise_with_cause(exc_cls, message, *args, **kwargs):
exceptions constructor. exceptions constructor.
:param kwargs: any additional keyword arguments to pass to the :param kwargs: any additional keyword arguments to pass to the
exceptions constructor. exceptions constructor.
.. versionadded:: 1.6
""" """
if 'cause' not in kwargs: if 'cause' not in kwargs:
exc_type, exc, exc_tb = sys.exc_info() exc_type, exc, exc_tb = sys.exc_info()
@ -174,6 +178,9 @@ class save_and_reraise_exception(object):
[if statements to determine whether to raise a new exception] [if statements to determine whether to raise a new exception]
# Not raising a new exception, so reraise # Not raising a new exception, so reraise
ctxt.reraise = True ctxt.reraise = True
.. versionchanged:: 1.4
Added *logger* optional parameter.
""" """
def __init__(self, reraise=True, logger=None): def __init__(self, reraise=True, logger=None):
self.reraise = reraise self.reraise = reraise

View File

@ -13,6 +13,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
"""
File utilities.
.. versionadded:: 1.8
"""
import contextlib import contextlib
import errno import errno
import logging import logging
@ -87,6 +93,8 @@ def write_to_tempfile(content, path=None, suffix='', prefix='tmp'):
For example: it can be used in database tests for creating For example: it can be used in database tests for creating
configuration files. configuration files.
.. versionadded:: 1.9
""" """
if path: if path:
ensure_tree(path) ensure_tree(path)

View File

@ -14,6 +14,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
"""
Test fixtures.
.. versionadded:: 1.3
"""
import fixtures import fixtures
from oslo_utils import timeutils from oslo_utils import timeutils

View File

@ -22,7 +22,10 @@ import traceback
def import_class(import_str): def import_class(import_str):
"""Returns a class from a string including module and class.""" """Returns a class from a string including module and class.
.. versionadded:: 0.3
"""
mod_str, _sep, class_str = import_str.rpartition('.') mod_str, _sep, class_str = import_str.rpartition('.')
__import__(mod_str) __import__(mod_str)
try: try:
@ -34,7 +37,10 @@ def import_class(import_str):
def import_object(import_str, *args, **kwargs): def import_object(import_str, *args, **kwargs):
"""Import a class and return an instance of it.""" """Import a class and return an instance of it.
.. versionadded:: 0.3
"""
return import_class(import_str)(*args, **kwargs) return import_class(import_str)(*args, **kwargs)
@ -44,6 +50,12 @@ def import_object_ns(name_space, import_str, *args, **kwargs):
Imports a class and return an instance of it, first by trying Imports a class and return an instance of it, first by trying
to find the class in a default namespace, then failing back to to find the class in a default namespace, then failing back to
a full path if not found in the default namespace. 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) import_value = "%s.%s" % (name_space, import_str)
try: try:
@ -54,12 +66,19 @@ def import_object_ns(name_space, import_str, *args, **kwargs):
def import_module(import_str): def import_module(import_str):
"""Import a module.""" """Import a module.
.. versionadded:: 0.3
"""
__import__(import_str) __import__(import_str)
return sys.modules[import_str] return sys.modules[import_str]
def import_versioned_module(version, submodule=None): def import_versioned_module(version, submodule=None):
"""Import a versioned module.
.. versionadded:: 0.3
"""
module = 'oslo.v%s' % version module = 'oslo.v%s' % version
if submodule: if submodule:
module = '.'.join((module, submodule)) module = '.'.join((module, submodule))

View File

@ -87,6 +87,8 @@ def is_valid_ipv4(address):
:param address: Value to verify :param address: Value to verify
:type address: string :type address: string
:returns: bool :returns: bool
.. versionadded:: 1.1
""" """
try: try:
return netaddr.valid_ipv4(address) return netaddr.valid_ipv4(address)
@ -100,6 +102,8 @@ def is_valid_ipv6(address):
:param address: Value to verify :param address: Value to verify
:type address: string :type address: string
:returns: bool :returns: bool
.. versionadded:: 1.1
""" """
try: try:
return netaddr.valid_ipv6(address) return netaddr.valid_ipv6(address)
@ -117,6 +121,8 @@ def get_ipv6_addr_by_EUI64(prefix, mac):
:param mac: IEEE 802 48-bit MAC address. :param mac: IEEE 802 48-bit MAC address.
:returns: IPv6 address on success. :returns: IPv6 address on success.
:raises ValueError, TypeError: For any invalid input. :raises ValueError, TypeError: For any invalid input.
.. versionadded:: 1.4
""" """
# Check if the prefix is an IPv4 address # Check if the prefix is an IPv4 address
if netaddr.valid_ipv4(prefix): if netaddr.valid_ipv4(prefix):
@ -143,6 +149,7 @@ def is_ipv6_enabled():
:returns: True if the platform has IPv6 support, False otherwise. :returns: True if the platform has IPv6 support, False otherwise.
.. versionadded:: 1.4
""" """
global _IS_IPV6_ENABLED global _IS_IPV6_ENABLED
@ -164,12 +171,17 @@ def is_valid_ip(address):
:param address: Value to verify :param address: Value to verify
:type address: string :type address: string
:returns: bool :returns: bool
.. versionadded:: 1.1
""" """
return is_valid_ipv4(address) or is_valid_ipv6(address) return is_valid_ipv4(address) or is_valid_ipv6(address)
def is_valid_port(port): def is_valid_port(port):
"""Verify that port represents a valid port number.""" """Verify that port represents a valid port number.
.. versionadded:: 1.1.1
"""
try: try:
val = int(port) val = int(port)
except (ValueError, TypeError): except (ValueError, TypeError):
@ -185,6 +197,11 @@ def get_my_ipv4():
were to be sent out to some well known address on the Internet. In this 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 case, IP from RFC5737 is used, but the specific address does not
matter much. No traffic is actually sent. 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: try:
csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

View File

@ -14,6 +14,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
"""
Reflection module.
.. versionadded:: 1.1
"""
import inspect import inspect
import types import types
@ -31,7 +37,10 @@ _BUILTIN_MODULES = ('builtins', '__builtin__', '__builtins__', 'exceptions')
def get_members(obj, exclude_hidden=True): def get_members(obj, exclude_hidden=True):
"""Yields the members of an object, filtering by hidden/not hidden.""" """Yields the members of an object, filtering by hidden/not hidden.
.. versionadded:: 2.3
"""
for (name, value) in inspect.getmembers(obj): for (name, value) in inspect.getmembers(obj):
if name.startswith("_") and exclude_hidden: if name.startswith("_") and exclude_hidden:
continue continue

View File

@ -213,7 +213,7 @@ def to_slug(value, incoming=None, errors="strict"):
def mask_password(message, secret="***"): def mask_password(message, secret="***"):
"""Replace password with 'secret' in message. """Replace password with *secret* in message.
:param message: The string which includes security information. :param message: The string which includes security information.
:param secret: value with which to replace passwords. :param secret: value with which to replace passwords.
@ -231,6 +231,24 @@ def mask_password(message, secret="***"):
"'original_password' : '***'" "'original_password' : '***'"
>>> mask_password("u'original_password' : u'aaaaa'") >>> mask_password("u'original_password' : u'aaaaa'")
"u'original_password' : u'***'" "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.
""" """
try: try:
@ -262,6 +280,8 @@ def is_int_like(val):
:param val: Value to verify :param val: Value to verify
:type val: string :type val: string
:returns: bool :returns: bool
.. versionadded:: 1.1
""" """
try: try:
return six.text_type(int(val)) == six.text_type(val) return six.text_type(int(val)) == six.text_type(val)

View File

@ -105,7 +105,12 @@ def normalize_time(timestamp):
def is_older_than(before, seconds): def is_older_than(before, seconds):
"""Return True if before is older than 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): if isinstance(before, six.string_types):
before = parse_isotime(before) before = parse_isotime(before)
@ -115,7 +120,12 @@ def is_older_than(before, seconds):
def is_newer_than(after, seconds): def is_newer_than(after, seconds):
"""Return True if after is newer than 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): if isinstance(after, six.string_types):
after = parse_isotime(after) after = parse_isotime(after)
@ -129,6 +139,8 @@ def utcnow_ts(microsecond=False):
See :py:class:`oslo_utils.fixture.TimeFixture`. See :py:class:`oslo_utils.fixture.TimeFixture`.
.. versionchanged:: 1.3
Added optional *microsecond* parameter.
""" """
if utcnow.override_time is None: if utcnow.override_time is None:
# NOTE(kgriffs): This is several times faster # NOTE(kgriffs): This is several times faster
@ -152,6 +164,8 @@ def utcnow(with_timezone=False):
See :py:class:`oslo_utils.fixture.TimeFixture`. See :py:class:`oslo_utils.fixture.TimeFixture`.
.. versionchanged:: 1.6
Added *with_timezone* parameter.
""" """
if utcnow.override_time: if utcnow.override_time:
try: try:
@ -171,6 +185,9 @@ def utcnow(with_timezone=False):
def iso8601_from_timestamp(timestamp, microsecond=False): def iso8601_from_timestamp(timestamp, microsecond=False):
"""Returns an iso8601 formatted date from timestamp. """Returns an iso8601 formatted date from timestamp.
.. versionchanged:: 1.3
Added optional *microsecond* parameter.
.. deprecated:: 1.5.0 .. deprecated:: 1.5.0
Use :func:`datetime.datetime.utcfromtimestamp` and Use :func:`datetime.datetime.utcfromtimestamp` and
:func:`datetime.datetime.isoformat` instead. :func:`datetime.datetime.isoformat` instead.
@ -227,7 +244,11 @@ def clear_time_override():
def marshall_now(now=None): def marshall_now(now=None):
"""Make an rpc-safe datetime with microseconds.""" """Make an rpc-safe datetime with microseconds.
.. versionchanged:: 1.6
Timezone information is now serialized instead of being stripped.
"""
if not now: if not now:
now = utcnow() now = utcnow()
d = dict(day=now.day, month=now.month, year=now.year, hour=now.hour, d = dict(day=now.day, month=now.month, year=now.year, hour=now.hour,
@ -239,7 +260,14 @@ def marshall_now(now=None):
def unmarshall_time(tyme): def unmarshall_time(tyme):
"""Unmarshall a datetime dict.""" """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, # NOTE(ihrachys): datetime does not support leap seconds,
# so the best thing we can do for now is dropping them # so the best thing we can do for now is dropping them
@ -298,6 +326,8 @@ class Split(object):
"""A *immutable* stopwatch split. """A *immutable* stopwatch split.
See: http://en.wikipedia.org/wiki/Stopwatch for what this is/represents. See: http://en.wikipedia.org/wiki/Stopwatch for what this is/represents.
.. versionadded:: 1.4
""" """
__slots__ = ['_elapsed', '_length'] __slots__ = ['_elapsed', '_length']
@ -337,6 +367,8 @@ class StopWatch(object):
depending on operating system and python version). depending on operating system and python version).
.. _monotonic: https://pypi.python.org/pypi/monotonic/ .. _monotonic: https://pypi.python.org/pypi/monotonic/
.. versionadded:: 1.4
""" """
_STARTED = 'STARTED' _STARTED = 'STARTED'
_STOPPED = 'STOPPED' _STOPPED = 'STOPPED'

View File

@ -15,6 +15,8 @@
""" """
UUID related utilities and helper functions. UUID related utilities and helper functions.
.. versionadded:: 1.1
""" """
import uuid import uuid
@ -42,6 +44,9 @@ def is_uuid_like(val):
:param val: Value to verify :param val: Value to verify
:type val: string :type val: string
:returns: bool :returns: bool
.. versionchanged:: 1.1.1
Support non-lowercase UUIDs.
""" """
try: try:
return str(uuid.UUID(val)).replace('-', '') == _format_uuid_string(val) return str(uuid.UUID(val)).replace('-', '') == _format_uuid_string(val)

View File

@ -15,6 +15,8 @@
""" """
Helpers for comparing version strings. Helpers for comparing version strings.
.. versionadded:: 1.6
""" """
import logging import logging
@ -51,6 +53,12 @@ def is_compatible(requested_version, current_version, same_major=True):
def convert_version_to_int(version): 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: try:
if isinstance(version, six.string_types): if isinstance(version, six.string_types):
version = convert_version_to_tuple(version) version = convert_version_to_tuple(version)
@ -62,6 +70,10 @@ def convert_version_to_int(version):
def convert_version_to_str(version_int): def convert_version_to_str(version_int):
"""Convert a version integer to a string with dots.
.. versionadded:: 2.0
"""
version_numbers = [] version_numbers = []
factor = 1000 factor = 1000
while version_int != 0: while version_int != 0:
@ -73,4 +85,8 @@ def convert_version_to_str(version_int):
def convert_version_to_tuple(version_str): 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('.')) return tuple(int(part) for part in version_str.split('.'))