Skip to content

Commit

Permalink
Separate GitHub URL logic from HTML processing (#644)
Browse files Browse the repository at this point in the history
This is prework for fixing GitHub source code URLs for Qiskit back when
it was a metapackage. We need to link out to multiple GitHub
repositories for historical versions of Qiskit. So it's no longer viable
to have a single `baseGithubUrl` per `Pkg`—the `baseGithubUrl` depends
on the specific file in question, like `qiskit.aer.my_file` should go to
the Aer repository whereas `qiskit.transpiler` should go to Qiskit
Terra.

To prepare for that, this PR moves all the computation of the full
GitHub URL to live in `Pkg`. This is a cleaner interface anyways; it
separates out the URL computation from the HTML processing done inside
`processHtml.ts`.
  • Loading branch information
Eric-Arellano authored Jan 18, 2024
1 parent d959335 commit 0d660c5
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 40 deletions.
2 changes: 1 addition & 1 deletion scripts/commands/updateApiDocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ async function convertHtmlToMarkdown(
const result = await sphinxHtmlToMarkdown({
html,
fileName: file,
baseGitHubUrl: pkg.baseGitHubUrl(),
determineGithubUrl: pkg.determineGithubUrlFn(),
imageDestination: pkg.outputDir("/images"),
releaseNotesTitle: `${pkg.title} ${pkg.versionWithoutPatch} release notes`,
});
Expand Down
57 changes: 57 additions & 0 deletions scripts/lib/api/Pkg.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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 { expect, test } from "@jest/globals";

import { Pkg } from "./Pkg";

test("Pkg.determineGithubUrlFn()", () => {
const provider = Pkg.mock({
name: "qiskit-ibm-provider",
githubSlug: "qiskit/qiskit-ibm-provider",
versionWithoutPatch: "0.7",
}).determineGithubUrlFn();
const runtime = Pkg.mock({
name: "qiskit-ibm-runtime",
githubSlug: "qiskit/qiskit-ibm-runtime",
versionWithoutPatch: "0.15",
}).determineGithubUrlFn();
const qiskit = Pkg.mock({
name: "qiskit",
githubSlug: "qiskit/qiskit",
versionWithoutPatch: "0.45",
}).determineGithubUrlFn();

expect(provider("qiskit_ibm_provider/job/exceptions")).toEqual(
"https://github.com/qiskit/qiskit-ibm-provider/tree/stable/0.7/qiskit_ibm_provider/job/exceptions.py",
);
expect(provider("qiskit_ibm_provider")).toEqual(
"https://github.com/qiskit/qiskit-ibm-provider/tree/stable/0.7/qiskit_ibm_provider/__init__.py",
);

expect(runtime("qiskit_ibm_runtime/ibm_backend")).toEqual(
"https://github.com/qiskit/qiskit-ibm-runtime/tree/stable/0.15/qiskit_ibm_runtime/ibm_backend.py",
);

expect(qiskit("qiskit/exceptions")).toEqual(
"https://github.com/qiskit/qiskit/tree/stable/0.45/qiskit/exceptions.py",
);
expect(qiskit("qiskit/qasm2")).toEqual(
"https://github.com/qiskit/qiskit/tree/stable/0.45/qiskit/qasm2/__init__.py",
);
expect(qiskit("qiskit/qasm3")).toEqual(
"https://github.com/qiskit/qiskit/tree/stable/0.45/qiskit/qasm3/__init__.py",
);
expect(qiskit("qiskit/transpiler/preset_passmanagers")).toEqual(
"https://github.com/qiskit/qiskit/tree/stable/0.45/qiskit/transpiler/preset_passmanagers/__init__.py",
);
});
25 changes: 23 additions & 2 deletions scripts/lib/api/Pkg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,29 @@ export class Pkg {
return `${getRoot()}/.out/python/sources/${this.name}/${this.version}`;
}

baseGitHubUrl(): string {
return `https://github.com/${this.githubSlug}/tree/stable/${this.versionWithoutPatch}/`;
/**
* Returns a function that takes in a fileName like `qiskit_ibm_provider/job/exceptions` and returns the
* stable GitHub URL to the file for this package's version.
*
* The input fileName will not have a file extension. It will use whatever file name was generated by
* `sphinx.ext.viewcode`, which means we need to deal with its quirks like handling `__init__.py`.
*/
determineGithubUrlFn(): (fileName: string) => string {
// For files like `my_module/__init__.py`, `sphinx.ext.viewcode` will title the
// file `my_module.py`. We need to add back the `/__init__.py` when linking to GitHub.
const convertToInitPy = new Set([
"qiskit_ibm_provider",
"qiskit/qasm2",
"qiskit/qasm3",
"qiskit/transpiler/preset_passmanagers",
]);
const baseUrl = `https://github.com/${this.githubSlug}/tree/stable/${this.versionWithoutPatch}/`;
return (fileName) => {
if (convertToInitPy.has(fileName)) {
fileName = `${fileName}/__init__`;
}
return `${baseUrl}${fileName}.py`;
};
}
}

Expand Down
3 changes: 2 additions & 1 deletion scripts/lib/api/htmlToMd.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import { sphinxHtmlToMarkdown } from "./htmlToMd";

const DEFAULT_ARGS = {
imageDestination: "/images/qiskit",
baseGitHubUrl: "https://github.com/Qiskit/qiskit-ibm-runtime/tree/0.9.2/",
determineGithubUrl: (fileName: string) =>
`https://github.com/Qiskit/qiskit-ibm-runtime/tree/0.9.2/${fileName}.py`,
releaseNotesTitle: "My Quantum release notes",
};

Expand Down
3 changes: 1 addition & 2 deletions scripts/lib/api/htmlToMd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ export async function sphinxHtmlToMarkdown(options: {
html: string;
fileName: string;
imageDestination: string;
// E.g. https://github.com/Qiskit/qiskit-ibm-runtime/tree/0.9.2/
baseGitHubUrl: string;
determineGithubUrl: (fileName: string) => string;
releaseNotesTitle: string;
}): Promise<HtmlToMdResult> {
const processedHtml = processHtml(options);
Expand Down
21 changes: 6 additions & 15 deletions scripts/lib/api/processHtml.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,27 +232,18 @@ test("addLanguageClassToCodeBlocks()", () => {
test("replaceSourceLinksWithGitHub()", () => {
// Assumes that removeHtmlExtensionsInRelativeLinks() has already removed .html from the URL.
const doc = Doc.load(
`<a href="../_modules/qiskit_ibm_runtime/ibm_backend#IBMBackend"></a>
<a href="../_modules/qiskit_ibm_provider/job/exceptions#IBMJobApiError"></a>
<a href="../_modules/qiskit_ibm_provider#least_busy"></a>
<a href="../_modules/qiskit/qasm2#compile"></a>
<a href="../_modules/qiskit/qasm3#compile"></a>
<a href="../_modules/qiskit/transpiler/preset_passmanagers#foo"></a>
<a href="#qiskit_ibm_runtime.IBMBackend"></a>`,
`<a href="../_modules/my_quantum_project/my_file#IBMBackend"></a>
<a href="#my_quantum_project.IBMBackend"></a>`,
);
replaceViewcodeLinksWithGitHub(
doc.$,
doc.$main,
"https://github.com/Qiskit/my-project/tree/stable/0.9/",
(fileName) =>
`https://github.com/Qiskit/my-project/tree/stable/0.9/${fileName}.py`,
);
doc.expectHtml(
`<a href="https://github.com/Qiskit/my-project/tree/stable/0.9/qiskit_ibm_runtime/ibm_backend.py"></a>
<a href="https://github.com/Qiskit/my-project/tree/stable/0.9/qiskit_ibm_provider/job/exceptions.py"></a>
<a href="https://github.com/Qiskit/my-project/tree/stable/0.9/qiskit_ibm_provider/__init__.py"></a>
<a href="https://github.com/Qiskit/my-project/tree/stable/0.9/qiskit/qasm2/__init__.py"></a>
<a href="https://github.com/Qiskit/my-project/tree/stable/0.9/qiskit/qasm3/__init__.py"></a>
<a href="https://github.com/Qiskit/my-project/tree/stable/0.9/qiskit/transpiler/preset_passmanagers/__init__.py"></a>
<a href="#qiskit_ibm_runtime.IBMBackend"></a>`,
`<a href="https://github.com/Qiskit/my-project/tree/stable/0.9/my_quantum_project/my_file.py"></a>
<a href="#my_quantum_project.IBMBackend"></a>`,
);
});

Expand Down
31 changes: 12 additions & 19 deletions scripts/lib/api/processHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,16 @@ export function processHtml(options: {
html: string;
fileName: string;
imageDestination: string;
baseGitHubUrl: string;
determineGithubUrl: (fileName: string) => string;
releaseNotesTitle: string;
}): ProcessedHtml {
const { html, fileName, imageDestination, baseGitHubUrl, releaseNotesTitle } =
options;
const {
html,
fileName,
imageDestination,
determineGithubUrl,
releaseNotesTitle,
} = options;
const $ = load(html);
const $main = $(`[role='main']`);

Expand All @@ -47,7 +52,7 @@ export function processHtml(options: {
removeDownloadSourceCode($main);
handleSphinxDesignCards($, $main);
addLanguageClassToCodeBlocks($, $main);
replaceViewcodeLinksWithGitHub($, $main, baseGitHubUrl);
replaceViewcodeLinksWithGitHub($, $main, determineGithubUrl);
convertRubricsToHeaders($, $main);
processSimpleFieldLists($, $main);
removeColonSpans($main);
Expand Down Expand Up @@ -166,16 +171,8 @@ export function addLanguageClassToCodeBlocks(
export function replaceViewcodeLinksWithGitHub(
$: CheerioAPI,
$main: Cheerio<any>,
baseGitHubUrl: string,
determineGithubUrl: (fileName: string) => string,
): void {
// For files like `my_module/__init__.py`, `sphinx.ext.viewcode` will title the
// file `my_module.py`. We need to add back the `/__init__.py` when linking to GitHub.
const convertToInitPy = new Set([
"qiskit_ibm_provider",
"qiskit/qasm2",
"qiskit/qasm3",
"qiskit/transpiler/preset_passmanagers",
]);
$main.find("a").each((_, a) => {
const $a = $(a);
const href = $a.attr("href");
Expand All @@ -187,12 +184,8 @@ export function replaceViewcodeLinksWithGitHub(
return;
}
// E.g. `qiskit_ibm_runtime/ibm_backend`
let fullFileName = href.match(/_modules\/(.*?)(#|$)/)![1];
if (convertToInitPy.has(fullFileName)) {
fullFileName = `${fullFileName}/__init__`;
}
const newHref = `${baseGitHubUrl}${fullFileName}.py`;
$a.attr("href", newHref);
const fullFileName = href.match(/_modules\/(.*?)(#|$)/)![1];
$a.attr("href", determineGithubUrl(fullFileName));
});
}

Expand Down

0 comments on commit 0d660c5

Please sign in to comment.