Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add objects.inv #522

Merged
merged 51 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
3dbbb7b
Squash commits
frankharkins Dec 14, 2023
217c706
Improve download logic
frankharkins Dec 22, 2023
2cddffc
Continue
frankharkins Dec 29, 2023
7c204c2
Merge branch 'main' of https://github.com/Qiskit/documentation into F…
frankharkins Dec 29, 2023
fbcbc93
lint :sparkles:
frankharkins Dec 29, 2023
4d6bd50
checkpoint
frankharkins Jan 2, 2024
f609020
Merge branch 'main' of https://github.com/Qiskit/documentation into F…
frankharkins Jan 2, 2024
a35e55a
Checkpoint
frankharkins Jan 2, 2024
eb6d46d
Re-fix merge conflict
frankharkins Jan 2, 2024
87f15e1
Fix and neaten tests
frankharkins Jan 3, 2024
7d90e7e
Remove tutorials from objects.inv
frankharkins Jan 3, 2024
da3a2f8
Remove console log
frankharkins Jan 3, 2024
490cd4b
Pull main
frankharkins Jan 10, 2024
aac8ca8
Fix tests
frankharkins Jan 10, 2024
8ccd12d
Handle special cases in objects.inv
frankharkins Jan 10, 2024
ccc5456
Add objects.inv files
frankharkins Jan 10, 2024
0e88fbf
pull main
frankharkins Jan 10, 2024
f1ad63d
Remove unneeded compress/expand (happens on read/write)
frankharkins Jan 10, 2024
f3da368
Fix relative filepaths
frankharkins Jan 10, 2024
8dbb25a
Ignore files from objects.inv
frankharkins Jan 10, 2024
a646ffe
Regen objects.inv
frankharkins Jan 10, 2024
ee58482
Disable link-checking in objects.inv
frankharkins Jan 11, 2024
b953189
Ignore more files
frankharkins Jan 11, 2024
fda5840
Convert anchors to lower case
frankharkins Jan 11, 2024
869ec07
Regen objects.inv
frankharkins Jan 11, 2024
994f9ed
Update snapshots
frankharkins Jan 11, 2024
3014893
Bugfixes
frankharkins Jan 12, 2024
b189efd
Move objects.inv files to /public
frankharkins Jan 12, 2024
ffef5fb
Rename 'markdown.ts' -> 'extractLinks.ts'
frankharkins Jan 12, 2024
c28c20a
Disable link checking
frankharkins Jan 12, 2024
1c8fc2c
Remove release notes and legacy release notes
frankharkins Jan 12, 2024
10f2c15
Apply suggestions from code review
frankharkins Jan 15, 2024
ada7bd4
Merge branch 'main' of https://github.com/Qiskit/documentation into F…
frankharkins Jan 15, 2024
6c66a6e
Merge branch 'FH/objects.inv' of https://github.com/Qiskit/documentat…
frankharkins Jan 15, 2024
acee94e
Feedback: allow passing directory only
frankharkins Jan 15, 2024
79de476
Feedback: Function naming
frankharkins Jan 15, 2024
5f79584
Feedback: Do not allow links to .inv files
frankharkins Jan 15, 2024
0131c9d
Feedback: Improve comment
frankharkins Jan 15, 2024
5078141
Update tests
frankharkins Jan 15, 2024
0698d16
Rename folder
frankharkins Jan 15, 2024
12ceb68
Use ignore.ts mechanism to ignore broken links
frankharkins Jan 15, 2024
0988f2c
Merge branch 'main' of https://github.com/Qiskit/documentation into F…
frankharkins Jan 15, 2024
81b0c17
Remove code duplication
frankharkins Jan 15, 2024
8ab0bab
Apply suggestions from code review
frankharkins Jan 17, 2024
860e191
Review suggestion: Always write to objects.inv
frankharkins Jan 17, 2024
21b7582
Review suggestion: _ => #
frankharkins Jan 17, 2024
ac85d66
Review suggestion: toMatchInlineSnapshot -> toEqual
frankharkins Jan 17, 2024
6f6bcd4
Fix link checker
frankharkins Jan 17, 2024
b47fd42
Merge branch 'main' of https://github.com/Qiskit/documentation into F…
frankharkins Jan 17, 2024
08596b6
Merge branch 'main' of https://github.com/Qiskit/documentation into F…
frankharkins Jan 17, 2024
7315df4
Update scripts/lib/api/objectsInv.ts
frankharkins Jan 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added public/api/qiskit-ibm-provider/objects.inv
Binary file not shown.
Binary file added public/api/qiskit-ibm-runtime/objects.inv
Binary file not shown.
Binary file added public/api/qiskit/objects.inv
Binary file not shown.
11 changes: 9 additions & 2 deletions scripts/commands/checkLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,10 @@ async function determineCurrentDocsFileBatch(
): Promise<FileBatch> {
const toCheck = [
"docs/**/*.{ipynb,md,mdx}",
"public/api/*/objects.inv",
// Ignore historical versions
"!docs/api/{qiskit,qiskit-ibm-provider,qiskit-ibm-runtime}/[0-9]*/*",
"!public/api/{qiskit,qiskit-ibm-provider,qiskit-ibm-runtime}/[0-9]*/*",
];
const toLoad = [
"docs/api/qiskit/0.44/{algorithms,opflow}.md",
Expand All @@ -127,7 +129,9 @@ async function determineCurrentDocsFileBatch(
];

if (!args.currentApis) {
toCheck.push("!docs/api/{qiskit,qiskit-ibm-provider,qiskit-ibm-runtime}/*");
toCheck.push(
"!{public,docs}/api/{qiskit,qiskit-ibm-provider,qiskit-ibm-runtime}/*",
);
toLoad.push("docs/api/{qiskit,qiskit-ibm-provider,qiskit-ibm-runtime}/*");
}

Expand Down Expand Up @@ -161,7 +165,10 @@ async function determineHistoricalFileBatches(
const result = [];
for (const folder of historicalFolders) {
const fileBatch = await FileBatch.fromGlobs(
[`docs/api/${projectName}/${folder.name}/*`],
[
`docs/api/${projectName}/${folder.name}/*`,
`public/api/${projectName}/${folder.name}/objects.inv`,
frankharkins marked this conversation as resolved.
Show resolved Hide resolved
],
toLoad,
`${projectName} v${folder.name}`,
);
Expand Down
5 changes: 4 additions & 1 deletion scripts/commands/updateApiDocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import yargs from "yargs/yargs";
import { hideBin } from "yargs/helpers";
import transformLinks from "transform-markdown-links";

import { ObjectsInv } from "../lib/api/objectsInv";
frankharkins marked this conversation as resolved.
Show resolved Hide resolved
import { sphinxHtmlToMarkdown } from "../lib/api/htmlToMd";
import { saveImages } from "../lib/api/saveImages";
import { generateToc } from "../lib/api/generateToc";
Expand Down Expand Up @@ -188,6 +189,7 @@ async function convertHtmlToMarkdown(
baseGitHubUrl: string,
pkg: Pkg,
) {
const objectsInv = await ObjectsInv.fromFile(htmlPath);
const files = await globby(
[
"apidocs/**.html",
Expand Down Expand Up @@ -245,10 +247,11 @@ async function convertHtmlToMarkdown(
results = await mergeClassMembers(results);
flattenFolders(results);
specialCaseResults(results);
await updateLinks(results, pkg.transformLink);
await updateLinks(results, objectsInv, pkg.transformLink);
await dedupeHtmlIdsFromResults(results);
addFrontMatter(results, pkg);

await objectsInv.write(getPkgRoot(pkg, "public"));
for (const result of results) {
let path = urlToPath(result.url);

Expand Down
127 changes: 127 additions & 0 deletions scripts/lib/api/objectsInv.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// This code is a Qiskit project.
//
// (C) Copyright IBM 2023.
//
// This code is licensed under the Apache License, Version 2.0. You may
// obtain a copy of this license in the LICENSE file in the root directory
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
//
// Any modifications or derivative works of this code must retain this
// copyright notice, and modified files need to carry a notice indicating
// that they have been altered from the originals.

import { describe, expect, test } from "@jest/globals";
import { ObjectsInv, ObjectsInvEntry } from "./objectsInv";
import { unlink, stat } from "fs/promises";

const TEST_FOLDER = "scripts/lib/api/testdata/";
const TEMP_FOLDER = "scripts/lib/api/testdata/temp/";

describe("objects.inv", () => {
test("read file and decompress", async () => {
const objectsInv = await ObjectsInv.fromFile(TEST_FOLDER);

expect(objectsInv.preamble).toMatch(
"# Sphinx inventory version 2\n" +
"# Project: Qiskit\n" +
"# Version: 0.45\n" +
"# The remainder of this file is compressed using zlib.\n",
);

const uriIndices = [10, 88, 107, 1419, 23575];
// This test fails when you include / exclude entries, which shifts some array indices.
// Use the following code to find the new indices.
// console.log(objectsInv.entries.findLastIndex( e => { return e.uri.includes("index") }))
expect(uriIndices.map((i) => objectsInv.entries[i].uri))
.toMatchInlineSnapshot(`
[
"stubs/qiskit.algorithms.AlgorithmJob.html#qiskit.algorithms.AlgorithmJob.job_id",
"stubs/qiskit.algorithms.FasterAmplitudeEstimation.html#qiskit.algorithms.FasterAmplitudeEstimation.sampler",
"stubs/qiskit.algorithms.Grover.html#qiskit.algorithms.Grover.quantum_instance",
"apidoc/assembler.html#qiskit.assembler.disassemble",
"index.html",
]
`);
const nameIndices = [23575, 24146];
expect(nameIndices.map((i) => objectsInv.entries[i].dispname))
.toMatchInlineSnapshot(`
[
"Qiskit 0.45 documentation",
"FakeOslo",
]
`);
});

test("write file and re-read matches original", async () => {
const originalObjectsInv = await ObjectsInv.fromFile(TEST_FOLDER);
await originalObjectsInv.write(TEMP_FOLDER);

const newObjectsInv = await ObjectsInv.fromFile(TEMP_FOLDER);
expect(originalObjectsInv.entries.length).toEqual(
newObjectsInv.entries.length,
);
expect(originalObjectsInv.preamble).toMatch(newObjectsInv.preamble);
expect(originalObjectsInv.entriesString()).toMatch(
newObjectsInv.entriesString(),
);
});

test("URI transform works correctly", () => {
const preamble = `# Simple preamble\n`;
// Use nonsense transform function to check things are actually changing
const transformFunction = (x: string) => x.replaceAll("i", "a");
const entries: ObjectsInvEntry[] = [
{
name: "qiskit_ibm_runtime.RuntimeJob.job_id",
domainAndRole: "py:method",
priority: "1",
uri: "qiskit_ibm_runtime.RuntimeJob#qiskit_ibm_runtime.RuntimeJob.job_id",
dispname: "-",
},
{
name: "stubs/qiskit_ibm_provider.transpiler.passes.scheduling.ASAPScheduleAnalysis.__call__",
domainAndRole: "std:doc",
priority: "-1",
uri: "stubs/qiskit_ibm_provider.transpiler.passes.scheduling.ASAPScheduleAnalysis.__call__.html",
dispname: "ASAPScheduleAnalysis.__call__",
},
{
name: "search",
domainAndRole: "std:label",
priority: "-1",
uri: "search.html",
dispname: "Search Page",
},
{
name: "release notes_ignis_0.5.0",
domainAndRole: "std:label",
priority: "-1",
uri: "legacy_release_notes.html#release-notes-ignis-0-5-0",
dispname: "Ignis 0.5.0",
},
{
name: "index",
domainAndRole: "std:doc",
priority: "-1",
uri: "index.html",
dispname: "Qiskit IBM Quantum Provider API docs preview",
},
];

const objectsInv = new ObjectsInv(preamble, entries);
objectsInv.updateUris(transformFunction);
expect(objectsInv.entries.map((i) => i.uri)).toEqual([
"qaskat_abm_runtame.RuntameJob#qaskat_abm_runtame.RuntameJob.job_ad",
"stubs/qaskat_abm_provader.transpaler.passes.schedulang.ASAPScheduleAnalysas.__call__",
"search",
"legacy_release_notes#release-notes-agnas-0-5-0",
"andex",
]);
});

afterAll(async () => {
if (await stat(TEMP_FOLDER + "objects.inv")) {
await unlink(TEMP_FOLDER + "objects.inv");
}
});
});
170 changes: 170 additions & 0 deletions scripts/lib/api/objectsInv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// This code is a Qiskit project.
//
// (C) Copyright IBM 2024.
//
// This code is licensed under the Apache License, Version 2.0. You may
// obtain a copy of this license in the LICENSE file in the root directory
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
//
// Any modifications or derivative works of this code must retain this
// copyright notice, and modified files need to carry a notice indicating
// that they have been altered from the originals.

import { readFile, writeFile } from "fs/promises";
import { unzipSync, deflateSync } from "zlib";
import { removeSuffix } from "../stringUtils";
import { join, dirname } from "path";
import { mkdirp } from "mkdirp";

/**
* Some pages exist in the sphinx docs but not in our docs
* If any URIs match these cases, we remove their entries.
**/
const ENTRIES_TO_EXCLUDE = [
frankharkins marked this conversation as resolved.
Show resolved Hide resolved
/^genindex(\.html)?$/,
/^py-modindex(\.html)?$/,
/^search(\.html)?$/,
/^explanation(\.html)?(?=\/|#|$)/,
/^how_to(\.html)?(?=\/|#|$)/,
/^tutorials(\.html)?(?=\/|#|$)/,
/^migration_guides(\.html)?(?=\/|#|$)/,
/^configuration(\.html)?(?=#|$)/,
/^contributing_to_qiskit(\.html)?(?=#|$)/,
/^deprecation_policy(\.html)?(?=#|$)/,
/^faq(\.html)?(?=#|$)/,
/^getting_started(\.html)?(?=#|$)/,
/^intro_tutorial1(\.html)?(?=#|$)/,
/^maintainers_guide(\.html)?(?=#|$)/,
/^qc_intro(\.html)?(?=#|$)/,
/^release[-_]notes(\.html)?(?=#|$)/,
/^legacy_release_notes(\.html)?(?=#|$)/,
];

function shouldExcludePage(uri: string): boolean {
return ENTRIES_TO_EXCLUDE.some((condition) => uri.match(condition));
}

export type ObjectsInvEntry = {
name: string;
domainAndRole: string;
priority: string;
uri: string;
dispname: string;
};

/**
* Class to hold and operate on data from Sphinx's objects.inv file.
* For information on the syntax, see:
* https://sphobjinv.readthedocs.io/en/stable/syntax.html
*/
export class ObjectsInv {
preamble: string;
entries: ObjectsInvEntry[];
constructor(preamble: string, entries: ObjectsInvEntry[]) {
frankharkins marked this conversation as resolved.
Show resolved Hide resolved
this.preamble = preamble;
this.entries = entries;
}

/**
* Decompress Sphinx's objects.inv.
* This function follows the process from:
* https://github.com/bskinn/sphobjinv/blob/stable/src/sphobjinv/zlib.py
*/
static async fromFile(directoryPath: string): Promise<ObjectsInv> {
const path = join(directoryPath, "objects.inv");
let buffer = await readFile(path);
// Extract preamble (first 4 lines of file)
let preamble = "";
for (let line = 0; line < 4; line++) {
let nextNewline = buffer.indexOf(10) + 1;
preamble += buffer.toString("utf8", 0, nextNewline);
buffer = buffer.subarray(nextNewline);
}

// Decompress the rest
const lines = unzipSync(buffer).toString("utf8").trim().split("\n");

// Sort the strings into their parts
const entries: ObjectsInvEntry[] = [];
for (const line of lines) {
// Regex from sphinx source
// https://github.com/sphinx-doc/sphinx/blob/2f60b44999d7e610d932529784f082fc1c6af989/sphinx/util/inventory.py#L115-L116
const parts = line.match(/(.+?)\s+(\S+)\s+(-?\d+)\s+?(\S*)\s+(.*)/);
if (parts == null || parts.length != 6) {
console.warn(`Error parsing line of objects.inv: ${line}`);
continue;
}
const entry = {
name: parts[1],
domainAndRole: parts[2],
priority: parts[3],
uri: parts[4],
dispname: parts[5],
};
entry.uri = ObjectsInv.#expandUri(entry.uri, entry.name);
if (shouldExcludePage(entry.uri)) {
continue;
}

entries.push(entry);
}

return new ObjectsInv(preamble, entries);
}

static #expandUri(uri: string, name: string): string {
if (uri.includes("#") && uri.endsWith("$")) {
// #$ is a shorthand for "anchor==name"; see "For illustration" in
// https://sphobjinv.readthedocs.io/en/stable/syntax.html
uri = removeSuffix(uri, "$") + name;
}
return uri;
}

static #compressUri(uri: string, name: string): string {
if (uri.includes("#") && uri.endsWith(name)) {
uri = removeSuffix(uri, name) + "$";
}
return uri;
}

updateUris(transformLink: Function): void {
for (const entry of this.entries) {
entry.uri = entry.uri.replace(/\.html/, "");
entry.uri = transformLink(entry.uri);
}
}

/**
* Return all entries joined together as a single string
* to be compressed before writing
*/
entriesString(): string {
const lines: string[] = [];
for (const e of this.entries) {
lines.push(
[
e.name,
e.domainAndRole,
e.priority,
ObjectsInv.#compressUri(e.uri, e.name),
e.dispname,
].join(" "),
);
}
return lines.join("\n");
}

/**
* Compress and write to file
*/
async write(directoryPath: string): Promise<void> {
const path = join(directoryPath, "objects.inv");
const preamble = Buffer.from(this.preamble);
const compressed = deflateSync(Buffer.from(this.entriesString(), "utf8"), {
level: 9,
});
await mkdirp(dirname(path));
await writeFile(path, Buffer.concat([preamble, compressed]));
}
}
1 change: 0 additions & 1 deletion scripts/lib/api/specialCaseResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ export function specialCaseResults(results: HtmlToMdResultWithUrl[]): void {
hardcodedFrontmatter: RUNTIME_INDEX_META,
};
}

result.url = transformSpecialCaseUrl(result.url);
}
}
Binary file added scripts/lib/api/testdata/objects.inv
Binary file not shown.
Loading