This is an automated email from the ASF dual-hosted git repository. potiuk pushed a commit to branch v2-0-test in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v2-0-test by this push: new 601193f Less docker magic in docs building (#15176) 601193f is described below commit 601193f3bc47adae277535c1a54f2d81445b9d1c Author: Kamil BreguĊa <mik-...@users.noreply.github.com> AuthorDate: Tue Apr 6 03:10:42 2021 +0200 Less docker magic in docs building (#15176) (cherry picked from commit 3bd11631ff0fbff4859452513efe03674b04b141) --- .github/workflows/ci.yml | 12 -- docs/build_docs.py | 91 +++------------ docs/conf.py | 2 +- docs/exts/docs_build/code_utils.py | 16 +-- docs/exts/docs_build/docs_builder.py | 171 +++++++---------------------- docs/exts/docs_build/errors.py | 6 +- docs/exts/docs_build/run_patched_sphinx.py | 105 ++++++++++++++++++ docs/exts/docs_build/spelling_checks.py | 6 +- docs/exts/provider_init_hack.py | 10 +- scripts/ci/docs/ci_docs.sh | 15 +-- 10 files changed, 170 insertions(+), 264 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc98f5c..86bc960 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -463,24 +463,12 @@ ${{ hashFiles('.pre-commit-config.yaml') }}" env: RUNS_ON: ${{ fromJson(needs.build-info.outputs.runsOn) }} GITHUB_REGISTRY: ${{ needs.ci-images.outputs.githubRegistry }} - PYTHON_MAJOR_MINOR_VERSION: ${{needs.build-info.outputs.defaultPythonVersion}} steps: - name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )" uses: actions/checkout@v2 with: persist-credentials: false submodules: recursive - - name: "Setup python" - uses: actions/setup-python@v2 - with: - python-version: ${{needs.build-info.outputs.defaultPythonVersion}} - - uses: actions/cache@v2 - id: cache-venv-docs - with: - path: ./.docs-venv/ - key: ${{ runner.os }}-docs-venv-${{ hashFiles('setup.py', 'setup.cfg') }} - restore-keys: | - ${{ runner.os }}-docs-venv- - name: "Free space" run: ./scripts/ci/tools/ci_free_space_on_ci.sh - name: "Prepare CI image ${{env.PYTHON_MAJOR_MINOR_VERSION}}:${{ env.GITHUB_REGISTRY_PULL_IMAGE_TAG }}" diff --git a/docs/build_docs.py b/docs/build_docs.py index 59e1681..5f1a534 100755 --- a/docs/build_docs.py +++ b/docs/build_docs.py @@ -18,23 +18,15 @@ import argparse import multiprocessing import os -import platform import sys from collections import defaultdict -from subprocess import run from typing import Dict, List, NamedTuple, Optional, Tuple from rich.console import Console from tabulate import tabulate from docs.exts.docs_build import dev_index_generator, lint_checks # pylint: disable=no-name-in-module -from docs.exts.docs_build.code_utils import ( - CONSOLE_WIDTH, - DOCKER_PROJECT_DIR, - ROOT_PROJECT_DIR, - TEXT_RED, - TEXT_RESET, -) +from docs.exts.docs_build.code_utils import CONSOLE_WIDTH, PROVIDER_INIT_FILE, TEXT_RED, TEXT_RESET from docs.exts.docs_build.docs_builder import ( # pylint: disable=no-name-in-module DOCS_DIR, AirflowDocsBuilder, @@ -52,7 +44,7 @@ from docs.exts.docs_build.spelling_checks import ( # pylint: disable=no-name-in display_spelling_error_summary, ) -if __name__ != "__main__": +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." "To run this script, run the ./build_docs.py command" @@ -131,27 +123,13 @@ def _get_parser(): "--jobs", dest='jobs', type=int, - default=1, + default=0, help=( - """ - Number of parallel processes that will be spawned to build the docs. - - This is usually used in CI system only. Though you can also use it to run complete check - of the documntation locally if you have powerful local machine. - Default is 1 - which means that doc check runs sequentially, This is the default behaviour - because autoapi extension we use is not capable of running parallel builds at the same time using - the same source files. - - In parallel builds we are using dockerised version of image built from local sources but the image - has to be prepared locally (similarly as it is in CI) before you run the docs build. Any changes you - have done locally after building the image, will not be checked. - - Typically you run parallel build in this way if you want to quickly run complete check for all docs: + """\ + Number of parallel processes that will be spawned to build the docs. - ./breeze build-image --python 3.6 - ./docs/build-docs.py -j 0 - -""" + If passed 0, the value will be determined based on the number of CPUs. + """ ), ) parser.add_argument( @@ -174,7 +152,6 @@ class BuildSpecification(NamedTuple): package_name: str for_production: bool verbose: bool - dockerized: bool class BuildDocsResult(NamedTuple): @@ -202,7 +179,6 @@ def perform_docs_build_for_single_package(build_specification: BuildSpecificatio result = BuildDocsResult( package_name=build_specification.package_name, errors=builder.build_sphinx_docs( - dockerized=build_specification.dockerized, verbose=build_specification.verbose, ), log_file_name=builder.log_build_filename, @@ -219,7 +195,6 @@ def perform_spell_check_for_single_package(build_specification: BuildSpecificati result = SpellCheckResult( package_name=build_specification.package_name, errors=builder.check_spelling( - dockerized=build_specification.dockerized, verbose=build_specification.verbose, ), log_file_name=builder.log_spelling_filename, @@ -245,11 +220,6 @@ def build_docs_for_packages( builder = AirflowDocsBuilder(package_name=package_name, for_production=for_production) builder.clean_files() if jobs > 1: - if os.getenv('CI', '') == '': - console.print("[yellow] PARALLEL DOCKERIZED EXECUTION REQUIRES IMAGE TO BE BUILD BEFORE !!!![/]") - console.print("[yellow] Make sure that you've build the image before runnning docs build.[/]") - console.print("[yellow] otherwise local changes you've done will not be used during the check[/]") - console.print() run_in_parallel( all_build_errors, all_spelling_errors, @@ -289,7 +259,6 @@ def run_sequentially( build_specification=BuildSpecification( package_name=package_name, for_production=for_production, - dockerized=False, verbose=verbose, ) ) @@ -302,7 +271,6 @@ def run_sequentially( build_specification=BuildSpecification( package_name=package_name, for_production=for_production, - dockerized=False, verbose=verbose, ) ) @@ -323,15 +291,12 @@ def run_in_parallel( ): """Run both - spellcheck and docs build sequentially without multiprocessing""" pool = multiprocessing.Pool(processes=jobs) - # until we fix autoapi, we need to run parallel builds as dockerized images - dockerized = True if not spellcheck_only: run_docs_build_in_parallel( all_build_errors=all_build_errors, for_production=for_production, current_packages=current_packages, verbose=verbose, - dockerized=dockerized, pool=pool, ) if not docs_only: @@ -340,34 +305,8 @@ def run_in_parallel( for_production=for_production, current_packages=current_packages, verbose=verbose, - dockerized=dockerized, pool=pool, ) - fix_ownership() - - -def fix_ownership(): - """Fixes ownership for all files created with root user,""" - console.print("Fixing ownership for generated files") - python_version = os.getenv('PYTHON_MAJOR_MINOR_VERSION', "3.6") - fix_cmd = [ - "docker", - "run", - "--entrypoint", - "/bin/bash", - "--rm", - "-e", - f"HOST_OS={platform.system()}", - "-e" f"HOST_USER_ID={os.getuid()}", - "-e", - f"HOST_GROUP_ID={os.getgid()}", - "-v", - f"{ROOT_PROJECT_DIR}:{DOCKER_PROJECT_DIR}", - f"apache/airflow:master-python{python_version}-ci", - "-c", - "/opt/airflow/scripts/in_container/run_fix_ownership.sh", - ] - run(fix_cmd, check=True) def print_build_output(result: BuildDocsResult): @@ -386,7 +325,6 @@ def run_docs_build_in_parallel( for_production: bool, current_packages: List[str], verbose: bool, - dockerized: bool, pool, ): """Runs documentation building in parallel.""" @@ -399,7 +337,6 @@ def run_docs_build_in_parallel( package_name=package_name, for_production=for_production, verbose=verbose, - dockerized=dockerized, ) ) with with_group("Running docs building"): @@ -428,7 +365,6 @@ def run_spell_check_in_parallel( for_production: bool, current_packages: List[str], verbose: bool, - dockerized: bool, pool, ): """Runs spell check in parallel.""" @@ -437,12 +373,7 @@ def run_spell_check_in_parallel( for package_name in current_packages: console.print(f"[blue]{package_name:60}:[/] Scheduling spellchecking") spell_check_specifications.append( - BuildSpecification( - package_name=package_name, - for_production=for_production, - verbose=verbose, - dockerized=dockerized, - ) + BuildSpecification(package_name=package_name, for_production=for_production, verbose=verbose) ) with with_group("Running spell checking of documentation"): console.print() @@ -572,10 +503,14 @@ def main(): if not package_filters: _promote_new_flags() + if os.path.exists(PROVIDER_INIT_FILE): + os.remove(PROVIDER_INIT_FILE) + print_build_errors_and_exit( all_build_errors, all_spelling_errors, ) -main() +if __name__ == "__main__": + main() diff --git a/docs/conf.py b/docs/conf.py index 678f053..11708f9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -506,7 +506,7 @@ autoapi_keep_files = True # Relative path to output the AutoAPI files into. This can also be used to place the generated documentation # anywhere in your documentation hierarchy. -autoapi_root = f'{PACKAGE_NAME}/_api' +autoapi_root = '_api' # Whether to insert the generated documentation into the TOC tree. If this is False, the default AutoAPI # index page is not generated and you will need to include the generated documentation in a diff --git a/docs/exts/docs_build/code_utils.py b/docs/exts/docs_build/code_utils.py index 5c88797..adab5c2 100644 --- a/docs/exts/docs_build/code_utils.py +++ b/docs/exts/docs_build/code_utils.py @@ -22,12 +22,10 @@ from docs.exts.provider_yaml_utils import load_package_data ROOT_PROJECT_DIR = os.path.abspath( os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir, os.pardir) ) +PROVIDER_INIT_FILE = os.path.join(ROOT_PROJECT_DIR, "airflow", "providers", "__init__.py") DOCS_DIR = os.path.join(ROOT_PROJECT_DIR, "docs") AIRFLOW_DIR = os.path.join(ROOT_PROJECT_DIR, "airflow") -DOCKER_PROJECT_DIR = "/opt/airflow" -DOCKER_DOCS_DIR = os.path.join(DOCKER_PROJECT_DIR, "docs") -DOCKER_AIRFLOW_DIR = os.path.join(DOCKER_PROJECT_DIR, "/airflow") ALL_PROVIDER_YAMLS = load_package_data() AIRFLOW_SITE_DIR = os.environ.get('AIRFLOW_SITE_DIRECTORY') PROCESS_TIMEOUT = 8 * 60 # 400 seconds @@ -38,18 +36,6 @@ TEXT_RESET = '\033[0m' CONSOLE_WIDTH = 180 -def remap_from_docker(file_name: str, dockerized: bool): - """ - Remaps filename from Docker to Host. - :param file_name: name of file - :param dockerized: whether builds were running in docker environment. - :return: - """ - if dockerized and file_name.startswith(DOCKER_PROJECT_DIR): - return file_name.replace(DOCKER_PROJECT_DIR, ROOT_PROJECT_DIR) - return file_name - - def prepare_code_snippet(file_path: str, line_no: int, context_lines_count: int = 5) -> str: """ Prepares code snippet. diff --git a/docs/exts/docs_build/docs_builder.py b/docs/exts/docs_build/docs_builder.py index 0669c75..669d76d 100644 --- a/docs/exts/docs_build/docs_builder.py +++ b/docs/exts/docs_build/docs_builder.py @@ -28,9 +28,9 @@ from docs.exts.docs_build.code_utils import ( AIRFLOW_SITE_DIR, ALL_PROVIDER_YAMLS, CONSOLE_WIDTH, - DOCKER_DOCS_DIR, DOCS_DIR, PROCESS_TIMEOUT, + ROOT_PROJECT_DIR, pretty_format_path, ) from docs.exts.docs_build.errors import DocBuildError, parse_sphinx_warnings @@ -55,18 +55,10 @@ class AirflowDocsBuilder: return f"{DOCS_DIR}/_doctrees/docs/{self.package_name}" @property - def _docker_doctree_dir(self) -> str: - return f"{DOCKER_DOCS_DIR}/_doctrees/docs/{self.package_name}" - - @property def _inventory_cache_dir(self) -> str: return f"{DOCS_DIR}/_inventory_cache" @property - def _docker_inventory_cache_dir(self) -> str: - return f"{DOCKER_DOCS_DIR}/_inventory_cache" - - @property def is_versioned(self): """Is current documentation package versioned?""" # Disable versioning. This documentation does not apply to any released product and we can update @@ -87,49 +79,21 @@ class AirflowDocsBuilder: return os.path.join(self._build_dir, f"output-spelling-{self.package_name}.log") @property - def docker_log_spelling_filename(self) -> str: - """Log from spelling job in docker.""" - return os.path.join(self._docker_build_dir, f"output-spelling-{self.package_name}.log") - - @property def log_spelling_output_dir(self) -> str: """Results from spelling job.""" return os.path.join(self._build_dir, f"output-spelling-results-{self.package_name}") @property - def docker_log_spelling_output_dir(self) -> str: - """Results from spelling job in docker.""" - return os.path.join(self._docker_build_dir, f"output-spelling-results-{self.package_name}") - - @property def log_build_filename(self) -> str: """Log from build job.""" return os.path.join(self._build_dir, f"output-build-{self.package_name}.log") @property - def docker_log_build_filename(self) -> str: - """Log from build job in docker.""" - return os.path.join(self._docker_build_dir, f"output-build-{self.package_name}.log") - - @property def log_build_warning_filename(self) -> str: """Warnings from build job.""" return os.path.join(self._build_dir, f"warning-build-{self.package_name}.log") @property - def docker_log_warning_filename(self) -> str: - """Warnings from build job in docker.""" - return os.path.join(self._docker_build_dir, f"warning-build-{self.package_name}.log") - - @property - def _docker_build_dir(self) -> str: - if self.is_versioned: - version = "stable" if self.for_production else "latest" - return f"{DOCKER_DOCS_DIR}/_build/docs/{self.package_name}/{version}" - else: - return f"{DOCKER_DOCS_DIR}/_build/docs/{self.package_name}" - - @property def _current_version(self): if not self.is_versioned: raise Exception("This documentation package is not versioned") @@ -153,10 +117,6 @@ class AirflowDocsBuilder: def _src_dir(self) -> str: return f"{DOCS_DIR}/{self.package_name}" - @property - def _docker_src_dir(self) -> str: - return f"{DOCKER_DOCS_DIR}/{self.package_name}" - def clean_files(self) -> None: """Cleanup all artifacts generated by previous builds.""" api_dir = os.path.join(self._src_dir, "_api") @@ -166,58 +126,33 @@ class AirflowDocsBuilder: os.makedirs(api_dir, exist_ok=True) os.makedirs(self._build_dir, exist_ok=True) - def check_spelling(self, verbose: bool, dockerized: bool) -> List[SpellingError]: + def check_spelling(self, verbose: bool) -> List[SpellingError]: """ Checks spelling :param verbose: whether to show output while running - :param dockerized: whether to run dockerized build (required for paralllel processing on CI) :return: list of errors """ spelling_errors = [] os.makedirs(self._build_dir, exist_ok=True) shutil.rmtree(self.log_spelling_output_dir, ignore_errors=True) os.makedirs(self.log_spelling_output_dir, exist_ok=True) - if dockerized: - python_version = os.getenv('PYTHON_MAJOR_MINOR_VERSION', "3.6") - build_cmd = [ - "docker", - "run", - "--rm", - "-e", - "AIRFLOW_FOR_PRODUCTION", - "-e", - "AIRFLOW_PACKAGE_NAME", - "-v", - f"{self._build_dir}:{self._docker_build_dir}", - "-v", - f"{self._inventory_cache_dir}:{self._docker_inventory_cache_dir}", - "-w", - DOCKER_DOCS_DIR, - f"apache/airflow:master-python{python_version}-ci", - "/opt/airflow/scripts/in_container/run_anything.sh", - ] - else: - build_cmd = [] - - build_cmd.extend( - [ - "sphinx-build", - "-W", # turn warnings into errors - "--color", # do emit colored output - "-T", # show full traceback on exception - "-b", # builder to use - "spelling", - "-c", - DOCS_DIR if not dockerized else DOCKER_DOCS_DIR, - "-d", # path for the cached environment and doctree files - self._doctree_dir if not dockerized else self._docker_doctree_dir, - self._src_dir - if not dockerized - else self._docker_src_dir, # path to documentation source files - self.log_spelling_output_dir if not dockerized else self.docker_log_spelling_output_dir, - ] - ) + + build_cmd = [ + os.path.join(ROOT_PROJECT_DIR, "docs", "exts", "docs_build", "run_patched_sphinx.py"), + "-W", # turn warnings into errors + "--color", # do emit colored output + "-T", # show full traceback on exception + "-b", # builder to use + "spelling", + "-c", + DOCS_DIR, + "-d", # path for the cached environment and doctree files + self._doctree_dir, + self._src_dir, # path to documentation source files + self.log_spelling_output_dir, + ] + env = os.environ.copy() env['AIRFLOW_PACKAGE_NAME'] = self.package_name if self.for_production: @@ -246,7 +181,7 @@ class AirflowDocsBuilder: suggestion=None, context_line=None, message=( - f"Sphinx spellcheck returned non-zero exit status: " f"{completed_proc.returncode}." + f"Sphinx spellcheck returned non-zero exit status: {completed_proc.returncode}." ), ) ) @@ -254,69 +189,45 @@ class AirflowDocsBuilder: for filepath in glob(f"{self.log_spelling_output_dir}/**/*.spelling", recursive=True): with open(filepath) as spelling_file: warning_text += spelling_file.read() - spelling_errors.extend(parse_spelling_warnings(warning_text, self._src_dir, dockerized)) + + spelling_errors.extend(parse_spelling_warnings(warning_text, self._src_dir)) console.print(f"[blue]{self.package_name:60}:[/] [red]Finished spell-checking with errors[/]") else: if spelling_errors: console.print( - f"[blue]{self.package_name:60}:[/] [yellow]Finished spell-checking " f"with warnings[/]" + f"[blue]{self.package_name:60}:[/] [yellow]Finished spell-checking with warnings[/]" ) else: console.print( - f"[blue]{self.package_name:60}:[/] [green]Finished spell-checking " f"successfully[/]" + f"[blue]{self.package_name:60}:[/] [green]Finished spell-checking successfully[/]" ) return spelling_errors - def build_sphinx_docs(self, verbose: bool, dockerized: bool) -> List[DocBuildError]: + def build_sphinx_docs(self, verbose: bool) -> List[DocBuildError]: """ Build Sphinx documentation. :param verbose: whether to show output while running - :param dockerized: whether to run dockerized build (required for paralllel processing on CI) :return: list of errors """ build_errors = [] os.makedirs(self._build_dir, exist_ok=True) - if dockerized: - python_version = os.getenv('PYTHON_MAJOR_MINOR_VERSION', "3.6") - build_cmd = [ - "docker", - "run", - "--rm", - "-e", - "AIRFLOW_FOR_PRODUCTION", - "-e", - "AIRFLOW_PACKAGE_NAME", - "-v", - f"{self._build_dir}:{self._docker_build_dir}", - "-v", - f"{self._inventory_cache_dir}:{self._docker_inventory_cache_dir}", - "-w", - DOCKER_DOCS_DIR, - f"apache/airflow:master-python{python_version}-ci", - "/opt/airflow/scripts/in_container/run_anything.sh", - ] - else: - build_cmd = [] - build_cmd.extend( - [ - "sphinx-build", - "-T", # show full traceback on exception - "--color", # do emit colored output - "-b", # builder to use - "html", - "-d", # path for the cached environment and doctree files - self._doctree_dir if not dockerized else self._docker_doctree_dir, - "-c", - DOCS_DIR if not dockerized else DOCKER_DOCS_DIR, - "-w", # write warnings (and errors) to given file - self.log_build_warning_filename if not dockerized else self.docker_log_warning_filename, - self._src_dir - if not dockerized - else self._docker_src_dir, # path to documentation source files - self._build_dir if not dockerized else self._docker_build_dir, # path to output directory - ] - ) + + build_cmd = [ + os.path.join(ROOT_PROJECT_DIR, "docs", "exts", "docs_build", "run_patched_sphinx.py"), + "-T", # show full traceback on exception + "--color", # do emit colored output + "-b", # builder to use + "html", + "-d", # path for the cached environment and doctree files + self._doctree_dir, + "-c", + DOCS_DIR, + "-w", # write warnings (and errors) to given file + self.log_build_warning_filename, + self._src_dir, + self._build_dir, # path to output directory + ] env = os.environ.copy() env['AIRFLOW_PACKAGE_NAME'] = self.package_name if self.for_production: @@ -353,7 +264,7 @@ class AirflowDocsBuilder: warning_text = warning_file.read() # Remove 7-bit C1 ANSI escape sequences warning_text = re.sub(r"\x1B[@-_][0-?]*[ -/]*[@-~]", "", warning_text) - build_errors.extend(parse_sphinx_warnings(warning_text, self._src_dir, dockerized)) + build_errors.extend(parse_sphinx_warnings(warning_text, self._src_dir)) if build_errors: console.print(f"[blue]{self.package_name:60}:[/] [red]Finished docs building with errors[/]") else: diff --git a/docs/exts/docs_build/errors.py b/docs/exts/docs_build/errors.py index 954262d..1a2ae06 100644 --- a/docs/exts/docs_build/errors.py +++ b/docs/exts/docs_build/errors.py @@ -21,7 +21,7 @@ from typing import Dict, List, NamedTuple, Optional from rich.console import Console from airflow.utils.code_utils import prepare_code_snippet -from docs.exts.docs_build.code_utils import CONSOLE_WIDTH, remap_from_docker +from docs.exts.docs_build.code_utils import CONSOLE_WIDTH CURRENT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__))) DOCS_DIR = os.path.abspath(os.path.join(CURRENT_DIR, os.pardir, os.pardir)) @@ -82,7 +82,7 @@ def display_errors_summary(build_errors: Dict[str, List[DocBuildError]]) -> None console.print() -def parse_sphinx_warnings(warning_text: str, docs_dir: str, dockerized: bool) -> List[DocBuildError]: +def parse_sphinx_warnings(warning_text: str, docs_dir: str) -> List[DocBuildError]: """ Parses warnings from Sphinx. @@ -98,7 +98,7 @@ def parse_sphinx_warnings(warning_text: str, docs_dir: str, dockerized: bool) -> try: sphinx_build_errors.append( DocBuildError( - file_path=remap_from_docker(os.path.join(docs_dir, warning_parts[0]), dockerized), + file_path=os.path.join(docs_dir, warning_parts[0]), line_no=int(warning_parts[1]), message=warning_parts[2], ) diff --git a/docs/exts/docs_build/run_patched_sphinx.py b/docs/exts/docs_build/run_patched_sphinx.py new file mode 100755 index 0000000..887b982 --- /dev/null +++ b/docs/exts/docs_build/run_patched_sphinx.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# 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 os +import sys + +import autoapi +from autoapi.extension import ( + LOGGER, + ExtensionError, + bold, + darkgreen, + default_backend_mapping, + default_file_mapping, + default_ignore_patterns, +) +from sphinx.cmd.build import main + + +def run_autoapi(app): + """Load AutoAPI data from the filesystem.""" + if not app.config.autoapi_dirs: + raise ExtensionError("You must configure an autoapi_dirs setting") + + # Make sure the paths are full + normalized_dirs = [] + autoapi_dirs = app.config.autoapi_dirs + if isinstance(autoapi_dirs, str): + autoapi_dirs = [autoapi_dirs] + for path in autoapi_dirs: + if os.path.isabs(path): + normalized_dirs.append(path) + else: + normalized_dirs.append(os.path.normpath(os.path.join(app.confdir, path))) + + for _dir in normalized_dirs: + if not os.path.exists(_dir): + raise ExtensionError( + "AutoAPI Directory `{dir}` not found. " + "Please check your `autoapi_dirs` setting.".format(dir=_dir) + ) + + # Change from app.confdir to app.srcdir. + # Before: + # - normalized_root = os.path.normpath( + # - os.path.join(app.confdir, app.config.autoapi_root) + # -) + normalized_root = os.path.normpath(os.path.join(app.srcdir, app.config.autoapi_root)) + url_root = os.path.join("/", app.config.autoapi_root) + sphinx_mapper = default_backend_mapping[app.config.autoapi_type] + sphinx_mapper_obj = sphinx_mapper(app, template_dir=app.config.autoapi_template_dir, url_root=url_root) + app.env.autoapi_mapper = sphinx_mapper_obj + + if app.config.autoapi_file_patterns: + file_patterns = app.config.autoapi_file_patterns + else: + file_patterns = default_file_mapping.get(app.config.autoapi_type, []) + + if app.config.autoapi_ignore: + ignore_patterns = app.config.autoapi_ignore + else: + ignore_patterns = default_ignore_patterns.get(app.config.autoapi_type, []) + + if ".rst" in app.config.source_suffix: + out_suffix = ".rst" + elif ".txt" in app.config.source_suffix: + out_suffix = ".txt" + else: + # Fallback to first suffix listed + out_suffix = app.config.source_suffix[0] + + # Actual meat of the run. + LOGGER.info(bold("[AutoAPI] ") + darkgreen("Loading Data")) + sphinx_mapper_obj.load(patterns=file_patterns, dirs=normalized_dirs, ignore=ignore_patterns) + + LOGGER.info(bold("[AutoAPI] ") + darkgreen("Mapping Data")) + sphinx_mapper_obj.map(options=app.config.autoapi_options) + + if app.config.autoapi_generate_api_docs: + LOGGER.info(bold("[AutoAPI] ") + darkgreen("Rendering Data")) + sphinx_mapper_obj.output_rst(root=normalized_root, source_suffix=out_suffix) + + +# HACK: sphinx-auto map did not correctly use the confdir attribute instead of srcdir when specifying the +# directory to contain the generated files. +# Unfortunately we have a problem updating to a newer version of this library and we have to use +# sphinx-autoapi v1.0.0, so I am monkeypatching this library to fix this one problem. +autoapi.extension.run_autoapi = run_autoapi + +sys.exit(main(sys.argv[1:])) diff --git a/docs/exts/docs_build/spelling_checks.py b/docs/exts/docs_build/spelling_checks.py index 2be9cca..4d3c26d 100644 --- a/docs/exts/docs_build/spelling_checks.py +++ b/docs/exts/docs_build/spelling_checks.py @@ -23,7 +23,7 @@ from typing import Dict, List, NamedTuple, Optional from rich.console import Console from airflow.utils.code_utils import prepare_code_snippet -from docs.exts.docs_build.code_utils import CONSOLE_WIDTH, remap_from_docker +from docs.exts.docs_build.code_utils import CONSOLE_WIDTH CURRENT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__))) DOCS_DIR = os.path.abspath(os.path.join(CURRENT_DIR, os.pardir, os.pardir)) @@ -80,7 +80,7 @@ class SpellingError(NamedTuple): return left < right -def parse_spelling_warnings(warning_text: str, docs_dir: str, dockerized: bool) -> List[SpellingError]: +def parse_spelling_warnings(warning_text: str, docs_dir: str) -> List[SpellingError]: """ Parses warnings from Sphinx. @@ -99,7 +99,7 @@ def parse_spelling_warnings(warning_text: str, docs_dir: str, dockerized: bool) try: sphinx_spelling_errors.append( SpellingError( - file_path=remap_from_docker(os.path.join(docs_dir, warning_parts[0]), dockerized), + file_path=os.path.join(docs_dir, warning_parts[0]), line_no=int(warning_parts[1]) if warning_parts[1] not in ('None', '') else None, spelling=warning_parts[2], suggestion=warning_parts[3] if warning_parts[3] else None, diff --git a/docs/exts/provider_init_hack.py b/docs/exts/provider_init_hack.py index 0d88559..40f7fef 100644 --- a/docs/exts/provider_init_hack.py +++ b/docs/exts/provider_init_hack.py @@ -34,17 +34,12 @@ PROVIDER_INIT_FILE = os.path.join(ROOT_PROJECT_DIR, "airflow", "providers", "__i def _create_init_py(app, config): del app del config + # This file is deleted by /docs/build_docs.py. If you are not using the script, the file will be + # deleted by pre-commit. with open(PROVIDER_INIT_FILE, "wt"): pass -def _delete_init_py(app, exception): - del app - del exception - if os.path.exists(PROVIDER_INIT_FILE): - os.remove(PROVIDER_INIT_FILE) - - def setup(app: Sphinx): """ Sets the plugin up and returns configuration of the plugin. @@ -53,6 +48,5 @@ def setup(app: Sphinx): :return json description of the configuration that is needed by the plugin. """ app.connect("config-inited", _create_init_py) - app.connect("build-finished", _delete_init_py) return {"version": "builtin", "parallel_read_safe": True, "parallel_write_safe": True} diff --git a/scripts/ci/docs/ci_docs.sh b/scripts/ci/docs/ci_docs.sh index 003a8c2..be0d2ed 100755 --- a/scripts/ci/docs/ci_docs.sh +++ b/scripts/ci/docs/ci_docs.sh @@ -22,17 +22,4 @@ build_images::prepare_ci_build build_images::rebuild_ci_image_if_needed_with_group -start_end::group_start "Preparing venv for doc building" - -python3 -m venv .docs-venv -source .docs-venv/bin/activate -export PYTHONPATH=${AIRFLOW_SOURCES} - -pip install --upgrade pip==20.2.4 - -pip install .[doc] --upgrade --constraint \ - "https://raw.githubusercontent.com/apache/airflow/constraints-${DEFAULT_BRANCH}/constraints-${PYTHON_MAJOR_MINOR_VERSION}.txt" - -start_end::group_end - -"${AIRFLOW_SOURCES}/docs/build_docs.py" -j 0 "${@}" +runs::run_docs "${@}"