Skip to content

Import heavy third-party modules lazily (matplotlib, numpy, cv2)#110

Merged
dccote merged 4 commits into
masterfrom
lazy-imports
Jul 8, 2026
Merged

Import heavy third-party modules lazily (matplotlib, numpy, cv2)#110
dccote merged 4 commits into
masterfrom
lazy-imports

Conversation

@dccote

@dccote dccote commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Problem

import hardwarelibrary eagerly loaded matplotlib and numpy through the spectrometer and oscilloscope packages, even when nothing was ever plotted or acquired. Several imports were also dead: oceaninsight.py imported matplotlib it never used, and SutterDevice/EchoDevice imported pyftdi.Ftdi they never referenced (both already flagged #FIXME: should not be here).

Solution

Defer each non-standard module to its point of use, and delete the dead imports:

  • Spectrometers (010ab73): SpectraViewer now imported inside display()/displayAny(); numpy inside the __init__/getSpectrum methods that use it; base.py gains from __future__ import annotations so the -> np.array hint no longer forces numpy at class-definition time. Removed the unused matplotlib import block in oceaninsight.py. viewer.py is unchanged — it legitimately needs matplotlib and is now only imported when a viewer is shown.
  • Oscilloscope (ffb1457): matplotlib.pyplot moved into displayWaveforms(), its only caller.
  • Cameras (ba2a102): removed the top-level guarded import cv2; deferred into the three methods that use it. A missing OpenCV now surfaces as a clear ModuleNotFoundError only when a camera op is invoked, instead of a print at import time.
  • Dead imports (6013246): removed unused from pyftdi.ftdi import Ftdi from SutterDevice and EchoDevice.

Result

import hardwarelibrary: 336 ms -> 59 ms, with matplotlib and numpy no longer loaded until you actually plot or acquire.

Reviewed and left as-is

  • pyftdi in communication/serialport.py — genuinely used across several methods, ~10 ms to import, core serial dependency.
  • pyserial/pyusb — lightweight core comms deps, used pervasively.
  • u3 (LabJack) in daq/labjackdevice.py — not on the default import path.
  • pylablib in motion/thorlabs.py — already lazily imported.

Testing

Full suite: 398 passed, 235 skipped (hardware-absent), unchanged from baseline.

Deferred paths exercised directly: DebugSpectro.getSpectrum() (numpy), oscilloscope + cameras imports without matplotlib/cv2 loaded.

🤖 Generated with Claude Code

dccote and others added 4 commits July 8, 2026 01:34
Problem: Importing anything from hardwarelibrary.spectrometers eagerly
loaded matplotlib and numpy. base.py did `from .viewer import *` (viewer
is a pure matplotlib GUI module) purely to reach SpectraViewer, and
imported numpy at module scope. oceaninsight.py imported matplotlib
(backends/pyplot/animation/Button/TextBox) that it never actually used,
plus numpy at module scope. __init__.py imported SpectraViewer at the
top. As a result `import hardwarelibrary` paid the full matplotlib +
numpy cost even when no plot was ever drawn.

Solution: Move the heavy imports to their point of use. SpectraViewer is
now imported inside Spectrometer.display(), OISpectrometer.display() and
displayAny(). numpy is imported inside the __init__/getSpectrum methods
that use it; base.py gains `from __future__ import annotations` so the
`-> np.array` hint no longer forces numpy at class-definition time. The
dead matplotlib import block in oceaninsight.py (never referenced) is
removed. viewer.py is unchanged: it legitimately needs matplotlib, and
is now only imported when a viewer is actually shown.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem: oscilloscopedevice.py imported matplotlib.pyplot at module
scope, so `import hardwarelibrary` (which imports the oscilloscope
package) loaded matplotlib even though plt is only ever used by the
optional displayWaveforms() method.

Solution: Move `import matplotlib.pyplot as plt` into displayWaveforms(),
its only caller.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem: camera.py imported cv2 at module scope behind a try/except.
Importing the cameras package therefore loaded OpenCV eagerly, and a
missing OpenCV only surfaced as a printed warning at import time while
leaving cv2 undefined for later calls.

Solution: Remove the top-level guarded import and import cv2 inside the
three methods that use it (captureLoopSynchronous, openCVProperties,
doInitializeDevice). The module now imports without OpenCV present, and a
missing dependency surfaces as a clear ModuleNotFoundError only when a
camera operation is actually invoked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem: sutterdevice.py and echodevice.py both did
`from pyftdi.ftdi import Ftdi`, each already flagged with a
`#FIXME: should not be here.` comment. Neither file references Ftdi
anywhere, so the imports were dead code that pulled pyftdi in eagerly.

Solution: Delete the unused imports. SutterDevice reaches FTDI hardware
through SerialPort, which owns the pyftdi dependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dccote
dccote merged commit 20a9d4c into master Jul 8, 2026
14 checks passed
@dccote
dccote deleted the lazy-imports branch July 8, 2026 06:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant