From b1871e57590efdbdf0bfada7bf0ae2c48bec9922 Mon Sep 17 00:00:00 2001 From: "ChangBo Guo(gcb)" Date: Thu, 3 Mar 2016 20:13:16 +0800 Subject: [PATCH] Nuke cliutils from oslo-incubator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Oslo team won't create new libary to include functions of module cliutils. Nova only use two methods in cmd/manage.py, so copy these two methods to nova/utils.py and clean up oslo-incubator related stuff. Note:There is tiny change of method validate_args to make it Python 3 compatible. Change-Id: Ibef5076f9556fa3224c2ca32e8c7fe2c4c1f739b --- nova/cmd/manage.py | 12 +- nova/openstack/__init__.py | 0 nova/openstack/common/README | 16 -- nova/openstack/common/__init__.py | 0 nova/openstack/common/_i18n.py | 45 ----- nova/openstack/common/cliutils.py | 272 ------------------------------ nova/utils.py | 64 +++++++ openstack-common.conf | 7 - tox.ini | 4 +- 9 files changed, 71 insertions(+), 349 deletions(-) delete mode 100644 nova/openstack/__init__.py delete mode 100644 nova/openstack/common/README delete mode 100644 nova/openstack/common/__init__.py delete mode 100644 nova/openstack/common/_i18n.py delete mode 100644 nova/openstack/common/cliutils.py delete mode 100644 openstack-common.conf diff --git a/nova/cmd/manage.py b/nova/cmd/manage.py index 77359e7538..40c8fda559 100644 --- a/nova/cmd/manage.py +++ b/nova/cmd/manage.py @@ -80,7 +80,6 @@ from nova import exception from nova.i18n import _ from nova import objects from nova.objects import flavor as flavor_obj -from nova.openstack.common import cliutils from nova import quota from nova import rpc from nova import servicegroup @@ -961,8 +960,8 @@ class DbCommands(object): table_to_rows_archived = db.archive_deleted_rows(max_rows) if verbose: if table_to_rows_archived: - cliutils.print_dict(table_to_rows_archived, _('Table'), - dict_value=_('Number of Rows Archived')) + utils.print_dict(table_to_rows_archived, _('Table'), + dict_value=_('Number of Rows Archived')) else: print(_('Nothing was archived.')) @@ -1557,15 +1556,14 @@ def main(): # call the action with the remaining arguments # check arguments - try: - cliutils.validate_args(fn, *fn_args, **fn_kwargs) - except cliutils.MissingArgs as e: + missing = utils.validate_args(fn, *fn_args, **fn_kwargs) + if missing: # NOTE(mikal): this isn't the most helpful error message ever. It is # long, and tells you a lot of things you probably don't want to know # if you just got a single arg wrong. print(fn.__doc__) CONF.print_help() - print(e) + print(_("Missing arguments: %s") % ", ".join(missing)) return(1) try: ret = fn(*fn_args, **fn_kwargs) diff --git a/nova/openstack/__init__.py b/nova/openstack/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/nova/openstack/common/README b/nova/openstack/common/README deleted file mode 100644 index 04a616648b..0000000000 --- a/nova/openstack/common/README +++ /dev/null @@ -1,16 +0,0 @@ -oslo-incubator --------------- - -A number of modules from oslo-incubator are imported into this project. -You can clone the oslo-incubator repository using the following url: - - git://git.openstack.org/openstack/oslo-incubator - -These modules are "incubating" in oslo-incubator and are kept in sync -with the help of oslo-incubator's update.py script. See: - - https://wiki.openstack.org/wiki/Oslo#Syncing_Code_from_Incubator - -The copy of the code should never be directly modified here. Please -always update oslo-incubator first and then run the script to copy -the changes across. diff --git a/nova/openstack/common/__init__.py b/nova/openstack/common/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/nova/openstack/common/_i18n.py b/nova/openstack/common/_i18n.py deleted file mode 100644 index 391f321d88..0000000000 --- a/nova/openstack/common/_i18n.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 http://docs.openstack.org/developer/oslo.i18n/usage.html - -""" - -try: - import oslo_i18n - - # NOTE(dhellmann): This reference to o-s-l-o will be replaced by the - # application name when this module is synced into the separate - # repository. It is OK to have more than one translation function - # using the same domain, since there will still only be one message - # catalog. - _translators = oslo_i18n.TranslatorFactory(domain='nova') - - # The primary translation function using the well-known name "_" - _ = _translators.primary - - # Translators for log levels. - # - # The abbreviated names are meant to reflect the usual use of a short - # name like '_'. The "L" is for "log" and the other letter comes from - # the level. - _LI = _translators.log_info - _LW = _translators.log_warning - _LE = _translators.log_error - _LC = _translators.log_critical -except ImportError: - # NOTE(dims): Support for cases where a project wants to use - # code from oslo-incubator, but is not ready to be internationalized - # (like tempest) - _ = _LI = _LW = _LE = _LC = lambda x: x diff --git a/nova/openstack/common/cliutils.py b/nova/openstack/common/cliutils.py deleted file mode 100644 index 3d06069144..0000000000 --- a/nova/openstack/common/cliutils.py +++ /dev/null @@ -1,272 +0,0 @@ -# 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. - -# W0603: Using the global statement -# W0621: Redefining name %s from outer scope -# pylint: disable=W0603,W0621 - -from __future__ import print_function - -import getpass -import inspect -import os -import sys -import textwrap - -from oslo_utils import encodeutils -from oslo_utils import strutils -import prettytable -import six -from six import moves - -from nova.openstack.common._i18n import _ - - -class MissingArgs(Exception): - """Supplied arguments are not sufficient for calling a function.""" - def __init__(self, missing): - self.missing = missing - msg = _("Missing arguments: %s") % ", ".join(missing) - super(MissingArgs, self).__init__(msg) - - -def validate_args(fn, *args, **kwargs): - """Check that the supplied args are sufficient for calling a function. - - >>> validate_args(lambda a: None) - Traceback (most recent call last): - ... - MissingArgs: Missing argument(s): a - >>> validate_args(lambda a, b, c, d: None, 0, c=1) - Traceback (most recent call last): - ... - MissingArgs: Missing argument(s): b, d - - :param fn: the function to check - :param arg: the positional arguments supplied - :param kwargs: the keyword arguments supplied - """ - argspec = inspect.getargspec(fn) - - num_defaults = len(argspec.defaults or []) - required_args = argspec.args[:len(argspec.args) - num_defaults] - - def isbound(method): - return getattr(method, '__self__', None) is not None - - if isbound(fn): - required_args.pop(0) - - missing = [arg for arg in required_args if arg not in kwargs] - missing = missing[len(args):] - if missing: - raise MissingArgs(missing) - - -def arg(*args, **kwargs): - """Decorator for CLI args. - - Example: - - >>> @arg("name", help="Name of the new entity") - ... def entity_create(args): - ... pass - """ - def _decorator(func): - add_arg(func, *args, **kwargs) - return func - return _decorator - - -def env(*args, **kwargs): - """Returns the first environment variable set. - - If all are empty, defaults to '' or keyword arg `default`. - """ - for arg in args: - value = os.environ.get(arg) - if value: - return value - return kwargs.get('default', '') - - -def add_arg(func, *args, **kwargs): - """Bind CLI arguments to a shell.py `do_foo` function.""" - - if not hasattr(func, 'arguments'): - func.arguments = [] - - # NOTE(sirp): avoid dups that can occur when the module is shared across - # tests. - if (args, kwargs) not in func.arguments: - # Because of the semantics of decorator composition if we just append - # to the options list positional options will appear to be backwards. - func.arguments.insert(0, (args, kwargs)) - - -def unauthenticated(func): - """Adds 'unauthenticated' attribute to decorated function. - - Usage: - - >>> @unauthenticated - ... def mymethod(f): - ... pass - """ - func.unauthenticated = True - return func - - -def isunauthenticated(func): - """Checks if the function does not require authentication. - - Mark such functions with the `@unauthenticated` decorator. - - :returns: bool - """ - return getattr(func, 'unauthenticated', False) - - -def print_list(objs, fields, formatters=None, sortby_index=0, - mixed_case_fields=None, field_labels=None): - """Print a list or objects as a table, one row per object. - - :param objs: iterable of :class:`Resource` - :param fields: attributes that correspond to columns, in order - :param formatters: `dict` of callables for field formatting - :param sortby_index: index of the field for sorting table rows - :param mixed_case_fields: fields corresponding to object attributes that - have mixed case names (e.g., 'serverId') - :param field_labels: Labels to use in the heading of the table, default to - fields. - """ - formatters = formatters or {} - mixed_case_fields = mixed_case_fields or [] - field_labels = field_labels or fields - if len(field_labels) != len(fields): - raise ValueError(_("Field labels list %(labels)s has different number " - "of elements than fields list %(fields)s"), - {'labels': field_labels, 'fields': fields}) - - if sortby_index is None: - kwargs = {} - else: - kwargs = {'sortby': field_labels[sortby_index]} - pt = prettytable.PrettyTable(field_labels) - pt.align = 'l' - - for o in objs: - row = [] - for field in fields: - if field in formatters: - row.append(formatters[field](o)) - else: - if field in mixed_case_fields: - field_name = field.replace(' ', '_') - else: - field_name = field.lower().replace(' ', '_') - data = getattr(o, field_name, '') - row.append(data) - pt.add_row(row) - - if six.PY2: - print(encodeutils.safe_encode(pt.get_string(**kwargs))) - else: - print(encodeutils.safe_encode(pt.get_string(**kwargs)).decode()) - - -def print_dict(dct, dict_property="Property", wrap=0, dict_value='Value'): - """Print a `dict` as a table of two columns. - - :param dct: `dict` to print - :param dict_property: name of the first column - :param wrap: wrapping for the second column - :param dict_value: header label for the value (second) column - """ - pt = prettytable.PrettyTable([dict_property, dict_value]) - pt.align = 'l' - for k, v in sorted(dct.items()): - # convert dict to str to check length - if isinstance(v, dict): - v = six.text_type(v) - if wrap > 0: - v = textwrap.fill(six.text_type(v), wrap) - # if value has a newline, add in multiple rows - # e.g. fault with stacktrace - if v and isinstance(v, six.string_types) and r'\n' in v: - lines = v.strip().split(r'\n') - col1 = k - for line in lines: - pt.add_row([col1, line]) - col1 = '' - else: - pt.add_row([k, v]) - - if six.PY2: - print(encodeutils.safe_encode(pt.get_string())) - else: - print(encodeutils.safe_encode(pt.get_string()).decode()) - - -def get_password(max_password_prompts=3): - """Read password from TTY.""" - verify = strutils.bool_from_string(env("OS_VERIFY_PASSWORD")) - pw = None - if hasattr(sys.stdin, "isatty") and sys.stdin.isatty(): - # Check for Ctrl-D - try: - for __ in moves.range(max_password_prompts): - pw1 = getpass.getpass("OS Password: ") - if verify: - pw2 = getpass.getpass("Please verify: ") - else: - pw2 = pw1 - if pw1 == pw2 and pw1: - pw = pw1 - break - except EOFError: - pass - return pw - - -def service_type(stype): - """Adds 'service_type' attribute to decorated function. - - Usage: - - .. code-block:: python - - @service_type('volume') - def mymethod(f): - ... - """ - def inner(f): - f.service_type = stype - return f - return inner - - -def get_service_type(f): - """Retrieves service type from function.""" - return getattr(f, 'service_type', None) - - -def pretty_choice_list(l): - return ', '.join("'%s'" % i for i in l) - - -def exit(msg=''): - if msg: - print (msg, file=sys.stderr) - sys.exit(1) diff --git a/nova/utils.py b/nova/utils.py index 72c92e1836..eb12e4350c 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -34,6 +34,7 @@ import socket import struct import sys import tempfile +import textwrap import time from xml.sax import saxutils @@ -51,6 +52,7 @@ from oslo_utils import importutils from oslo_utils import strutils from oslo_utils import timeutils from oslo_utils import units +import prettytable import six from six.moves import range @@ -1473,3 +1475,65 @@ def isotime(at=None): def strtime(at): return at.strftime("%Y-%m-%dT%H:%M:%S.%f") + + +def print_dict(dct, dict_property="Property", wrap=0, dict_value='Value'): + """Print a `dict` as a table of two columns. + + :param dct: `dict` to print + :param dict_property: name of the first column + :param wrap: wrapping for the second column + :param dict_value: header label for the value (second) column + """ + pt = prettytable.PrettyTable([dict_property, dict_value]) + pt.align = 'l' + for k, v in sorted(dct.items()): + # convert dict to str to check length + if isinstance(v, dict): + v = six.text_type(v) + if wrap > 0: + v = textwrap.fill(six.text_type(v), wrap) + # if value has a newline, add in multiple rows + # e.g. fault with stacktrace + if v and isinstance(v, six.string_types) and r'\n' in v: + lines = v.strip().split(r'\n') + col1 = k + for line in lines: + pt.add_row([col1, line]) + col1 = '' + else: + pt.add_row([k, v]) + + if six.PY2: + print(encodeutils.safe_encode(pt.get_string())) + else: + print(encodeutils.safe_encode(pt.get_string()).decode()) + + +def validate_args(fn, *args, **kwargs): + """Check that the supplied args are sufficient for calling a function. + + >>> validate_args(lambda a: None) + Traceback (most recent call last): + ... + MissingArgs: Missing argument(s): a + >>> validate_args(lambda a, b, c, d: None, 0, c=1) + Traceback (most recent call last): + ... + MissingArgs: Missing argument(s): b, d + + :param fn: the function to check + :param arg: the positional arguments supplied + :param kwargs: the keyword arguments supplied + """ + argspec = inspect.getargspec(fn) + + num_defaults = len(argspec.defaults or []) + required_args = argspec.args[:len(argspec.args) - num_defaults] + + if six.get_method_self(fn) is not None: + required_args.pop(0) + + missing = [arg for arg in required_args if arg not in kwargs] + missing = missing[len(args):] + return missing diff --git a/openstack-common.conf b/openstack-common.conf deleted file mode 100644 index f61374c369..0000000000 --- a/openstack-common.conf +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] - -# The list of modules to copy from oslo-incubator -module=cliutils - -# The base module to hold the copy of openstack.common -base=nova diff --git a/tox.ini b/tox.ini index c4a02abe31..8af415dd71 100644 --- a/tox.ini +++ b/tox.ini @@ -75,7 +75,7 @@ commands = python setup.py testr --coverage \ --testr-args='{posargs}' coverage combine - coverage html --include='nova/*' --omit='nova/openstack/common/*' -d covhtml -i + coverage html --include='nova/*' -d covhtml -i [testenv:venv] # NOTE(jaegerandi): This target does not use constraints because @@ -136,7 +136,7 @@ commands = # E251 Skipped due to https://github.com/jcrocholl/pep8/issues/301 ignore = E121,E122,E123,E124,E125,E126,E127,E128,E129,E131,E251,H405 -exclude = .venv,.git,.tox,dist,doc,*openstack/common/*,*lib/python*,*egg,build,tools/xenserver*,releasenotes +exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools/xenserver*,releasenotes # To get a list of functions that are more complex than 25, set max-complexity # to 25 and run 'tox -epep8'. # 34 is currently the most complex thing we have