From 0a3ff43d41d33d05fb3996e61785919effa9a2fa Mon Sep 17 00:00:00 2001 From: Daniel Standish <15932138+dstandish@users.noreply.github.com> Date: Tue, 8 Feb 2022 15:07:54 -0800 Subject: [PATCH] Add pre-commit check for docstring param types (#21398) Param types can now be inferred by sphinx from type annotations, so we no longer need them in docstrings. This pre-commit check fails when type declarations are included in docstrings, and seems to do a reasonable job of not catching false positives. --- .pre-commit-config.yaml | 9 +++ BREEZE.rst | 14 ++--- STATIC_CODE_CHECKS.rst | 2 + airflow/jobs/backfill_job.py | 1 - airflow/models/dag.py | 3 - airflow/providers/amazon/aws/hooks/s3.py | 1 - airflow/providers/amazon/aws/operators/eks.py | 5 -- .../backcompat/pod_runtime_info_env.py | 2 - .../elasticsearch/log/es_task_handler.py | 1 - airflow/providers/github/hooks/github.py | 1 - airflow/providers/github/operators/github.py | 4 -- airflow/providers/github/sensors/github.py | 9 --- airflow/providers/google/cloud/hooks/gcs.py | 1 - .../cloud/log/stackdriver_task_handler.py | 2 - .../google/cloud/operators/dataproc.py | 4 -- .../providers/google/cloud/operators/gcs.py | 1 - .../google/cloud/operators/vision.py | 2 - .../cloud/utils/credentials_provider.py | 1 - airflow/providers/http/operators/http.py | 2 - .../microsoft/azure/hooks/base_azure.py | 1 - .../providers/microsoft/psrp/hooks/psrp.py | 7 --- airflow/sensors/smart_sensor.py | 16 +---- breeze-complete | 1 + .../src/airflow_breeze/pre_commit_ids.py | 1 + .../pre_commit_docstring_param_type.py | 58 +++++++++++++++++++ 25 files changed, 81 insertions(+), 68 deletions(-) create mode 100755 scripts/ci/pre_commit/pre_commit_docstring_param_type.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a8844fe4238fb..259303c3073e8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -690,6 +690,15 @@ repos: pass_filenames: true files: \.github/workflows/.*\.yml$ additional_dependencies: ['PyYAML', 'rich'] + - id: docstring-params + name: Check that docstrings do not specify param types + entry: ./scripts/ci/pre_commit/pre_commit_docstring_param_type.py + language: python + pass_filenames: true + files: \.py$ + pass_filenames: true + exclude: ^airflow/_vendor/ + additional_dependencies: ['rich'] - id: chart-schema-lint name: Lint chart/values.schema.json file entry: ./scripts/ci/pre_commit/pre_commit_chart_schema.py diff --git a/BREEZE.rst b/BREEZE.rst index c45004e1f7720..ffb72c74da24f 100644 --- a/BREEZE.rst +++ b/BREEZE.rst @@ -2188,13 +2188,13 @@ This is the current syntax for `./breeze <./breeze>`_: changelog-duplicates check-apache-license check-builtin-literals check-executables-have-shebangs check-extras-order check-hooks-apply check-integrations check-merge-conflict check-xml daysago-import-check - debug-statements detect-private-key doctoc dont-use-safe-filter end-of-file-fixer - fix-encoding-pragma flake8 flynt codespell forbid-tabs helm-lint identity - incorrect-use-of-LoggingMixin insert-license isort json-schema language-matters - lint-dockerfile lint-openapi markdownlint mermaid mixed-line-ending mypy mypy-helm - no-providers-in-core-examples no-relative-imports persist-credentials-disabled - pre-commit-descriptions pre-commit-hook-names pretty-format-json - provide-create-sessions providers-changelogs providers-init-file + debug-statements detect-private-key docstring-params doctoc dont-use-safe-filter + end-of-file-fixer fix-encoding-pragma flake8 flynt codespell forbid-tabs helm-lint + identity incorrect-use-of-LoggingMixin insert-license isort json-schema + language-matters lint-dockerfile lint-openapi markdownlint mermaid mixed-line-ending + mypy mypy-helm no-providers-in-core-examples no-relative-imports + persist-credentials-disabled pre-commit-descriptions pre-commit-hook-names + pretty-format-json provide-create-sessions providers-changelogs providers-init-file providers-subpackages-init-file provider-yamls pydevd pydocstyle python-no-log-warn pyupgrade restrict-start_date rst-backticks setup-order setup-extra-packages shellcheck sort-in-the-wild sort-spelling-wordlist stylelint trailing-whitespace diff --git a/STATIC_CODE_CHECKS.rst b/STATIC_CODE_CHECKS.rst index b65c8b790ad65..08449f7c90815 100644 --- a/STATIC_CODE_CHECKS.rst +++ b/STATIC_CODE_CHECKS.rst @@ -172,6 +172,8 @@ require Breeze Docker images to be installed locally. ------------------------------------ ---------------------------------------------------------------- ------------ ``detect-private-key`` Detects if private key is added to the repository ------------------------------------ ---------------------------------------------------------------- ------------ +``docstring-params`` Checks that param types not specified in docstring +------------------------------------ ---------------------------------------------------------------- ------------ ``doctoc`` Refreshes the table of contents for MD files ------------------------------------ ---------------------------------------------------------------- ------------ ``dont-use-safe-filter`` Don't use safe in templates diff --git a/airflow/jobs/backfill_job.py b/airflow/jobs/backfill_job.py index 10a5d08225977..c1bbb4341f87b 100644 --- a/airflow/jobs/backfill_job.py +++ b/airflow/jobs/backfill_job.py @@ -135,7 +135,6 @@ def __init__( :param run_backwards: Whether to process the dates from most to least recent :param run_at_least_once: If true, always run the DAG at least once even if no logical run exists within the time range. - :type: bool :param args: :param kwargs: """ diff --git a/airflow/models/dag.py b/airflow/models/dag.py index 5cf1731dc6c8c..71cd2a6c4734d 100644 --- a/airflow/models/dag.py +++ b/airflow/models/dag.py @@ -2207,12 +2207,9 @@ def run( :param verbose: Make logging output more verbose :param conf: user defined dictionary passed from CLI :param rerun_failed_tasks: - :type: bool :param run_backwards: - :type: bool :param run_at_least_once: If true, always run the DAG at least once even if no logical run exists within the time range. - :type: bool """ from airflow.jobs.backfill_job import BackfillJob diff --git a/airflow/providers/amazon/aws/hooks/s3.py b/airflow/providers/amazon/aws/hooks/s3.py index 0f3c0451a42f4..9d3c60403310f 100644 --- a/airflow/providers/amazon/aws/hooks/s3.py +++ b/airflow/providers/amazon/aws/hooks/s3.py @@ -132,7 +132,6 @@ def parse_s3_url(s3url: str) -> Tuple[str, str]: Parses the S3 Url into a bucket name and key. :param s3url: The S3 Url to parse. - :rtype s3url: str :return: the parsed bucket name and key :rtype: tuple of str """ diff --git a/airflow/providers/amazon/aws/operators/eks.py b/airflow/providers/amazon/aws/operators/eks.py index acc323ea176d4..ef40cab2e1e8a 100644 --- a/airflow/providers/amazon/aws/operators/eks.py +++ b/airflow/providers/amazon/aws/operators/eks.py @@ -77,7 +77,6 @@ class EksCreateClusterOperator(BaseOperator): :param compute: The type of compute architecture to generate along with the cluster. (templated) Defaults to 'nodegroup' to generate an EKS Managed Nodegroup. :param create_cluster_kwargs: Optional parameters to pass to the CreateCluster API (templated) - :type: Dict :param aws_conn_id: The Airflow connection used for AWS credentials. (templated) If this is None or empty then the default boto3 behaviour is used. If running Airflow in a distributed manner and aws_conn_id is None or @@ -92,7 +91,6 @@ class EksCreateClusterOperator(BaseOperator): :param nodegroup_role_arn: *REQUIRED* The Amazon Resource Name (ARN) of the IAM role to associate with the Amazon EKS managed node group. (templated) :param create_nodegroup_kwargs: Optional parameters to pass to the CreateNodegroup API (templated) - :type: Dict If compute is assigned the value of 'fargate': @@ -103,7 +101,6 @@ class EksCreateClusterOperator(BaseOperator): :param fargate_selectors: The selectors to match for pods to use this AWS Fargate profile. (templated) :param create_fargate_profile_kwargs: Optional parameters to pass to the CreateFargateProfile API (templated) - :type: Dict """ @@ -241,7 +238,6 @@ class EksCreateNodegroupOperator(BaseOperator): :param nodegroup_role_arn: The Amazon Resource Name (ARN) of the IAM role to associate with the managed nodegroup. (templated) :param create_nodegroup_kwargs: Optional parameters to pass to the Create Nodegroup API (templated) - :type: Dict :param aws_conn_id: The Airflow connection used for AWS credentials. (templated) If this is None or empty then the default boto3 behaviour is used. If running Airflow in a distributed manner and aws_conn_id is None or @@ -324,7 +320,6 @@ class EksCreateFargateProfileOperator(BaseOperator): :param fargate_profile_name: The unique name to give your AWS Fargate profile. (templated) :param create_fargate_profile_kwargs: Optional parameters to pass to the CreateFargate Profile API (templated) - :type: Dict :param aws_conn_id: The Airflow connection used for AWS credentials. (templated) If this is None or empty then the default boto3 behaviour is used. If diff --git a/airflow/providers/cncf/kubernetes/backcompat/pod_runtime_info_env.py b/airflow/providers/cncf/kubernetes/backcompat/pod_runtime_info_env.py index 47d96c15e9c29..f08aecff33a89 100644 --- a/airflow/providers/cncf/kubernetes/backcompat/pod_runtime_info_env.py +++ b/airflow/providers/cncf/kubernetes/backcompat/pod_runtime_info_env.py @@ -40,9 +40,7 @@ def __init__(self, name, field_path): Full list of options can be found in kubernetes documentation. :param name: the name of the environment variable - :type: name: str :param field_path: path to pod runtime info. Ex: metadata.namespace | status.podIP - :type: field_path: str """ self.name = name self.field_path = field_path diff --git a/airflow/providers/elasticsearch/log/es_task_handler.py b/airflow/providers/elasticsearch/log/es_task_handler.py index c3f8d4f3fb2b4..35fed06bfbb56 100644 --- a/airflow/providers/elasticsearch/log/es_task_handler.py +++ b/airflow/providers/elasticsearch/log/es_task_handler.py @@ -353,7 +353,6 @@ def get_external_log_url(self, task_instance: TaskInstance, try_number: int) -> Creates an address for an external log collecting service. :param task_instance: task instance object - :type: task_instance: TaskInstance :param try_number: task instance try_number to read logs from. :return: URL to the external log collection service :rtype: str diff --git a/airflow/providers/github/hooks/github.py b/airflow/providers/github/hooks/github.py index 5338f495f835c..3514b77d35b7e 100644 --- a/airflow/providers/github/hooks/github.py +++ b/airflow/providers/github/hooks/github.py @@ -31,7 +31,6 @@ class GithubHook(BaseHook): Performs a connection to GitHub and retrieves client. :param github_conn_id: Reference to :ref:`GitHub connection id `. - :type github_conn_id: str """ conn_name_attr = 'github_conn_id' diff --git a/airflow/providers/github/operators/github.py b/airflow/providers/github/operators/github.py index 170b9a8ae7b82..73e11810714c6 100644 --- a/airflow/providers/github/operators/github.py +++ b/airflow/providers/github/operators/github.py @@ -38,13 +38,9 @@ class GithubOperator(BaseOperator): :ref:`howto/operator:GithubOperator` :param github_conn_id: reference to a pre-defined GitHub Connection - :type github_conn_id: str :param github_method: method name from GitHub Python SDK to be called - :type github_method: str :param github_method_args: required method parameters for the github_method. (templated) - :type github_method_args: dict :param result_processor: function to further process the response from GitHub API - :type result_processor: function """ template_fields = ("github_method_args",) diff --git a/airflow/providers/github/sensors/github.py b/airflow/providers/github/sensors/github.py index 3d0265e23d003..ff1f269aa9e18 100644 --- a/airflow/providers/github/sensors/github.py +++ b/airflow/providers/github/sensors/github.py @@ -33,13 +33,9 @@ class GithubSensor(BaseSensorOperator): Base GithubSensor which can monitor for any change. :param github_conn_id: reference to a pre-defined Github Connection - :type github_conn_id: str :param method_name: method name from PyGithub to be executed - :type method_name: str :param method_params: parameters for the method method_name - :type method_params: dict :param result_processor: function that return boolean and act as a sensor response - :type result_processor: function """ def __init__( @@ -75,9 +71,7 @@ class BaseGithubRepositorySensor(GithubSensor): Base GitHub sensor at Repository level. :param github_conn_id: reference to a pre-defined GitHub Connection - :type github_conn_id: str :param repository_name: full qualified name of the repository to be monitored, ex. "apache/airflow" - :type repository_name: str """ def __init__( @@ -109,11 +103,8 @@ class GithubTagSensor(BaseGithubRepositorySensor): Monitors a github tag for its creation. :param github_conn_id: reference to a pre-defined Github Connection - :type github_conn_id: str :param tag_name: name of the tag to be monitored - :type tag_name: str :param repository_name: fully qualified name of the repository to be monitored, ex. "apache/airflow" - :type repository_name: str """ template_fields = ("tag_name",) diff --git a/airflow/providers/google/cloud/hooks/gcs.py b/airflow/providers/google/cloud/hooks/gcs.py index 8a1e9340477ba..9d6cf2ff722cd 100644 --- a/airflow/providers/google/cloud/hooks/gcs.py +++ b/airflow/providers/google/cloud/hooks/gcs.py @@ -662,7 +662,6 @@ def delete_bucket(self, bucket_name: str, force: bool = False) -> None: :param bucket_name: name of the bucket which will be deleted :param force: false not allow to delete non empty bucket, set force=True allows to delete non empty bucket - :type: bool """ client = self.get_conn() bucket = client.bucket(bucket_name) diff --git a/airflow/providers/google/cloud/log/stackdriver_task_handler.py b/airflow/providers/google/cloud/log/stackdriver_task_handler.py index 37365cd33fcbf..2a477182ccce3 100644 --- a/airflow/providers/google/cloud/log/stackdriver_task_handler.py +++ b/airflow/providers/google/cloud/log/stackdriver_task_handler.py @@ -215,7 +215,6 @@ def _prepare_log_filter(self, ti_labels: Dict[str, str]) -> str: https://cloud.google.com/logging/docs/view/advanced-queries :param ti_labels: Task Instance's labels that will be used to search for logs - :type: Dict[str, str] :return: logs filter """ @@ -331,7 +330,6 @@ def get_external_log_url(self, task_instance: TaskInstance, try_number: int) -> """ Creates an address for an external log collecting service. :param task_instance: task instance object - :type: task_instance: TaskInstance :param try_number: task instance try_number to read logs from. :return: URL to the external log collection service :rtype: str diff --git a/airflow/providers/google/cloud/operators/dataproc.py b/airflow/providers/google/cloud/operators/dataproc.py index 4bb54e9e440a5..ce6e96e5b7042 100644 --- a/airflow/providers/google/cloud/operators/dataproc.py +++ b/airflow/providers/google/cloud/operators/dataproc.py @@ -2035,15 +2035,11 @@ class DataprocCreateBatchOperator(BaseOperator): Creates a batch workload. :param project_id: Required. The ID of the Google Cloud project that the cluster belongs to. (templated) - :type project_id: str :param region: Required. The Cloud Dataproc region in which to handle the request. (templated) - :type region: str :param batch: Required. The batch to create. (templated) - :type batch: google.cloud.dataproc_v1.types.Batch :param batch_id: Optional. The ID to use for the batch, which will become the final component of the batch's resource name. This value must be 4-63 characters. Valid characters are /[a-z][0-9]-/. (templated) - :type batch_id: str :param request_id: Optional. A unique id used to identify the request. If the server receives two ``CreateBatchRequest`` requests with the same id, then the second request will be ignored and the first ``google.longrunning.Operation`` created and stored in the backend is returned. diff --git a/airflow/providers/google/cloud/operators/gcs.py b/airflow/providers/google/cloud/operators/gcs.py index 8b260715c25de..8565c89ec528f 100644 --- a/airflow/providers/google/cloud/operators/gcs.py +++ b/airflow/providers/google/cloud/operators/gcs.py @@ -865,7 +865,6 @@ class GCSDeleteBucketOperator(BaseOperator): :param bucket_name: name of the bucket which will be deleted :param force: false not allow to delete non empty bucket, set force=True allows to delete non empty bucket - :type: bool :param gcp_conn_id: The connection ID to use connecting to Google Cloud. :param impersonation_chain: Optional service account to impersonate using short-term credentials, or chained list of accounts required to get the access_token diff --git a/airflow/providers/google/cloud/operators/vision.py b/airflow/providers/google/cloud/operators/vision.py index 3f34917e7a038..b346b30b8b457 100644 --- a/airflow/providers/google/cloud/operators/vision.py +++ b/airflow/providers/google/cloud/operators/vision.py @@ -1043,7 +1043,6 @@ class CloudVisionAddProductToProductSetOperator(BaseOperator): :param product_id: (Required) The resource id of this Product. :param location: (Required) The region where the ProductSet is located. Valid regions (as of 2019-02-05) are: us-east1, us-west1, europe-west1, asia-east1 - :type: str :param project_id: (Optional) The project in which the Product is located. If set to None or missing, the default project_id from the Google Cloud connection is used. :param retry: (Optional) A retry object used to retry requests. If `None` is @@ -1127,7 +1126,6 @@ class CloudVisionRemoveProductFromProductSetOperator(BaseOperator): :param product_id: (Required) The resource id of this Product. :param location: (Required) The region where the ProductSet is located. Valid regions (as of 2019-02-05) are: us-east1, us-west1, europe-west1, asia-east1 - :type: str :param project_id: (Optional) The project in which the Product is located. If set to None or missing, the default project_id from the Google Cloud connection is used. :param retry: (Optional) A retry object used to retry requests. If `None` is diff --git a/airflow/providers/google/cloud/utils/credentials_provider.py b/airflow/providers/google/cloud/utils/credentials_provider.py index 907e8e1cdd307..0af03d24595c4 100644 --- a/airflow/providers/google/cloud/utils/credentials_provider.py +++ b/airflow/providers/google/cloud/utils/credentials_provider.py @@ -218,7 +218,6 @@ def get_credentials_and_project(self) -> Tuple[google.auth.credentials.Credentia Get current credentials and project ID. :return: Google Auth Credentials - :type: Tuple[google.auth.credentials.Credentials, str] """ if self.key_path: credentials, project_id = self._get_credentials_using_key_path() diff --git a/airflow/providers/http/operators/http.py b/airflow/providers/http/operators/http.py index eedd8632dd360..0622b8e7d19e9 100644 --- a/airflow/providers/http/operators/http.py +++ b/airflow/providers/http/operators/http.py @@ -41,8 +41,6 @@ class SimpleHttpOperator(BaseOperator): :param method: The HTTP method to use, default = "POST" :param data: The data to pass. POST-data in POST/PUT and params in the URL for a GET request. (templated) - :type data: For POST/PUT, depends on the content-type parameter, - for GET a dictionary of key/value string pairs :param headers: The HTTP headers to be added to the GET request :param response_check: A check against the 'requests' response object. The callable takes the response object as the first positional argument diff --git a/airflow/providers/microsoft/azure/hooks/base_azure.py b/airflow/providers/microsoft/azure/hooks/base_azure.py index 3205809dd4b5d..85e634b633721 100644 --- a/airflow/providers/microsoft/azure/hooks/base_azure.py +++ b/airflow/providers/microsoft/azure/hooks/base_azure.py @@ -32,7 +32,6 @@ class AzureBaseHook(BaseHook): :param sdk_client: The SDKClient to use. :param conn_id: The :ref:`Azure connection id` which refers to the information to connect to the service. - :type: str """ conn_name_attr = 'azure_conn_id' diff --git a/airflow/providers/microsoft/psrp/hooks/psrp.py b/airflow/providers/microsoft/psrp/hooks/psrp.py index 98f9190bc7c96..d5f70b7867892 100644 --- a/airflow/providers/microsoft/psrp/hooks/psrp.py +++ b/airflow/providers/microsoft/psrp/hooks/psrp.py @@ -47,30 +47,23 @@ class PsrpHook(BaseHook): sessions. :param psrp_conn_id: Required. The name of the PSRP connection. - :type psrp_conn_id: str :param logging_level: Logging level for message streams which are received during remote execution. The default is to include all messages in the task log. - :type logging_level: int :param operation_timeout: Override the default WSMan timeout when polling the pipeline. - :type operation_timeout: float :param runspace_options: Optional dictionary which is passed when creating the runspace pool. See :py:class:`~pypsrp.powershell.RunspacePool` for a description of the available options. - :type runspace_options: dict :param wsman_options: Optional dictionary which is passed when creating the `WSMan` client. See :py:class:`~pypsrp.wsman.WSMan` for a description of the available options. - :type wsman_options: dict :param on_output_callback: Optional callback function to be called whenever an output response item is received during job status polling. - :type on_output_callback: OutputCallback :param exchange_keys: If true (default), automatically initiate a session key exchange when the hook is used as a context manager. - :type exchange_keys: bool You can provide an alternative `configuration_name` using either `runspace_options` or by setting this key as the extra fields of your connection. diff --git a/airflow/sensors/smart_sensor.py b/airflow/sensors/smart_sensor.py index 63c394e71814a..23bce578a8f26 100644 --- a/airflow/sensors/smart_sensor.py +++ b/airflow/sensors/smart_sensor.py @@ -251,10 +251,7 @@ def set_infra_failure_timeout(self): self._infra_failure_timeout = timezone.utcnow() + self._infra_failure_retry_window def should_fail_current_run(self): - """ - :return: Should the sensor fail - :type: boolean - """ + """:return: Should the sensor fail""" return not self.is_infra_failure or timezone.utcnow() > self._infra_failure_timeout @property @@ -264,18 +261,11 @@ def exception_info(self): @property def is_infra_failure(self): - """ - - :return: If the exception is an infra failure - :type: boolean - """ + """:return: If the exception is an infra failure""" return self._is_infra_failure def is_expired(self): - """ - :return: If current exception need to be kept. - :type: boolean - """ + """:return: If current exception need to be kept.""" if not self._is_infra_failure: return True return timezone.utcnow() > self._infra_failure_timeout + datetime.timedelta(minutes=30) diff --git a/breeze-complete b/breeze-complete index 4049e1a3f935a..6b2f52ee9e8b2 100644 --- a/breeze-complete +++ b/breeze-complete @@ -97,6 +97,7 @@ check-xml daysago-import-check debug-statements detect-private-key +docstring-params doctoc dont-use-safe-filter end-of-file-fixer diff --git a/dev/breeze/src/airflow_breeze/pre_commit_ids.py b/dev/breeze/src/airflow_breeze/pre_commit_ids.py index 7dbb49ecc2f1c..99bc6a77430fc 100644 --- a/dev/breeze/src/airflow_breeze/pre_commit_ids.py +++ b/dev/breeze/src/airflow_breeze/pre_commit_ids.py @@ -45,6 +45,7 @@ 'daysago-import-check', 'debug-statements', 'detect-private-key', + 'docstring-params', 'doctoc', 'dont-use-safe-filter', 'end-of-file-fixer', diff --git a/scripts/ci/pre_commit/pre_commit_docstring_param_type.py b/scripts/ci/pre_commit/pre_commit_docstring_param_type.py new file mode 100755 index 0000000000000..38f7d4081c2d6 --- /dev/null +++ b/scripts/ci/pre_commit/pre_commit_docstring_param_type.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +from pathlib import Path + +from rich.console import Console + +if __name__ not in ("__main__", "__mp_main__"): + raise SystemExit( + "This file is intended to be executed as an executable program. You cannot use it as a module." + f"To run this script, run the ./{__file__} command [FILE] ..." + ) + + +console = Console(color_system="standard", width=200) + + +def _check_file(file: Path) -> list: + content = file.read_text() + return re.findall(r' +\:type .+?\:', content) + + +def _join_with_newline(list_): + return '\n'.join(list_) + + +if __name__ == '__main__': + error_list = [] + for file in sys.argv[1:]: + matches = _check_file(Path(file)) + if matches: + error_list.append((file, matches)) + if error_list: + error_message = '\n'.join([f"{f}: \n{_join_with_newline(m)}" for f, m in error_list]) + console.print( + f""" +[red]Found files with types specified in docstring. +This is no longer needed since sphinx can now infer types from type annotations.[/] +{error_message} +""" + ) + sys.exit(1)