Skip to content

Perf: defer heavy startup imports to reduce launch time by ~500ms#25976

Open
eendebakpt wants to merge 3 commits intospyder-ide:masterfrom
eendebakpt:perf/reduce-startup-imports
Open

Perf: defer heavy startup imports to reduce launch time by ~500ms#25976
eendebakpt wants to merge 3 commits intospyder-ide:masterfrom
eendebakpt:perf/reduce-startup-imports

Conversation

@eendebakpt
Copy link
Copy Markdown
Contributor

@eendebakpt eendebakpt commented Apr 22, 2026

Problem

Spyder's startup time was unnecessarily slow due to several third-party libraries being imported eagerly at module level, even though they are only needed lazily or in rare code paths.

Profiled using python -X importtime and subprocess timing.

Changes

spyder/utils/programs.py — removes requests and defers asyncio

  • requests (~250ms saved): CaseInsensitiveDict from requests.structures was imported at module level but only used in one Windows-only branch inside alter_subprocess_kwargs_by_platform(). Replaced with an inline case-insensitive key lookup using only stdlib ({k.lower(): k for k in env}).
  • asyncio (~100ms saved, deferred): Moved inside the if asynchronous: branch of run_shell_command(), which is called only from environ.py with asynchronous=True.

spyder/config/manager.py — defers keyring (~200ms saved)

keyring and keyring.errors.NoKeyringError were imported at the top of the module. These are only needed inside if secure: branches (get, set, remove_option), which are only hit when remote/credential-secured config options are accessed. Moved the imports to those three call sites.

spyder/config/appearance.py — avoids importing sphinxify (~300ms saved)

from spyder.plugins.help.utils.sphinxify import CSS_PATH was triggering the full sphinx + docutils + jinja2 import chain at startup, just to get a path string constant. Replaced with importlib.util.find_spec('spyder.plugins.help.utils') which resolves the path without loading the Sphinx stack.

Measured improvement

Module Before After
spyder.utils.programs ~440ms ~130ms
spyder.config.manager ~616ms ~220ms
spyder.app.start ~431ms ~167ms

🤖 Generated with Claude Code

- Remove top-level `requests` import in programs.py (saved ~250ms): only
  `CaseInsensitiveDict` was used in one Windows-only branch; replaced with
  an inline case-insensitive dict lookup using stdlib.
- Defer `keyring` import in config/manager.py (saved ~200ms): keyring is
  only needed inside `if secure:` branches, which are hit only when remote
  credentials are accessed — import moved to those call sites.
- Avoid importing sphinxify (sphinx+docutils+jinja2) in config/appearance.py
  (saved ~300ms): `CSS_PATH` is just a directory path that we now compute
  directly via `importlib.util.find_spec` without loading the Sphinx stack.
- Defer `asyncio` import in programs.py: moved inside the `asynchronous=True`
  branch of `run_shell_command`, which is called only from environ.py.

Total wall-clock reduction on the critical import path:
  spyder.utils.programs:   ~440ms → ~130ms
  spyder.config.manager:   ~616ms → ~220ms
  spyder.app.start:        ~431ms → ~167ms

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@eendebakpt eendebakpt closed this Apr 22, 2026
@ccordoba12
Copy link
Copy Markdown
Member

Hey @eendebakpt, why did you close this PR? The gains in startup time seem significant enough to merit its inclusion in our next version.

@eendebakpt
Copy link
Copy Markdown
Contributor Author

@ccordoba12 PR was opened too soon by Claude. I wanted to review the PR myself first to avoid wasting reviewers resources and I wanted to validate the actual gains. Now that you are looking anyway: if change makes sense just reopen it and we can iterate until the PR is good.

For python 3.15 I would recommend to adopt PEP 810 (it will help with more imports).

eendebakpt and others added 2 commits April 22, 2026 23:31
- Replace importlib.util.find_spec (which can return None, triggering a
  type error) with osp.dirname(spyder.__file__) to derive CSS_PATH. The
  spyder package is always in sys.modules at this point, so this is both
  safe and simpler.
- Remove stray # lazy import inline comments on keyring imports in
  config/manager.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ccordoba12
Copy link
Copy Markdown
Member

@dalthviz, please take a look at @eendebakpt's work.

@ccordoba12 ccordoba12 assigned eendebakpt and unassigned dalthviz Apr 23, 2026
@ccordoba12 ccordoba12 requested a review from dalthviz April 23, 2026 20:34
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.

3 participants