def validate_object(import_path): exit_status = 0 results = validate(import_path) for err_code, err_desc in results["errors"]: exit_status += 1 print(":".join([import_path, err_code, err_desc])) return exit_status
def _valid_obj(full_import_path, obj, verbose): if verbose: v = "# -- " + full_import_path + " " v += "-" * (80 - len(v)) print(v) try: _ = inspect.signature(obj) except ValueError: sys.stderr.write(full_import_path) sys.stderr.write(": can't get signature\n") return 1 errs = validate(full_import_path)["errors"] if not errs: return 0 exit_code = 0 for code, msg in errs: if code in IGNORE: continue output = "%s:%s:%s" % (full_import_path, code, msg) sys.stderr.write("\n\t".join(textwrap.wrap(output, 79))) sys.stderr.write("\n") exit_code = 1 return exit_code
def validate_object(import_path: str) -> list: """ Check docstrings of an entity that can be imported. Parameters ---------- import_path : str Python-like import path. Returns ------- errors : list List with string representations of errors. """ from numpydoc.validate import validate errors = [] doc = Docstring(import_path) results = validate(import_path) results = validate_modin_error(doc, results) noqa_checks = get_noqa_checks(doc) for err_code, err_desc in results["errors"]: if (err_code not in NUMPYDOC_BASE_ERROR_CODES and err_code not in MODIN_ERROR_CODES) or skip_check_if_noqa( doc, err_code, noqa_checks): continue errors.append(":".join( [import_path, str(results["file_line"]), err_code, err_desc])) return errors
def pandas_validate(func_name: str): """ Call the numpydoc validation, and add the errors specific to pandas. Parameters ---------- func_name : str Name of the object of the docstring to validate. Returns ------- dict Information about the docstring and the errors found. """ doc = PandasDocstring(func_name) result = validate(func_name) mentioned_errs = doc.mentioned_private_classes if mentioned_errs: result["errors"].append( pandas_error("GL04", mentioned_private_classes=", ".join(mentioned_errs))) if doc.see_also: for rel_name in doc.see_also: if rel_name.startswith("pandas."): result["errors"].append( pandas_error( "SA05", reference_name=rel_name, right_reference=rel_name[len("pandas."):], )) result["examples_errs"] = "" if doc.examples: result["examples_errs"] = doc.examples_errors if result["examples_errs"]: result["errors"].append( pandas_error("EX02", doctest_log=result["examples_errs"])) for error_code, error_message, error_count in doc.validate_pep8(): times_happening = f" ({error_count} times)" if error_count > 1 else "" result["errors"].append( pandas_error( "EX03", error_code=error_code, error_message=error_message, times_happening=times_happening, )) examples_source_code = "".join(doc.examples_source_code) for wrong_import in ("numpy", "pandas"): if f"import {wrong_import}" in examples_source_code: result["errors"].append( pandas_error("EX04", imported_library=wrong_import)) if doc.non_hyphenated_array_like(): result["errors"].append(pandas_error("GL05")) plt.close("all") return result
def callback(obj): result = validate(obj) errors = [] for errcode, errmsg in result.get('errors', []): if allow_rules and errcode not in allow_rules: continue if disallow_rules and errcode in disallow_rules: continue errors.append((errcode, errmsg)) if len(errors): result['errors'] = errors results.append((obj, result))
def callback(obj): result = validate(obj) errors = [] for errcode, errmsg in result.get('errors', []): if rules_whitelist and errcode not in rules_whitelist: continue if rules_blacklist and errcode in rules_blacklist: continue errors.append((errcode, errmsg)) if len(errors): result['errors'] = errors results.append((obj, result))
def check_parameters_match(func): """Check docstring, return list of incorrect results.""" from numpydoc.validate import validate name = _func_name(func) skip = (not name.startswith('mne.') or any(re.match(d, name) for d in docstring_ignores) or 'deprecation_wrapped' in getattr( getattr(func, '__code__', None), 'co_name', '')) if skip: return list() incorrect = ['%s : %s : %s' % (name, err[0], err[1]) for err in validate(name)['errors'] if err[0] not in error_ignores] return incorrect
def check_parameters_match(func, cls=None): """Check docstring, return list of incorrect results.""" from numpydoc.validate import validate name = _func_name(func, cls) skip = (not name.startswith('mne.') or any(re.match(d, name) for d in docstring_ignores)) if skip: return list() if cls is not None: for subclass, ignores in subclass_name_ignores: if issubclass(cls, subclass) and name.split('.')[-1] in ignores: return list() incorrect = ['%s : %s : %s' % (name, err[0], err[1]) for err in validate(name)['errors'] if err[0] not in error_ignores and (name.split('.')[-1], err[0]) not in error_ignores_specific] return incorrect
def callback(obj): try: result = validate(obj) except OSError as e: symbol = f"{obj.__module__}.{obj.__name__}" logger.warning(f"Unable to validate `{symbol}` due to `{e}`") return errors = [] for errcode, errmsg in result.get('errors', []): if allow_rules and errcode not in allow_rules: continue if disallow_rules and errcode in disallow_rules: continue errors.append((errcode, errmsg)) if len(errors): result['errors'] = errors results.append((obj, result))
def test_docstrings(self): failures = {} # Loop over directories for dirpath in sorted(directories): # Loop over files for file_name in sorted(os.listdir(dirpath)): if not file_name.startswith("_") and file_name[-3:] == '.py' \ and not os.path.isdir(file_name): # To construct module name, remove part of abs path that # precedes 'openmdao', and then replace '/' with '.' in the remainder. mod1 = re.sub(r'.*openmdao', 'openmdao', dirpath).replace('/', '.') # Then, get rid of the '.py' to get final part of module name. mod2 = file_name[:-3] module_name = f'{mod1}.{mod2}' try: mod = importlib.import_module(module_name) except ImportError as err: print('Skipped:', err) # e.g. PETSc is not installed continue classes = [ x for x in dir(mod) if not x.startswith('_') and inspect.isclass(getattr(mod, x)) and getattr(mod, x).__module__ == module_name ] # Loop over classes. for class_name in classes: full_class_path = f'{module_name}.{class_name}' try: result = validate.validate(full_class_path) except: continue failures.update( self._failure_dict(full_class_path, result)) clss = getattr(mod, class_name) methods = [ x for x in dir(clss) if (inspect.ismethod(getattr(clss, x)) or inspect.isfunction(getattr(clss, x))) and x in clss.__dict__ ] # Loop over class methods. for method_name in methods: if not method_name.startswith('_'): full_method_path = f'{module_name}.{class_name}.{method_name}' try: result = validate.validate( full_method_path) except: continue failures.update( self._failure_dict(full_method_path, result)) tree = ast.parse(inspect.getsource(mod)) if hasattr(tree, 'body'): funcs = [ node.name for node in tree.body if isinstance(node, ast.FunctionDef) ] else: funcs = [] # Loop over standalone functions. for func_name in funcs: if not func_name.startswith('_'): full_function_path = f'{module_name}.{func_name}' try: result = validate.validate(full_function_path) except: continue failures.update( self._failure_dict(full_function_path, result)) if failures: msg = '\n' count = 0 for key in failures: msg += f'{key}\n' count += len(failures[key]) for failure in failures[key]: msg += f' {failure}\n' msg += f'Found {count} issues in docstrings' self.fail(msg)
def spectrochempy_validate(func_name, exclude=[]): """ Call the numpydoc validation, and add the errors specific to spectrochempy. Parameters ---------- func_name : str Name of the object of the docstring to validate. exclude : list List of error code to exclude, e.g. ["SA01", ...]. Returns ------- dict Information about the docstring and the errors found. """ func_obj = Validator._load_obj(func_name) doc_obj = get_doc_object(func_obj) doc = SpectroChemPyDocstring(func_name, doc_obj) result = validate(doc_obj) errs = result["errors"] mentioned_errs = doc.mentioned_private_classes if mentioned_errs: errs.append( spectrochempy_error( "GL04", mentioned_private_classes=", ".join(mentioned_errs))) has_kwargs = doc.has_kwargs if has_kwargs: errs = remove_errors(errs, "PR02") if not doc.doc_other_parameters: errs.append(spectrochempy_error("GL11")) if exclude: errs = remove_errors(errs, exclude) if doc.see_also: for rel_name in doc.see_also: if rel_name.startswith("spectrochempy."): errs.append( spectrochempy_error( "SA05", reference_name=rel_name, right_reference=rel_name[len("spectrochempy."):], )) result["examples_errs"] = "" if doc.examples: result["examples_errs"] = doc.examples_errors if result["examples_errs"]: errs.append( spectrochempy_error("EX02", doctest_log=result["examples_errs"])) for error_code, error_message, error_count in doc.validate_pep8(): times_happening = f" ({error_count} times)" if error_count > 1 else "" errs.append( spectrochempy_error( "EX03", error_code=error_code, error_message=error_message, times_happening=times_happening, )) examples_source_code = "".join(doc.examples_source_code) for wrong_import in ("numpy", "spectrochempy"): if f"import {wrong_import}" in examples_source_code: errs.append( spectrochempy_error("EX04", imported_library=wrong_import)) if doc.non_hyphenated_array_like(): errs.append(spectrochempy_error("GL05")) # cases where docrep dedent was used if error("GL01") in errs and not doc.raw_doc.startswith(""): errs = remove_errors(errs, "GL01") if error("GL02") in errs and not doc.raw_doc.startswith(""): errs = remove_errors(errs, "GL02") # case of properties (we accept a single line summary) if hasattr(doc.code_obj, "fget"): errs = remove_errors(errs, "ES01") result["errors"] = errs plt.close("all") if result["file"] is None: # sometimes it is because the code_obj is a property if hasattr(doc.code_obj, "fget"): try: result["file"] = inspect.getsourcefile(doc.code_obj.fget) result["file_line"] = inspect.getsourcelines( doc.code_obj.fget)[-1] except (OSError, TypeError): pass return result
"paths", nargs="+", type=pathlib.Path, help= "Filenames or directories; in case of direstories perform recursive check", ) parser.add_argument( "--add-ignore", nargs="*", default=[], help="Pydocstyle error codes; for example: D100,D100,D102", ) parser.add_argument( "--disable-numpydoc", default=False, action="store_true", help="Determine if numpydoc checks are not needed", ) args = parser.parse_args() check_args(args) return args if __name__ == "__main__": args = get_args() monkeypatching() if not validate(args.paths, args.add_ignore, not args.disable_numpydoc): logging.error("INVALID DOCUMENTATION FOUND") exit(1) logging.info("SUCCESSFUL CHECK")