You wire up a 3D point-cloud pipeline in Python. The program runs, and the editor still underlines half the attributes: visualization is not a known member of module "open3d". The reflex — reinstall the package, add paths to every settings field, flip every suggestion switch — burns an afternoon, because the warning does not come from Python. It comes from the editor's static analyzer. That analyzer cannot see inside packages whose real code is a compiled extension behind a thin wrapper. You will leave with a two-command test that separates an environment problem from a missing-type-information problem, the fix that matches each, and settings that quiet the editor without touching the program running in the cell's controller.
Why does the editor warn about a member the program runs without?
The warning comes from the editor's static type checker, not from the Python runtime. A library whose real code lives in a compiled C extension exposes its submodules dynamically. With no py.typed marker or stub files to read, the checker reports valid attributes as unknown.
The analyzer reads source text and stub files only. At runtime the attribute resolves to compiled code and the call executes — the window opens in under 2 s and the geometry draws. The checker has no source for the compiled part and no stub to read, so as far as it can tell the attribute does not exist. Hence the wording is not a known member of module. NumPy behaves differently because it ships its type information inside the package. One planning consequence follows: reinstalling cannot change this, because the release simply does not carry type information for the compiled part.
How do you prove the import resolves in the interpreter the editor uses?
Run one line in the terminal that owns your packages, then read the same path in the editor's interpreter picker. The command python -c "import sys; print(sys.executable)" prints the interpreter that has the library installed. The picker must show the very same path before you blame the package.
Two symptoms reach your screen, and they need different fixes:
| What the editor shows | What the program does | What it means | The fix |
|---|---|---|---|
Import underlined, could not be resolved
|
Runs, or fails with ModuleNotFoundError | The analyzer is looking at another environment | Select the project interpreter; add its site-packages folder to python.analysis.extraPaths |
Import clean, members flagged not a known member
|
Runs | The package carries no type information | Suppress the diagnostic or add a stub |
| The same fault reported twice in different words | Runs | Two analyzers are active | Keep the language server, switch the second off |
Worked example, on the versions this failure was reported with — Python 3.9.13, Open3D 0.17.0, project venv:
> python -c "import sys; print(sys.executable)"
C:\dev\pick3d\venv\Scripts\python.exe
That path is the venv's own interpreter; anything else under the picker means the analyzer reads a different environment. Then run the script that builds a LineSet from ten random lines and opens a window: all ten draw, so the runtime path is intact. The remaining warning is the missing-type-information case, not an installation case. If the picker showed a system Python instead, every clean import you saw was the luck of a second, different installation — the interpreter-mismatch pattern repeats across camera packages, and 3D camera header version mismatch: fix firmware and SDK alignment covers the version side of that same split.
Which settings in settings.json actually matter?
In VS Code, 3 keys decide the behavior: python.languageServer chooses the analyzer, python.analysis.typeCheckingMode sets how loudly it reports, and a leftover python.linting.mypyEnabled runs a second checker that hits the same missing-stubs wall.
Trimmed to what the failing project actually needed:
{
"python.languageServer": "Pylance",
"python.analysis.typeCheckingMode": "basic"
}
Notes on the keys around them:
-
"python.analysis.typeCheckingMode": "basic"keeps the useful checks but still reports unknown members on untyped packages, which is why the warning survived this exact setting. Setting it to"off"removes the member warnings along with everything else — a blunt instrument for a workspace that mixes typed and untyped libraries. -
python.analysis.extraPathsis the fallback, not the fix: with the venv interpreter selected, the failing project's extraPaths entry into its venv site-packages folder is redundant. Add a site-packages path only when the picker alone does not resolve the import. The olderpython.autoComplete.extraPathskey predates the current language server and can go. -
"python.linting.mypyEnabled": truegives you a second analyzer that needs stubs too, so it repeats the complaint in its own vocabulary; switch it off and let the language server own diagnostics. - Word-based suggestions complete words from your open files, not package members — handy, and they explain completions that keep appearing while the checker complains.
How do you silence the warning without losing every other check?
Two scopes exist. A # type: ignore comment silences the diagnostics on the one line you put it on, and the analyzer's severity settings let you turn the attribute-access rule down to none across the workspace.
For a handful of flagged lines, annotate them where they are:
import open3d as o3d
mat = o3d.visualization.rendering.MaterialRecord() # type: ignore
mat.shader = "unlitLine"
Keep each suppressed line to a single call, because the comment hides everything else the checker would say about that line. For a workspace where dozens of lines touch the wrapper, turn the one rule down instead of annotating every line. The Python analysis settings expose a severity override list:
{
"python.analysis.diagnosticSeverityOverrides": {
"reportAttributeAccessIssue": "none"
}
}
Current analyzer builds call the rule reportAttributeAccessIssue; older builds called it reportGeneralTypeIssues — the one to lower is whichever name your installed version prints under the warning. With it at "none", unknown-member reports disappear for every untyped package in the workspace, while basic checks such as undefined names stay on. If you also run a separately installed pyright in CI, expect small differences: the editor and the CLI update on their own schedules.
When is a stub file the better fix than suppression?
A stub wins when the same library appears across many files or repositories. Write one small .pyi, commit it with the project, and every analyzer that reads the code — yours, a teammate's, the build's — stops guessing at the same attributes.
Check first whether the package already carries types; it costs one minute in the installed folder:
> python -c "import open3d, os; print(os.path.dirname(open3d.__file__))"
A py.typed marker or .pyi files there mean the release ships types. Restart the language server from the command palette, and a stale analyzer's warnings clear after its reindex, typically under 5 s on a project of a few thousand lines. No marker and no stubs mean you write the smallest useful stub, declaring the submodules your code touches:
# Minimal stub for a compiled package
from typing import Any
visualization: Any
utility: Any
geometry: Any
Put it in the folder the analyzer reads as stubs: the Python analysis settings name that location the stub path, and a workspace folder named typings is picked up by default. Names declared as Any kill the flag but give no signature help; declaring real classes and functions restores full autocomplete. If your package is one of many that need the same treatment, the community publishes stub-only packages whose names start with types-; installing the matching one is often faster than writing your own.
Why do compiled camera SDKs trip this more often than NumPy?
A machine-vision SDK usually ships its engine as a compiled core with a thin Python wrapper over it. The same missing-type-information warning therefore follows it into every project, while NumPy and the standard library carry their type information inside the package.
The one-minute folder check works for any new dependency, so make it part of adoption day. Py.typed present: expect quiet completions. Absent: expect member warnings, and choose suppression or a stub now rather than mid-sprint. The same interpreter discipline applies to the snippets you paste around a camera. Keep every experiment in the project's selected environment — the habit behind access a camera parameter tree over TCP from a second app, where two processes must agree on one interpreter.
What does the fixed pipeline look like on real hardware?
With the warning handled, your code is ordinary Python again, and it runs unchanged on the controller that drives the cell. The profile sensor feeds point clouds to the script, the controller executes it, and the editor's diagnostics never leave your workstation.
In a scanning cell those point clouds come from a 3D laser profile sensor — the class you can browse in the 3D sensors category and narrow down in the camera finder.
- Type
- Laser profiler
The script itself belongs on a vision controller rather than an office desktop. The model below pairs 32 GB of memory with PoE camera ports and 8 built-in light channels, so cameras and lights wire into the same box that runs your Python.
- LAN ports
- 5+ LAN ports
- USB 3 ports
- 3-4 USB 3 ports
- PoE
- PoE camera ports
- Light outputs
- 8 built-in channels
With the editor quiet, the next real work is the cloud itself: 3D point cloud x y z resolution settings without distortion picks up exactly there.
How do you keep the warning from coming back?
Three habits keep analyzer and runtime in step, and each costs under a minute: one interpreter per project selected before the first import, a py.typed check the day you adopt a library, and exactly one analyzer watching diagnostics.
- Create one virtual environment per project and select it in the editor's picker before writing imports; interpreter drift sits behind most
resolved yesterday, unresolved todayreports. - Check the installed folder for py.typed or .pyi on adoption day; no type information means you plan the suppression or the stub immediately.
- Keep the language server as the only diagnostic source, legacy linters off, so warnings do not arrive twice in two vocabularies.
- Commit the stub with the repository and record the library version next to it, because a stub describes the release it was written against.
If the cell behind this code is still on the bench, the 3D vision guides gather the camera-side questions next, and the contact page reaches the applications team for a parts list matched to the pipeline.
Questions
Does # type: ignore hide real errors on that line?
Yes: the comment suppresses every diagnostic on that one line, not only the unknown-member warning. Keep the suppressed line to a single call so a typo or a wrong argument count cannot slip through under it. For a line you exercise on every run anyway, that trade is worth taking.
Will the fix reach teammates and the build machine?
Only what the repository carries travels with it: a .pyi stub and a workspace settings file under .vscode commit with the project, so every clone gets the same quiet editor. Editor settings saved at user level fix one machine only. Suppression comments live inside the source and reach every code check that reads it.
Should I reinstall or upgrade the library to remove the warning?
A clean reinstall of the same version reproduces the same warning, because type information ships with the release. Upgrading helps only when a release adds stubs, so check the installed folder for a py.typed file after any upgrade. Pin the version first, so the cure is a settings change rather than a version gamble.
Does the warning change anything when the script runs on the vision controller?
No: diagnostics live in the editor and never ship with the code, so a flagged line and a clean line execute identically. The runtime risk to manage is version drift between the workstation virtual environment and the controller's, which pinned requirements files prevent. Keep both environments installed from the same pins.
Can I write stubs for the camera SDK's own Python wrapper?
Yes: the mechanism is the same as for any untyped package. Declare the classes and functions you call, with argument and return types where you know them and Any where you do not, then keep the stub beside the project. Refresh it whenever you adopt an SDK version that adds new calls.

