Type stubs (PEP 561), a connection helper, and a rebuild tool for the Zemax OpticStudio ZOS-API
Project description
zosapi-stubs
PEP 561 type stubs for the Zemax OpticStudio ZOS-API — full autocomplete, inline type hints, signature help, go-to-definition, and hover documentation in VS Code (Pylance/Pyright) and PyCharm, for an API that ships no stubs of its own.
The ZOS-API is a .NET Framework 4.8 assembly accessed from Python through
pythonnet. Because the types only exist as a
dynamically loaded CLR module at runtime, editors have nothing to introspect and
give you no completion. This package solves that by reflecting over
ZOSAPI.dll and ZOSAPI_Interfaces.dll and emitting .pyi stub files, with
docstrings lifted from the API's .xml documentation so you also get hover
help.
It ships three things in one wheel:
ZOSAPI-stubs/— the prebuilt stub package your editor reads.zosapi_stubs.Connect()— a one-line, registry-discovered connection helper (no hard-coded path, noZOSAPI_NetHelper.dll).zosapi-stubgen— a console command that rebuilds the stubs against a newer OpticStudio install, in place, with no source checkout.
Table of contents
- What's included
- Installation
- Connecting to the ZOS-API
- Using the stubs
- Rebuilding for a new OpticStudio version
- How it works
- Limitations & caveats
- Project layout
- License
What's included
The wheel covers the complete public surface of the two ZOS-API assemblies:
| Top-level types | 831 (interfaces, classes, enums) |
| Namespaces | 39 (one __init__.pyi per namespace) |
| Docstrings | 1,432 injected, drawn from 1,161 documented members in the .xml docs |
| Top-level packages | ZOSAPI, plus Analysis, Common, Editors, Preferences, SystemData, Tools, Wizards and their sub-namespaces |
For every type the stubs describe:
- Properties — as
@property(with.setterwhen the .NET property is writable), correctly typed. - Methods — full signatures with typed parameters and return types;
multiple .NET overloads become
@overloadblocks. out/refparameters — folded into the return type as aTuple[...], matching how pythonnet actually returns them.- Indexers — emitted as
__getitem__/__setitem__. - Enums — as
IntEnum(orIntFlagfor[Flags]enums), with every member and its integer value. - Inheritance — base classes and implemented interfaces are preserved, so inherited members resolve.
- Generics — generic interfaces/containers map to
typingequivalents (IList<T>→List[T],IEnumerable<T>→Iterable[T], etc.). - Cross-namespace references — e.g.
IOpticalSystem.LDEis correctly typed asZOSAPI.Editors.LDE.ILensDataEditor.
Docstrings (__doc__)
Docstrings are taken from ZOSAPI_Interfaces.xml / ZOSAPI.xml (shipped next to
the DLLs by OpticStudio) and rendered as proper Python docstrings — summary,
Args:, and Returns: sections, with <see cref="..."> references resolved to
plain names. Example of a generated stub entry:
def RemoveSurfaceAt(self, SurfaceNumber: int) -> bool:
"""Removes the surface at the specified location.
Args:
SurfaceNumber: The surface number to remove (1 to NumberOfSurfaces-1).
Returns:
true if the surface was successfully remove; otherwise, false.
"""
...
These appear on hover and in signature-help popups in any editor that reads docstrings from stubs (Pylance, Pyright, PyCharm).
Installation
pip install zosapi-stubs
That is the whole setup. No settings.json edits, no extraPaths, no
stubPath. Because this is a PEP 561 *-stubs package, any standards-
compliant type checker discovers it automatically as long as it's installed in
the same environment your editor uses for the project.
Install it into the same interpreter/venv your project uses — that's how the editor finds it. If VS Code/PyCharm is pointed at a venv,
pip installinto that venv.
To install from the locally built wheel instead of an index:
pip install dist/zosapi_stubs-1.1.0-py3-none-any.whl
To also use the one-line connection helper (or to rebuild the stubs), include
the connect extra, which pulls in pythonnet:
pip install "zosapi-stubs[connect]"
Connecting to the ZOS-API
The wheel includes a small runtime package, zosapi_stubs, that opens a robust
connection in one line — no hard-coded install path, no ZOSAPI_NetHelper.dll:
import zosapi_stubs
ZOSAPI, TheSystem = zosapi_stubs.Connect() # launch a new standalone instance
ZOSAPI, TheSystem = zosapi_stubs.Connect(extension=True) # attach to a running OpticStudio
Both returned objects are fully typed by the bundled stubs: ZOSAPI is the CLR
namespace module and TheSystem is the primary IOpticalSystem, so completion
and hover docs work immediately.
The install directory is discovered automatically from the Windows registry — a
pure-Python port of Zemax's Helper.cs (ZOSAPI_Initializer): it checks the
COM CodeBase entry and the HKCU\Software\Zemax\ZemaxRoot key, validates that
a candidate holds ZOSAPI.dll, ZOSAPI_Interfaces.dll and
OpticStudio.exe/ZemaxServer.exe, and registers a .NET AssemblyResolve hook
so dependent assemblies load from that directory. Pass an explicit path to
override discovery: Connect(path=r"D:\Zemax\OpticStudio").
For finer control, use the Connection class directly:
from zosapi_stubs import Connection
conn = Connection() # connects on construction (extension=, path= optional)
conn.directory # resolved install dir
conn.edition # 'Premium' | 'Professional' | 'Standard' | 'Invalid'
conn.TheApplication # IZOSAPI_Application
conn.TheSystem # IOpticalSystem
conn.open_file(r"C:\lens.zmx", False)
conn.close() # also works as a context manager
with Connection() as conn:
print(conn.TheSystem.SystemName)
Connection problems raise specific exceptions you can catch:
InitializationException (OpticStudio not found / app won't start),
ConnectionException, LicenseException, SystemNotPresentException — all
subclasses of ZOSAPIError.
Connect()needspythonnetand a local OpticStudio install — it runs the API for real. Install with theconnectextra. The stubs alone (for pure autocomplete) need neither.
Using the stubs
You can also connect manually; the stubs type everything either way. Nothing about the runtime changes — the stubs are invisible to Python at execution time and only inform the editor.
Write your ZOS-API code exactly as you already do. Nothing about the runtime changes — the stubs are invisible to Python at execution time and only inform the editor.
import clr
from pathlib import Path
zos_path = r"C:\Program Files\Zemax OpticStudio"
clr.AddReference(str(Path(zos_path) / "ZOSAPI.dll"))
clr.AddReference(str(Path(zos_path) / "ZOSAPI_Interfaces.dll"))
import ZOSAPI # runtime: the real CLR module
conn = ZOSAPI.ZOSAPI_Connection() # ← autocompletes
app = conn.CreateNewApplication() # ← ConnectAsExtension(0) for an extension
system = app.PrimarySystem # ← inferred as IOpticalSystem
lde = system.LDE # ← inferred as ILensDataEditor (hover docs)
row = lde.AddSurface() # ← inferred as ILDERow
What your editor now knows (verified with Pyright against the installed wheel):
| Expression | Inferred type |
|---|---|
system |
IOpticalSystem |
system.LDE |
ILensDataEditor |
system.LDE.AddSurface() |
ILDERow |
You get completion on system., lde., etc., parameter hints while typing a
call, and docstrings on hover.
Rebuilding for a new OpticStudio version
When you upgrade OpticStudio, regenerate the stubs so they match the new API.
The wheel installs a console command that rewrites the stubs in place (right
where pip installed them), so you never need this repo:
pip install "zosapi-stubs[rebuild]" # adds pythonnet, used only to read the DLLs
zosapi-stubgen # reflects over the installed DLLs, rewrites the stubs
Options:
# non-default OpticStudio install location
zosapi-stubgen --dll-dir "D:\Zemax\OpticStudio"
# write to a folder instead of rebuilding in place (e.g. a project-local typings dir)
zosapi-stubgen -o ./typings
zosapi-stubgen reads ZOSAPI.dll, ZOSAPI_Interfaces.dll and their
ZOSAPI*.xml doc files from --dll-dir (default
C:\Program Files\Zemax OpticStudio). Restart your editor's language server (or
the editor) afterward so it re-reads the refreshed stubs.
How it works
zosapi_stubs/connection.pyis the runtime helper: it portsHelper.csto Python (registry discovery +AssemblyResolvehook) and exposesConnect()/Connection.zosapi_stubs/stubgen/generator.pyloads the assemblies through pythonnet and walks every public type with .NET reflection (System.Reflection), mapping .NET types to Python annotations and writing one__init__.pyiper namespace.zosapi_stubs/stubgen/xmldocs.pyparses the.xmldocumentation. To attach a docstring to the right member it reconstructs each member's .NET XML documentation-comment ID (e.g.M:ZOSAPI.Editors.LDE.ILensDataEditor.RemoveSurfaceAt(System.Int32)) from the reflected metadata and looks it up — handling byref/array/generic parameter encodings — so signatures and docs line up exactly.zosapi_stubs/stubgen/cli.pyis thezosapi-stubgenentry point; with no-oit locates the installedZOSAPI-stubsdirectory and rebuilds it there.
Why it's runtime-safe
The stub directory is named ZOSAPI-stubs (the PEP 561 stub-package suffix)
and contains only .pyi files — no __init__.py. This is deliberate:
- A directory literally named
ZOSAPI(or any dir with an__init__.py/just.pyifiles onsys.path) would shadowimport ZOSAPIand hide pythonnet's dynamically generated CLR module, breakingZOSAPI.ZOSAPI_Connection()at runtime. - The
-stubssuffix is recognized by type checkers as "stubs for theZOSAPImodule" while being impossible to import at runtime. Your code keeps importing and running against the real DLL; the stubs are consulted only by the editor.
Limitations & caveats
- Editing the stubs is pointless — they're regenerated wholesale. Don't
hand-edit; change the generator and re-run
zosapi-stubgen. - Stubs describe shape, not runtime behavior. They reflect the API surface of the OpticStudio version they were generated from. After an upgrade, rebuild to stay accurate.
pythonnetis required only to connect or rebuild, not for autocomplete. It's an optional extra ([connect]/[rebuild], same dependency). Using the stubs for autocomplete needs nothing but the wheel;Connect()and stub generation both need pythonnet and a local OpticStudio install (the DLLs are read at runtime / generation time, not bundled).- Python version for connecting/rebuilding:
Connect()andzosapi-stubgenmust run on an interpreter where pythonnet works — currently CPython 3.9–3.13. pythonnet does not yet support 3.14; once it does, no change to this package is needed. The stubs themselves are plain text and impose no Python-version limit on your project. reportMissingModuleSourcewarning is expected. Pyright/Pylance may emit a warning (not an error) likeImport "ZOSAPI" could not be resolved from source— it found the stubs but no.pysource, which is correct: the source is a compiled DLL. Type checking and completion work fully; you can silence it in your project's Pyright config if desired.out/refreturns are modeled as tuples. Where a .NET method hasoutparameters, the stub return type is aTuple[...]of the return value followed by the out values — matching pythonnet's behavior. Confirm ordering against the API help when a method has several.System.Objectand unmapped types fall back toAny. A small number of exotic or pointer types are annotatedAnyrather than guessing.- Windows / OpticStudio required for generation. The DLLs and their
.xmldocs come from a local OpticStudio installation; the prebuilt stubs in the wheel were generated from one specific install and version.
Project layout
zosapi-stubs/
├── ZOSAPI-stubs/ # prebuilt PEP 561 stub package (ships in the wheel)
│ ├── __init__.pyi # top-level ZOSAPI namespace
│ ├── py.typed # PEP 561 marker
│ └── Analysis/ Editors/ … # one __init__.pyi per sub-namespace
├── src/zosapi_stubs/ # runtime package (ships in the wheel)
│ ├── __init__.py # exposes Connect / Connection
│ ├── connection.py # Helper.cs port + Connect()
│ └── stubgen/ # the rebuild tool
│ ├── generator.py # reflects over the DLLs → .pyi
│ ├── xmldocs.py # parses .xml docs → docstrings
│ └── cli.py # `zosapi-stubgen` entry point
├── pyproject.toml # hatchling build; force-includes ZOSAPI-stubs
├── README.md
└── dist/ # built wheel
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zosapi_stubs-1.1.3.tar.gz.
File metadata
- Download URL: zosapi_stubs-1.1.3.tar.gz
- Upload date:
- Size: 155.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8c4fdef18f4f5d372bcaac6119b08192aae002927de21492336e9a9dc4afae34
|
|
| MD5 |
b1e888bcd12aa89df83844cab01790d3
|
|
| BLAKE2b-256 |
e3d15599037ae92eacec94ff6bd34853df8d202be36ee5a1cc0c472ad487d417
|
Provenance
The following attestation bundles were made for zosapi_stubs-1.1.3.tar.gz:
Publisher:
release.yml on x68507/zosapi-stubs
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zosapi_stubs-1.1.3.tar.gz -
Subject digest:
8c4fdef18f4f5d372bcaac6119b08192aae002927de21492336e9a9dc4afae34 - Sigstore transparency entry: 1722259575
- Sigstore integration time:
-
Permalink:
x68507/zosapi-stubs@d82eed6923946f4de10ae37012a336d47715efcc -
Branch / Tag:
refs/tags/v1.1.3 - Owner: https://github.com/x68507
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d82eed6923946f4de10ae37012a336d47715efcc -
Trigger Event:
push
-
Statement type:
File details
Details for the file zosapi_stubs-1.1.3-py3-none-any.whl.
File metadata
- Download URL: zosapi_stubs-1.1.3-py3-none-any.whl
- Upload date:
- Size: 184.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f812f09ea3d3ae514485f64cf7dec5b486185dabf5964040fa07712c7513a6ab
|
|
| MD5 |
fe77c8dacf5a30b05f2935f706ad5d8f
|
|
| BLAKE2b-256 |
db3ad5c756a8c6c459d92fa942425277fce6f9c4a994d4a123c225f73f416014
|
Provenance
The following attestation bundles were made for zosapi_stubs-1.1.3-py3-none-any.whl:
Publisher:
release.yml on x68507/zosapi-stubs
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zosapi_stubs-1.1.3-py3-none-any.whl -
Subject digest:
f812f09ea3d3ae514485f64cf7dec5b486185dabf5964040fa07712c7513a6ab - Sigstore transparency entry: 1722259659
- Sigstore integration time:
-
Permalink:
x68507/zosapi-stubs@d82eed6923946f4de10ae37012a336d47715efcc -
Branch / Tag:
refs/tags/v1.1.3 - Owner: https://github.com/x68507
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d82eed6923946f4de10ae37012a336d47715efcc -
Trigger Event:
push
-
Statement type: