From 9c45ba636638760aaad00efd9868255b33a54de6 Mon Sep 17 00:00:00 2001 From: F-G Fernandez <76527547+fg-mindee@users.noreply.github.com> Date: Thu, 9 Dec 2021 14:43:57 +0100 Subject: [PATCH] docs: Added minimal docstring sanity check (#686) * docs: Fixed docstrings of doctr/utils * docs: Fixed docstrings of MASTER * chore: Added pydocstyle config * chore: Updated deps * docs: Updated Makefile & CONTRIBUTING * ci: Added a docstring check job --- .github/workflows/style.yml | 19 ++++++++++++++ .pydocstyle | 3 +++ CONTRIBUTING.md | 9 +++++++ Makefile | 1 + doctr/models/recognition/master/pytorch.py | 6 +++-- doctr/models/recognition/master/tensorflow.py | 4 ++- doctr/utils/fonts.py | 9 +++++++ doctr/utils/metrics.py | 25 ++++++++++++++++++- doctr/utils/visualization.py | 3 ++- setup.py | 4 ++- tests/common/test_requirements.py | 2 +- 11 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 .pydocstyle diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index fb8b1dd002..359212bff6 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -78,3 +78,22 @@ jobs: run: | mypy --version mypy --config-file mypy.ini doctr/ + + pydocstyle-py3: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + python: [3.7] + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python }} + architecture: x64 + - name: Run isort + run: | + pip install pydocstyle + pydocstyle --version + pydocstyle doctr/ diff --git a/.pydocstyle b/.pydocstyle new file mode 100644 index 0000000000..f81d27efc9 --- /dev/null +++ b/.pydocstyle @@ -0,0 +1,3 @@ +[pydocstyle] +select = D300,D301,D417 +match = .*\.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 545fc34e90..485e9c68d4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -98,6 +98,15 @@ mypy --config-file mypy.ini doctr/ ``` The `mypy.ini` file will be read to check your typing. +#### Docstring format + +To keep a sane docstring structure, if you install [pydocstyle](https://github.com/PyCQA/pydocstyle), you can verify your docstrings as follows: + +```shell +pydocstyle doctr/ +``` +The `.pydocstyle` file will be read to configure this operation. + ### Modifying the documentation diff --git a/Makefile b/Makefile index 0584b14be6..25b7c98c85 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ quality: isort . -c -v flake8 ./ mypy doctr/ + pydocstyle doctr/ # this target runs checks on all files and potentially modifies some of them style: diff --git a/doctr/models/recognition/master/pytorch.py b/doctr/models/recognition/master/pytorch.py index 2ed4b0f6dc..1c906cc247 100644 --- a/doctr/models/recognition/master/pytorch.py +++ b/doctr/models/recognition/master/pytorch.py @@ -160,7 +160,9 @@ class MASTER(_MASTER, nn.Module): num_heads: number of heads for the mutli-head attention module num_layers: number of decoder layers to stack max_length: maximum length of character sequence handled by the model - input_size: size of the image inputs + dropout: dropout probability of the decoder + input_shape: size of the image inputs + cfg: config dictionary """ feature_pe: torch.Tensor @@ -263,7 +265,7 @@ def forward( return_model_output: if True, return logits return_preds: if True, decode logits - Return: + Returns: A torch tensor, containing logits """ diff --git a/doctr/models/recognition/master/tensorflow.py b/doctr/models/recognition/master/tensorflow.py index f63b608631..171b3f8d49 100644 --- a/doctr/models/recognition/master/tensorflow.py +++ b/doctr/models/recognition/master/tensorflow.py @@ -182,7 +182,9 @@ class MASTER(_MASTER, Model): num_heads: number of heads for the mutli-head attention module num_layers: number of decoder layers to stack max_length: maximum length of character sequence handled by the model - input_size: size of the image inputs + dropout: dropout probability of the decoder + input_shape: size of the image inputs + cfg: config dictionary """ def __init__( diff --git a/doctr/utils/fonts.py b/doctr/utils/fonts.py index cf8fb4dd21..6a82acb5d7 100644 --- a/doctr/utils/fonts.py +++ b/doctr/utils/fonts.py @@ -13,6 +13,15 @@ def get_font(font_family: Optional[str] = None, font_size: int = 13) -> ImageFont.ImageFont: + """Resolves a compatible ImageFont for the system + + Args: + font_family: the font family to use + font_size: the size of the font upon rendering + + Returns: + the Pillow font + """ # Font selection if font_family is None: diff --git a/doctr/utils/metrics.py b/doctr/utils/metrics.py index dd577a4bba..7d8c9c49ed 100644 --- a/doctr/utils/metrics.py +++ b/doctr/utils/metrics.py @@ -79,7 +79,8 @@ def update( Args: gt: list of groung-truth character sequences - pred: list of predicted character sequences""" + pred: list of predicted character sequences + """ if len(gt) != len(pred): raise AssertionError("prediction size does not match with ground-truth labels size") @@ -391,6 +392,12 @@ def __init__( self.reset() def update(self, gts: np.ndarray, preds: np.ndarray) -> None: + """Updates the metric + + Args: + gts: a set of relative bounding boxes either of shape (N, 4) or (N, 5) if they are rotated ones + preds: a set of relative bounding boxes either of shape (M, 4) or (M, 5) if they are rotated ones + """ if preds.shape[0] > 0: # Compute IoU @@ -497,6 +504,14 @@ def update( gt_labels: List[str], pred_labels: List[str], ) -> None: + """Updates the metric + + Args: + gt_boxes: a set of relative bounding boxes either of shape (N, 4) or (N, 5) if they are rotated ones + pred_boxes: a set of relative bounding boxes either of shape (M, 4) or (M, 5) if they are rotated ones + gt_labels: a list of N string labels + pred_labels: a list of M string labels + """ if gt_boxes.shape[0] != len(gt_labels) or pred_boxes.shape[0] != len(pred_labels): raise AssertionError("there should be the same number of boxes and string both for the ground truth " @@ -627,6 +642,14 @@ def update( gt_labels: np.ndarray, pred_labels: np.ndarray, ) -> None: + """Updates the metric + + Args: + gt_boxes: a set of relative bounding boxes either of shape (N, 4) or (N, 5) if they are rotated ones + pred_boxes: a set of relative bounding boxes either of shape (M, 4) or (M, 5) if they are rotated ones + gt_labels: an array of class indices of shape (N,) + pred_labels: an array of class indices of shape (M,) + """ if gt_boxes.shape[0] != gt_labels.shape[0] or pred_boxes.shape[0] != pred_labels.shape[0]: raise AssertionError("there should be the same number of boxes and string both for the ground truth " diff --git a/doctr/utils/visualization.py b/doctr/utils/visualization.py index bf6d7ae4a3..d1a29aacb3 100644 --- a/doctr/utils/visualization.py +++ b/doctr/utils/visualization.py @@ -305,7 +305,7 @@ def synthesize_page( def draw_boxes( boxes: np.ndarray, image: np.ndarray, - color: Optional[Tuple] = None, + color: Optional[Tuple[int, int, int]] = None, **kwargs ) -> None: """Draw an array of relative straight boxes on an image @@ -313,6 +313,7 @@ def draw_boxes( Args: boxes: array of relative boxes, of shape (*, 4) image: np array, float32 or uint8 + color: color to use for bounding box edges """ h, w = image.shape[:2] # Convert boxes to absolute coords diff --git a/setup.py b/setup.py index 211b5ce04d..d8d8f4b932 100644 --- a/setup.py +++ b/setup.py @@ -68,6 +68,7 @@ "flake8>=3.9.0", "isort>=5.7.0", "mypy>=0.812", + "pydocstyle>=6.1.1", # Docs "sphinx<3.5.0", "sphinx-rtd-theme==0.4.3", @@ -135,7 +136,8 @@ def deps_list(*pkgs): extras["quality"] = deps_list( "flake8", "isort", - "mypy" + "mypy", + "pydocstyle", ) extras["docs_specific"] = deps_list( diff --git a/tests/common/test_requirements.py b/tests/common/test_requirements.py index c6676a16ba..79fed1f84d 100644 --- a/tests/common/test_requirements.py +++ b/tests/common/test_requirements.py @@ -6,7 +6,7 @@ def test_deps_consistency(): - IGNORE = ["flake8", "isort", "mypy", "importlib_metadata", "tensorflow-cpu"] + IGNORE = ["flake8", "isort", "mypy", "pydocstyle", "importlib_metadata", "tensorflow-cpu"] # Collect the deps from all requirements.txt REQ_FILES = ["requirements.txt", "requirements-pt.txt", "tests/requirements.txt", "docs/requirements.txt"] folder = Path(__file__).parent.parent.parent.absolute()