From 184f94ed0b455cc02834cbfac1ec0de4bffef989 Mon Sep 17 00:00:00 2001 From: Spencer Churchill <25377399+splch@users.noreply.github.com> Date: Sun, 26 Apr 2026 11:14:18 -0700 Subject: [PATCH 01/11] Add public modules to __all__ for pdoc submodule discovery pdoc uses __all__ to gate which submodules it discovers. Without the module names in __all__, pdoc only rendered the flat symbol list and skipped the submodule pages with their module-level docstrings. --- custom-templates/package_init.py.jinja | 6 ++++++ ionq_core/__init__.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/custom-templates/package_init.py.jinja b/custom-templates/package_init.py.jinja index e532b23..a4a6a36 100644 --- a/custom-templates/package_init.py.jinja +++ b/custom-templates/package_init.py.jinja @@ -27,6 +27,12 @@ from .ionq_client import IonQClient, __version__ from .types import UNSET, Unset __all__ = ( + "exceptions", + "extensions", + "gates", + "pagination", + "polling", + "session", "UNSET", "APIConnectionError", "APIError", diff --git a/ionq_core/__init__.py b/ionq_core/__init__.py index b05def5..d8b27b2 100644 --- a/ionq_core/__init__.py +++ b/ionq_core/__init__.py @@ -55,11 +55,17 @@ "aiter_jobs", "aiter_session_jobs", "async_wait_for_job", + "exceptions", + "extensions", + "gates", "gpi2_matrix", "gpi_matrix", "iter_jobs", "iter_session_jobs", "ms_matrix", + "pagination", + "polling", + "session", "wait_for_job", "zz_matrix", ) From e9b30d757bb5069268f9179bff00209d0f17b4dd Mon Sep 17 00:00:00 2001 From: Spencer Churchill <25377399+splch@users.noreply.github.com> Date: Sun, 26 Apr 2026 11:17:49 -0700 Subject: [PATCH 02/11] Sort module names alphabetically in template to match generated output --- custom-templates/package_init.py.jinja | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/custom-templates/package_init.py.jinja b/custom-templates/package_init.py.jinja index a4a6a36..077dd31 100644 --- a/custom-templates/package_init.py.jinja +++ b/custom-templates/package_init.py.jinja @@ -27,12 +27,6 @@ from .ionq_client import IonQClient, __version__ from .types import UNSET, Unset __all__ = ( - "exceptions", - "extensions", - "gates", - "pagination", - "polling", - "session", "UNSET", "APIConnectionError", "APIError", @@ -58,11 +52,17 @@ __all__ = ( "aiter_jobs", "aiter_session_jobs", "async_wait_for_job", + "exceptions", + "extensions", + "gates", "gpi2_matrix", "gpi_matrix", "iter_jobs", "iter_session_jobs", "ms_matrix", + "pagination", + "polling", + "session", "wait_for_job", "zz_matrix", ) From 77be981446370db4c0303cb3cbf162f6a8b9f8e8 Mon Sep 17 00:00:00 2001 From: Spencer Churchill <25377399+splch@users.noreply.github.com> Date: Sun, 26 Apr 2026 11:44:06 -0700 Subject: [PATCH 03/11] Replace custom template with post-hook script Delete the 69-line custom template override. Instead, add __all__ to each hand-written module (Python best practice) and use a 37-line post-hook script that reads those declarations via AST to extend the generated __init__.py. The only hand-maintained list is the 7 module names; all exported symbols are derived from the code itself. --- .github/workflows/generated.yml | 1 - CONTRIBUTING.md | 1 - custom-templates/package_init.py.jinja | 68 -------------------------- ionq_core/__init__.py | 9 ++-- ionq_core/exceptions.py | 13 +++++ ionq_core/extensions.py | 2 + ionq_core/gates.py | 2 + ionq_core/ionq_client.py | 2 + ionq_core/pagination.py | 2 + ionq_core/polling.py | 2 + ionq_core/session.py | 2 + openapi-python-client-config.yaml | 1 + scripts/extend_init.py | 40 +++++++++++++++ 13 files changed, 69 insertions(+), 76 deletions(-) delete mode 100644 custom-templates/package_init.py.jinja create mode 100644 scripts/extend_init.py diff --git a/.github/workflows/generated.yml b/.github/workflows/generated.yml index 1a7dfe6..f258846 100644 --- a/.github/workflows/generated.yml +++ b/.github/workflows/generated.yml @@ -36,7 +36,6 @@ jobs: --path /tmp/patched-spec.json \ --meta none \ --config openapi-python-client-config.yaml \ - --custom-template-path custom-templates \ --output-path ionq_core \ --overwrite - name: Check for uncommitted changes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2bf4971..aabcf18 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -55,7 +55,6 @@ uvx openapi-python-client==0.28.3 generate \ --path /tmp/patched-spec.json \ --meta none \ --config openapi-python-client-config.yaml \ - --custom-template-path custom-templates \ --output-path ionq_core \ --overwrite ``` diff --git a/custom-templates/package_init.py.jinja b/custom-templates/package_init.py.jinja deleted file mode 100644 index 077dd31..0000000 --- a/custom-templates/package_init.py.jinja +++ /dev/null @@ -1,68 +0,0 @@ -{% from "helpers.jinja" import safe_docstring %} -{{ safe_docstring(package_description) }} -from .exceptions import ( - APIConnectionError, - APIError, - APITimeoutError, - AuthenticationError, - BadRequestError, - IonQError, - NotFoundError, - PermissionDeniedError, - RateLimitError, - ServerError, -) -from .extensions import AsyncEventHook, ClientExtension, EventHook -from .gates import gpi2_matrix, gpi_matrix, ms_matrix, zz_matrix -from .pagination import aiter_jobs, aiter_session_jobs, iter_jobs, iter_session_jobs -from .polling import ( - JobFailedError, - JobTimeoutError, - async_wait_for_job, - wait_for_job, -) -from .session import SessionManager -from .client import AuthenticatedClient, Client -from .ionq_client import IonQClient, __version__ -from .types import UNSET, Unset - -__all__ = ( - "UNSET", - "APIConnectionError", - "APIError", - "APITimeoutError", - "AsyncEventHook", - "AuthenticatedClient", - "AuthenticationError", - "BadRequestError", - "Client", - "ClientExtension", - "EventHook", - "IonQClient", - "IonQError", - "JobFailedError", - "JobTimeoutError", - "NotFoundError", - "PermissionDeniedError", - "RateLimitError", - "ServerError", - "SessionManager", - "Unset", - "__version__", - "aiter_jobs", - "aiter_session_jobs", - "async_wait_for_job", - "exceptions", - "extensions", - "gates", - "gpi2_matrix", - "gpi_matrix", - "iter_jobs", - "iter_session_jobs", - "ms_matrix", - "pagination", - "polling", - "session", - "wait_for_job", - "zz_matrix", -) diff --git a/ionq_core/__init__.py b/ionq_core/__init__.py index d8b27b2..ebdd148 100644 --- a/ionq_core/__init__.py +++ b/ionq_core/__init__.py @@ -1,6 +1,7 @@ # Copyright 2026 IonQ, Inc. # SPDX-License-Identifier: Apache-2.0 + """A client library for accessing IonQ Cloud Platform API""" from .client import AuthenticatedClient, Client @@ -20,12 +21,7 @@ from .gates import gpi2_matrix, gpi_matrix, ms_matrix, zz_matrix from .ionq_client import IonQClient, __version__ from .pagination import aiter_jobs, aiter_session_jobs, iter_jobs, iter_session_jobs -from .polling import ( - JobFailedError, - JobTimeoutError, - async_wait_for_job, - wait_for_job, -) +from .polling import JobFailedError, JobTimeoutError, async_wait_for_job, wait_for_job from .session import SessionManager from .types import UNSET, Unset @@ -60,6 +56,7 @@ "gates", "gpi2_matrix", "gpi_matrix", + "ionq_client", "iter_jobs", "iter_session_jobs", "ms_matrix", diff --git a/ionq_core/exceptions.py b/ionq_core/exceptions.py index 5296db8..be4e4dd 100644 --- a/ionq_core/exceptions.py +++ b/ionq_core/exceptions.py @@ -32,6 +32,19 @@ ``` """ +__all__ = [ + "APIConnectionError", + "APIError", + "APITimeoutError", + "AuthenticationError", + "BadRequestError", + "IonQError", + "NotFoundError", + "PermissionDeniedError", + "RateLimitError", + "ServerError", +] + class IonQError(Exception): """Base exception for all IonQ errors. diff --git a/ionq_core/extensions.py b/ionq_core/extensions.py index 65fd24a..44fdb0d 100644 --- a/ionq_core/extensions.py +++ b/ionq_core/extensions.py @@ -38,6 +38,8 @@ def on_response(self, request: httpx.Request, response: httpx.Response) -> None: import attrs import httpx +__all__ = ["AsyncEventHook", "ClientExtension", "EventHook"] + logger = logging.getLogger("ionq_core") diff --git a/ionq_core/gates.py b/ionq_core/gates.py index c6a440c..0ee486a 100644 --- a/ionq_core/gates.py +++ b/ionq_core/gates.py @@ -28,6 +28,8 @@ ``` """ +__all__ = ["gpi2_matrix", "gpi_matrix", "ms_matrix", "zz_matrix"] + import cmath import math diff --git a/ionq_core/ionq_client.py b/ionq_core/ionq_client.py index c80036c..4660c87 100644 --- a/ionq_core/ionq_client.py +++ b/ionq_core/ionq_client.py @@ -9,6 +9,8 @@ async httpx transports. """ +__all__ = ["IonQClient", "__version__"] + import os import platform import warnings diff --git a/ionq_core/pagination.py b/ionq_core/pagination.py index 1c9f5fa..bc5fc7b 100644 --- a/ionq_core/pagination.py +++ b/ionq_core/pagination.py @@ -19,6 +19,8 @@ from __future__ import annotations +__all__ = ["aiter_jobs", "aiter_session_jobs", "iter_jobs", "iter_session_jobs"] + import logging from collections.abc import AsyncIterator, Callable, Iterator from typing import TYPE_CHECKING, Any diff --git a/ionq_core/polling.py b/ionq_core/polling.py index bdf8959..c82a15e 100644 --- a/ionq_core/polling.py +++ b/ionq_core/polling.py @@ -22,6 +22,8 @@ from __future__ import annotations +__all__ = ["JobFailedError", "JobTimeoutError", "async_wait_for_job", "wait_for_job"] + import asyncio import logging import time diff --git a/ionq_core/session.py b/ionq_core/session.py index d2df148..e24ced7 100644 --- a/ionq_core/session.py +++ b/ionq_core/session.py @@ -27,6 +27,8 @@ from __future__ import annotations +__all__ = ["SessionManager"] + import logging from typing import TYPE_CHECKING diff --git a/openapi-python-client-config.yaml b/openapi-python-client-config.yaml index f5cd209..1b2fcf6 100644 --- a/openapi-python-client-config.yaml +++ b/openapi-python-client-config.yaml @@ -5,6 +5,7 @@ literal_enums: true post_hooks: - "perl -pi -e 's/token: str\\K$/ = field(repr=False)/' client.py" - "perl -0777 -pi -e 's/\\A(?!# Copyright)/# Copyright 2026 IonQ, Inc.\\n# SPDX-License-Identifier: Apache-2.0\\n\\n/' $(find . -name '*.py')" + - "python3 ../scripts/extend_init.py" - "ruff check . --fix-only" - "ruff format ." diff --git a/scripts/extend_init.py b/scripts/extend_init.py new file mode 100644 index 0000000..cf2cd4d --- /dev/null +++ b/scripts/extend_init.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +"""Extend generated __init__.py with exports from hand-written modules.""" + +import ast +from pathlib import Path + +MODULES = ["exceptions", "extensions", "gates", "ionq_client", "pagination", "polling", "session"] +EXTRAS = {"types": ["UNSET", "Unset"]} + +init = Path("__init__.py") +source = init.read_text() + +existing_all = set() +for node in ast.walk(ast.parse(source)): + if isinstance(node, ast.Assign): + for target in node.targets: + if isinstance(target, ast.Name) and target.id == "__all__": + existing_all = {elt.value for elt in node.value.elts} + +all_names = set(existing_all) +imports = [] +for mod in MODULES: + mod_tree = ast.parse(Path(f"{mod}.py").read_text()) + for node in ast.walk(mod_tree): + if isinstance(node, ast.Assign): + for target in node.targets: + if isinstance(target, ast.Name) and target.id == "__all__": + names = sorted(elt.value for elt in node.value.elts) + imports.append(f"from .{mod} import {', '.join(names)}") + all_names.update(names) + all_names.add(mod) + +for mod, names in EXTRAS.items(): + imports.append(f"from .{mod} import {', '.join(sorted(names))}") + all_names.update(names) + +text = source.split("__all__")[0].rstrip() + "\n" +text += "\n".join(imports) + "\n" +text += "\n__all__ = (\n" + "".join(f' "{n}",\n' for n in sorted(all_names)) + ")\n" +init.write_text(text) From d1b4c65cfd03da70186899a2e51e05d59ca270c5 Mon Sep 17 00:00:00 2001 From: Spencer Churchill <25377399+splch@users.noreply.github.com> Date: Sun, 26 Apr 2026 11:56:48 -0700 Subject: [PATCH 04/11] Replace post-hook script with minimal star-import template Use star imports from modules that define __all__ (Python best practice) and compose __init__.__all__ dynamically from submodule __all__ lists. Template shrinks from 69 lines to 24 with no external script. All exported symbols are derived from the code itself. --- .github/workflows/generated.yml | 1 + CONTRIBUTING.md | 1 + custom-templates/package_init.py.jinja | 23 +++++++ ionq_core/__init__.py | 88 +++++++++----------------- openapi-python-client-config.yaml | 1 - scripts/extend_init.py | 40 ------------ 6 files changed, 54 insertions(+), 100 deletions(-) create mode 100644 custom-templates/package_init.py.jinja delete mode 100644 scripts/extend_init.py diff --git a/.github/workflows/generated.yml b/.github/workflows/generated.yml index f258846..1a7dfe6 100644 --- a/.github/workflows/generated.yml +++ b/.github/workflows/generated.yml @@ -36,6 +36,7 @@ jobs: --path /tmp/patched-spec.json \ --meta none \ --config openapi-python-client-config.yaml \ + --custom-template-path custom-templates \ --output-path ionq_core \ --overwrite - name: Check for uncommitted changes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aabcf18..2bf4971 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -55,6 +55,7 @@ uvx openapi-python-client==0.28.3 generate \ --path /tmp/patched-spec.json \ --meta none \ --config openapi-python-client-config.yaml \ + --custom-template-path custom-templates \ --output-path ionq_core \ --overwrite ``` diff --git a/custom-templates/package_init.py.jinja b/custom-templates/package_init.py.jinja new file mode 100644 index 0000000..6c4d684 --- /dev/null +++ b/custom-templates/package_init.py.jinja @@ -0,0 +1,23 @@ +{% from "helpers.jinja" import safe_docstring %} +{{ safe_docstring(package_description) }} +from . import exceptions, extensions, gates, ionq_client, pagination, polling, session +from .client import AuthenticatedClient, Client +from .exceptions import * # noqa: F403 +from .extensions import * # noqa: F403 +from .gates import * # noqa: F403 +from .ionq_client import * # noqa: F403 +from .pagination import * # noqa: F403 +from .polling import * # noqa: F403 +from .session import * # noqa: F403 +from .types import UNSET, Unset + +__all__ = sorted( + { + "AuthenticatedClient", "Client", "UNSET", "Unset", + "exceptions", "extensions", "gates", "ionq_client", + "pagination", "polling", "session", + *exceptions.__all__, *extensions.__all__, *gates.__all__, + *ionq_client.__all__, *pagination.__all__, *polling.__all__, + *session.__all__, + } +) diff --git a/ionq_core/__init__.py b/ionq_core/__init__.py index ebdd148..d6bb9d7 100644 --- a/ionq_core/__init__.py +++ b/ionq_core/__init__.py @@ -1,68 +1,38 @@ # Copyright 2026 IonQ, Inc. # SPDX-License-Identifier: Apache-2.0 - """A client library for accessing IonQ Cloud Platform API""" +from . import exceptions, extensions, gates, ionq_client, pagination, polling, session from .client import AuthenticatedClient, Client -from .exceptions import ( - APIConnectionError, - APIError, - APITimeoutError, - AuthenticationError, - BadRequestError, - IonQError, - NotFoundError, - PermissionDeniedError, - RateLimitError, - ServerError, -) -from .extensions import AsyncEventHook, ClientExtension, EventHook -from .gates import gpi2_matrix, gpi_matrix, ms_matrix, zz_matrix -from .ionq_client import IonQClient, __version__ -from .pagination import aiter_jobs, aiter_session_jobs, iter_jobs, iter_session_jobs -from .polling import JobFailedError, JobTimeoutError, async_wait_for_job, wait_for_job -from .session import SessionManager +from .exceptions import * # noqa: F403 +from .extensions import * # noqa: F403 +from .gates import * # noqa: F403 +from .ionq_client import * # noqa: F403 +from .pagination import * # noqa: F403 +from .polling import * # noqa: F403 +from .session import * # noqa: F403 from .types import UNSET, Unset -__all__ = ( - "UNSET", - "APIConnectionError", - "APIError", - "APITimeoutError", - "AsyncEventHook", - "AuthenticatedClient", - "AuthenticationError", - "BadRequestError", - "Client", - "ClientExtension", - "EventHook", - "IonQClient", - "IonQError", - "JobFailedError", - "JobTimeoutError", - "NotFoundError", - "PermissionDeniedError", - "RateLimitError", - "ServerError", - "SessionManager", - "Unset", - "__version__", - "aiter_jobs", - "aiter_session_jobs", - "async_wait_for_job", - "exceptions", - "extensions", - "gates", - "gpi2_matrix", - "gpi_matrix", - "ionq_client", - "iter_jobs", - "iter_session_jobs", - "ms_matrix", - "pagination", - "polling", - "session", - "wait_for_job", - "zz_matrix", +__all__ = sorted( + { + "AuthenticatedClient", + "Client", + "UNSET", + "Unset", + "exceptions", + "extensions", + "gates", + "ionq_client", + "pagination", + "polling", + "session", + *exceptions.__all__, + *extensions.__all__, + *gates.__all__, + *ionq_client.__all__, + *pagination.__all__, + *polling.__all__, + *session.__all__, + } ) diff --git a/openapi-python-client-config.yaml b/openapi-python-client-config.yaml index 1b2fcf6..f5cd209 100644 --- a/openapi-python-client-config.yaml +++ b/openapi-python-client-config.yaml @@ -5,7 +5,6 @@ literal_enums: true post_hooks: - "perl -pi -e 's/token: str\\K$/ = field(repr=False)/' client.py" - "perl -0777 -pi -e 's/\\A(?!# Copyright)/# Copyright 2026 IonQ, Inc.\\n# SPDX-License-Identifier: Apache-2.0\\n\\n/' $(find . -name '*.py')" - - "python3 ../scripts/extend_init.py" - "ruff check . --fix-only" - "ruff format ." diff --git a/scripts/extend_init.py b/scripts/extend_init.py deleted file mode 100644 index cf2cd4d..0000000 --- a/scripts/extend_init.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python3 -"""Extend generated __init__.py with exports from hand-written modules.""" - -import ast -from pathlib import Path - -MODULES = ["exceptions", "extensions", "gates", "ionq_client", "pagination", "polling", "session"] -EXTRAS = {"types": ["UNSET", "Unset"]} - -init = Path("__init__.py") -source = init.read_text() - -existing_all = set() -for node in ast.walk(ast.parse(source)): - if isinstance(node, ast.Assign): - for target in node.targets: - if isinstance(target, ast.Name) and target.id == "__all__": - existing_all = {elt.value for elt in node.value.elts} - -all_names = set(existing_all) -imports = [] -for mod in MODULES: - mod_tree = ast.parse(Path(f"{mod}.py").read_text()) - for node in ast.walk(mod_tree): - if isinstance(node, ast.Assign): - for target in node.targets: - if isinstance(target, ast.Name) and target.id == "__all__": - names = sorted(elt.value for elt in node.value.elts) - imports.append(f"from .{mod} import {', '.join(names)}") - all_names.update(names) - all_names.add(mod) - -for mod, names in EXTRAS.items(): - imports.append(f"from .{mod} import {', '.join(sorted(names))}") - all_names.update(names) - -text = source.split("__all__")[0].rstrip() + "\n" -text += "\n".join(imports) + "\n" -text += "\n__all__ = (\n" + "".join(f' "{n}",\n' for n in sorted(all_names)) + ")\n" -init.write_text(text) From 12e2327c1a58d7158e1c19beef50b76b628e3da6 Mon Sep 17 00:00:00 2001 From: Spencer Churchill <25377399+splch@users.noreply.github.com> Date: Sun, 26 Apr 2026 12:00:25 -0700 Subject: [PATCH 05/11] Compact template using Jinja2 loops Module names listed once; Jinja2 generates the imports and __all__ composition. Template shrinks from 24 to 13 lines. --- custom-templates/package_init.py.jinja | 23 +++++++---------------- ionq_core/__init__.py | 16 +++++----------- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/custom-templates/package_init.py.jinja b/custom-templates/package_init.py.jinja index 6c4d684..a2f9ed3 100644 --- a/custom-templates/package_init.py.jinja +++ b/custom-templates/package_init.py.jinja @@ -1,23 +1,14 @@ {% from "helpers.jinja" import safe_docstring %} +{%- set mods = ["exceptions", "extensions", "gates", "ionq_client", "pagination", "polling", "session"] %} {{ safe_docstring(package_description) }} -from . import exceptions, extensions, gates, ionq_client, pagination, polling, session +from . import {{ mods | join(", ") }} from .client import AuthenticatedClient, Client -from .exceptions import * # noqa: F403 -from .extensions import * # noqa: F403 -from .gates import * # noqa: F403 -from .ionq_client import * # noqa: F403 -from .pagination import * # noqa: F403 -from .polling import * # noqa: F403 -from .session import * # noqa: F403 +{% for m in mods -%} +from .{{ m }} import * # noqa: F403 +{% endfor -%} from .types import UNSET, Unset __all__ = sorted( - { - "AuthenticatedClient", "Client", "UNSET", "Unset", - "exceptions", "extensions", "gates", "ionq_client", - "pagination", "polling", "session", - *exceptions.__all__, *extensions.__all__, *gates.__all__, - *ionq_client.__all__, *pagination.__all__, *polling.__all__, - *session.__all__, - } + {{"{"}} {{ mods | map("tojson") | join(", ") }}, "AuthenticatedClient", "Client", "UNSET", "Unset" {{"}"}} + | {n for m in ({{ mods | join(", ") }}) for n in m.__all__} ) diff --git a/ionq_core/__init__.py b/ionq_core/__init__.py index d6bb9d7..a980be2 100644 --- a/ionq_core/__init__.py +++ b/ionq_core/__init__.py @@ -16,10 +16,6 @@ __all__ = sorted( { - "AuthenticatedClient", - "Client", - "UNSET", - "Unset", "exceptions", "extensions", "gates", @@ -27,12 +23,10 @@ "pagination", "polling", "session", - *exceptions.__all__, - *extensions.__all__, - *gates.__all__, - *ionq_client.__all__, - *pagination.__all__, - *polling.__all__, - *session.__all__, + "AuthenticatedClient", + "Client", + "UNSET", + "Unset", } + | {n for m in (exceptions, extensions, gates, ionq_client, pagination, polling, session) for n in m.__all__} ) From 9fc03f720985c7fe328ea80ac054413d84ed3c3e Mon Sep 17 00:00:00 2001 From: Spencer Churchill <25377399+splch@users.noreply.github.com> Date: Sun, 26 Apr 2026 12:02:27 -0700 Subject: [PATCH 06/11] Fix lint: add noqa for re-exports invisible to static analysis Ruff cannot statically resolve the dynamic __all__ and flags the explicit imports as unused. Add F401/F403 suppression comments. --- custom-templates/package_init.py.jinja | 4 ++-- ionq_core/__init__.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/custom-templates/package_init.py.jinja b/custom-templates/package_init.py.jinja index a2f9ed3..f7a9dd7 100644 --- a/custom-templates/package_init.py.jinja +++ b/custom-templates/package_init.py.jinja @@ -2,11 +2,11 @@ {%- set mods = ["exceptions", "extensions", "gates", "ionq_client", "pagination", "polling", "session"] %} {{ safe_docstring(package_description) }} from . import {{ mods | join(", ") }} -from .client import AuthenticatedClient, Client +from .client import AuthenticatedClient, Client # noqa: F401 {% for m in mods -%} from .{{ m }} import * # noqa: F403 {% endfor -%} -from .types import UNSET, Unset +from .types import UNSET, Unset # noqa: F401 __all__ = sorted( {{"{"}} {{ mods | map("tojson") | join(", ") }}, "AuthenticatedClient", "Client", "UNSET", "Unset" {{"}"}} diff --git a/ionq_core/__init__.py b/ionq_core/__init__.py index a980be2..58723bc 100644 --- a/ionq_core/__init__.py +++ b/ionq_core/__init__.py @@ -4,7 +4,7 @@ """A client library for accessing IonQ Cloud Platform API""" from . import exceptions, extensions, gates, ionq_client, pagination, polling, session -from .client import AuthenticatedClient, Client +from .client import AuthenticatedClient, Client # noqa: F401 from .exceptions import * # noqa: F403 from .extensions import * # noqa: F403 from .gates import * # noqa: F403 @@ -12,7 +12,7 @@ from .pagination import * # noqa: F403 from .polling import * # noqa: F403 from .session import * # noqa: F403 -from .types import UNSET, Unset +from .types import UNSET, Unset # noqa: F401 __all__ = sorted( { From 25719a73bf2a031f4fdfcfba473921e01686bffa Mon Sep 17 00:00:00 2001 From: Spencer Churchill <25377399+splch@users.noreply.github.com> Date: Sun, 26 Apr 2026 12:08:54 -0700 Subject: [PATCH 07/11] Simplify __all__ construction in template Replace set-union with splat unpacking in a single set literal. Fewer Jinja brace escapes and consistent use of the for-loop pattern. --- custom-templates/package_init.py.jinja | 8 ++++---- ionq_core/__init__.py | 8 +++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/custom-templates/package_init.py.jinja b/custom-templates/package_init.py.jinja index f7a9dd7..88d5256 100644 --- a/custom-templates/package_init.py.jinja +++ b/custom-templates/package_init.py.jinja @@ -8,7 +8,7 @@ from .{{ m }} import * # noqa: F403 {% endfor -%} from .types import UNSET, Unset # noqa: F401 -__all__ = sorted( - {{"{"}} {{ mods | map("tojson") | join(", ") }}, "AuthenticatedClient", "Client", "UNSET", "Unset" {{"}"}} - | {n for m in ({{ mods | join(", ") }}) for n in m.__all__} -) +__all__ = sorted({{ "{" }} + {{ mods | map("tojson") | join(", ") }}, "AuthenticatedClient", "Client", "UNSET", "Unset", + {% for m in mods %}*{{ m }}.__all__, {% endfor %} +{{ "}" }}) diff --git a/ionq_core/__init__.py b/ionq_core/__init__.py index 58723bc..4c9d70e 100644 --- a/ionq_core/__init__.py +++ b/ionq_core/__init__.py @@ -27,6 +27,12 @@ "Client", "UNSET", "Unset", + *exceptions.__all__, + *extensions.__all__, + *gates.__all__, + *ionq_client.__all__, + *pagination.__all__, + *polling.__all__, + *session.__all__, } - | {n for m in (exceptions, extensions, gates, ionq_client, pagination, polling, session) for n in m.__all__} ) From 7f66f5836770659b08f579fc4e41a8a74833d41e Mon Sep 17 00:00:00 2001 From: Spencer Churchill <25377399+splch@users.noreply.github.com> Date: Sun, 26 Apr 2026 12:13:54 -0700 Subject: [PATCH 08/11] Compact template to 10 lines Collapse __all__ block to single line (ruff reformats the output) and remove unnecessary blank line. --- custom-templates/package_init.py.jinja | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/custom-templates/package_init.py.jinja b/custom-templates/package_init.py.jinja index 88d5256..b49705f 100644 --- a/custom-templates/package_init.py.jinja +++ b/custom-templates/package_init.py.jinja @@ -7,8 +7,4 @@ from .client import AuthenticatedClient, Client # noqa: F401 from .{{ m }} import * # noqa: F403 {% endfor -%} from .types import UNSET, Unset # noqa: F401 - -__all__ = sorted({{ "{" }} - {{ mods | map("tojson") | join(", ") }}, "AuthenticatedClient", "Client", "UNSET", "Unset", - {% for m in mods %}*{{ m }}.__all__, {% endfor %} -{{ "}" }}) +__all__ = sorted({{ "{" }}{{ mods | map("tojson") | join(", ") }}, "AuthenticatedClient", "Client", "UNSET", "Unset", {% for m in mods %}*{{ m }}.__all__, {% endfor %}{{ "}" }}) From df256a4b1e73dc524b5f7d5141fc4b4fb631e0b0 Mon Sep 17 00:00:00 2001 From: Spencer Churchill <25377399+splch@users.noreply.github.com> Date: Sun, 26 Apr 2026 12:17:39 -0700 Subject: [PATCH 09/11] Format template for readability Consistent whitespace control on all tags, readable __all__ block with one entry per line, blank line separating imports from __all__. --- custom-templates/package_init.py.jinja | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/custom-templates/package_init.py.jinja b/custom-templates/package_init.py.jinja index b49705f..55dfd9d 100644 --- a/custom-templates/package_init.py.jinja +++ b/custom-templates/package_init.py.jinja @@ -1,5 +1,6 @@ -{% from "helpers.jinja" import safe_docstring %} -{%- set mods = ["exceptions", "extensions", "gates", "ionq_client", "pagination", "polling", "session"] %} +{% from "helpers.jinja" import safe_docstring -%} +{% set mods = ["exceptions", "extensions", "gates", "ionq_client", "pagination", "polling", "session"] -%} + {{ safe_docstring(package_description) }} from . import {{ mods | join(", ") }} from .client import AuthenticatedClient, Client # noqa: F401 @@ -7,4 +8,11 @@ from .client import AuthenticatedClient, Client # noqa: F401 from .{{ m }} import * # noqa: F403 {% endfor -%} from .types import UNSET, Unset # noqa: F401 -__all__ = sorted({{ "{" }}{{ mods | map("tojson") | join(", ") }}, "AuthenticatedClient", "Client", "UNSET", "Unset", {% for m in mods %}*{{ m }}.__all__, {% endfor %}{{ "}" }}) + +__all__ = sorted({ + {{ mods | map("tojson") | join(", ") }}, + "AuthenticatedClient", "Client", "UNSET", "Unset", +{% for m in mods -%} + *{{ m }}.__all__, +{% endfor -%} +}) From e03399478aa892c4fcae87a210b9f7f7ffe4af2f Mon Sep 17 00:00:00 2001 From: Spencer Churchill <25377399+splch@users.noreply.github.com> Date: Sun, 26 Apr 2026 12:20:47 -0700 Subject: [PATCH 10/11] Apply Jinja2 best practices to template - Rename mods -> modules (descriptive names, not abbreviations) - Remove all -%} dash modifiers (trim_blocks and lstrip_blocks are enabled in the Environment, making manual trimming unnecessary) --- custom-templates/package_init.py.jinja | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/custom-templates/package_init.py.jinja b/custom-templates/package_init.py.jinja index 55dfd9d..d05c1c0 100644 --- a/custom-templates/package_init.py.jinja +++ b/custom-templates/package_init.py.jinja @@ -1,18 +1,17 @@ -{% from "helpers.jinja" import safe_docstring -%} -{% set mods = ["exceptions", "extensions", "gates", "ionq_client", "pagination", "polling", "session"] -%} - +{% from "helpers.jinja" import safe_docstring %} +{% set modules = ["exceptions", "extensions", "gates", "ionq_client", "pagination", "polling", "session"] %} {{ safe_docstring(package_description) }} -from . import {{ mods | join(", ") }} +from . import {{ modules | join(", ") }} from .client import AuthenticatedClient, Client # noqa: F401 -{% for m in mods -%} +{% for m in modules %} from .{{ m }} import * # noqa: F403 -{% endfor -%} +{% endfor %} from .types import UNSET, Unset # noqa: F401 __all__ = sorted({ - {{ mods | map("tojson") | join(", ") }}, + {{ modules | map("tojson") | join(", ") }}, "AuthenticatedClient", "Client", "UNSET", "Unset", -{% for m in mods -%} +{% for m in modules %} *{{ m }}.__all__, -{% endfor -%} +{% endfor %} }) From ccde35c50c22efc867ebe30b99cd7725708a6146 Mon Sep 17 00:00:00 2001 From: Spencer Churchill <25377399+splch@users.noreply.github.com> Date: Mon, 27 Apr 2026 11:34:43 -0700 Subject: [PATCH 11/11] Move __all__ before imports in extensions.py PEP 8's "Module Level Dunder Names" rule places __all__ after the docstring but before any non-__future__ imports. The other hand-written modules already follow this; extensions.py was the outlier. --- ionq_core/extensions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ionq_core/extensions.py b/ionq_core/extensions.py index 44fdb0d..d5174cc 100644 --- a/ionq_core/extensions.py +++ b/ionq_core/extensions.py @@ -31,6 +31,8 @@ def on_response(self, request: httpx.Request, response: httpx.Response) -> None: ``` """ +__all__ = ["AsyncEventHook", "ClientExtension", "EventHook"] + import logging from collections.abc import Callable from typing import Protocol, runtime_checkable @@ -38,8 +40,6 @@ def on_response(self, request: httpx.Request, response: httpx.Response) -> None: import attrs import httpx -__all__ = ["AsyncEventHook", "ClientExtension", "EventHook"] - logger = logging.getLogger("ionq_core")