From 1fb156f033e589bb6aaff8486cfd15ad46ec7f5f Mon Sep 17 00:00:00 2001 From: Fangchen Li Date: Wed, 7 Jul 2021 15:10:17 -0500 Subject: [PATCH 1/2] CLN: clean doc validation script --- scripts/validate_docstrings.py | 59 ++++++++++++++-------------------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/scripts/validate_docstrings.py b/scripts/validate_docstrings.py index 9b65204403612..60667bb33a88a 100755 --- a/scripts/validate_docstrings.py +++ b/scripts/validate_docstrings.py @@ -15,40 +15,28 @@ """ from __future__ import annotations -import argparse +from argparse import ArgumentParser import doctest -import glob -import importlib -import json -import os -import subprocess +from importlib import import_module +from io import StringIO +from json import dumps +from pathlib import Path +from subprocess import run import sys -import tempfile +from tempfile import NamedTemporaryFile -try: - from io import StringIO -except ImportError: - from cStringIO import StringIO +import matplotlib +import numpy +from numpydoc.validate import ( + Docstring, + validate, +) -# Template backend makes matplotlib to not plot anything. This is useful -# to avoid that plot windows are open from the doctests while running the -# script. Setting here before matplotlib is loaded. -# We don't warn for the number of open plots, as none is actually being opened -os.environ["MPLBACKEND"] = "Template" -import matplotlib # isort:skip +import pandas +matplotlib.rcParams["backend"] = "template" matplotlib.rc("figure", max_open_warning=10000) -import numpy # isort:skip - -BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - -sys.path.insert(0, os.path.join(BASE_PATH)) -import pandas # isort:skip - -sys.path.insert(1, os.path.join(BASE_PATH, "doc", "sphinxext")) -from numpydoc.validate import validate, Docstring # isort:skip - PRIVATE_CLASSES = ["NDFrame", "IndexOpsMixin"] ERROR_MSGS = { @@ -130,7 +118,7 @@ def get_api_items(api_doc_fd): position = None continue item = line.strip() - func = importlib.import_module(current_module) + func = import_module(current_module) for part in item.split("."): func = getattr(func, part) @@ -182,11 +170,11 @@ def validate_pep8(self): ) error_messages = [] - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as file: + with NamedTemporaryFile(mode="w", encoding="utf-8") as file: file.write(content) file.flush() cmd = ["python", "-m", "flake8", "--quiet", "--statistics", file.name] - response = subprocess.run(cmd, capture_output=True, text=True) + response = run(cmd, capture_output=True, text=True) stdout = response.stdout stdout = stdout.replace(file.name, "") messages = stdout.strip("\n") @@ -288,13 +276,14 @@ def validate_all(prefix, ignore_deprecated=False): result = {} seen = {} - api_doc_fnames = os.path.join(BASE_PATH, "doc", "source", "reference", "*.rst") + base_path = Path(__file__).parent.parent + api_doc_fnames = Path(base_path, "doc", "source", "reference") api_items = [] - for api_doc_fname in glob.glob(api_doc_fnames): + for api_doc_fname in api_doc_fnames.glob("*.rst"): with open(api_doc_fname) as f: api_items += list(get_api_items(f)) - for func_name, func_obj, section, subsection in api_items: + for func_name, _, section, subsection in api_items: if prefix and not func_name.startswith(prefix): continue doc_info = pandas_validate(func_name) @@ -330,7 +319,7 @@ def print_validate_all_results( result = validate_all(prefix, ignore_deprecated) if output_format == "json": - sys.stdout.write(json.dumps(result)) + sys.stdout.write(dumps(result)) return 0 prefix = "##[error]" if output_format == "actions" else "" @@ -398,7 +387,7 @@ def main(func_name, prefix, errors, output_format, ignore_deprecated): "if not provided, all docstrings are validated and returned " "as JSON" ) - argparser = argparse.ArgumentParser(description="validate pandas docstrings") + argparser = ArgumentParser(description="validate pandas docstrings") argparser.add_argument("function", nargs="?", default=None, help=func_help) argparser.add_argument( "--format", From cd19cf5d81032fdd63a1d8c9a0123f8abd0c94f9 Mon Sep 17 00:00:00 2001 From: Fangchen Li Date: Wed, 7 Jul 2021 22:18:29 -0500 Subject: [PATCH 2/2] revert import syntax --- scripts/validate_docstrings.py | 36 ++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/scripts/validate_docstrings.py b/scripts/validate_docstrings.py index 60667bb33a88a..7562895d9db3e 100755 --- a/scripts/validate_docstrings.py +++ b/scripts/validate_docstrings.py @@ -15,17 +15,18 @@ """ from __future__ import annotations -from argparse import ArgumentParser +import argparse import doctest -from importlib import import_module -from io import StringIO -from json import dumps -from pathlib import Path -from subprocess import run +import importlib +import io +import json +import pathlib +import subprocess import sys -from tempfile import NamedTemporaryFile +import tempfile import matplotlib +import matplotlib.pyplot as plt import numpy from numpydoc.validate import ( Docstring, @@ -34,8 +35,8 @@ import pandas -matplotlib.rcParams["backend"] = "template" -matplotlib.rc("figure", max_open_warning=10000) +# With template backend, matplotlib plots nothing +matplotlib.use("template") PRIVATE_CLASSES = ["NDFrame", "IndexOpsMixin"] @@ -118,7 +119,7 @@ def get_api_items(api_doc_fd): position = None continue item = line.strip() - func = import_module(current_module) + func = importlib.import_module(current_module) for part in item.split("."): func = getattr(func, part) @@ -145,7 +146,7 @@ def examples_errors(self): context = {"np": numpy, "pd": pandas} error_msgs = "" for test in finder.find(self.raw_doc, self.name, globs=context): - f = StringIO() + f = io.StringIO() runner.run(test, out=f.write) error_msgs += f.getvalue() return error_msgs @@ -170,11 +171,11 @@ def validate_pep8(self): ) error_messages = [] - with NamedTemporaryFile(mode="w", encoding="utf-8") as file: + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as file: file.write(content) file.flush() cmd = ["python", "-m", "flake8", "--quiet", "--statistics", file.name] - response = run(cmd, capture_output=True, text=True) + response = subprocess.run(cmd, capture_output=True, text=True) stdout = response.stdout stdout = stdout.replace(file.name, "") messages = stdout.strip("\n") @@ -251,6 +252,7 @@ def pandas_validate(func_name: str): if doc.non_hyphenated_array_like(): result["errors"].append(pandas_error("GL05")) + plt.close("all") return result @@ -276,8 +278,8 @@ def validate_all(prefix, ignore_deprecated=False): result = {} seen = {} - base_path = Path(__file__).parent.parent - api_doc_fnames = Path(base_path, "doc", "source", "reference") + base_path = pathlib.Path(__file__).parent.parent + api_doc_fnames = pathlib.Path(base_path, "doc", "source", "reference") api_items = [] for api_doc_fname in api_doc_fnames.glob("*.rst"): with open(api_doc_fname) as f: @@ -319,7 +321,7 @@ def print_validate_all_results( result = validate_all(prefix, ignore_deprecated) if output_format == "json": - sys.stdout.write(dumps(result)) + sys.stdout.write(json.dumps(result)) return 0 prefix = "##[error]" if output_format == "actions" else "" @@ -387,7 +389,7 @@ def main(func_name, prefix, errors, output_format, ignore_deprecated): "if not provided, all docstrings are validated and returned " "as JSON" ) - argparser = ArgumentParser(description="validate pandas docstrings") + argparser = argparse.ArgumentParser(description="validate pandas docstrings") argparser.add_argument("function", nargs="?", default=None, help=func_help) argparser.add_argument( "--format",