You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While investigating #884, I discovered a bit of a roadblock.
currently, in msgspec/__init__.py we have the following:
from . importinspect, json, msgpack, structs, toml, yaml
which means that all submodules will always be imported. Especially inspect is a heavy hitter, since it imports typing, typing_extensions and a whole bunch of other stuff. This means that, even if we make the core extension super lightweight on imports, we won't see a noticeable difference, unless we remove those imports.
Functionally, the difference is that currently you can do: import msgspec; msgspec.inspect.is_struct(), whereas with these imports removed, you'd have to do import msgspec.inspect; msgspec.inspect.is_struct().
I think it's worth doing, and it's standard practice, however, it would be a breaking change. If we do this, it'd be a 2 part change: 1. Mark them deprecated, 2. Remove them. Since we're currently in 0ver, we could do this in a minor release, but we haven't had much breaking changes, so we should give ample time to users to react.
@msgspec/committers, I'd like to know your opinion on this.
As for the performance impact: Judging by some crude testing, I think we can easily bring down the import time by ~50%, with a bit more effort maybe ~75%.
Can we start the first phase immediately and use PEP 562? We would gain the immediate benefit of their "removal" while simultaneously emitting deprecation warnings.
In my previous comment I proposed basically 3. with deprecation warnings. It's fine to keep them for a while but we shouldn't maintain an API that we don't want indefinitely.
You bring up an interesting point however: is our code compatible with the new lazy imports? I can't recall if there was a PR that added support.
My 2c: lazy __getattr__ without a deprecation period, i.e. what @sobolevn suggested.
The import-time win lands immediately and identically either way. A deprecation only buys us the option to eventually delete the __getattr__ hook itself, which is ~15 lines we can keep forever at zero cost.
The churn side of a DeprecationWarning is real and ecosystem-wide: import msgspec; msgspec.json.encode(...) is the spelling in half the tutorials and downstream libraries. Everyone gets warning noise, everyone ships a trivial fix release, and what we get back is deleting a tiny hook.
I'd still update the docs to prefer import msgspec.json as the canonical spelling going forward, so new code trends the right way without forcing old code to move.
One thing to settle before landing: the interplay with 3.15 lazy imports (#1095) - our own __getattr__ laziness and PYTHON_LAZY_IMPORTS should compose, and sobolevn is already digging there.
Activity
provinzkraut commentedon Jun 19, 2026
As for the performance impact: Judging by some crude testing, I think we can easily bring down the import time by ~50%, with a bit more effort maybe ~75%.
ofek commentedon Jun 19, 2026
Can we start the first phase immediately and use PEP 562? We would gain the immediate benefit of their "removal" while simultaneously emitting deprecation warnings.
sobolevn commentedon Jun 21, 2026
In my opinion deprecation is not really worth it.
Reasons:
lazy importstatementmsgspecAPI seems rather compact and simpleofek commentedon Jun 21, 2026
In my previous comment I proposed basically 3. with deprecation warnings. It's fine to keep them for a while but we shouldn't maintain an API that we don't want indefinitely.
You bring up an interesting point however: is our code compatible with the new
lazyimports? I can't recall if there was a PR that added support.sobolevn commentedon Jun 21, 2026
I will check this out and report back :)
sobolevn commentedon Jun 21, 2026
pytestdoes not work with this newPYTHON_LAZY_IMPORTS=allmode: pytest-dev/pytest#14632 So, I can't tell if the tests pass 😆RonnyPfannschmidt commentedon Jun 21, 2026
Try with assertion rewrite disabled
Siyet commentedon Jul 3, 2026
My 2c: lazy
__getattr__without a deprecation period, i.e. what @sobolevn suggested.__getattr__hook itself, which is ~15 lines we can keep forever at zero cost.DeprecationWarningis real and ecosystem-wide:import msgspec; msgspec.json.encode(...)is the spelling in half the tutorials and downstream libraries. Everyone gets warning noise, everyone ships a trivial fix release, and what we get back is deleting a tiny hook.import msgspec.jsonas the canonical spelling going forward, so new code trends the right way without forcing old code to move.One thing to settle before landing: the interplay with 3.15 lazy imports (#1095) - our own
__getattr__laziness andPYTHON_LAZY_IMPORTSshould compose, and sobolevn is already digging there.ofek commentedon Jul 3, 2026
We will not be doing this.