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 links to Function/Class signatures #2774

Closed
wants to merge 12 commits into from
12 changes: 6 additions & 6 deletions docs/api/qiskit-addon-aqc-tensor/simulation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ In each function below, the documentation shows every distinct implementation av

###

<Function github="https://github.com/Qiskit/qiskit-addon-aqc-tensor/tree/stable/0.1/qiskit_addon_aqc_tensor/simulation/abstract.py" signature="tensornetwork_from_circuit(qc: qiskit.circuit.quantumcircuit.QuantumCircuit, settings: qiskit_addon_aqc_tensor.simulation.aer.simulation.QiskitAerSimulationSettings | plum.type.ModuleType[qiskit_aer.AerSimulator], /, *, out_state: numpy.ndarray | None = None) → qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS">
<Function github="https://github.com/Qiskit/qiskit-addon-aqc-tensor/tree/stable/0.1/qiskit_addon_aqc_tensor/simulation/abstract.py" signature="tensornetwork_from_circuit(qc: [qiskit.circuit.quantumcircuit.QuantumCircuit](/api/qiskit/qiskit.circuit.QuantumCircuit '(in Qiskit v1.2)'), settings: [qiskit_addon_aqc_tensor.simulation.aer.simulation.QiskitAerSimulationSettings](simulation-aer-qiskit-aer-simulation-settings 'qiskit_addon_aqc_tensor.simulation.aer.simulation.QiskitAerSimulationSettings') | plum.type.ModuleType[[qiskit_aer.AerSimulator](https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.AerSimulator.html#qiskit_aer.AerSimulator '(in Qiskit Aer v0.15.0)')], /, *, out_state: [numpy.ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray '(in NumPy v2.1)') | [None](https://docs.python.org/3/library/constants.html#None '(in Python v3.13)') = None) → [qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS](simulation-aer-qiskit-aer-mps 'qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS')">
**Parameters**

* **qc** ([*QuantumCircuit*](/api/qiskit/qiskit.circuit.QuantumCircuit "(in Qiskit v1.2)"))
Expand All @@ -51,7 +51,7 @@ In each function below, the documentation shows every distinct implementation av

###

<Function github="https://github.com/Qiskit/qiskit-addon-aqc-tensor/tree/stable/0.1/qiskit_addon_aqc_tensor/simulation/abstract.py" signature="tensornetwork_from_circuit(qc: qiskit.circuit.quantumcircuit.QuantumCircuit, settings: qiskit_addon_aqc_tensor.simulation.quimb.QuimbSimulator, /) → quimb.tensor.Circuit">
<Function github="https://github.com/Qiskit/qiskit-addon-aqc-tensor/tree/stable/0.1/qiskit_addon_aqc_tensor/simulation/abstract.py" signature="tensornetwork_from_circuit(qc: [qiskit.circuit.quantumcircuit.QuantumCircuit](/api/qiskit/qiskit.circuit.QuantumCircuit '(in Qiskit v1.2)'), settings: [qiskit_addon_aqc_tensor.simulation.quimb.QuimbSimulator](simulation-quimb-quimb-simulator 'qiskit_addon_aqc_tensor.simulation.quimb.QuimbSimulator'), /) → [quimb.tensor.Circuit](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.Circuit '(in quimb v1.9)')">
**Parameters**

* **qc** ([*QuantumCircuit*](/api/qiskit/qiskit.circuit.QuantumCircuit "(in Qiskit v1.2)"))
Expand Down Expand Up @@ -96,7 +96,7 @@ In each function below, the documentation shows every distinct implementation av

###

<Function github="https://github.com/Qiskit/qiskit-addon-aqc-tensor/tree/stable/0.1/qiskit_addon_aqc_tensor/simulation/abstract.py" signature="apply_circuit_to_state(qc: qiskit.circuit.quantumcircuit.QuantumCircuit, psi: qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS, settings: qiskit_addon_aqc_tensor.simulation.aer.simulation.QiskitAerSimulationSettings | plum.type.ModuleType[qiskit_aer.AerSimulator], /, *, out_state: numpy.ndarray | None = None) → qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS">
<Function github="https://github.com/Qiskit/qiskit-addon-aqc-tensor/tree/stable/0.1/qiskit_addon_aqc_tensor/simulation/abstract.py" signature="apply_circuit_to_state(qc: [qiskit.circuit.quantumcircuit.QuantumCircuit](/api/qiskit/qiskit.circuit.QuantumCircuit '(in Qiskit v1.2)'), psi: [qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS](simulation-aer-qiskit-aer-mps 'qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS'), settings: [qiskit_addon_aqc_tensor.simulation.aer.simulation.QiskitAerSimulationSettings](simulation-aer-qiskit-aer-simulation-settings 'qiskit_addon_aqc_tensor.simulation.aer.simulation.QiskitAerSimulationSettings') | plum.type.ModuleType[[qiskit_aer.AerSimulator](https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.AerSimulator.html#qiskit_aer.AerSimulator '(in Qiskit Aer v0.15.0)')], /, *, out_state: [numpy.ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray '(in NumPy v2.1)') | [None](https://docs.python.org/3/library/constants.html#None '(in Python v3.13)') = None) → [qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS](simulation-aer-qiskit-aer-mps 'qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS')">
**Parameters**

* **qc** ([*QuantumCircuit*](/api/qiskit/qiskit.circuit.QuantumCircuit "(in Qiskit v1.2)"))
Expand All @@ -111,7 +111,7 @@ In each function below, the documentation shows every distinct implementation av

###

<Function github="https://github.com/Qiskit/qiskit-addon-aqc-tensor/tree/stable/0.1/qiskit_addon_aqc_tensor/simulation/abstract.py" signature="apply_circuit_to_state(qc: qiskit.circuit.quantumcircuit.QuantumCircuit, circ0: plum.type.ModuleType[quimb.tensor.Circuit], settings: qiskit_addon_aqc_tensor.simulation.quimb.QuimbSimulator, /, *, out_state: numpy.ndarray | None = None) → quimb.tensor.Circuit">
<Function github="https://github.com/Qiskit/qiskit-addon-aqc-tensor/tree/stable/0.1/qiskit_addon_aqc_tensor/simulation/abstract.py" signature="apply_circuit_to_state(qc: [qiskit.circuit.quantumcircuit.QuantumCircuit](/api/qiskit/qiskit.circuit.QuantumCircuit '(in Qiskit v1.2)'), circ0: plum.type.ModuleType[[quimb.tensor.Circuit](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.Circuit '(in quimb v1.9)')], settings: [qiskit_addon_aqc_tensor.simulation.quimb.QuimbSimulator](simulation-quimb-quimb-simulator 'qiskit_addon_aqc_tensor.simulation.quimb.QuimbSimulator'), /, *, out_state: [numpy.ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray '(in NumPy v2.1)') | [None](https://docs.python.org/3/library/constants.html#None '(in Python v3.13)') = None) → [quimb.tensor.Circuit](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.Circuit '(in quimb v1.9)')">
**Parameters**

* **qc** ([*QuantumCircuit*](/api/qiskit/qiskit.circuit.QuantumCircuit "(in Qiskit v1.2)"))
Expand Down Expand Up @@ -166,7 +166,7 @@ In each function below, the documentation shows every distinct implementation av

###

<Function github="https://github.com/Qiskit/qiskit-addon-aqc-tensor/tree/stable/0.1/qiskit_addon_aqc_tensor/simulation/abstract.py" signature="compute_overlap(mps1: qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS, mps2: qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS, /) → complex">
<Function github="https://github.com/Qiskit/qiskit-addon-aqc-tensor/tree/stable/0.1/qiskit_addon_aqc_tensor/simulation/abstract.py" signature="compute_overlap(mps1: [qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS](simulation-aer-qiskit-aer-mps 'qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS'), mps2: [qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS](simulation-aer-qiskit-aer-mps 'qiskit_addon_aqc_tensor.simulation.aer.state.QiskitAerMPS'), /) → [complex](https://docs.python.org/3/library/functions.html#complex '(in Python v3.13)')">
**Parameters**

* **psi\_1** ([*TensorNetworkState*](#qiskit_addon_aqc_tensor.simulation.TensorNetworkState "qiskit_addon_aqc_tensor.simulation.abstract.TensorNetworkState"))
Expand All @@ -179,7 +179,7 @@ In each function below, the documentation shows every distinct implementation av

###

<Function github="https://github.com/Qiskit/qiskit-addon-aqc-tensor/tree/stable/0.1/qiskit_addon_aqc_tensor/simulation/abstract.py" signature="compute_overlap(circ1: plum.type.ModuleType[quimb.tensor.Circuit], circ2: plum.type.ModuleType[quimb.tensor.Circuit], /) → complex">
<Function github="https://github.com/Qiskit/qiskit-addon-aqc-tensor/tree/stable/0.1/qiskit_addon_aqc_tensor/simulation/abstract.py" signature="compute_overlap(circ1: plum.type.ModuleType[[quimb.tensor.Circuit](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.Circuit '(in quimb v1.9)')], circ2: plum.type.ModuleType[[quimb.tensor.Circuit](https://quimb.readthedocs.io/en/latest/autoapi/quimb/tensor/index.html#quimb.tensor.Circuit '(in quimb v1.9)')], /) → [complex](https://docs.python.org/3/library/functions.html#complex '(in Python v3.13)')">
**Parameters**

* **psi\_1** ([*TensorNetworkState*](#qiskit_addon_aqc_tensor.simulation.TensorNetworkState "qiskit_addon_aqc_tensor.simulation.abstract.TensorNetworkState"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ python_api_name: api_example.Electron

### overloaded\_func

<Function id="api_example.Electron.overloaded_func" signature="overloaded_func(arg1: tuple[str, str], arg2: list[str], arg3: int, arg4: Electron) → None" extraSignatures={["overloaded_func(arg1: tuple[int, int], arg2: list[int], arg3: bool, arg4: set[Electron]) → None"]}>
<Function id="api_example.Electron.overloaded_func" signature="overloaded_func(arg1: tuple[str, str], arg2: list[str], arg3: int, arg4: [Electron](#api_example.Electron 'api_example.electron.Electron')) → None" extraSignatures={["overloaded_func(arg1: tuple[int, int], arg2: list[int], arg3: bool, arg4: set[[Electron](#api_example.Electron 'api_example.electron.Electron')]) → None"]}>
This is meant to test out [https://github.com/Qiskit/qiskit\_sphinx\_theme/pull/319](https://github.com/Qiskit/qiskit_sphinx_theme/pull/319).

**Parameters**
Expand Down
2 changes: 2 additions & 0 deletions scripts/js/lib/api/conversionPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ async function convertFilesToMarkdown(
html,
fileName: file,
determineGithubUrl: pkg.determineGithubUrlFn(),
pkgName: pkg.name,
kebabCaseAndShorten: pkg.kebabCaseAndShortenUrls,
imageDestination: pkg.outputDir("/images"),
releaseNotesTitle: pkg.releaseNotesTitle(),
hasSeparateReleaseNotes: pkg.hasSeparateReleaseNotes(),
Expand Down
16 changes: 11 additions & 5 deletions scripts/js/lib/api/generateApiComponents.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,15 @@ import {
import { CheerioDoc } from "../testUtils.js";

const RAW_SIGNATURE_EXAMPLE = `<span class='sig-prename descclassname'><span class='pre'>Estimator.</span></span><span class='sig-name descname'><span class='pre'>run</span></span><span class='sig-paren'>(</span><em class='sig-param'><span class='n'><span class='pre'>circuits</span></span></em>, <em class='sig-param'><span class='n'><span class='pre'>observables</span></span></em>, <em class='sig-param'><span class='n'><span class='pre'>parameter_values</span></span><span class='o'><span class='pre'>=</span></span><span class='default_value'><span class='pre'>None</span></span></em>, <em class='sig-param'><span class='o'><span class='pre'>**</span></span><span class='n'><span class='pre'>kwargs</span></span></em><span class='sig-paren'>)</span></dt>`;
const OPTIONS = {
kebabCaseAndShorten: true,
pkgName: "qiskit",
};

const determineSignatureUrl = (rawLink: string) => rawLink;

test("htmlSignatureToMd", async () => {
const result = await htmlSignatureToMd(RAW_SIGNATURE_EXAMPLE);
const result = await htmlSignatureToMd(RAW_SIGNATURE_EXAMPLE, OPTIONS);
expect(result).toEqual(
`Estimator.run(circuits, observables, parameter_values=None, **kwargs)`,
);
Expand Down Expand Up @@ -71,7 +77,7 @@ test.describe("createOpeningTag()", () => {
rawSignature: RAW_SIGNATURE_EXAMPLE,
};

const tag = await createOpeningTag("Function", componentProps);
const tag = await createOpeningTag("Function", componentProps, OPTIONS);
expect(tag).toEqual(`<Function
id='qiskit_ibm_runtime.Estimator.run'
attributeTypeHint='undefined'
Expand All @@ -92,7 +98,7 @@ test.describe("createOpeningTag()", () => {
extraRawSignatures: [RAW_SIGNATURE_EXAMPLE, RAW_SIGNATURE_EXAMPLE],
};

const tag = await createOpeningTag("Function", componentProps);
const tag = await createOpeningTag("Function", componentProps, OPTIONS);
expect(tag).toEqual(`<Function
id='qiskit_ibm_runtime.Estimator.run'
attributeTypeHint='undefined'
Expand All @@ -113,7 +119,7 @@ test.describe("createOpeningTag()", () => {
attributeValue: "None",
};

const tag = await createOpeningTag("Attribute", componentProps);
const tag = await createOpeningTag("Attribute", componentProps, OPTIONS);
expect(tag).toEqual(`<Attribute
id='qiskit.circuit.QuantumCircuit.instance'
attributeTypeHint='str | None'
Expand All @@ -132,7 +138,7 @@ test.describe("createOpeningTag()", () => {
id: "qiskit.circuit.Sampler",
};

const tag = await createOpeningTag("Class", componentProps);
const tag = await createOpeningTag("Class", componentProps, OPTIONS);
expect(tag).toEqual(`<Class
id='qiskit.circuit.Sampler'
attributeTypeHint='undefined'
Expand Down
62 changes: 59 additions & 3 deletions scripts/js/lib/api/generateApiComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ import { unified } from "unified";
import rehypeParse from "rehype-parse";
import rehypeRemark from "rehype-remark";
import remarkStringify from "remark-stringify";
import { Root } from "mdast";
import { visit } from "unist-util-visit";
import { toText } from "hast-util-to-text";

import { ApiType } from "./Metadata.js";
import {
getLastPartFromFullIdentifier,
removeSuffix,
APOSTROPHE_HEX_CODE,
} from "../stringUtils.js";
import { UrlOptions } from "./updateLinks.js";

export type ComponentProps = {
id?: string;
Expand All @@ -45,13 +49,20 @@ const APITYPE_TO_TAG: Record<Exclude<ApiType, "module">, string> = {
struct: "class",
};

export const SIGNATURE_PLACEHOLDER = "[[SignatureLinkPlaceholder]]";
export const SIGNATURE_PLACEHOLDER_ESCAPED = SIGNATURE_PLACEHOLDER.replaceAll(
/(\[|\])/g,
"\\$1",
);

export async function processMdxComponent(
$: CheerioAPI,
signatures: Cheerio<Element>[],
$dl: Cheerio<any>,
priorApiType: ApiType | undefined,
apiType: Exclude<ApiType, "module">,
id: string,
options: UrlOptions,
): Promise<[string, string]> {
const tagName = APITYPE_TO_TAG[apiType];

Expand All @@ -71,7 +82,10 @@ export async function processMdxComponent(
);
addExtraSignatures(componentProps, extraProps);

return [await createOpeningTag(tagName, componentProps), `</${tagName}>`];
return [
await createOpeningTag(tagName, componentProps, options),
`</${tagName}>`,
];
}

// ------------------------------------------------------------------
Expand Down Expand Up @@ -310,6 +324,7 @@ function prepareFunctionProps(
export async function createOpeningTag(
tagName: string,
props: ComponentProps,
options: UrlOptions,
): Promise<string> {
const attributeTypeHint = props.attributeTypeHint?.replaceAll(
"'",
Expand All @@ -319,10 +334,10 @@ export async function createOpeningTag(
"'",
APOSTROPHE_HEX_CODE,
);
const signature = await htmlSignatureToMd(props.rawSignature!);
const signature = await htmlSignatureToMd(props.rawSignature!, options);
const extraSignatures: string[] = [];
for (const sig of props.extraRawSignatures ?? []) {
extraSignatures.push(`"${await htmlSignatureToMd(sig!)}"`);
extraSignatures.push(`"${await htmlSignatureToMd(sig!, options)}"`);
}

return `<${tagName}
Expand Down Expand Up @@ -398,14 +413,55 @@ export function addExtraSignatures(
*/
export async function htmlSignatureToMd(
signatureHtml: string,
options: UrlOptions,
): Promise<string> {
if (!signatureHtml) {
return "";
}

const { pkgName, kebabCaseAndShorten } = options;

// The `code` tag helps us remove some undesired elements like asterisks surrounding
// the parameters.
const html = `<code>${signatureHtml}</code>`;
const file = await unified()
.use(rehypeParse)
.use(() => (tree: Root) => {
visit(tree, { tagName: "a" }, (node: any) => {
// We transform the links into markdown as `text` nodes to avoid losing them once
// transforming the signature from HTML to markdown. This could happen because we
// have the signatures inside a `code` element. Moreover, we have more freedom by
// manually creating the links as text.
if (!node.properties?.href || !node.children?.length) {
return;
}

// We encode some conflicting characters that could make the markdown link break
// by using URL-encoding form.
const href = node.properties.href
.replaceAll('"', "%22")
.replaceAll("(", "%28")
.replaceAll(")", "%29");
const title = node.properties.title;

// We use a placeholder in the link to be able to easily find it and update it once the
// whole markdown file has been generated. We can't update the link here because we need
// the context of all the pages to decide if it's already correct or not.
const hrefWithPlaceholder = `${SIGNATURE_PLACEHOLDER}${href}${SIGNATURE_PLACEHOLDER}`;

// We only show the title if exists
const link = title
? `${hrefWithPlaceholder} \'${title}\'`
: `${hrefWithPlaceholder}`;

// The hast `Element` nodes only have one child:
// https://github.com/syntax-tree/hast?tab=readme-ov-file#element
const linkText = toText(node.children[0]);

node.type = "text";
node.value = `[${linkText}](${link})`;
});
})
.use(rehypeRemark)
.use(remarkStringify)
.process(html);
Expand Down
2 changes: 2 additions & 0 deletions scripts/js/lib/api/htmlToMd.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const DEFAULT_ARGS = {
`https://github.com/Qiskit/qiskit-ibm-runtime/tree/0.9.2/${fileName}.py`,
releaseNotesTitle: "My Quantum release notes",
hasSeparateReleaseNotes: false,
pkgName: "my-package",
kebabCaseAndShorten: true,
};

async function toMd(
Expand Down
21 changes: 12 additions & 9 deletions scripts/js/lib/api/htmlToMd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,19 @@ import { HtmlToMdResult } from "./HtmlToMdResult.js";
import { Metadata, ApiType } from "./Metadata.js";
import { removePrefix, removeSuffix, capitalize } from "../stringUtils.js";
import { remarkStringifyOptions } from "./commonParserConfig.js";
import { UrlOptions } from "./updateLinks.js";

export async function sphinxHtmlToMarkdown(options: {
html: string;
fileName: string;
imageDestination: string;
determineGithubUrl: (fileName: string) => string;
releaseNotesTitle: string;
hasSeparateReleaseNotes: boolean;
isCApi: boolean;
}): Promise<HtmlToMdResult> {
export async function sphinxHtmlToMarkdown(
options: UrlOptions & {
html: string;
fileName: string;
imageDestination: string;
determineGithubUrl: (fileName: string) => string;
releaseNotesTitle: string;
hasSeparateReleaseNotes: boolean;
isCApi: boolean;
},
): Promise<HtmlToMdResult> {
const processedHtml = await processHtml(options);
const markdown = await generateMarkdownFile(
processedHtml.html,
Expand Down
Loading