← oracle_full

rich_3930

resolved RESOLVED UNSUBMITTED PASS · None tool calls · 0 s · Textualize/rich

Task input

Handle graphemes

Rich has never handled multi-codepoint emoji, because terminals were highly inconsistent about how they were rendered. And there was no way to know in advance how wide a given collection of codepoints was going to be. 

This is technically still the case. However modern terminals are more consistent now, and they tend to follow unicode rules. Which means that Rich can also follow those rules and get it right more of the time.

This PR uses the data from https://github.com/jquast/wcwidth to calculate widths in the same way. I didn't use wcwidth directly, as Rich has more complex requirements. Calculating the width of a string is not enough, if you want to do wrapping, cropping etc.

This PR adds the capability to split strings in to sequences that produce a single glyph in the terminal. And that is used in a few low-level functions.

The data is stored in a way that keeps startup time low, and makes width calculations more optimal than wcwidth.

This iteration should hopefully offer similar performance to the earlier versions. In the nearish future there will be a Rust library to speed up some of the lower level operations.

Fixes https://github.com/Textualize/rich/issues/3897

Tool calls (0)

#ToolArgumentsResult
No trace captured.

Patch

--- a/rich/__main__.py
+++ b/rich/__main__.py
@@ -238,8 +238,9 @@ def iter_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]:
             "[b magenta]Hope you enjoy using Rich![/]\n\n"
             "Please consider sponsoring me if you get value from my work.\n\n"
             "Even the price of a ☕ can brighten my day!\n\n"
-            "https://github.com/sponsors/willmcgugan",
-            border_style="red",
+            "[cyan]https://github.com/sponsors/willmcgugan[/cyan]",
+            border_style="green",
             title="Help ensure Rich is maintained",
+            padding=(1, 2),
         )
     )
--- a/rich/_cell_widths.py
+++ b/rich/_cell_widths.py
@@ -1,454 +0,0 @@
-# Auto generated by make_terminal_widths.py
-
-CELL_WIDTHS = [
-    (0, 0, 0),
-    (1, 31, -1),
-    (127, 159, -1),
-    (173, 173, 0),
-    (768, 879, 0),
-    (1155, 1161, 0),
-    (1425, 1469, 0),
-    (1471, 1471, 0),
-    (1473, 1474, 0),
-    (1476, 1477, 0),
-    (1479, 1479, 0),
-    (1536, 1541, 0),
-    (1552, 1562, 0),
-    (1564, 1564, 0),
-    (1611, 1631, 0),
-    (1648, 1648, 0),
-    (1750, 1757, 0),
-    (1759, 1764, 0),
-    (1767, 1768, 0),
-    (1770, 1773, 0),
-    (1807, 1807, 0),
-    (1809, 1809, 0),
-    (1840, 1866, 0),
-    (1958, 1968, 0),
-    (2027, 2035, 0),
-    (2045, 2045, 0),
-    (2070, 2073, 0),
-    (2075, 2083, 0),
-    (2085, 2087, 0),
-    (2089, 2093, 0),
-    (2137, 2139, 0),
-    (2192, 2193, 0),
-    (2200, 2207, 0),
-    (2250, 2307, 0),
-    (2362, 2364, 0),
-    (2366, 2383, 0),
-    (2385, 2391, 0),
-    (2402, 2403, 0),
-    (2433, 2435, 0),
-    (2492, 2492, 0),
-    (2494, 2500, 0),
-    (2503, 2504, 0),
-    (2507, 2509, 0),
-    (2519, 2519, 0),
-    (2530, 2531, 0),
-    (2558, 2558, 0),
-    (2561, 2563, 0),
-    (2620, 2620, 0),
-    (2622, 2626, 0),
-    (2631, 2632, 0),
-    (2635, 2637, 0),
-    (2641, 2641, 0),
-    (2672, 2673, 0),
-    (2677, 2677, 0),
-    (2689, 2691, 0),
-    (2748, 2748, 0),
-    (2750, 2757, 0),
-    (2759, 2761, 0),
-    (2763, 2765, 0),
-    (2786, 2787, 0),
-    (2810, 2815, 0),
-    (2817, 2819, 0),
-    (2876, 2876, 0),
-    (2878, 2884, 0),
-    (2887, 2888, 0),
-    (2891, 2893, 0),
-    (2901, 2903, 0),
-    (2914, 2915, 0),
-    (2946, 2946, 0),
-    (3006, 3010, 0),
-    (3014, 3016, 0),
-    (3018, 3021, 0),
-    (3031, 3031, 0),
-    (3072, 3076, 0),
-    (3132, 3132, 0),
-    (3134, 3140, 0),
-    (3142, 3144, 0),
-    (3146, 3149, 0),
-    (3157, 3158, 0),
-    (3170, 3171, 0),
-    (3201, 3203, 0),
-    (3260, 3260, 0),
-    (3262, 3268, 0),
-    (3270, 3272, 0),
-    (3274, 3277, 0),
-    (3285, 3286, 0),
-    (3298, 3299, 0),
-    (3315, 3315, 0),
-    (3328, 3331, 0),
-    (3387, 3388, 0),
-    (3390, 3396, 0),
-    (3398, 3400, 0),
-    (3402, 3405, 0),
-    (3415, 3415, 0),
-    (3426, 3427, 0),
-    (3457, 3459, 0),
-    (3530, 3530, 0),
-    (3535, 3540, 0),
-    (3542, 3542, 0),
-    (3544, 3551, 0),
-    (3570, 3571, 0),
-    (3633, 3633, 0),
-    (3636, 3642, 0),
-    (3655, 3662, 0),
-    (3761, 3761, 0),
-    (3764, 3772, 0),
-    (3784, 3790, 0),
-    (3864, 3865, 0),
-    (3893, 3893, 0),
-    (3895, 3895, 0),
-    (3897, 3897, 0),
-    (3902, 3903, 0),
-    (3953, 3972, 0),
-    (3974, 3975, 0),
-    (3981, 3991, 0),
-    (3993, 4028, 0),
-    (4038, 4038, 0),
-    (4139, 4158, 0),
-    (4182, 4185, 0),
-    (4190, 4192, 0),
-    (4194, 4196, 0),
-    (4199, 4205, 0),
-    (4209, 4212, 0),
-    (4226, 4237, 0),
-    (4239, 4239, 0),
-    (4250, 4253, 0),
-    (4352, 4447, 2),
-    (4448, 4607, 0),
-    (4957, 4959, 0),
-    (5906, 5909, 0),
-    (5938, 5940, 0),
-    (5970, 5971, 0),
-    (6002, 6003, 0),
-    (6068, 6099, 0),
-    (6109, 6109, 0),
-    (6155, 6159, 0),
-    (6277, 6278, 0),
-    (6313, 6313, 0),
-    (6432, 6443, 0),
-    (6448, 6459, 0),
-    (6679, 6683, 0),
-    (6741, 6750, 0),
-    (6752, 6780, 0),
-    (6783, 6783, 0),
-    (6832, 6862, 0),
-    (6912, 6916, 0),
-    (6964, 6980, 0),
-    (7019, 7027, 0),
-    (7040, 7042, 0),
-    (7073, 7085, 0),
-    (7142, 7155, 0),
-    (7204, 7223, 0),
-    (7376, 7378, 0),
-    (7380, 7400, 0),
-    (7405, 7405, 0),
-    (7412, 7412, 0),
-    (7415, 7417, 0),
-    (7616, 7679, 0),
-    (8203, 8207, 0),
-    (8232, 8238, 0),
-    (8288, 8292, 0),
-    (8294, 8303, 0),
-    (8400, 8432, 0),
-    (8986, 8987, 2),
-    (9001, 9002, 2),
-    (9193, 9196, 2),
-    (9200, 9200, 2),
-    (9203, 9203, 2),
-    (9725, 9726, 2),
-    (9748, 9749, 2),
-    (9800, 9811, 2),
-    (9855, 9855, 2),
-    (9875, 9875, 2),
-    (9889, 9889, 2),
-    (9898, 9899, 2),
-    (9917, 9918, 2),
-    (9924, 9925, 2),
-    (9934, 9934, 2),
-    (9940, 9940, 2),
-    (9962, 9962, 2),
-    (9970, 9971, 2),
-    (9973, 9973, 2),
-    (9978, 9978, 2),
-    (9981, 9981, 2),
-    (9989, 9989, 2),
-    (9994, 9995, 2),
-    (10024, 10024, 2),
-    (10060, 10060, 2),
-    (10062, 10062, 2),
-    (10067, 10069, 2),
-    (10071, 10071, 2),
-    (10133, 10135, 2),
-    (10160, 10160, 2),
-    (10175, 10175, 2),
-    (11035, 11036, 2),
-    (11088, 11088, 2),
-    (11093, 11093, 2),
-    (11503, 11505, 0),
-    (11647, 11647, 0),
-    (11744, 11775, 0),
-    (11904, 11929, 2),
-    (11931, 12019, 2),
-    (12032, 12245, 2),
-    (12272, 12329, 2),
-    (12330, 12335, 0),
-    (12336, 12350, 2),
-    (12353, 12438, 2),
-    (12441, 12442, 0),
-    (12443, 12543, 2),
-    (12549, 12591, 2),
-    (12593, 12686, 2),
-    (12688, 12771, 2),
-    (12783, 12830, 2),
-    (12832, 12871, 2),
-    (12880, 19903, 2),
-    (19968, 42124, 2),
-    (42128, 42182, 2),
-    (42607, 42610, 0),
-    (42612, 42621, 0),
-    (42654, 42655, 0),
-    (42736, 42737, 0),
-    (43010, 43010, 0),
-    (43014, 43014, 0),
-    (43019, 43019, 0),
-    (43043, 43047, 0),
-    (43052, 43052, 0),
-    (43136, 43137, 0),
-    (43188, 43205, 0),
-    (43232, 43249, 0),
-    (43263, 43263, 0),
-    (43302, 43309, 0),
-    (43335, 43347, 0),
-    (43360, 43388, 2),
-    (43392, 43395, 0),
-    (43443, 43456, 0),
-    (43493, 43493, 0),
-    (43561, 43574, 0),
-    (43587, 43587, 0),
-    (43596, 43597, 0),
-    (43643, 43645, 0),
-    (43696, 43696, 0),
-    (43698, 43700, 0),
-    (43703, 43704, 0),
-    (43710, 43711, 0),
-    (43713, 43713, 0),
-    (43755, 43759, 0),
-    (43765, 43766, 0),
-    (44003, 44010, 0),
-    (44012, 44013, 0),
-    (44032, 55203, 2),
-    (55216, 55295, 0),
-    (63744, 64255, 2),
-    (64286, 64286, 0),
-    (65024, 65039, 0),
-    (65040, 65049, 2),
-    (65056, 65071, 0),
-    (65072, 65106, 2),
-    (65108, 65126, 2),
-    (65128, 65131, 2),
-    (65279, 65279, 0),
-    (65281, 65376, 2),
-    (65504, 65510, 2),
-    (65529, 65531, 0),
-    (66045, 66045, 0),
-    (66272, 66272, 0),
-    (66422, 66426, 0),
-    (68097, 68099, 0),
-    (68101, 68102, 0),
-    (68108, 68111, 0),
-    (68152, 68154, 0),
-    (68159, 68159, 0),
-    (68325, 68326, 0),
-    (68900, 68903, 0),
-    (69291, 69292, 0),
-    (69373, 69375, 0),
-    (69446, 69456, 0),
-    (69506, 69509, 0),
-    (69632, 69634, 0),
-    (69688, 69702, 0),
-    (69744, 69744, 0),
-    (69747, 69748, 0),
-    (69759, 69762, 0),
-    (69808, 69818, 0),
-    (69821, 69821, 0),
-    (69826, 69826, 0),
-    (69837, 69837, 0),
-    (69888, 69890, 0),
-    (69927, 69940, 0),
-    (69957, 69958, 0),
-    (70003, 70003, 0),
-    (70016, 70018, 0),
-    (70067, 70080, 0),
-    (70089, 70092, 0),
-    (70094, 70095, 0),
-    (70188, 70199, 0),
-    (70206, 70206, 0),
-    (70209, 70209, 0),
-    (70367, 70378, 0),
-    (70400, 70403, 0),
-    (70459, 70460, 0),
-    (70462, 70468, 0),
-    (70471, 70472, 0),
-    (70475, 70477, 0),
-    (70487, 70487, 0),
-    (70498, 70499, 0),
-    (70502, 70508, 0),
-    (70512, 70516, 0),
-    (70709, 70726, 0),
-    (70750, 70750, 0),
-    (70832, 70851, 0),
-    (71087, 71093, 0),
-    (71096, 71104, 0),
-    (71132, 71133, 0),
-    (71216, 71232, 0),
-    (71339, 71351, 0),
-    (71453, 71467, 0),
-    (71724, 71738, 0),
-    (71984, 71989, 0),
-    (71991, 71992, 0),
-    (71995, 71998, 0),
-    (72000, 72000, 0),
-    (72002, 72003, 0),
-    (72145, 72151, 0),
-    (72154, 72160, 0),
-    (72164, 72164, 0),
-    (72193, 72202, 0),
-    (72243, 72249, 0),
-    (72251, 72254, 0),
-    (72263, 72263, 0),
-    (72273, 72283, 0),
-    (72330, 72345, 0),
-    (72751, 72758, 0),
-    (72760, 72767, 0),
-    (72850, 72871, 0),
-    (72873, 72886, 0),
-    (73009, 73014, 0),
-    (73018, 73018, 0),
-    (73020, 73021, 0),
-    (73023, 73029, 0),
-    (73031, 73031, 0),
-    (73098, 73102, 0),
-    (73104, 73105, 0),
-    (73107, 73111, 0),
-    (73459, 73462, 0),
-    (73472, 73473, 0),
-    (73475, 73475, 0),
-    (73524, 73530, 0),
-    (73534, 73538, 0),
-    (78896, 78912, 0),
-    (78919, 78933, 0),
-    (92912, 92916, 0),
-    (92976, 92982, 0),
-    (94031, 94031, 0),
-    (94033, 94087, 0),
-    (94095, 94098, 0),
-    (94176, 94179, 2),
-    (94180, 94180, 0),
-    (94192, 94193, 0),
-    (94208, 100343, 2),
-    (100352, 101589, 2),
-    (101632, 101640, 2),
-    (110576, 110579, 2),
-    (110581, 110587, 2),
-    (110589, 110590, 2),
-    (110592, 110882, 2),
-    (110898, 110898, 2),
-    (110928, 110930, 2),
-    (110933, 110933, 2),
-    (110948, 110951, 2),
-    (110960, 111355, 2),
-    (113821, 113822, 0),
-    (113824, 113827, 0),
-    (118528, 118573, 0),
-    (118576, 118598, 0),
-    (119141, 119145, 0),
-    (119149, 119170, 0),
-    (119173, 119179, 0),
-    (119210, 119213, 0),
-    (119362, 119364, 0),
-    (121344, 121398, 0),
-    (121403, 121452, 0),
-    (121461, 121461, 0),
-    (121476, 121476, 0),
-    (121499, 121503, 0),
-    (121505, 121519, 0),
-    (122880, 122886, 0),
-    (122888, 122904, 0),
-    (122907, 122913, 0),
-    (122915, 122916, 0),
-    (122918, 122922, 0),
-    (123023, 123023, 0),
-    (123184, 123190, 0),
-    (123566, 123566, 0),
-    (123628, 123631, 0),
-    (124140, 124143, 0),
-    (125136, 125142, 0),
-    (125252, 125258, 0),
-    (126980, 126980, 2),
-    (127183, 127183, 2),
-    (127374, 127374, 2),
-    (127377, 127386, 2),
-    (127488, 127490, 2),
-    (127504, 127547, 2),
-    (127552, 127560, 2),
-    (127568, 127569, 2),
-    (127584, 127589, 2),
-    (127744, 127776, 2),
-    (127789, 127797, 2),
-    (127799, 127868, 2),
-    (127870, 127891, 2),
-    (127904, 127946, 2),
-    (127951, 127955, 2),
-    (127968, 127984, 2),
-    (127988, 127988, 2),
-    (127992, 127994, 2),
-    (127995, 127999, 0),
-    (128000, 128062, 2),
-    (128064, 128064, 2),
-    (128066, 128252, 2),
-    (128255, 128317, 2),
-    (128331, 128334, 2),
-    (128336, 128359, 2),
-    (128378, 128378, 2),
-    (128405, 128406, 2),
-    (128420, 128420, 2),
-    (128507, 128591, 2),
-    (128640, 128709, 2),
-    (128716, 128716, 2),
-    (128720, 128722, 2),
-    (128725, 128727, 2),
-    (128732, 128735, 2),
-    (128747, 128748, 2),
-    (128756, 128764, 2),
-    (128992, 129003, 2),
-    (129008, 129008, 2),
-    (129292, 129338, 2),
-    (129340, 129349, 2),
-    (129351, 129535, 2),
-    (129648, 129660, 2),
-    (129664, 129672, 2),
-    (129680, 129725, 2),
-    (129727, 129733, 2),
-    (129742, 129755, 2),
-    (129760, 129768, 2),
-    (129776, 129784, 2),
-    (131072, 196605, 2),
-    (196608, 262141, 2),
-    (917505, 917505, 0),
-    (917536, 917631, 0),
-    (917760, 917999, 0),
-]
--- a/rich/_unicode_data/__init__.py
+++ b/rich/_unicode_data/__init__.py
@@ -0,0 +1,93 @@
+from __future__ import annotations
+
+import bisect
+import os
+import sys
+
+if sys.version_info[:2] >= (3, 9):
+    from functools import cache
+else:
+    from functools import lru_cache as cache  # pragma: no cover
+
+from importlib import import_module
+from typing import TYPE_CHECKING, cast
+
+from rich._unicode_data._versions import VERSIONS
+
+if TYPE_CHECKING:
+    from rich.cells import CellTable
+
+VERSION_ORDER = sorted(
+    [
+        tuple(
+            map(int, version.split(".")),
+        )
+        for version in VERSIONS
+    ]
+)
+VERSION_SET = frozenset(VERSIONS)
+
+
+def _parse_version(version: str) -> tuple[int, int, int]:
+    """Parse a version string into a tuple of 3 integers.
+
+    Args:
+        version: A version string.
+
+    Raises:
+        ValueError: If the version string is invalid.
+
+    Returns:
+        A tuple of 3 integers.
+    """
+    version_integers: tuple[int, ...]
+    try:
+        version_integers = tuple(
+            map(int, version.split(".")),
+        )
+    except ValueError:
+        raise ValueError(
+            f"unicode version string {version!r} is badly formatted"
+        ) from None
+    while len(version_integers) < 3:
+        version_integers = version_integers + (0,)
+    triple = cast("tuple[int, int, int]", version_integers[:3])
+    return triple
+
+
+@cache
+def load(unicode_version: str = "auto") -> CellTable:
+    """Load a cell table for the given unicode version.
+
+    Args:
+        unicode_version: Unicode version, or `None` to auto-detect.
+
+    """
+    if unicode_version == "auto":
+        unicode_version = os.environ.get("UNICODE_VERSION", "latest")
+        try:
+            _parse_version(unicode_version)
+        except ValueError:
+            # The environment variable is invalid
+            # Fallback to using the latest version seems reasonable
+            unicode_version = "latest"
+
+    if unicode_version == "latest":
+        version = VERSIONS[-1]
+    else:
+        try:
+            version_numbers = _parse_version(unicode_version)
+        except ValueError:
+            version_numbers = _parse_version(VERSIONS[-1])
+        major, minor, patch = version_numbers
+        version = f"{major}.{minor}.{patch}"
+        if version not in VERSION_SET:
+            insert_position = bisect.bisect_left(VERSION_ORDER, version_numbers)
+            version = VERSIONS[max(0, insert_position - 1)]
+
+    version_path_component = version.replace(".", "-")
+    module_name = f".unicode{version_path_component}"
+    module = import_module(module_name, "rich._unicode_data")
+    if TYPE_CHECKING:
+        assert isinstance(module.cell_table, CellTable)
+    return module.cell_table
--- a/rich/_unicode_data/_versions.py
+++ b/rich/_unicode_data/_versions.py
@@ -0,0 +1,23 @@
+VERSIONS = (
+    "4.1.0",
+    "5.0.0",
+    "5.1.0",
+    "5.2.0",
+    "6.0.0",
+    "6.1.0",
+    "6.2.0",
+    "6.3.0",
+    "7.0.0",
+    "8.0.0",
+    "9.0.0",
+    "10.0.0",
+    "11.0.0",
+    "12.0.0",
+    "12.1.0",
+    "13.0.0",
+    "14.0.0",
+    "15.0.0",
+    "15.1.0",
+    "16.0.0",
+    "17.0.0",
+)
--- a/rich/_unicode_data/unicode10-0-0.py
+++ b/rich/_unicode_data/unicode10-0-0.py
@@ -0,0 +1,611 @@
+# Auto generated by tools/make_width_tables.py
+# Data from wcwidth project (https://github.com/jquast/wcwidth)
+
+from rich.cells import CellTable
+
+cell_table = CellTable(
+    "10.0.0",
+    [
+        (0, 0, 0),
+        (768, 879, 0),
+        (1155, 1161, 0),
+        (1425, 1469, 0),
+        (1471, 1471, 0),
+        (1473, 1474, 0),
+        (1476, 1477, 0),
+        (1479, 1479, 0),
+        (1552, 1562, 0),
+        (1564, 1564, 0),
+        (1611, 1631, 0),
+        (1648, 1648, 0),
+        (1750, 1756, 0),
+        (1759, 1764, 0),
+        (1767, 1768, 0),
+        (1770, 1773, 0),
+        (1809, 1809, 0),
+        (1840, 1866, 0),
+        (1958, 1968, 0),
+        (2027, 2035, 0),
+        (2070, 2073, 0),
+        (2075, 2083, 0),
+        (2085, 2087, 0),
+        (2089, 2093, 0),
+        (2137, 2139, 0),
+        (2260, 2273, 0),
+        (2275, 2307, 0),
+        (2362, 2364, 0),
+        (2366, 2383, 0),
+        (2385, 2391, 0),
+        (2402, 2403, 0),
+        (2433, 2435, 0),
+        (2492, 2492, 0),
+        (2494, 2500, 0),
+        (2503, 2504, 0),
+        (2507, 2509, 0),
+        (2519, 2519, 0),
+        (2530, 2531, 0),
+        (2561, 2563, 0),
+        (2620, 2620, 0),
+        (2622, 2626, 0),
+        (2631, 2632, 0),
+        (2635, 2637, 0),
+        (2641, 2641, 0),
+        (2672, 2673, 0),
+        (2677, 2677, 0),
+        (2689, 2691, 0),
+        (2748, 2748, 0),
+        (2750, 2757, 0),
+        (2759, 2761, 0),
+        (2763, 2765, 0),
+        (2786, 2787, 0),
+        (2810, 2815, 0),
+        (2817, 2819, 0),
+        (2876, 2876, 0),
+        (2878, 2884, 0),
+        (2887, 2888, 0),
+        (2891, 2893, 0),
+        (2902, 2903, 0),
+        (2914, 2915, 0),
+        (2946, 2946, 0),
+        (3006, 3010, 0),
+        (3014, 3016, 0),
+        (3018, 3021, 0),
+        (3031, 3031, 0),
+        (3072, 3075, 0),
+        (3134, 3140, 0),
+        (3142, 3144, 0),
+        (3146, 3149, 0),
+        (3157, 3158, 0),
+        (3170, 3171, 0),
+        (3201, 3203, 0),
+        (3260, 3260, 0),
+        (3262, 3268, 0),
+        (3270, 3272, 0),
+        (3274, 3277, 0),
+        (3285, 3286, 0),
+        (3298, 3299, 0),
+        (3328, 3331, 0),
+        (3387, 3388, 0),
+        (3390, 3396, 0),
+        (3398, 3400, 0),
+        (3402, 3405, 0),
+        (3415, 3415, 0),
+        (3426, 3427, 0),
+        (3458, 3459, 0),
+        (3530, 3530, 0),
+        (3535, 3540, 0),
+        (3542, 3542, 0),
+        (3544, 3551, 0),
+        (3570, 3571, 0),
+        (3633, 3633, 0),
+        (3636, 3642, 0),
+        (3655, 3662, 0),
+        (3761, 3761, 0),
+        (3764, 3769, 0),
+        (3771, 3772, 0),
+        (3784, 3789, 0),
+        (3864, 3865, 0),
+        (3893, 3893, 0),
+        (3895, 3895, 0),
+        (3897, 3897, 0),
+        (3902, 3903, 0),
+        (3953, 3972, 0),
+        (3974, 3975, 0),
+        (3981, 3991, 0),
+        (3993, 4028, 0),
+        (4038, 4038, 0),
+        (4139, 4158, 0),
+        (4182, 4185, 0),
+        (4190, 4192, 0),
+        (4194, 4196, 0),
+        (4199, 4205, 0),
+        (4209, 4212, 0),
+        (4226, 4237, 0),
+        (4239, 4239, 0),
+        (4250, 4253, 0),
+        (4352, 4447, 2),
+        (4448, 4607, 0),
+        (4957, 4959, 0),
+        (5906, 5908, 0),
+        (5938, 5940, 0),
+        (5970, 5971, 0),
+        (6002, 6003, 0),
+        (6068, 6099, 0),
+        (6109, 6109, 0),
+        (6155, 6158, 0),
+        (6277, 6278, 0),
+        (6313, 6313, 0),
+        (6432, 6443, 0),
+        (6448, 6459, 0),
+        (6679, 6683, 0),
+        (6741, 6750, 0),
+        (6752, 6780, 0),
+        (6783, 6783, 0),
+        (6832, 6846, 0),
+        (6912, 6916, 0),
+        (6964, 6980, 0),
+        (7019, 7027, 0),
+        (7040, 7042, 0),
+        (7073, 7085, 0),
+        (7142, 7155, 0),
+        (7204, 7223, 0),
+        (7376, 7378, 0),
+        (7380, 7400, 0),
+        (7405, 7405, 0),
+        (7410, 7412, 0),
+        (7415, 7417, 0),
+        (7616, 7673, 0),
+        (7675, 7679, 0),
+        (8203, 8207, 0),
+        (8232, 8238, 0),
+        (8288, 8303, 0),
+        (8400, 8432, 0),
+        (8986, 8987, 2),
+        (9001, 9002, 2),
+        (9193, 9196, 2),
+        (9200, 9200, 2),
+        (9203, 9203, 2),
+        (9725, 9726, 2),
+        (9748, 9749, 2),
+        (9800, 9811, 2),
+        (9855, 9855, 2),
+        (9875, 9875, 2),
+        (9889, 9889, 2),
+        (9898, 9899, 2),
+        (9917, 9918, 2),
+        (9924, 9925, 2),
+        (9934, 9934, 2),
+        (9940, 9940, 2),
+        (9962, 9962, 2),
+        (9970, 9971, 2),
+        (9973, 9973, 2),
+        (9978, 9978, 2),
+        (9981, 9981, 2),
+        (9989, 9989, 2),
+        (9994, 9995, 2),
+        (10024, 10024, 2),
+        (10060, 10060, 2),
+        (10062, 10062, 2),
+        (10067, 10069, 2),
+        (10071, 10071, 2),
+        (10133, 10135, 2),
+        (10160, 10160, 2),
+        (10175, 10175, 2),
+        (11035, 11036, 2),
+        (11088, 11088, 2),
+        (11093, 11093, 2),
+        (11503, 11505, 0),
+        (11647, 11647, 0),
+        (11744, 11775, 0),
+        (11904, 11929, 2),
+        (11931, 12019, 2),
+        (12032, 12245, 2),
+        (12272, 12283, 2),
+        (122

Test output

show
........................................................................ [ 38%]
........................................................................ [ 76%]
.............................................                            [100%]
189 passed in 0.30s