Spaces:
Runtime error
Runtime error
| # https://github.com/openstack/rally/blob/master/rally/common/plugin/info.py | |
| # Copyright 2015: Mirantis 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 re | |
| import sys | |
| __all__ = ['parse_docstring'] | |
| FIELDS = 'param|val' # supported fields | |
| PARAM_OR_RETURN_REGEX = re.compile(f":(?:{FIELDS}|return)") | |
| RETURN_REGEX = re.compile(":return: (?P<doc>.*)", re.S) | |
| NEW_REGEX = re.compile(f":(?P<field>{FIELDS}) (?P<name>[\*\w]+): (?P<doc>.*?)" | |
| f"(?:(?=:(?:{FIELDS}|return|raises))|\Z)", re.S) | |
| def trim(docstring): | |
| """trim function from PEP-257""" | |
| if not docstring: | |
| return "" | |
| # Convert tabs to spaces (following the normal Python rules) | |
| # and split into a list of lines: | |
| lines = docstring.expandtabs().splitlines() | |
| # Determine minimum indentation (first line doesn't count): | |
| indent = sys.maxsize | |
| for line in lines[1:]: | |
| stripped = line.lstrip() | |
| if stripped: | |
| indent = min(indent, len(line) - len(stripped)) | |
| # Remove indentation (first line is special): | |
| trimmed = [lines[0].strip()] | |
| if indent < sys.maxsize: | |
| for line in lines[1:]: | |
| trimmed.append(line[indent:].rstrip()) | |
| # Strip off trailing and leading blank lines: | |
| while trimmed and not trimmed[-1]: | |
| trimmed.pop() | |
| while trimmed and not trimmed[0]: | |
| trimmed.pop(0) | |
| # Current code/unittests expects a line return at | |
| # end of multiline docstrings | |
| # workaround expected behavior from unittests | |
| if "\n" in docstring: | |
| trimmed.append("") | |
| # Return a single string: | |
| return "\n".join(trimmed) | |
| def reindent(string): | |
| return "\n".join(l.strip() for l in string.strip().split("\n")) | |
| def parse_docstring(docstring): | |
| """Parse the docstring into its components. | |
| :return: a dictionary of form | |
| { | |
| "short_description": ..., | |
| "long_description": ..., | |
| "params": [{"name": ..., "doc": ...}, ...], | |
| "vals": [{"name": ..., "doc": ...}, ...], | |
| "return": ... | |
| } | |
| """ | |
| short_description = long_description = return_str = "" | |
| args = [] | |
| if docstring: | |
| docstring = trim(docstring.lstrip("\n")) | |
| lines = docstring.split("\n", 1) | |
| short_description = lines[0] | |
| if len(lines) > 1: | |
| long_description = lines[1].strip() | |
| params_return_desc = None | |
| match = PARAM_OR_RETURN_REGEX.search(long_description) | |
| if match: | |
| long_desc_end = match.start() | |
| params_return_desc = long_description[long_desc_end:].strip() | |
| long_description = long_description[:long_desc_end].rstrip() | |
| if params_return_desc: | |
| args = [ | |
| {"name": name, "doc": trim(doc), "field": field} | |
| for field, name, doc in NEW_REGEX.findall(params_return_desc) | |
| ] | |
| match = RETURN_REGEX.search(params_return_desc) | |
| if match: | |
| return_str = reindent(match.group("doc")) | |
| comments = {p['name']: p['doc'] for p in args} | |
| return { | |
| "short_description": short_description, | |
| "long_description": long_description, | |
| "args": args, | |
| "comments": comments, | |
| "return": return_str | |
| } | |
| class InfoMixin(object): | |
| def _get_doc(cls): | |
| """Return documentary of class | |
| By default it returns docstring of class, but it can be overridden | |
| for example for cases like merging own docstring with parent | |
| """ | |
| return cls.__doc__ | |
| def get_info(cls): | |
| doc = parse_docstring(cls._get_doc()) | |
| return { | |
| "name": cls.get_name(), | |
| "platform": cls.get_platform(), | |
| "module": cls.__module__, | |
| "title": doc["short_description"], | |
| "description": doc["long_description"], | |
| "parameters": doc["params"], | |
| "schema": getattr(cls, "CONFIG_SCHEMA", None), | |
| "return": doc["return"] | |
| } | |