about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/colorama
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/colorama')
-rw-r--r--.venv/lib/python3.12/site-packages/colorama/__init__.py7
-rw-r--r--.venv/lib/python3.12/site-packages/colorama/ansi.py102
-rw-r--r--.venv/lib/python3.12/site-packages/colorama/ansitowin32.py277
-rw-r--r--.venv/lib/python3.12/site-packages/colorama/initialise.py121
-rw-r--r--.venv/lib/python3.12/site-packages/colorama/tests/__init__.py1
-rw-r--r--.venv/lib/python3.12/site-packages/colorama/tests/ansi_test.py76
-rw-r--r--.venv/lib/python3.12/site-packages/colorama/tests/ansitowin32_test.py294
-rw-r--r--.venv/lib/python3.12/site-packages/colorama/tests/initialise_test.py189
-rw-r--r--.venv/lib/python3.12/site-packages/colorama/tests/isatty_test.py57
-rw-r--r--.venv/lib/python3.12/site-packages/colorama/tests/utils.py49
-rw-r--r--.venv/lib/python3.12/site-packages/colorama/tests/winterm_test.py131
-rw-r--r--.venv/lib/python3.12/site-packages/colorama/win32.py180
-rw-r--r--.venv/lib/python3.12/site-packages/colorama/winterm.py195
13 files changed, 1679 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/colorama/__init__.py b/.venv/lib/python3.12/site-packages/colorama/__init__.py
new file mode 100644
index 00000000..383101cd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/colorama/__init__.py
@@ -0,0 +1,7 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+from .initialise import init, deinit, reinit, colorama_text, just_fix_windows_console
+from .ansi import Fore, Back, Style, Cursor
+from .ansitowin32 import AnsiToWin32
+
+__version__ = '0.4.6'
+
diff --git a/.venv/lib/python3.12/site-packages/colorama/ansi.py b/.venv/lib/python3.12/site-packages/colorama/ansi.py
new file mode 100644
index 00000000..11ec695f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/colorama/ansi.py
@@ -0,0 +1,102 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+'''
+This module generates ANSI character codes to printing colors to terminals.
+See: http://en.wikipedia.org/wiki/ANSI_escape_code
+'''
+
+CSI = '\033['
+OSC = '\033]'
+BEL = '\a'
+
+
+def code_to_chars(code):
+    return CSI + str(code) + 'm'
+
+def set_title(title):
+    return OSC + '2;' + title + BEL
+
+def clear_screen(mode=2):
+    return CSI + str(mode) + 'J'
+
+def clear_line(mode=2):
+    return CSI + str(mode) + 'K'
+
+
+class AnsiCodes(object):
+    def __init__(self):
+        # the subclasses declare class attributes which are numbers.
+        # Upon instantiation we define instance attributes, which are the same
+        # as the class attributes but wrapped with the ANSI escape sequence
+        for name in dir(self):
+            if not name.startswith('_'):
+                value = getattr(self, name)
+                setattr(self, name, code_to_chars(value))
+
+
+class AnsiCursor(object):
+    def UP(self, n=1):
+        return CSI + str(n) + 'A'
+    def DOWN(self, n=1):
+        return CSI + str(n) + 'B'
+    def FORWARD(self, n=1):
+        return CSI + str(n) + 'C'
+    def BACK(self, n=1):
+        return CSI + str(n) + 'D'
+    def POS(self, x=1, y=1):
+        return CSI + str(y) + ';' + str(x) + 'H'
+
+
+class AnsiFore(AnsiCodes):
+    BLACK           = 30
+    RED             = 31
+    GREEN           = 32
+    YELLOW          = 33
+    BLUE            = 34
+    MAGENTA         = 35
+    CYAN            = 36
+    WHITE           = 37
+    RESET           = 39
+
+    # These are fairly well supported, but not part of the standard.
+    LIGHTBLACK_EX   = 90
+    LIGHTRED_EX     = 91
+    LIGHTGREEN_EX   = 92
+    LIGHTYELLOW_EX  = 93
+    LIGHTBLUE_EX    = 94
+    LIGHTMAGENTA_EX = 95
+    LIGHTCYAN_EX    = 96
+    LIGHTWHITE_EX   = 97
+
+
+class AnsiBack(AnsiCodes):
+    BLACK           = 40
+    RED             = 41
+    GREEN           = 42
+    YELLOW          = 43
+    BLUE            = 44
+    MAGENTA         = 45
+    CYAN            = 46
+    WHITE           = 47
+    RESET           = 49
+
+    # These are fairly well supported, but not part of the standard.
+    LIGHTBLACK_EX   = 100
+    LIGHTRED_EX     = 101
+    LIGHTGREEN_EX   = 102
+    LIGHTYELLOW_EX  = 103
+    LIGHTBLUE_EX    = 104
+    LIGHTMAGENTA_EX = 105
+    LIGHTCYAN_EX    = 106
+    LIGHTWHITE_EX   = 107
+
+
+class AnsiStyle(AnsiCodes):
+    BRIGHT    = 1
+    DIM       = 2
+    NORMAL    = 22
+    RESET_ALL = 0
+
+Fore   = AnsiFore()
+Back   = AnsiBack()
+Style  = AnsiStyle()
+Cursor = AnsiCursor()
diff --git a/.venv/lib/python3.12/site-packages/colorama/ansitowin32.py b/.venv/lib/python3.12/site-packages/colorama/ansitowin32.py
new file mode 100644
index 00000000..abf209e6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/colorama/ansitowin32.py
@@ -0,0 +1,277 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+import re
+import sys
+import os
+
+from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL
+from .winterm import enable_vt_processing, WinTerm, WinColor, WinStyle
+from .win32 import windll, winapi_test
+
+
+winterm = None
+if windll is not None:
+    winterm = WinTerm()
+
+
+class StreamWrapper(object):
+    '''
+    Wraps a stream (such as stdout), acting as a transparent proxy for all
+    attribute access apart from method 'write()', which is delegated to our
+    Converter instance.
+    '''
+    def __init__(self, wrapped, converter):
+        # double-underscore everything to prevent clashes with names of
+        # attributes on the wrapped stream object.
+        self.__wrapped = wrapped
+        self.__convertor = converter
+
+    def __getattr__(self, name):
+        return getattr(self.__wrapped, name)
+
+    def __enter__(self, *args, **kwargs):
+        # special method lookup bypasses __getattr__/__getattribute__, see
+        # https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit
+        # thus, contextlib magic methods are not proxied via __getattr__
+        return self.__wrapped.__enter__(*args, **kwargs)
+
+    def __exit__(self, *args, **kwargs):
+        return self.__wrapped.__exit__(*args, **kwargs)
+
+    def __setstate__(self, state):
+        self.__dict__ = state
+
+    def __getstate__(self):
+        return self.__dict__
+
+    def write(self, text):
+        self.__convertor.write(text)
+
+    def isatty(self):
+        stream = self.__wrapped
+        if 'PYCHARM_HOSTED' in os.environ:
+            if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__):
+                return True
+        try:
+            stream_isatty = stream.isatty
+        except AttributeError:
+            return False
+        else:
+            return stream_isatty()
+
+    @property
+    def closed(self):
+        stream = self.__wrapped
+        try:
+            return stream.closed
+        # AttributeError in the case that the stream doesn't support being closed
+        # ValueError for the case that the stream has already been detached when atexit runs
+        except (AttributeError, ValueError):
+            return True
+
+
+class AnsiToWin32(object):
+    '''
+    Implements a 'write()' method which, on Windows, will strip ANSI character
+    sequences from the text, and if outputting to a tty, will convert them into
+    win32 function calls.
+    '''
+    ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?')   # Control Sequence Introducer
+    ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?')             # Operating System Command
+
+    def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
+        # The wrapped stream (normally sys.stdout or sys.stderr)
+        self.wrapped = wrapped
+
+        # should we reset colors to defaults after every .write()
+        self.autoreset = autoreset
+
+        # create the proxy wrapping our output stream
+        self.stream = StreamWrapper(wrapped, self)
+
+        on_windows = os.name == 'nt'
+        # We test if the WinAPI works, because even if we are on Windows
+        # we may be using a terminal that doesn't support the WinAPI
+        # (e.g. Cygwin Terminal). In this case it's up to the terminal
+        # to support the ANSI codes.
+        conversion_supported = on_windows and winapi_test()
+        try:
+            fd = wrapped.fileno()
+        except Exception:
+            fd = -1
+        system_has_native_ansi = not on_windows or enable_vt_processing(fd)
+        have_tty = not self.stream.closed and self.stream.isatty()
+        need_conversion = conversion_supported and not system_has_native_ansi
+
+        # should we strip ANSI sequences from our output?
+        if strip is None:
+            strip = need_conversion or not have_tty
+        self.strip = strip
+
+        # should we should convert ANSI sequences into win32 calls?
+        if convert is None:
+            convert = need_conversion and have_tty
+        self.convert = convert
+
+        # dict of ansi codes to win32 functions and parameters
+        self.win32_calls = self.get_win32_calls()
+
+        # are we wrapping stderr?
+        self.on_stderr = self.wrapped is sys.stderr
+
+    def should_wrap(self):
+        '''
+        True if this class is actually needed. If false, then the output
+        stream will not be affected, nor will win32 calls be issued, so
+        wrapping stdout is not actually required. This will generally be
+        False on non-Windows platforms, unless optional functionality like
+        autoreset has been requested using kwargs to init()
+        '''
+        return self.convert or self.strip or self.autoreset
+
+    def get_win32_calls(self):
+        if self.convert and winterm:
+            return {
+                AnsiStyle.RESET_ALL: (winterm.reset_all, ),
+                AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT),
+                AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL),
+                AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL),
+                AnsiFore.BLACK: (winterm.fore, WinColor.BLACK),
+                AnsiFore.RED: (winterm.fore, WinColor.RED),
+                AnsiFore.GREEN: (winterm.fore, WinColor.GREEN),
+                AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW),
+                AnsiFore.BLUE: (winterm.fore, WinColor.BLUE),
+                AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA),
+                AnsiFore.CYAN: (winterm.fore, WinColor.CYAN),
+                AnsiFore.WHITE: (winterm.fore, WinColor.GREY),
+                AnsiFore.RESET: (winterm.fore, ),
+                AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True),
+                AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True),
+                AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True),
+                AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True),
+                AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True),
+                AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True),
+                AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True),
+                AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True),
+                AnsiBack.BLACK: (winterm.back, WinColor.BLACK),
+                AnsiBack.RED: (winterm.back, WinColor.RED),
+                AnsiBack.GREEN: (winterm.back, WinColor.GREEN),
+                AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW),
+                AnsiBack.BLUE: (winterm.back, WinColor.BLUE),
+                AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA),
+                AnsiBack.CYAN: (winterm.back, WinColor.CYAN),
+                AnsiBack.WHITE: (winterm.back, WinColor.GREY),
+                AnsiBack.RESET: (winterm.back, ),
+                AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True),
+                AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True),
+                AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True),
+                AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True),
+                AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True),
+                AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True),
+                AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True),
+                AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True),
+            }
+        return dict()
+
+    def write(self, text):
+        if self.strip or self.convert:
+            self.write_and_convert(text)
+        else:
+            self.wrapped.write(text)
+            self.wrapped.flush()
+        if self.autoreset:
+            self.reset_all()
+
+
+    def reset_all(self):
+        if self.convert:
+            self.call_win32('m', (0,))
+        elif not self.strip and not self.stream.closed:
+            self.wrapped.write(Style.RESET_ALL)
+
+
+    def write_and_convert(self, text):
+        '''
+        Write the given text to our wrapped stream, stripping any ANSI
+        sequences from the text, and optionally converting them into win32
+        calls.
+        '''
+        cursor = 0
+        text = self.convert_osc(text)
+        for match in self.ANSI_CSI_RE.finditer(text):
+            start, end = match.span()
+            self.write_plain_text(text, cursor, start)
+            self.convert_ansi(*match.groups())
+            cursor = end
+        self.write_plain_text(text, cursor, len(text))
+
+
+    def write_plain_text(self, text, start, end):
+        if start < end:
+            self.wrapped.write(text[start:end])
+            self.wrapped.flush()
+
+
+    def convert_ansi(self, paramstring, command):
+        if self.convert:
+            params = self.extract_params(command, paramstring)
+            self.call_win32(command, params)
+
+
+    def extract_params(self, command, paramstring):
+        if command in 'Hf':
+            params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';'))
+            while len(params) < 2:
+                # defaults:
+                params = params + (1,)
+        else:
+            params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0)
+            if len(params) == 0:
+                # defaults:
+                if command in 'JKm':
+                    params = (0,)
+                elif command in 'ABCD':
+                    params = (1,)
+
+        return params
+
+
+    def call_win32(self, command, params):
+        if command == 'm':
+            for param in params:
+                if param in self.win32_calls:
+                    func_args = self.win32_calls[param]
+                    func = func_args[0]
+                    args = func_args[1:]
+                    kwargs = dict(on_stderr=self.on_stderr)
+                    func(*args, **kwargs)
+        elif command in 'J':
+            winterm.erase_screen(params[0], on_stderr=self.on_stderr)
+        elif command in 'K':
+            winterm.erase_line(params[0], on_stderr=self.on_stderr)
+        elif command in 'Hf':     # cursor position - absolute
+            winterm.set_cursor_position(params, on_stderr=self.on_stderr)
+        elif command in 'ABCD':   # cursor position - relative
+            n = params[0]
+            # A - up, B - down, C - forward, D - back
+            x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command]
+            winterm.cursor_adjust(x, y, on_stderr=self.on_stderr)
+
+
+    def convert_osc(self, text):
+        for match in self.ANSI_OSC_RE.finditer(text):
+            start, end = match.span()
+            text = text[:start] + text[end:]
+            paramstring, command = match.groups()
+            if command == BEL:
+                if paramstring.count(";") == 1:
+                    params = paramstring.split(";")
+                    # 0 - change title and icon (we will only change title)
+                    # 1 - change icon (we don't support this)
+                    # 2 - change title
+                    if params[0] in '02':
+                        winterm.set_title(params[1])
+        return text
+
+
+    def flush(self):
+        self.wrapped.flush()
diff --git a/.venv/lib/python3.12/site-packages/colorama/initialise.py b/.venv/lib/python3.12/site-packages/colorama/initialise.py
new file mode 100644
index 00000000..d5fd4b71
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/colorama/initialise.py
@@ -0,0 +1,121 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+import atexit
+import contextlib
+import sys
+
+from .ansitowin32 import AnsiToWin32
+
+
+def _wipe_internal_state_for_tests():
+    global orig_stdout, orig_stderr
+    orig_stdout = None
+    orig_stderr = None
+
+    global wrapped_stdout, wrapped_stderr
+    wrapped_stdout = None
+    wrapped_stderr = None
+
+    global atexit_done
+    atexit_done = False
+
+    global fixed_windows_console
+    fixed_windows_console = False
+
+    try:
+        # no-op if it wasn't registered
+        atexit.unregister(reset_all)
+    except AttributeError:
+        # python 2: no atexit.unregister. Oh well, we did our best.
+        pass
+
+
+def reset_all():
+    if AnsiToWin32 is not None:    # Issue #74: objects might become None at exit
+        AnsiToWin32(orig_stdout).reset_all()
+
+
+def init(autoreset=False, convert=None, strip=None, wrap=True):
+
+    if not wrap and any([autoreset, convert, strip]):
+        raise ValueError('wrap=False conflicts with any other arg=True')
+
+    global wrapped_stdout, wrapped_stderr
+    global orig_stdout, orig_stderr
+
+    orig_stdout = sys.stdout
+    orig_stderr = sys.stderr
+
+    if sys.stdout is None:
+        wrapped_stdout = None
+    else:
+        sys.stdout = wrapped_stdout = \
+            wrap_stream(orig_stdout, convert, strip, autoreset, wrap)
+    if sys.stderr is None:
+        wrapped_stderr = None
+    else:
+        sys.stderr = wrapped_stderr = \
+            wrap_stream(orig_stderr, convert, strip, autoreset, wrap)
+
+    global atexit_done
+    if not atexit_done:
+        atexit.register(reset_all)
+        atexit_done = True
+
+
+def deinit():
+    if orig_stdout is not None:
+        sys.stdout = orig_stdout
+    if orig_stderr is not None:
+        sys.stderr = orig_stderr
+
+
+def just_fix_windows_console():
+    global fixed_windows_console
+
+    if sys.platform != "win32":
+        return
+    if fixed_windows_console:
+        return
+    if wrapped_stdout is not None or wrapped_stderr is not None:
+        # Someone already ran init() and it did stuff, so we won't second-guess them
+        return
+
+    # On newer versions of Windows, AnsiToWin32.__init__ will implicitly enable the
+    # native ANSI support in the console as a side-effect. We only need to actually
+    # replace sys.stdout/stderr if we're in the old-style conversion mode.
+    new_stdout = AnsiToWin32(sys.stdout, convert=None, strip=None, autoreset=False)
+    if new_stdout.convert:
+        sys.stdout = new_stdout
+    new_stderr = AnsiToWin32(sys.stderr, convert=None, strip=None, autoreset=False)
+    if new_stderr.convert:
+        sys.stderr = new_stderr
+
+    fixed_windows_console = True
+
+@contextlib.contextmanager
+def colorama_text(*args, **kwargs):
+    init(*args, **kwargs)
+    try:
+        yield
+    finally:
+        deinit()
+
+
+def reinit():
+    if wrapped_stdout is not None:
+        sys.stdout = wrapped_stdout
+    if wrapped_stderr is not None:
+        sys.stderr = wrapped_stderr
+
+
+def wrap_stream(stream, convert, strip, autoreset, wrap):
+    if wrap:
+        wrapper = AnsiToWin32(stream,
+            convert=convert, strip=strip, autoreset=autoreset)
+        if wrapper.should_wrap():
+            stream = wrapper.stream
+    return stream
+
+
+# Use this for initial setup as well, to reduce code duplication
+_wipe_internal_state_for_tests()
diff --git a/.venv/lib/python3.12/site-packages/colorama/tests/__init__.py b/.venv/lib/python3.12/site-packages/colorama/tests/__init__.py
new file mode 100644
index 00000000..8c5661e9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/colorama/tests/__init__.py
@@ -0,0 +1 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
diff --git a/.venv/lib/python3.12/site-packages/colorama/tests/ansi_test.py b/.venv/lib/python3.12/site-packages/colorama/tests/ansi_test.py
new file mode 100644
index 00000000..0a20c80f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/colorama/tests/ansi_test.py
@@ -0,0 +1,76 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+import sys
+from unittest import TestCase, main
+
+from ..ansi import Back, Fore, Style
+from ..ansitowin32 import AnsiToWin32
+
+stdout_orig = sys.stdout
+stderr_orig = sys.stderr
+
+
+class AnsiTest(TestCase):
+
+    def setUp(self):
+        # sanity check: stdout should be a file or StringIO object.
+        # It will only be AnsiToWin32 if init() has previously wrapped it
+        self.assertNotEqual(type(sys.stdout), AnsiToWin32)
+        self.assertNotEqual(type(sys.stderr), AnsiToWin32)
+
+    def tearDown(self):
+        sys.stdout = stdout_orig
+        sys.stderr = stderr_orig
+
+
+    def testForeAttributes(self):
+        self.assertEqual(Fore.BLACK, '\033[30m')
+        self.assertEqual(Fore.RED, '\033[31m')
+        self.assertEqual(Fore.GREEN, '\033[32m')
+        self.assertEqual(Fore.YELLOW, '\033[33m')
+        self.assertEqual(Fore.BLUE, '\033[34m')
+        self.assertEqual(Fore.MAGENTA, '\033[35m')
+        self.assertEqual(Fore.CYAN, '\033[36m')
+        self.assertEqual(Fore.WHITE, '\033[37m')
+        self.assertEqual(Fore.RESET, '\033[39m')
+
+        # Check the light, extended versions.
+        self.assertEqual(Fore.LIGHTBLACK_EX, '\033[90m')
+        self.assertEqual(Fore.LIGHTRED_EX, '\033[91m')
+        self.assertEqual(Fore.LIGHTGREEN_EX, '\033[92m')
+        self.assertEqual(Fore.LIGHTYELLOW_EX, '\033[93m')
+        self.assertEqual(Fore.LIGHTBLUE_EX, '\033[94m')
+        self.assertEqual(Fore.LIGHTMAGENTA_EX, '\033[95m')
+        self.assertEqual(Fore.LIGHTCYAN_EX, '\033[96m')
+        self.assertEqual(Fore.LIGHTWHITE_EX, '\033[97m')
+
+
+    def testBackAttributes(self):
+        self.assertEqual(Back.BLACK, '\033[40m')
+        self.assertEqual(Back.RED, '\033[41m')
+        self.assertEqual(Back.GREEN, '\033[42m')
+        self.assertEqual(Back.YELLOW, '\033[43m')
+        self.assertEqual(Back.BLUE, '\033[44m')
+        self.assertEqual(Back.MAGENTA, '\033[45m')
+        self.assertEqual(Back.CYAN, '\033[46m')
+        self.assertEqual(Back.WHITE, '\033[47m')
+        self.assertEqual(Back.RESET, '\033[49m')
+
+        # Check the light, extended versions.
+        self.assertEqual(Back.LIGHTBLACK_EX, '\033[100m')
+        self.assertEqual(Back.LIGHTRED_EX, '\033[101m')
+        self.assertEqual(Back.LIGHTGREEN_EX, '\033[102m')
+        self.assertEqual(Back.LIGHTYELLOW_EX, '\033[103m')
+        self.assertEqual(Back.LIGHTBLUE_EX, '\033[104m')
+        self.assertEqual(Back.LIGHTMAGENTA_EX, '\033[105m')
+        self.assertEqual(Back.LIGHTCYAN_EX, '\033[106m')
+        self.assertEqual(Back.LIGHTWHITE_EX, '\033[107m')
+
+
+    def testStyleAttributes(self):
+        self.assertEqual(Style.DIM, '\033[2m')
+        self.assertEqual(Style.NORMAL, '\033[22m')
+        self.assertEqual(Style.BRIGHT, '\033[1m')
+
+
+if __name__ == '__main__':
+    main()
diff --git a/.venv/lib/python3.12/site-packages/colorama/tests/ansitowin32_test.py b/.venv/lib/python3.12/site-packages/colorama/tests/ansitowin32_test.py
new file mode 100644
index 00000000..91ca551f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/colorama/tests/ansitowin32_test.py
@@ -0,0 +1,294 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+from io import StringIO, TextIOWrapper
+from unittest import TestCase, main
+try:
+    from contextlib import ExitStack
+except ImportError:
+    # python 2
+    from contextlib2 import ExitStack
+
+try:
+    from unittest.mock import MagicMock, Mock, patch
+except ImportError:
+    from mock import MagicMock, Mock, patch
+
+from ..ansitowin32 import AnsiToWin32, StreamWrapper
+from ..win32 import ENABLE_VIRTUAL_TERMINAL_PROCESSING
+from .utils import osname
+
+
+class StreamWrapperTest(TestCase):
+
+    def testIsAProxy(self):
+        mockStream = Mock()
+        wrapper = StreamWrapper(mockStream, None)
+        self.assertTrue( wrapper.random_attr is mockStream.random_attr )
+
+    def testDelegatesWrite(self):
+        mockStream = Mock()
+        mockConverter = Mock()
+        wrapper = StreamWrapper(mockStream, mockConverter)
+        wrapper.write('hello')
+        self.assertTrue(mockConverter.write.call_args, (('hello',), {}))
+
+    def testDelegatesContext(self):
+        mockConverter = Mock()
+        s = StringIO()
+        with StreamWrapper(s, mockConverter) as fp:
+            fp.write(u'hello')
+        self.assertTrue(s.closed)
+
+    def testProxyNoContextManager(self):
+        mockStream = MagicMock()
+        mockStream.__enter__.side_effect = AttributeError()
+        mockConverter = Mock()
+        with self.assertRaises(AttributeError) as excinfo:
+            with StreamWrapper(mockStream, mockConverter) as wrapper:
+                wrapper.write('hello')
+
+    def test_closed_shouldnt_raise_on_closed_stream(self):
+        stream = StringIO()
+        stream.close()
+        wrapper = StreamWrapper(stream, None)
+        self.assertEqual(wrapper.closed, True)
+
+    def test_closed_shouldnt_raise_on_detached_stream(self):
+        stream = TextIOWrapper(StringIO())
+        stream.detach()
+        wrapper = StreamWrapper(stream, None)
+        self.assertEqual(wrapper.closed, True)
+
+class AnsiToWin32Test(TestCase):
+
+    def testInit(self):
+        mockStdout = Mock()
+        auto = Mock()
+        stream = AnsiToWin32(mockStdout, autoreset=auto)
+        self.assertEqual(stream.wrapped, mockStdout)
+        self.assertEqual(stream.autoreset, auto)
+
+    @patch('colorama.ansitowin32.winterm', None)
+    @patch('colorama.ansitowin32.winapi_test', lambda *_: True)
+    def testStripIsTrueOnWindows(self):
+        with osname('nt'):
+            mockStdout = Mock()
+            stream = AnsiToWin32(mockStdout)
+            self.assertTrue(stream.strip)
+
+    def testStripIsFalseOffWindows(self):
+        with osname('posix'):
+            mockStdout = Mock(closed=False)
+            stream = AnsiToWin32(mockStdout)
+            self.assertFalse(stream.strip)
+
+    def testWriteStripsAnsi(self):
+        mockStdout = Mock()
+        stream = AnsiToWin32(mockStdout)
+        stream.wrapped = Mock()
+        stream.write_and_convert = Mock()
+        stream.strip = True
+
+        stream.write('abc')
+
+        self.assertFalse(stream.wrapped.write.called)
+        self.assertEqual(stream.write_and_convert.call_args, (('abc',), {}))
+
+    def testWriteDoesNotStripAnsi(self):
+        mockStdout = Mock()
+        stream = AnsiToWin32(mockStdout)
+        stream.wrapped = Mock()
+        stream.write_and_convert = Mock()
+        stream.strip = False
+        stream.convert = False
+
+        stream.write('abc')
+
+        self.assertFalse(stream.write_and_convert.called)
+        self.assertEqual(stream.wrapped.write.call_args, (('abc',), {}))
+
+    def assert_autoresets(self, convert, autoreset=True):
+        stream = AnsiToWin32(Mock())
+        stream.convert = convert
+        stream.reset_all = Mock()
+        stream.autoreset = autoreset
+        stream.winterm = Mock()
+
+        stream.write('abc')
+
+        self.assertEqual(stream.reset_all.called, autoreset)
+
+    def testWriteAutoresets(self):
+        self.assert_autoresets(convert=True)
+        self.assert_autoresets(convert=False)
+        self.assert_autoresets(convert=True, autoreset=False)
+        self.assert_autoresets(convert=False, autoreset=False)
+
+    def testWriteAndConvertWritesPlainText(self):
+        stream = AnsiToWin32(Mock())
+        stream.write_and_convert( 'abc' )
+        self.assertEqual( stream.wrapped.write.call_args, (('abc',), {}) )
+
+    def testWriteAndConvertStripsAllValidAnsi(self):
+        stream = AnsiToWin32(Mock())
+        stream.call_win32 = Mock()
+        data = [
+            'abc\033[mdef',
+            'abc\033[0mdef',
+            'abc\033[2mdef',
+            'abc\033[02mdef',
+            'abc\033[002mdef',
+            'abc\033[40mdef',
+            'abc\033[040mdef',
+            'abc\033[0;1mdef',
+            'abc\033[40;50mdef',
+            'abc\033[50;30;40mdef',
+            'abc\033[Adef',
+            'abc\033[0Gdef',
+            'abc\033[1;20;128Hdef',
+        ]
+        for datum in data:
+            stream.wrapped.write.reset_mock()
+            stream.write_and_convert( datum )
+            self.assertEqual(
+               [args[0] for args in stream.wrapped.write.call_args_list],
+               [ ('abc',), ('def',) ]
+            )
+
+    def testWriteAndConvertSkipsEmptySnippets(self):
+        stream = AnsiToWin32(Mock())
+        stream.call_win32 = Mock()
+        stream.write_and_convert( '\033[40m\033[41m' )
+        self.assertFalse( stream.wrapped.write.called )
+
+    def testWriteAndConvertCallsWin32WithParamsAndCommand(self):
+        stream = AnsiToWin32(Mock())
+        stream.convert = True
+        stream.call_win32 = Mock()
+        stream.extract_params = Mock(return_value='params')
+        data = {
+            'abc\033[adef':         ('a', 'params'),
+            'abc\033[;;bdef':       ('b', 'params'),
+            'abc\033[0cdef':        ('c', 'params'),
+            'abc\033[;;0;;Gdef':    ('G', 'params'),
+            'abc\033[1;20;128Hdef': ('H', 'params'),
+        }
+        for datum, expected in data.items():
+            stream.call_win32.reset_mock()
+            stream.write_and_convert( datum )
+            self.assertEqual( stream.call_win32.call_args[0], expected )
+
+    def test_reset_all_shouldnt_raise_on_closed_orig_stdout(self):
+        stream = StringIO()
+        converter = AnsiToWin32(stream)
+        stream.close()
+
+        converter.reset_all()
+
+    def test_wrap_shouldnt_raise_on_closed_orig_stdout(self):
+        stream = StringIO()
+        stream.close()
+        with \
+            patch("colorama.ansitowin32.os.name", "nt"), \
+            patch("colorama.ansitowin32.winapi_test", lambda: True):
+                converter = AnsiToWin32(stream)
+        self.assertTrue(converter.strip)
+        self.assertFalse(converter.convert)
+
+    def test_wrap_shouldnt_raise_on_missing_closed_attr(self):
+        with \
+            patch("colorama.ansitowin32.os.name", "nt"), \
+            patch("colorama.ansitowin32.winapi_test", lambda: True):
+                converter = AnsiToWin32(object())
+        self.assertTrue(converter.strip)
+        self.assertFalse(converter.convert)
+
+    def testExtractParams(self):
+        stream = AnsiToWin32(Mock())
+        data = {
+            '':               (0,),
+            ';;':             (0,),
+            '2':              (2,),
+            ';;002;;':        (2,),
+            '0;1':            (0, 1),
+            ';;003;;456;;':   (3, 456),
+            '11;22;33;44;55': (11, 22, 33, 44, 55),
+        }
+        for datum, expected in data.items():
+            self.assertEqual(stream.extract_params('m', datum), expected)
+
+    def testCallWin32UsesLookup(self):
+        listener = Mock()
+        stream = AnsiToWin32(listener)
+        stream.win32_calls = {
+            1: (lambda *_, **__: listener(11),),
+            2: (lambda *_, **__: listener(22),),
+            3: (lambda *_, **__: listener(33),),
+        }
+        stream.call_win32('m', (3, 1, 99, 2))
+        self.assertEqual(
+            [a[0][0] for a in listener.call_args_list],
+            [33, 11, 22] )
+
+    def test_osc_codes(self):
+        mockStdout = Mock()
+        stream = AnsiToWin32(mockStdout, convert=True)
+        with patch('colorama.ansitowin32.winterm') as winterm:
+            data = [
+                '\033]0\x07',                      # missing arguments
+                '\033]0;foo\x08',                  # wrong OSC command
+                '\033]0;colorama_test_title\x07',  # should work
+                '\033]1;colorama_test_title\x07',  # wrong set command
+                '\033]2;colorama_test_title\x07',  # should work
+                '\033]' + ';' * 64 + '\x08',       # see issue #247
+            ]
+            for code in data:
+                stream.write(code)
+            self.assertEqual(winterm.set_title.call_count, 2)
+
+    def test_native_windows_ansi(self):
+        with ExitStack() as stack:
+            def p(a, b):
+                stack.enter_context(patch(a, b, create=True))
+            # Pretend to be on Windows
+            p("colorama.ansitowin32.os.name", "nt")
+            p("colorama.ansitowin32.winapi_test", lambda: True)
+            p("colorama.win32.winapi_test", lambda: True)
+            p("colorama.winterm.win32.windll", "non-None")
+            p("colorama.winterm.get_osfhandle", lambda _: 1234)
+
+            # Pretend that our mock stream has native ANSI support
+            p(
+                "colorama.winterm.win32.GetConsoleMode",
+                lambda _: ENABLE_VIRTUAL_TERMINAL_PROCESSING,
+            )
+            SetConsoleMode = Mock()
+            p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode)
+
+            stdout = Mock()
+            stdout.closed = False
+            stdout.isatty.return_value = True
+            stdout.fileno.return_value = 1
+
+            # Our fake console says it has native vt support, so AnsiToWin32 should
+            # enable that support and do nothing else.
+            stream = AnsiToWin32(stdout)
+            SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING)
+            self.assertFalse(stream.strip)
+            self.assertFalse(stream.convert)
+            self.assertFalse(stream.should_wrap())
+
+            # Now let's pretend we're on an old Windows console, that doesn't have
+            # native ANSI support.
+            p("colorama.winterm.win32.GetConsoleMode", lambda _: 0)
+            SetConsoleMode = Mock()
+            p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode)
+
+            stream = AnsiToWin32(stdout)
+            SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING)
+            self.assertTrue(stream.strip)
+            self.assertTrue(stream.convert)
+            self.assertTrue(stream.should_wrap())
+
+
+if __name__ == '__main__':
+    main()
diff --git a/.venv/lib/python3.12/site-packages/colorama/tests/initialise_test.py b/.venv/lib/python3.12/site-packages/colorama/tests/initialise_test.py
new file mode 100644
index 00000000..89f9b075
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/colorama/tests/initialise_test.py
@@ -0,0 +1,189 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+import sys
+from unittest import TestCase, main, skipUnless
+
+try:
+    from unittest.mock import patch, Mock
+except ImportError:
+    from mock import patch, Mock
+
+from ..ansitowin32 import StreamWrapper
+from ..initialise import init, just_fix_windows_console, _wipe_internal_state_for_tests
+from .utils import osname, replace_by
+
+orig_stdout = sys.stdout
+orig_stderr = sys.stderr
+
+
+class InitTest(TestCase):
+
+    @skipUnless(sys.stdout.isatty(), "sys.stdout is not a tty")
+    def setUp(self):
+        # sanity check
+        self.assertNotWrapped()
+
+    def tearDown(self):
+        _wipe_internal_state_for_tests()
+        sys.stdout = orig_stdout
+        sys.stderr = orig_stderr
+
+    def assertWrapped(self):
+        self.assertIsNot(sys.stdout, orig_stdout, 'stdout should be wrapped')
+        self.assertIsNot(sys.stderr, orig_stderr, 'stderr should be wrapped')
+        self.assertTrue(isinstance(sys.stdout, StreamWrapper),
+            'bad stdout wrapper')
+        self.assertTrue(isinstance(sys.stderr, StreamWrapper),
+            'bad stderr wrapper')
+
+    def assertNotWrapped(self):
+        self.assertIs(sys.stdout, orig_stdout, 'stdout should not be wrapped')
+        self.assertIs(sys.stderr, orig_stderr, 'stderr should not be wrapped')
+
+    @patch('colorama.initialise.reset_all')
+    @patch('colorama.ansitowin32.winapi_test', lambda *_: True)
+    @patch('colorama.ansitowin32.enable_vt_processing', lambda *_: False)
+    def testInitWrapsOnWindows(self, _):
+        with osname("nt"):
+            init()
+            self.assertWrapped()
+
+    @patch('colorama.initialise.reset_all')
+    @patch('colorama.ansitowin32.winapi_test', lambda *_: False)
+    def testInitDoesntWrapOnEmulatedWindows(self, _):
+        with osname("nt"):
+            init()
+            self.assertNotWrapped()
+
+    def testInitDoesntWrapOnNonWindows(self):
+        with osname("posix"):
+            init()
+            self.assertNotWrapped()
+
+    def testInitDoesntWrapIfNone(self):
+        with replace_by(None):
+            init()
+            # We can't use assertNotWrapped here because replace_by(None)
+            # changes stdout/stderr already.
+            self.assertIsNone(sys.stdout)
+            self.assertIsNone(sys.stderr)
+
+    def testInitAutoresetOnWrapsOnAllPlatforms(self):
+        with osname("posix"):
+            init(autoreset=True)
+            self.assertWrapped()
+
+    def testInitWrapOffDoesntWrapOnWindows(self):
+        with osname("nt"):
+            init(wrap=False)
+            self.assertNotWrapped()
+
+    def testInitWrapOffIncompatibleWithAutoresetOn(self):
+        self.assertRaises(ValueError, lambda: init(autoreset=True, wrap=False))
+
+    @patch('colorama.win32.SetConsoleTextAttribute')
+    @patch('colorama.initialise.AnsiToWin32')
+    def testAutoResetPassedOn(self, mockATW32, _):
+        with osname("nt"):
+            init(autoreset=True)
+            self.assertEqual(len(mockATW32.call_args_list), 2)
+            self.assertEqual(mockATW32.call_args_list[1][1]['autoreset'], True)
+            self.assertEqual(mockATW32.call_args_list[0][1]['autoreset'], True)
+
+    @patch('colorama.initialise.AnsiToWin32')
+    def testAutoResetChangeable(self, mockATW32):
+        with osname("nt"):
+            init()
+
+            init(autoreset=True)
+            self.assertEqual(len(mockATW32.call_args_list), 4)
+            self.assertEqual(mockATW32.call_args_list[2][1]['autoreset'], True)
+            self.assertEqual(mockATW32.call_args_list[3][1]['autoreset'], True)
+
+            init()
+            self.assertEqual(len(mockATW32.call_args_list), 6)
+            self.assertEqual(
+                mockATW32.call_args_list[4][1]['autoreset'], False)
+            self.assertEqual(
+                mockATW32.call_args_list[5][1]['autoreset'], False)
+
+
+    @patch('colorama.initialise.atexit.register')
+    def testAtexitRegisteredOnlyOnce(self, mockRegister):
+        init()
+        self.assertTrue(mockRegister.called)
+        mockRegister.reset_mock()
+        init()
+        self.assertFalse(mockRegister.called)
+
+
+class JustFixWindowsConsoleTest(TestCase):
+    def _reset(self):
+        _wipe_internal_state_for_tests()
+        sys.stdout = orig_stdout
+        sys.stderr = orig_stderr
+
+    def tearDown(self):
+        self._reset()
+
+    @patch("colorama.ansitowin32.winapi_test", lambda: True)
+    def testJustFixWindowsConsole(self):
+        if sys.platform != "win32":
+            # just_fix_windows_console should be a no-op
+            just_fix_windows_console()
+            self.assertIs(sys.stdout, orig_stdout)
+            self.assertIs(sys.stderr, orig_stderr)
+        else:
+            def fake_std():
+                # Emulate stdout=not a tty, stderr=tty
+                # to check that we handle both cases correctly
+                stdout = Mock()
+                stdout.closed = False
+                stdout.isatty.return_value = False
+                stdout.fileno.return_value = 1
+                sys.stdout = stdout
+
+                stderr = Mock()
+                stderr.closed = False
+                stderr.isatty.return_value = True
+                stderr.fileno.return_value = 2
+                sys.stderr = stderr
+
+            for native_ansi in [False, True]:
+                with patch(
+                    'colorama.ansitowin32.enable_vt_processing',
+                    lambda *_: native_ansi
+                ):
+                    self._reset()
+                    fake_std()
+
+                    # Regular single-call test
+                    prev_stdout = sys.stdout
+                    prev_stderr = sys.stderr
+                    just_fix_windows_console()
+                    self.assertIs(sys.stdout, prev_stdout)
+                    if native_ansi:
+                        self.assertIs(sys.stderr, prev_stderr)
+                    else:
+                        self.assertIsNot(sys.stderr, prev_stderr)
+
+                    # second call without resetting is always a no-op
+                    prev_stdout = sys.stdout
+                    prev_stderr = sys.stderr
+                    just_fix_windows_console()
+                    self.assertIs(sys.stdout, prev_stdout)
+                    self.assertIs(sys.stderr, prev_stderr)
+
+                    self._reset()
+                    fake_std()
+
+                    # If init() runs first, just_fix_windows_console should be a no-op
+                    init()
+                    prev_stdout = sys.stdout
+                    prev_stderr = sys.stderr
+                    just_fix_windows_console()
+                    self.assertIs(prev_stdout, sys.stdout)
+                    self.assertIs(prev_stderr, sys.stderr)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/.venv/lib/python3.12/site-packages/colorama/tests/isatty_test.py b/.venv/lib/python3.12/site-packages/colorama/tests/isatty_test.py
new file mode 100644
index 00000000..0f84e4be
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/colorama/tests/isatty_test.py
@@ -0,0 +1,57 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+import sys
+from unittest import TestCase, main
+
+from ..ansitowin32 import StreamWrapper, AnsiToWin32
+from .utils import pycharm, replace_by, replace_original_by, StreamTTY, StreamNonTTY
+
+
+def is_a_tty(stream):
+    return StreamWrapper(stream, None).isatty()
+
+class IsattyTest(TestCase):
+
+    def test_TTY(self):
+        tty = StreamTTY()
+        self.assertTrue(is_a_tty(tty))
+        with pycharm():
+            self.assertTrue(is_a_tty(tty))
+
+    def test_nonTTY(self):
+        non_tty = StreamNonTTY()
+        self.assertFalse(is_a_tty(non_tty))
+        with pycharm():
+            self.assertFalse(is_a_tty(non_tty))
+
+    def test_withPycharm(self):
+        with pycharm():
+            self.assertTrue(is_a_tty(sys.stderr))
+            self.assertTrue(is_a_tty(sys.stdout))
+
+    def test_withPycharmTTYOverride(self):
+        tty = StreamTTY()
+        with pycharm(), replace_by(tty):
+            self.assertTrue(is_a_tty(tty))
+
+    def test_withPycharmNonTTYOverride(self):
+        non_tty = StreamNonTTY()
+        with pycharm(), replace_by(non_tty):
+            self.assertFalse(is_a_tty(non_tty))
+
+    def test_withPycharmNoneOverride(self):
+        with pycharm():
+            with replace_by(None), replace_original_by(None):
+                self.assertFalse(is_a_tty(None))
+                self.assertFalse(is_a_tty(StreamNonTTY()))
+                self.assertTrue(is_a_tty(StreamTTY()))
+
+    def test_withPycharmStreamWrapped(self):
+        with pycharm():
+            self.assertTrue(AnsiToWin32(StreamTTY()).stream.isatty())
+            self.assertFalse(AnsiToWin32(StreamNonTTY()).stream.isatty())
+            self.assertTrue(AnsiToWin32(sys.stdout).stream.isatty())
+            self.assertTrue(AnsiToWin32(sys.stderr).stream.isatty())
+
+
+if __name__ == '__main__':
+    main()
diff --git a/.venv/lib/python3.12/site-packages/colorama/tests/utils.py b/.venv/lib/python3.12/site-packages/colorama/tests/utils.py
new file mode 100644
index 00000000..472fafb4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/colorama/tests/utils.py
@@ -0,0 +1,49 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+from contextlib import contextmanager
+from io import StringIO
+import sys
+import os
+
+
+class StreamTTY(StringIO):
+    def isatty(self):
+        return True
+
+class StreamNonTTY(StringIO):
+    def isatty(self):
+        return False
+
+@contextmanager
+def osname(name):
+    orig = os.name
+    os.name = name
+    yield
+    os.name = orig
+
+@contextmanager
+def replace_by(stream):
+    orig_stdout = sys.stdout
+    orig_stderr = sys.stderr
+    sys.stdout = stream
+    sys.stderr = stream
+    yield
+    sys.stdout = orig_stdout
+    sys.stderr = orig_stderr
+
+@contextmanager
+def replace_original_by(stream):
+    orig_stdout = sys.__stdout__
+    orig_stderr = sys.__stderr__
+    sys.__stdout__ = stream
+    sys.__stderr__ = stream
+    yield
+    sys.__stdout__ = orig_stdout
+    sys.__stderr__ = orig_stderr
+
+@contextmanager
+def pycharm():
+    os.environ["PYCHARM_HOSTED"] = "1"
+    non_tty = StreamNonTTY()
+    with replace_by(non_tty), replace_original_by(non_tty):
+        yield
+    del os.environ["PYCHARM_HOSTED"]
diff --git a/.venv/lib/python3.12/site-packages/colorama/tests/winterm_test.py b/.venv/lib/python3.12/site-packages/colorama/tests/winterm_test.py
new file mode 100644
index 00000000..d0955f9e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/colorama/tests/winterm_test.py
@@ -0,0 +1,131 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+import sys
+from unittest import TestCase, main, skipUnless
+
+try:
+    from unittest.mock import Mock, patch
+except ImportError:
+    from mock import Mock, patch
+
+from ..winterm import WinColor, WinStyle, WinTerm
+
+
+class WinTermTest(TestCase):
+
+    @patch('colorama.winterm.win32')
+    def testInit(self, mockWin32):
+        mockAttr = Mock()
+        mockAttr.wAttributes = 7 + 6 * 16 + 8
+        mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr
+        term = WinTerm()
+        self.assertEqual(term._fore, 7)
+        self.assertEqual(term._back, 6)
+        self.assertEqual(term._style, 8)
+
+    @skipUnless(sys.platform.startswith("win"), "requires Windows")
+    def testGetAttrs(self):
+        term = WinTerm()
+
+        term._fore = 0
+        term._back = 0
+        term._style = 0
+        self.assertEqual(term.get_attrs(), 0)
+
+        term._fore = WinColor.YELLOW
+        self.assertEqual(term.get_attrs(), WinColor.YELLOW)
+
+        term._back = WinColor.MAGENTA
+        self.assertEqual(
+            term.get_attrs(),
+            WinColor.YELLOW + WinColor.MAGENTA * 16)
+
+        term._style = WinStyle.BRIGHT
+        self.assertEqual(
+            term.get_attrs(),
+            WinColor.YELLOW + WinColor.MAGENTA * 16 + WinStyle.BRIGHT)
+
+    @patch('colorama.winterm.win32')
+    def testResetAll(self, mockWin32):
+        mockAttr = Mock()
+        mockAttr.wAttributes = 1 + 2 * 16 + 8
+        mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr
+        term = WinTerm()
+
+        term.set_console = Mock()
+        term._fore = -1
+        term._back = -1
+        term._style = -1
+
+        term.reset_all()
+
+        self.assertEqual(term._fore, 1)
+        self.assertEqual(term._back, 2)
+        self.assertEqual(term._style, 8)
+        self.assertEqual(term.set_console.called, True)
+
+    @skipUnless(sys.platform.startswith("win"), "requires Windows")
+    def testFore(self):
+        term = WinTerm()
+        term.set_console = Mock()
+        term._fore = 0
+
+        term.fore(5)
+
+        self.assertEqual(term._fore, 5)
+        self.assertEqual(term.set_console.called, True)
+
+    @skipUnless(sys.platform.startswith("win"), "requires Windows")
+    def testBack(self):
+        term = WinTerm()
+        term.set_console = Mock()
+        term._back = 0
+
+        term.back(5)
+
+        self.assertEqual(term._back, 5)
+        self.assertEqual(term.set_console.called, True)
+
+    @skipUnless(sys.platform.startswith("win"), "requires Windows")
+    def testStyle(self):
+        term = WinTerm()
+        term.set_console = Mock()
+        term._style = 0
+
+        term.style(22)
+
+        self.assertEqual(term._style, 22)
+        self.assertEqual(term.set_console.called, True)
+
+    @patch('colorama.winterm.win32')
+    def testSetConsole(self, mockWin32):
+        mockAttr = Mock()
+        mockAttr.wAttributes = 0
+        mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr
+        term = WinTerm()
+        term.windll = Mock()
+
+        term.set_console()
+
+        self.assertEqual(
+            mockWin32.SetConsoleTextAttribute.call_args,
+            ((mockWin32.STDOUT, term.get_attrs()), {})
+        )
+
+    @patch('colorama.winterm.win32')
+    def testSetConsoleOnStderr(self, mockWin32):
+        mockAttr = Mock()
+        mockAttr.wAttributes = 0
+        mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr
+        term = WinTerm()
+        term.windll = Mock()
+
+        term.set_console(on_stderr=True)
+
+        self.assertEqual(
+            mockWin32.SetConsoleTextAttribute.call_args,
+            ((mockWin32.STDERR, term.get_attrs()), {})
+        )
+
+
+if __name__ == '__main__':
+    main()
diff --git a/.venv/lib/python3.12/site-packages/colorama/win32.py b/.venv/lib/python3.12/site-packages/colorama/win32.py
new file mode 100644
index 00000000..841b0e27
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/colorama/win32.py
@@ -0,0 +1,180 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+
+# from winbase.h
+STDOUT = -11
+STDERR = -12
+
+ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
+
+try:
+    import ctypes
+    from ctypes import LibraryLoader
+    windll = LibraryLoader(ctypes.WinDLL)
+    from ctypes import wintypes
+except (AttributeError, ImportError):
+    windll = None
+    SetConsoleTextAttribute = lambda *_: None
+    winapi_test = lambda *_: None
+else:
+    from ctypes import byref, Structure, c_char, POINTER
+
+    COORD = wintypes._COORD
+
+    class CONSOLE_SCREEN_BUFFER_INFO(Structure):
+        """struct in wincon.h."""
+        _fields_ = [
+            ("dwSize", COORD),
+            ("dwCursorPosition", COORD),
+            ("wAttributes", wintypes.WORD),
+            ("srWindow", wintypes.SMALL_RECT),
+            ("dwMaximumWindowSize", COORD),
+        ]
+        def __str__(self):
+            return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % (
+                self.dwSize.Y, self.dwSize.X
+                , self.dwCursorPosition.Y, self.dwCursorPosition.X
+                , self.wAttributes
+                , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right
+                , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X
+            )
+
+    _GetStdHandle = windll.kernel32.GetStdHandle
+    _GetStdHandle.argtypes = [
+        wintypes.DWORD,
+    ]
+    _GetStdHandle.restype = wintypes.HANDLE
+
+    _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo
+    _GetConsoleScreenBufferInfo.argtypes = [
+        wintypes.HANDLE,
+        POINTER(CONSOLE_SCREEN_BUFFER_INFO),
+    ]
+    _GetConsoleScreenBufferInfo.restype = wintypes.BOOL
+
+    _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute
+    _SetConsoleTextAttribute.argtypes = [
+        wintypes.HANDLE,
+        wintypes.WORD,
+    ]
+    _SetConsoleTextAttribute.restype = wintypes.BOOL
+
+    _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition
+    _SetConsoleCursorPosition.argtypes = [
+        wintypes.HANDLE,
+        COORD,
+    ]
+    _SetConsoleCursorPosition.restype = wintypes.BOOL
+
+    _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA
+    _FillConsoleOutputCharacterA.argtypes = [
+        wintypes.HANDLE,
+        c_char,
+        wintypes.DWORD,
+        COORD,
+        POINTER(wintypes.DWORD),
+    ]
+    _FillConsoleOutputCharacterA.restype = wintypes.BOOL
+
+    _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute
+    _FillConsoleOutputAttribute.argtypes = [
+        wintypes.HANDLE,
+        wintypes.WORD,
+        wintypes.DWORD,
+        COORD,
+        POINTER(wintypes.DWORD),
+    ]
+    _FillConsoleOutputAttribute.restype = wintypes.BOOL
+
+    _SetConsoleTitleW = windll.kernel32.SetConsoleTitleW
+    _SetConsoleTitleW.argtypes = [
+        wintypes.LPCWSTR
+    ]
+    _SetConsoleTitleW.restype = wintypes.BOOL
+
+    _GetConsoleMode = windll.kernel32.GetConsoleMode
+    _GetConsoleMode.argtypes = [
+        wintypes.HANDLE,
+        POINTER(wintypes.DWORD)
+    ]
+    _GetConsoleMode.restype = wintypes.BOOL
+
+    _SetConsoleMode = windll.kernel32.SetConsoleMode
+    _SetConsoleMode.argtypes = [
+        wintypes.HANDLE,
+        wintypes.DWORD
+    ]
+    _SetConsoleMode.restype = wintypes.BOOL
+
+    def _winapi_test(handle):
+        csbi = CONSOLE_SCREEN_BUFFER_INFO()
+        success = _GetConsoleScreenBufferInfo(
+            handle, byref(csbi))
+        return bool(success)
+
+    def winapi_test():
+        return any(_winapi_test(h) for h in
+                   (_GetStdHandle(STDOUT), _GetStdHandle(STDERR)))
+
+    def GetConsoleScreenBufferInfo(stream_id=STDOUT):
+        handle = _GetStdHandle(stream_id)
+        csbi = CONSOLE_SCREEN_BUFFER_INFO()
+        success = _GetConsoleScreenBufferInfo(
+            handle, byref(csbi))
+        return csbi
+
+    def SetConsoleTextAttribute(stream_id, attrs):
+        handle = _GetStdHandle(stream_id)
+        return _SetConsoleTextAttribute(handle, attrs)
+
+    def SetConsoleCursorPosition(stream_id, position, adjust=True):
+        position = COORD(*position)
+        # If the position is out of range, do nothing.
+        if position.Y <= 0 or position.X <= 0:
+            return
+        # Adjust for Windows' SetConsoleCursorPosition:
+        #    1. being 0-based, while ANSI is 1-based.
+        #    2. expecting (x,y), while ANSI uses (y,x).
+        adjusted_position = COORD(position.Y - 1, position.X - 1)
+        if adjust:
+            # Adjust for viewport's scroll position
+            sr = GetConsoleScreenBufferInfo(STDOUT).srWindow
+            adjusted_position.Y += sr.Top
+            adjusted_position.X += sr.Left
+        # Resume normal processing
+        handle = _GetStdHandle(stream_id)
+        return _SetConsoleCursorPosition(handle, adjusted_position)
+
+    def FillConsoleOutputCharacter(stream_id, char, length, start):
+        handle = _GetStdHandle(stream_id)
+        char = c_char(char.encode())
+        length = wintypes.DWORD(length)
+        num_written = wintypes.DWORD(0)
+        # Note that this is hard-coded for ANSI (vs wide) bytes.
+        success = _FillConsoleOutputCharacterA(
+            handle, char, length, start, byref(num_written))
+        return num_written.value
+
+    def FillConsoleOutputAttribute(stream_id, attr, length, start):
+        ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )'''
+        handle = _GetStdHandle(stream_id)
+        attribute = wintypes.WORD(attr)
+        length = wintypes.DWORD(length)
+        num_written = wintypes.DWORD(0)
+        # Note that this is hard-coded for ANSI (vs wide) bytes.
+        return _FillConsoleOutputAttribute(
+            handle, attribute, length, start, byref(num_written))
+
+    def SetConsoleTitle(title):
+        return _SetConsoleTitleW(title)
+
+    def GetConsoleMode(handle):
+        mode = wintypes.DWORD()
+        success = _GetConsoleMode(handle, byref(mode))
+        if not success:
+            raise ctypes.WinError()
+        return mode.value
+
+    def SetConsoleMode(handle, mode):
+        success = _SetConsoleMode(handle, mode)
+        if not success:
+            raise ctypes.WinError()
diff --git a/.venv/lib/python3.12/site-packages/colorama/winterm.py b/.venv/lib/python3.12/site-packages/colorama/winterm.py
new file mode 100644
index 00000000..aad867e8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/colorama/winterm.py
@@ -0,0 +1,195 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+try:
+    from msvcrt import get_osfhandle
+except ImportError:
+    def get_osfhandle(_):
+        raise OSError("This isn't windows!")
+
+
+from . import win32
+
+# from wincon.h
+class WinColor(object):
+    BLACK   = 0
+    BLUE    = 1
+    GREEN   = 2
+    CYAN    = 3
+    RED     = 4
+    MAGENTA = 5
+    YELLOW  = 6
+    GREY    = 7
+
+# from wincon.h
+class WinStyle(object):
+    NORMAL              = 0x00 # dim text, dim background
+    BRIGHT              = 0x08 # bright text, dim background
+    BRIGHT_BACKGROUND   = 0x80 # dim text, bright background
+
+class WinTerm(object):
+
+    def __init__(self):
+        self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes
+        self.set_attrs(self._default)
+        self._default_fore = self._fore
+        self._default_back = self._back
+        self._default_style = self._style
+        # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style.
+        # So that LIGHT_EX colors and BRIGHT style do not clobber each other,
+        # we track them separately, since LIGHT_EX is overwritten by Fore/Back
+        # and BRIGHT is overwritten by Style codes.
+        self._light = 0
+
+    def get_attrs(self):
+        return self._fore + self._back * 16 + (self._style | self._light)
+
+    def set_attrs(self, value):
+        self._fore = value & 7
+        self._back = (value >> 4) & 7
+        self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND)
+
+    def reset_all(self, on_stderr=None):
+        self.set_attrs(self._default)
+        self.set_console(attrs=self._default)
+        self._light = 0
+
+    def fore(self, fore=None, light=False, on_stderr=False):
+        if fore is None:
+            fore = self._default_fore
+        self._fore = fore
+        # Emulate LIGHT_EX with BRIGHT Style
+        if light:
+            self._light |= WinStyle.BRIGHT
+        else:
+            self._light &= ~WinStyle.BRIGHT
+        self.set_console(on_stderr=on_stderr)
+
+    def back(self, back=None, light=False, on_stderr=False):
+        if back is None:
+            back = self._default_back
+        self._back = back
+        # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style
+        if light:
+            self._light |= WinStyle.BRIGHT_BACKGROUND
+        else:
+            self._light &= ~WinStyle.BRIGHT_BACKGROUND
+        self.set_console(on_stderr=on_stderr)
+
+    def style(self, style=None, on_stderr=False):
+        if style is None:
+            style = self._default_style
+        self._style = style
+        self.set_console(on_stderr=on_stderr)
+
+    def set_console(self, attrs=None, on_stderr=False):
+        if attrs is None:
+            attrs = self.get_attrs()
+        handle = win32.STDOUT
+        if on_stderr:
+            handle = win32.STDERR
+        win32.SetConsoleTextAttribute(handle, attrs)
+
+    def get_position(self, handle):
+        position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition
+        # Because Windows coordinates are 0-based,
+        # and win32.SetConsoleCursorPosition expects 1-based.
+        position.X += 1
+        position.Y += 1
+        return position
+
+    def set_cursor_position(self, position=None, on_stderr=False):
+        if position is None:
+            # I'm not currently tracking the position, so there is no default.
+            # position = self.get_position()
+            return
+        handle = win32.STDOUT
+        if on_stderr:
+            handle = win32.STDERR
+        win32.SetConsoleCursorPosition(handle, position)
+
+    def cursor_adjust(self, x, y, on_stderr=False):
+        handle = win32.STDOUT
+        if on_stderr:
+            handle = win32.STDERR
+        position = self.get_position(handle)
+        adjusted_position = (position.Y + y, position.X + x)
+        win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False)
+
+    def erase_screen(self, mode=0, on_stderr=False):
+        # 0 should clear from the cursor to the end of the screen.
+        # 1 should clear from the cursor to the beginning of the screen.
+        # 2 should clear the entire screen, and move cursor to (1,1)
+        handle = win32.STDOUT
+        if on_stderr:
+            handle = win32.STDERR
+        csbi = win32.GetConsoleScreenBufferInfo(handle)
+        # get the number of character cells in the current buffer
+        cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y
+        # get number of character cells before current cursor position
+        cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X
+        if mode == 0:
+            from_coord = csbi.dwCursorPosition
+            cells_to_erase = cells_in_screen - cells_before_cursor
+        elif mode == 1:
+            from_coord = win32.COORD(0, 0)
+            cells_to_erase = cells_before_cursor
+        elif mode == 2:
+            from_coord = win32.COORD(0, 0)
+            cells_to_erase = cells_in_screen
+        else:
+            # invalid mode
+            return
+        # fill the entire screen with blanks
+        win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
+        # now set the buffer's attributes accordingly
+        win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
+        if mode == 2:
+            # put the cursor where needed
+            win32.SetConsoleCursorPosition(handle, (1, 1))
+
+    def erase_line(self, mode=0, on_stderr=False):
+        # 0 should clear from the cursor to the end of the line.
+        # 1 should clear from the cursor to the beginning of the line.
+        # 2 should clear the entire line.
+        handle = win32.STDOUT
+        if on_stderr:
+            handle = win32.STDERR
+        csbi = win32.GetConsoleScreenBufferInfo(handle)
+        if mode == 0:
+            from_coord = csbi.dwCursorPosition
+            cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X
+        elif mode == 1:
+            from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
+            cells_to_erase = csbi.dwCursorPosition.X
+        elif mode == 2:
+            from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
+            cells_to_erase = csbi.dwSize.X
+        else:
+            # invalid mode
+            return
+        # fill the entire screen with blanks
+        win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
+        # now set the buffer's attributes accordingly
+        win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
+
+    def set_title(self, title):
+        win32.SetConsoleTitle(title)
+
+
+def enable_vt_processing(fd):
+    if win32.windll is None or not win32.winapi_test():
+        return False
+
+    try:
+        handle = get_osfhandle(fd)
+        mode = win32.GetConsoleMode(handle)
+        win32.SetConsoleMode(
+            handle,
+            mode | win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING,
+        )
+
+        mode = win32.GetConsoleMode(handle)
+        if mode & win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING:
+            return True
+    # Can get TypeError in testsuite where 'fd' is a Mock()
+    except (OSError, TypeError):
+        return False