Repository navigation
_colorize module is slow to import, affecting traceback and logging modules #144384
Description
“It is a truth universally acknowledged, that a module in possession of great colors, suffers import time slowdown.
_colorize module is slow to import due to its use of dataclasses
python -Ximporttime -c "import _colorize"Visualization in tuna
Notice that significant time is also spent executing the _colorize, due to the creation of its dataclasses
❯ uvx python@3.15 -m cProfile -m _colorize | head -15
5609 function calls (5489 primitive calls) in 0.006 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
9/1 0.003 0.000 0.006 0.006 {built-in method builtins.exec}
1 0.000 0.000 0.006 0.006 <string>:1(<module>)
1 0.000 0.000 0.006 0.006 <frozen runpy>:199(run_module)
1 0.000 0.000 0.006 0.006 <frozen runpy>:65(_run_code)
1 0.000 0.000 0.006 0.006 _colorize.py:1(<module>)
7 0.000 0.000 0.005 0.001 dataclasses.py:1432(wrap)
7 0.000 0.000 0.005 0.001 dataclasses.py:986(_process_class)
7 0.000 0.000 0.003 0.000 dataclasses.py:478(add_fns_to_class)
4 0.000 0.000 0.001 0.000 inspect.py:3342(signature)
4 0.000 0.000 0.001 0.000 inspect.py:3055(from_callable)The slow import time directly affects (among others) the traceback module which in turn affects logging, which is 40% slower in 3.14 and 3.15 compared to 3.13. That is quite unfortunate since in applications that care about import time, logging module is typically hard to avoid (I originally discovered this when looking at pip's startup time)
hyperfine -w 10 "python3.13 -c 'import logging'" "python3.15 -c 'import logging'"
Benchmark 1: python3.13 -c 'import logging'
Time (mean ± σ): 21.0 ms ± 3.4 ms [User: 14.8 ms, System: 5.9 ms]
Range (min … max): 16.2 ms … 28.2 ms 155 runs
Benchmark 2: python3.15 -c 'import logging'
Time (mean ± σ): 29.6 ms ± 2.1 ms [User: 22.2 ms, System: 7.0 ms]
Range (min … max): 26.4 ms … 35.1 ms 100 runs
Summary
python3.13 -c 'import logging' ran
1.40 ± 0.25 times faster than /home/hollas/.local/share/uv/python/cpython-3.15.0a5-linux-x86_64-gnu/bin/python3.15 -c 'import logging'
It's not very clear how to make this better (besides not using dataclasses). We tried to make traceback lazy in logging, but failed. #112995
Activity
picnixz commentedon Feb 2, 2026
cc @hugovk
picnixz commentedon Feb 2, 2026
So the problem is colorize being imported by traceback which is imported by logging right? why not making colorize lazy in traceback?
picnixz commentedon Feb 2, 2026
OTOH, what if instead of a dataclass we use a namedtuple? (not from typing but from collections) or a custom class with slots? While we would lose kw-only support with namedtuples, we can backport the change and avoid changing other modules.
picnixz commentedon Feb 2, 2026
@StanFromIreland The fact that logging is affected makes it a bug for me as logging may be quite frequent in applications and those applications may also want to retain small startup time.
danielhollas commentedon Feb 2, 2026
I looked at that and it didn't seem easy as it is used in a bunch of places. But once the lazy import PR lands, it seems doable. But I am not sure if lazy importing in the
tracebackmodule is a great idea in general (e.g. you probably don't want to be importing modules in the middle of handling exceptions?).picnixz commentedon Feb 2, 2026
Unless I am mistaken, traceback is only meant for rendering tracebacks, so it should be fine. Lazy imports will not be available for 3.14 so it may be a good solution for the backport. How slower is the logging import?
ZeroIntensity commentedon Feb 2, 2026
How difficult would it be to speed up dataclass creation in general? I have two ideas:
dataclasses. This might be maintenance-heavy.__new__is called for the first time.Dataclasses are becoming increasingly common, and I think it would be nice if they weren't such a significant performance hit at import time.
picnixz commentedon Feb 2, 2026
I do not think the problem is
@dataclass; the problem is that we import a lot of modules in the dataclasses module:We import
inspectandenumand those are heavy modules.5 remaining items
hugovk commentedon Feb 16, 2026
Searching the top 15k PyPI packages from June (actually 14,041 with downloadable source), 2,485 match the
"(import dataclasses)|(from dataclasses import)"regex, or 17.7%.hugovk commentedon Feb 16, 2026
See also #144387 to lazy import
inspectindataclasses.DavidCEllis commentedon Feb 17, 2026
Outside of the import time improvements, most of the construction time in
dataclassesis spent on theexeccall to create all of the functions.Note: This is run on top of #144387 - without that you'll also see
inspectin here for docstring creationOne way to potentially make this faster in many cases would be to defer the generation of the methods using descriptors, only generating the methods that are actually used. It would be slightly slower for classes that use all methods but faster if some are never used1. This is how I generate the methods in my own classbuilder.
Footnotes
I suspect a lot of dataclasses never actually use
__repr__at runtime. ↩FFY00 commentedon Apr 1, 2026
I agree with @ambv about improving
dataclassesinstead, though I'd like to point out that an easy solution to speed up the type construction would be to freeze the_colorizemodule.hugovk commentedon Apr 2, 2026
Freezing
_colorizedoesn't make much difference:About two thirds of the time is spent importing
dataclasses, and about a third is creating thedataclassfor each theme.DavidCEllis commentedon Apr 10, 2026
I'd like to add that while this issue mentions
tracebackandlogging,_colorizealso makesargparsesignifcantly slower. It's not imported at top level, but it is imported as soon as you actually create a parser (color=Falsedoes not change this).Comparing 3.13.13 and 3.15.0a8:
Not all of this is colorize (
import shutilalso got a little slower due to the addition of zstd), but most of it is. If you don't create a parser, the import time looks the same but in practice it's gotten a lot slower.danielhollas commentedon Apr 10, 2026
@DavidCEllis interesting. Can you try measuring how much #144387 helps here?
DavidCEllis commentedon Apr 10, 2026
lazy_dataclassesis dataclasses from that PR, with an extra line to replacesys.modules["dataclasses"]so the time should be correct for that PR.It's definitely better, but
dataclassesis still getting hit byannotationlibwhich then hitsargparse.hugovk commentedon Apr 11, 2026
@DavidCEllis What OS is that with?
On macOS, 3.13 and 3.15 are much closer. Here is using the official installers (3.13.13, 3.15.0a8) and a local build of
mainwith optimisations:And #144387 is a big improvement, bringing it even closer to 3.13:
DavidCEllis commentedon Apr 11, 2026
It's a not exactly new laptop running Ubuntu 24.04, using the builds from
uv. Times on this machine are slower, but generally more consistent than on the faster desktop I have running Fedora.I'm actually just noting that on looking more closely - with the patch - it's less of an impact of the
astimport and more_colorizeitself being surprisingly slow? Is that all justexeccalls for dataclasses? I'll check on the faster machine early next week to see if my numbers are more like yours.hugovk commentedon Apr 11, 2026
Yes, I think so. See #144384 (comment) -- 2/3 importing
dataclasses, 1/3 execution.And see #144387 (comment) to show improvements for
_colorizewith the PR overmain.danielhollas commentedon Apr 11, 2026
Yes, that can be seen if your run the import under cProfile.
DavidCEllis commentedon Apr 13, 2026
I see better numbers on a faster machine, but still not as close to the 3.13 numbers as @hugovk
inspectmodule in dataclasses #144387 with optimisations.Is it worth making a separate issue for
argparseto defer_colorizefurther (until it actually needs to colour something)?hugovk commentedon May 6, 2026
I opened #149318 to lazily import
_colorize, which saves around 8ms and makes importing some modules between 17% and 80% faster.gh-144384: Lazily import `_colorize` (#149318)