Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/mypy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ jobs:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.13"
python-version: "3.15"
allow-prereleases: true
cache: pip
cache-dependency-path: Tools/requirements-dev.txt
- run: pip install -r Tools/requirements-dev.txt
Expand Down
2 changes: 1 addition & 1 deletion Lib/_pyrepl/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from __future__ import annotations

import os
import _colorize

from abc import ABC, abstractmethod
import ast
Expand All @@ -30,6 +29,7 @@
import re
import sys
from typing import TYPE_CHECKING
lazy import _colorize

from .render import RenderedScreen
from .trace import trace
Expand Down
3 changes: 2 additions & 1 deletion Lib/_pyrepl/fancycompleter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
#
# All Rights Reserved
"""Colorful tab completion for Python prompt"""
from _colorize import ANSIColors, get_colors, get_theme
import rlcompleter
import keyword
import types
lazy from _colorize import ANSIColors, get_colors, get_theme


class Completer(rlcompleter.Completer):
"""
Expand Down
2 changes: 1 addition & 1 deletion Lib/_pyrepl/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@
from __future__ import annotations

import sys
import _colorize

from contextlib import contextmanager
from dataclasses import dataclass, field, fields, replace
from typing import Self
lazy import _colorize

from . import commands, console, input
from .content import (
Expand Down
2 changes: 1 addition & 1 deletion Lib/_pyrepl/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
import token as T
import tokenize
import unicodedata
import _colorize

from collections import deque
from dataclasses import dataclass
from io import StringIO
from tokenize import TokenInfo as TI
from typing import Iterable, Iterator, Match, NamedTuple, Self
lazy import _colorize

from .types import CharBuffer, CharWidths
from .trace import trace
Expand Down
2 changes: 1 addition & 1 deletion Lib/difflib.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@
'Differ','IS_CHARACTER_JUNK', 'IS_LINE_JUNK', 'context_diff',
'unified_diff', 'diff_bytes', 'HtmlDiff', 'Match']

from _colorize import can_colorize, get_theme
from heapq import nlargest as _nlargest
from collections import namedtuple as _namedtuple
from types import GenericAlias
lazy from _colorize import can_colorize, get_theme

Match = _namedtuple('Match', 'a b size')

Expand Down
4 changes: 2 additions & 2 deletions Lib/doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ def _test():
import unittest
from io import StringIO, TextIOWrapper, BytesIO
from collections import namedtuple
import _colorize # Used in doctests
from _colorize import ANSIColors, can_colorize
lazy import _colorize # Used in doctests
lazy from _colorize import ANSIColors, can_colorize


class TestResults(namedtuple('TestResults', 'failed attempted')):
Expand Down
2 changes: 1 addition & 1 deletion Lib/json/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import json
import re
import sys
from _colorize import get_theme, can_colorize
lazy from _colorize import get_theme, can_colorize


# The string we are colorizing is valid JSON,
Expand Down
2 changes: 1 addition & 1 deletion Lib/pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@
import linecache
import selectors
import threading
import _colorize

from contextlib import ExitStack, closing, contextmanager
from types import CodeType
from warnings import deprecated
lazy import _colorize

try:
import _pyrepl.utils
Expand Down
2 changes: 1 addition & 1 deletion Lib/profiling/sampling/live_collector/collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import sys
import sysconfig
import time
import _colorize
lazy import _colorize

from ..collector import Collector, extract_lineno
from ..constants import (
Expand Down
2 changes: 1 addition & 1 deletion Lib/profiling/sampling/pstats_collector.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import collections
import marshal
import pstats
lazy from _colorize import ANSIColors

from _colorize import ANSIColors
from .collector import Collector, extract_lineno
from .constants import MICROSECONDS_PER_SECOND, PROFILING_MODE_CPU

Expand Down
2 changes: 1 addition & 1 deletion Lib/profiling/sampling/sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import sysconfig
import time
from collections import deque
from _colorize import ANSIColors
lazy from _colorize import ANSIColors

from .pstats_collector import PstatsCollector
from .stack_collector import CollapsedStackCollector, FlamegraphCollector
Expand Down
6 changes: 4 additions & 2 deletions Lib/sqlite3/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@
from argparse import ArgumentParser
from code import InteractiveConsole
from textwrap import dedent
from _colorize import get_theme, theme_no_color
lazy from _colorize import get_theme, theme_no_color

from ._completer import completer


def execute(c, sql, suppress_errors=True, theme=theme_no_color):
def execute(c, sql, suppress_errors=True, theme=None):
"""Helper that wraps execution of SQL code.
This is used both by the REPL and by direct execution from the CLI.
'c' may be a cursor or a connection.
'sql' is the SQL string to execute.
"""
if theme is None:
theme = theme_no_color

try:
for row in c.execute(sql):
Expand Down
8 changes: 8 additions & 0 deletions Lib/test/test_difflib.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import difflib
from test import support
from test.support import findfile, force_colorized
from test.support.import_helper import ensure_lazy_imports
import unittest
import doctest
import sys
Expand Down Expand Up @@ -644,6 +646,12 @@ def setUpModule():
difflib.HtmlDiff._default_prefix = 0


class LazyImportTest(unittest.TestCase):
@support.cpython_only
def test_lazy_import(self):
ensure_lazy_imports("difflib", {"_colorize"})


def load_tests(loader, tests, pattern):
tests.addTest(doctest.DocTestSuite(difflib))
return tests
Expand Down
7 changes: 7 additions & 0 deletions Lib/test/test_json/test_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from test import support
from test.support import force_colorized, force_not_colorized, os_helper
from test.support.import_helper import ensure_lazy_imports
from test.support.script_helper import assert_python_ok

from _colorize import get_theme
Expand Down Expand Up @@ -334,5 +335,11 @@ class TestTool(TestMain):
module = 'json.tool'


class LazyImportTest(unittest.TestCase):
@support.cpython_only
def test_lazy_import(self):
ensure_lazy_imports("json.tool", {"_colorize"})


if __name__ == "__main__":
unittest.main()
8 changes: 7 additions & 1 deletion Lib/test/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from io import StringIO
from test import support
from test.support import has_socket_support, os_helper
from test.support.import_helper import import_module
from test.support.import_helper import ensure_lazy_imports, import_module
from test.support.pty_helper import run_pty, FakeInput
from test.support.script_helper import kill_python
from unittest.mock import patch
Expand Down Expand Up @@ -5306,5 +5306,11 @@ def tearDown(test):
return tests


class LazyImportTest(unittest.TestCase):
@support.cpython_only
def test_lazy_import(self):
ensure_lazy_imports("pdb", {"_colorize"})


if __name__ == '__main__':
unittest.main()
8 changes: 7 additions & 1 deletion Lib/test/test_sqlite3/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
import os

from sqlite3.__main__ import main as cli
from test.support.import_helper import import_module
from test.support.import_helper import ensure_lazy_imports, import_module
from test.support.os_helper import TESTFN, unlink
from test.support.pty_helper import run_pty
from test.support import (
captured_stdout,
captured_stderr,
captured_stdin,
cpython_only,
force_not_colorized_test_class,
requires_subprocess,
verbose,
Expand Down Expand Up @@ -437,6 +438,11 @@ def test_complete_no_input(self):
raise


class LazyImportTest(unittest.TestCase):
@cpython_only
def test_lazy_import(self):
ensure_lazy_imports("sqlite3.__main__", {"_colorize"})


if __name__ == "__main__":
unittest.main()
8 changes: 7 additions & 1 deletion Lib/test/test_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
requires_subprocess, os_helper)
from test.support.os_helper import TESTFN, temp_dir, unlink
from test.support.script_helper import assert_python_ok, assert_python_failure, make_script
from test.support.import_helper import forget
from test.support.import_helper import ensure_lazy_imports, forget
from test.support import force_not_colorized, force_not_colorized_test_class

import json
Expand Down Expand Up @@ -5543,5 +5543,11 @@ def test_suggestion_still_works_for_non_lazy_attributes(self):
self.assertNotIn(b"BAR_MODULE_LOADED", stdout)


class LazyImportTest(unittest.TestCase):
@support.cpython_only
def test_lazy_import(self):
ensure_lazy_imports("traceback", {"_colorize"})


if __name__ == "__main__":
unittest.main()
49 changes: 39 additions & 10 deletions Lib/traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,38 @@
import io
import importlib.util
import pathlib
import _colorize

from contextlib import suppress
lazy import _colorize


class _ShutdownTheme:
Comment thread
hugovk marked this conversation as resolved.
"""Empty stand-in if `_colorize` cannot be imported during late shutdown."""
def __getattr__(self, _): return self
def __getitem__(self, _): return ""
def __format__(self, _): return ""
def __str__(self): return ""
def __add__(self, other): return other
__radd__ = __add__
Comment thread
picnixz marked this conversation as resolved.


_shutdown_theme = _ShutdownTheme()


def _safe_get_theme(*, force_color=False, force_no_color=False):
try:
return _colorize.get_theme(
force_color=force_color, force_no_color=force_no_color
)
except ImportError:
return _shutdown_theme

Comment thread
hugovk marked this conversation as resolved.

def _safe_can_colorize(*, file=None):
try:
return _colorize.can_colorize(file=file)
except ImportError:
return False

try:
from _missing_stdlib_info import _MISSING_STDLIB_MODULE_MESSAGES
Expand Down Expand Up @@ -151,7 +180,7 @@ def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \
def _print_exception_bltin(exc, file=None, /):
if file is None:
file = sys.stderr if sys.stderr is not None else sys.__stderr__
colorize = _colorize.can_colorize(file=file)
colorize = _safe_can_colorize(file=file)
return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file, colorize=colorize)


Expand Down Expand Up @@ -199,9 +228,9 @@ def _format_final_exc_line(etype, value, *, insert_final_newline=True, colorize=
valuestr = _safe_string(value, 'exception')
end_char = "\n" if insert_final_newline else ""
if colorize:
theme = _colorize.get_theme(force_color=True).traceback
theme = _safe_get_theme(force_color=True).traceback
else:
theme = _colorize.get_theme(force_no_color=True).traceback
theme = _safe_get_theme(force_no_color=True).traceback
if value is None or not valuestr:
line = f"{theme.type}{etype}{theme.reset}{end_char}"
else:
Expand Down Expand Up @@ -555,9 +584,9 @@ def format_frame_summary(self, frame_summary, **kwargs):
if frame_summary.filename.startswith("<stdin-") and frame_summary.filename.endswith('>'):
filename = "<stdin>"
if colorize:
theme = _colorize.get_theme(force_color=True).traceback
theme = _safe_get_theme(force_color=True).traceback
else:
theme = _colorize.get_theme(force_no_color=True).traceback
theme = _safe_get_theme(force_no_color=True).traceback
row.append(
' File {}"{}"{}, line {}{}{}, in {}{}{}\n'.format(
theme.filename,
Expand Down Expand Up @@ -1336,9 +1365,9 @@ def format_exception_only(self, *, show_group=False, _depth=0, **kwargs):
"""
colorize = kwargs.get("colorize", False)
if colorize:
theme = _colorize.get_theme(force_color=True).traceback
theme = _safe_get_theme(force_color=True).traceback
else:
theme = _colorize.get_theme(force_no_color=True).traceback
theme = _safe_get_theme(force_no_color=True).traceback

indent = 3 * _depth * ' '
if not self._have_exc_type:
Expand Down Expand Up @@ -1486,9 +1515,9 @@ def _format_syntax_error(self, stype, **kwargs):
# Show exactly where the problem was found.
colorize = kwargs.get("colorize", False)
if colorize:
theme = _colorize.get_theme(force_color=True).traceback
theme = _safe_get_theme(force_color=True).traceback
else:
theme = _colorize.get_theme(force_no_color=True).traceback
theme = _safe_get_theme(force_no_color=True).traceback
filename_suffix = ''
if self.lineno is not None:
yield ' File {}"{}"{}, line {}{}{}\n'.format(
Expand Down
3 changes: 1 addition & 2 deletions Lib/unittest/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@
import time
import warnings

from _colorize import get_theme

from . import result
from .case import _SubTest
from .signals import registerResult
lazy from _colorize import get_theme

__unittest = True

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Lazily import :mod:`!_colorize`. Patch by Hugo van Kemenade.
Loading