This is an automated email from the ASF dual-hosted git repository.
lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git
The following commit(s) were added to refs/heads/main by this push:
new 97765976 docs: enable linking to Javadoc via Intersphinx (#1381)
97765976 is described below
commit 97765976b65830159b9a9b52c92b160cb3b7987b
Author: David Li <[email protected]>
AuthorDate: Wed Dec 20 12:50:12 2023 -0500
docs: enable linking to Javadoc via Intersphinx (#1381)
- Generate a Sphinx Intersphinx inventory by scraping generated
Javadocs.
- Inject the location of this inventory in CI.
- Inject a fake URL to use for the location of the Javadocs since we
don't know this at documentation build time.
- At website build time, replace the fake URL with the real URL.
The approach here is extensible to other languages.
Fixes #1357.
---
.github/workflows/native-unix.yml | 7 ++
ci/scripts/docs_build.sh | 23 +++--
ci/scripts/website_build.sh | 7 ++
docs/source/conf.py | 25 +++++
docs/source/ext/adbc_java_domain.py | 47 ++++++++++
docs/source/ext/javadoc_inventory.py | 173 +++++++++++++++++++++++++++++++++++
docs/source/format/specification.rst | 10 +-
7 files changed, 282 insertions(+), 10 deletions(-)
diff --git a/.github/workflows/native-unix.yml
b/.github/workflows/native-unix.yml
index 4d6c50c5..d8084da7 100644
--- a/.github/workflows/native-unix.yml
+++ b/.github/workflows/native-unix.yml
@@ -571,6 +571,13 @@ jobs:
shell: bash -l {0}
run: |
./ci/scripts/docs_build.sh "$(pwd)"
+ - name: Archive docs
+ uses: actions/upload-artifact@v3
+ with:
+ name: docs
+ retention-days: 2
+ path: |
+ docs/build/html
- name: Test Recipes (C++)
shell: bash -l {0}
run: |
diff --git a/ci/scripts/docs_build.sh b/ci/scripts/docs_build.sh
index 7294e6d6..271dec45 100755
--- a/ci/scripts/docs_build.sh
+++ b/ci/scripts/docs_build.sh
@@ -25,15 +25,26 @@ main() {
doxygen
popd
- pushd "$source_dir/docs"
- make html
- make doctest
- popd
-
pushd "$source_dir/java"
mvn site
+ popd
+
+ pushd "$source_dir/docs"
+ # The project name/version don't really matter here.
+ python "$source_dir/docs/source/ext/javadoc_inventory.py" \
+ "ADBC" \
+ "version" \
+ "$source_dir/java/target/site/apidocs" \
+ "java/api"
+
+ # We need to determine the base URL without knowing it...
+ # Inject a dummy URL here, and fix it up in website_build.sh
+ export
ADBC_INTERSPHINX_MAPPING_java_adbc="http://javadocs.home.arpa/;$source_dir/java/target/site/apidocs/objects.inv"
+
+ make html
rm -rf "$source_dir/docs/build/html/java/api"
- cp -r target/site/apidocs "$source_dir/docs/build/html/java/api"
+ cp -r "$source_dir/java/target/site/apidocs"
"$source_dir/docs/build/html/java/api"
+ make doctest
popd
for desc_file in $(find "${source_dir}/r" -name DESCRIPTION); do
diff --git a/ci/scripts/website_build.sh b/ci/scripts/website_build.sh
index 480c57c9..31db174a 100755
--- a/ci/scripts/website_build.sh
+++ b/ci/scripts/website_build.sh
@@ -47,16 +47,23 @@ main() {
fi
local -r regex='^([0-9]+\.[0-9]+\.[0-9]+)$'
+ local directory="main"
if [[ "${new_version}" =~ $regex ]]; then
cp -r "${docs}" "${site}/${new_version}"
git -C "${site}" add --force "${new_version}"
+ directory="${new_version}"
else
# Assume this is dev docs
rm -rf "${site}/main"
cp -r "${docs}" "${site}/main"
git -C "${site}" add --force "main"
+ directory="main"
fi
+ # Fix up lazy Intersphinx links (see docs_build.sh)
+ # Assumes GNU sed
+ sed -i
"s|http://javadocs.home.arpa/|https://arrow.apache.org/adbc/${directory}/|g"
$(grep -Rl javadocs.home.arpa "${site}/${directory}/")
+
# Copy the version script and regenerate the version list
# The versions get embedded into the JavaScript file to save a roundtrip
rm -f "${site}/version.js"
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 6e4858a1..128dfc73 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -15,6 +15,7 @@
# specific language governing permissions and limitations
# under the License.
+import os
import sys
from pathlib import Path
@@ -35,7 +36,10 @@ version = release
exclude_patterns = []
extensions = [
+ # recipe directive
"adbc_cookbook",
+ # generic directives to enable intersphinx for java
+ "adbc_java_domain",
"breathe",
"numpydoc",
"sphinx.ext.autodoc",
@@ -111,6 +115,27 @@ intersphinx_mapping = {
"arrow": ("https://arrow.apache.org/docs/", None),
}
+# Add env vars like ADBC_INTERSPHINX_MAPPING_adbc_java = url;path
+# to inject more mappings
+
+
+def _find_intersphinx_mappings():
+ prefix = "ADBC_INTERSPHINX_MAPPING_"
+ for key, val in os.environ.items():
+ if key.startswith(prefix):
+ name = key[len(prefix) :]
+ url, _, path = val.partition(";")
+ print("[ADBC] Found Intersphinx mapping", name)
+ intersphinx_mapping[name] = (url, path)
+ # "adbc_java": (
+ # "http://localhost:8000/",
+ #
"/home/lidavidm/Code/arrow-adbc/java/target/site/apidocs/objects.inv",
+ # ),
+
+
+_find_intersphinx_mappings()
+
+
# -- Options for numpydoc ----------------------------------------------------
numpydoc_class_members_toctree = False
diff --git a/docs/source/ext/adbc_java_domain.py
b/docs/source/ext/adbc_java_domain.py
new file mode 100644
index 00000000..186141fc
--- /dev/null
+++ b/docs/source/ext/adbc_java_domain.py
@@ -0,0 +1,47 @@
+# 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.
+
+"""A basic Java domain for Sphinx."""
+
+import typing
+
+from sphinx.application import Sphinx
+
+
+def setup(app: Sphinx) -> dict[str, typing.Any]:
+ # XXX: despite documentation, this is added to 'std' domain not 'rst'
+ # domain (look at the source)
+ app.add_object_type(
+ "javatype",
+ "jtype",
+ objname="Java Type",
+ )
+ app.add_object_type(
+ "javamember",
+ "jmember",
+ objname="Java Member",
+ )
+ app.add_object_type(
+ "javapackage",
+ "jpackage",
+ objname="Java Package",
+ )
+ return {
+ "version": "0.1",
+ "parallel_read_safe": True,
+ "parallel_write_safe": True,
+ }
diff --git a/docs/source/ext/javadoc_inventory.py
b/docs/source/ext/javadoc_inventory.py
new file mode 100644
index 00000000..7c2fbcf2
--- /dev/null
+++ b/docs/source/ext/javadoc_inventory.py
@@ -0,0 +1,173 @@
+# 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.
+
+"""Generate a Sphinx inventory for a Javadoc site."""
+
+from __future__ import annotations
+
+import argparse
+import json
+import typing
+import urllib.parse
+from pathlib import Path
+
+import sphinx.util.inventory
+
+# XXX: we're taking advantage of duck typing to do stupid things here.
+
+
+class FakeEnv(typing.NamedTuple):
+ project: str
+ version: str
+
+
+class FakeObject(typing.NamedTuple):
+ # Looks like this
+ # name domainname:typ prio uri dispname
+ name: str
+ # written as '-' if equal to name
+ dispname: str
+ # member, doc, etc
+ typ: str
+ # passed through builder.get_target_uri
+ docname: str
+ # not including the #
+ anchor: str
+ # written, but never used
+ prio: str
+
+
+class FakeDomain(typing.NamedTuple):
+ objects: list[FakeObject]
+
+ def get_objects(self):
+ return self.objects
+
+
+class FakeBuildEnvironment(typing.NamedTuple):
+ config: FakeEnv
+ domains: dict[str, FakeDomain]
+
+
+class FakeBuilder:
+ def get_target_uri(self, docname: str) -> str:
+ return docname
+
+
+def extract_index(data: str, prelude: str) -> list:
+ epilogue = ";updateSearchResults();"
+ if not data.startswith(prelude):
+ raise ValueError(
+ f"Cannot parse search index; expected {prelude!r} but found
{data[:50]!r}"
+ )
+ if data.endswith(epilogue):
+ data = data[len(prelude) : -len(epilogue)]
+ else:
+ # Some JDK versions appear to generate without the epilogue
+ data = data[len(prelude) :]
+ return json.loads(data)
+
+
+def make_fake_domains(root: Path, base_url: str) -> dict[str, FakeDomain]:
+ if not base_url.endswith("/"):
+ base_url += "/"
+
+ # Scrape the search index generated by Javadoc
+ #
https://github.com/openjdk/jdk17/blob/4afbcaf55383ec2f5da53282a1547bac3d099e9d/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/IndexItem.java#L515
+ # "p" is containing package
+ # "m" is containing module
+ # "c" is containing class
+ # "l" is label
+ # "u" is the URL anchor
+
+ with open(root / "type-search-index.js") as f:
+ data = extract_index(f.read(), "typeSearchIndex = ")
+ with open(root / "member-search-index.js") as f:
+ data.extend(extract_index(f.read(), "memberSearchIndex = "))
+ with open(root / "package-search-index.js") as f:
+ data.extend(extract_index(f.read(), "packageSearchIndex = "))
+
+ domains = {
+ "std": FakeDomain(objects=[]),
+ }
+
+ for item in data:
+ if "p" not in item:
+ # Non-code item (package, or index)
+ if "All " in item["l"]:
+ # Ignore indices ("All Packages")
+ continue
+ # This is a package
+ name = item["l"]
+ url = f"{item['l'].replace('.', '/')}/package-summary.html"
+ anchor = ""
+ typ = "javapackage"
+ domain = "std"
+ elif "c" in item:
+ # This is a class member
+ name = f"{item['p']}.{item['c']}#{item['l']}"
+ url = f"{item['p'].replace('.', '/')}/{item['c']}.html"
+ anchor = item["u"] if "u" in item else item["l"]
+ typ = "javamember"
+ domain = "std"
+ else:
+ # This is a class/interface
+ name = f"{item['p']}.{item['l']}"
+ url = f"{item['p'].replace('.', '/')}/{item['l']}.html"
+ anchor = ""
+ typ = "javatype"
+ domain = "std"
+
+ url = urllib.parse.urljoin(base_url, url)
+ domains[domain].objects.append(
+ FakeObject(
+ name=name,
+ dispname=name,
+ typ=typ,
+ docname=url,
+ anchor=anchor,
+ prio=1,
+ )
+ )
+
+ return domains
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("project", help="Project name.")
+ parser.add_argument("version", help="Project version.")
+ parser.add_argument("path", type=Path, help="Path to the generated
Javadocs.")
+ parser.add_argument("url", help="Eventual base URL of the Javadocs.")
+
+ args = parser.parse_args()
+
+ domains = make_fake_domains(args.path, args.url)
+ config = FakeEnv(project=args.project, version=args.version)
+ env = FakeBuildEnvironment(config=config, domains=domains)
+
+ output = args.path / "objects.inv"
+ sphinx.util.inventory.InventoryFile.dump(
+ str(output),
+ env,
+ FakeBuilder(),
+ )
+ print("Wrote", output)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs/source/format/specification.rst
b/docs/source/format/specification.rst
index 19b73010..d1c91ea1 100644
--- a/docs/source/format/specification.rst
+++ b/docs/source/format/specification.rst
@@ -23,7 +23,9 @@ This document summarizes the general featureset.
- For C/C++ details, see :doc:`adbc.h <../../cpp/api/adbc>`.
- For Go details, see the `source
<https://github.com/apache/arrow-adbc/blob/main/go/adbc/adbc.go>`__.
-- For Java details, see the `source
<https://github.com/apache/arrow-adbc/tree/main/java/core>`__.
+- For Java details, see the `source
+ <https://github.com/apache/arrow-adbc/tree/main/java/core>`__, particularly
+ the package :jpackage:`org.apache.arrow.adbc.core`.
Databases
=========
@@ -34,7 +36,7 @@ provides a place to hold ownership of the in-memory database.
- C/C++: :cpp:class:`AdbcDatabase`
- Go: ``Driver``
-- Java: ``org.apache.arrow.adbc.core.AdbcDatabase``
+- Java: :jtype:`org.apache.arrow.adbc.core.AdbcDatabase`
Connections
===========
@@ -43,7 +45,7 @@ A connection is a single, logical connection to a database.
- C/C++: :cpp:class:`AdbcConnection`
- Go: ``Connection``
-- Java: ``org.apache.arrow.adbc.core.AdbcConnection``
+- Java: :jtype:`org.apache.arrow.adbc.core.AdbcConnection`
Autocommit
----------
@@ -55,7 +57,7 @@ implementations will support this.
- C/C++: :c:macro:`ADBC_CONNECTION_OPTION_AUTOCOMMIT`
- Go: ``OptionKeyAutoCommit``
-- Java: ``org.apache.arrow.adbc.core.AdbcConnection#setAutoCommit(boolean)``
+- Java:
:jmember:`org.apache.arrow.adbc.core.AdbcConnection#setAutoCommit(boolean)`
Metadata
--------