def report_deprecated_feature(logger, msg, *args, **kwargs): """Call this function when a deprecated feature is used. If the system is configured for fatal deprecations then the message is logged at the 'critical' level and :class:`DeprecatedConfig` will be raised. Otherwise, the message will be logged (once) at the 'warn' level. :raises: :class:`DeprecatedConfig` if the system is configured for fatal deprecations. """ stdmsg = _("Deprecated: %s") % msg CONF.register_opts(deprecated_opts) if CONF.fatal_deprecations: logger.critical(stdmsg, *args, **kwargs) raise DeprecatedConfig(msg=stdmsg) # Using a list because a tuple with dict can't be stored in a set. sent_args = _deprecated_messages_sent.setdefault(msg, list()) if args in sent_args: # Already logged this message, so don't log it again. return sent_args.append(args) logger.warn(stdmsg, *args, **kwargs)
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) 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) return strutils.string_to_bytes('%s%sB' % (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 == 'None': 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
class InvalidPeriodicTaskArg(Exception): message = _("Unexpected argument for periodic task creation: %(arg)s.")
class deprecated(object): """A decorator to mark callables as deprecated. This decorator logs a deprecation message when the callable it decorates is used. The message will include the release where the callable was deprecated, the release where it may be removed and possibly an optional replacement. Examples: 1. Specifying the required deprecated release >>> @deprecated(as_of=deprecated.ICEHOUSE) ... def a(): pass 2. Specifying a replacement: >>> @deprecated(as_of=deprecated.ICEHOUSE, in_favor_of='f()') ... def b(): pass 3. Specifying the release where the functionality may be removed: >>> @deprecated(as_of=deprecated.ICEHOUSE, remove_in=+1) ... def c(): pass 4. Specifying the deprecated functionality will not be removed: >>> @deprecated(as_of=deprecated.ICEHOUSE, remove_in=0) ... def d(): pass 5. Specifying a replacement, deprecated functionality will not be removed: >>> @deprecated(as_of=deprecated.ICEHOUSE, in_favor_of='f()', remove_in=0) ... def e(): pass """ # NOTE(morganfainberg): Bexar is used for unit test purposes, it is # expected we maintain a gap between Bexar and Folsom in this list. BEXAR = 'B' FOLSOM = 'F' GRIZZLY = 'G' HAVANA = 'H' ICEHOUSE = 'I' JUNO = 'J' KILO = 'K' LIBERTY = 'L' _RELEASES = { # NOTE(morganfainberg): Bexar is used for unit test purposes, it is # expected we maintain a gap between Bexar and Folsom in this list. 'B': 'Bexar', 'F': 'Folsom', 'G': 'Grizzly', 'H': 'Havana', 'I': 'Icehouse', 'J': 'Juno', 'K': 'Kilo', 'L': 'Liberty', } _deprecated_msg_with_alternative = _( '%(what)s is deprecated as of %(as_of)s in favor of ' '%(in_favor_of)s and may be removed in %(remove_in)s.') _deprecated_msg_no_alternative = _( '%(what)s is deprecated as of %(as_of)s and may be ' 'removed in %(remove_in)s. It will not be superseded.') _deprecated_msg_with_alternative_no_removal = _( '%(what)s is deprecated as of %(as_of)s in favor of %(in_favor_of)s.') _deprecated_msg_with_no_alternative_no_removal = _( '%(what)s is deprecated as of %(as_of)s. It will not be superseded.') def __init__(self, as_of, in_favor_of=None, remove_in=2, what=None): """Initialize decorator :param as_of: the release deprecating the callable. Constants are define in this class for convenience. :param in_favor_of: the replacement for the callable (optional) :param remove_in: an integer specifying how many releases to wait before removing (default: 2) :param what: name of the thing being deprecated (default: the callable's name) """ self.as_of = as_of self.in_favor_of = in_favor_of self.remove_in = remove_in self.what = what def __call__(self, func_or_cls): if not self.what: self.what = func_or_cls.__name__ + '()' msg, details = self._build_message() if inspect.isfunction(func_or_cls): @six.wraps(func_or_cls) def wrapped(*args, **kwargs): report_deprecated_feature(LOG, msg, details) return func_or_cls(*args, **kwargs) return wrapped elif inspect.isclass(func_or_cls): orig_init = func_or_cls.__init__ # TODO(tsufiev): change `functools` module to `six` as # soon as six 1.7.4 (with fix for passing `assigned` # argument to underlying `functools.wraps`) is released # and added to the oslo_incubator requrements @functools.wraps(orig_init, assigned=('__name__', '__doc__')) def new_init(self, *args, **kwargs): report_deprecated_feature(LOG, msg, details) orig_init(self, *args, **kwargs) func_or_cls.__init__ = new_init return func_or_cls else: raise TypeError('deprecated can be used only with functions or ' 'classes') def _get_safe_to_remove_release(self, release): # TODO(dstanek): this method will have to be reimplemented once # when we get to the X release because once we get to the Y # release, what is Y+2? new_release = chr(ord(release) + self.remove_in) if new_release in self._RELEASES: return self._RELEASES[new_release] else: return new_release def _build_message(self): details = dict(what=self.what, as_of=self._RELEASES[self.as_of], remove_in=self._get_safe_to_remove_release(self.as_of)) if self.in_favor_of: details['in_favor_of'] = self.in_favor_of if self.remove_in > 0: msg = self._deprecated_msg_with_alternative else: # There are no plans to remove this function, but it is # now deprecated. msg = self._deprecated_msg_with_alternative_no_removal else: if self.remove_in > 0: msg = self._deprecated_msg_no_alternative else: # There are no plans to remove this function, but it is # now deprecated. msg = self._deprecated_msg_with_no_alternative_no_removal return msg, details
class DeprecatedConfig(Exception): message = _("Fatal call to deprecated config: %(msg)s") def __init__(self, msg): super(Exception, self).__init__(self.message % dict(msg=msg))