We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Console formatting causing crash
### Expected behaviour:
Output the entry-list's entries

### Actual behaviour:
Output crash traceback

#### Config:
N\A
  
#### Log:
<details>
 <summary>(click to expand)</summary>

```
$ flexget entry-list list manga
Traceback (most recent call last):
  File "C:\Python37\Scripts\flexget-script.py", line 33, in <module>
    sys.exit(load_entry_point('FlexGet', 'console_scripts', 'flexget')())
  File "~\GitHub\Flexget\flexget\__init__.py", line 44, in main
    manager.start()
  File "~\GitHub\Flexget\flexget\manager.py", line 383, in start
    self.handle_cli()
  File "~\GitHub\Flexget\flexget\manager.py", line 412, in handle_cli
    options.cli_command_callback(self, command_options)
  File "~\GitHub\Flexget\flexget\components\managed_lists\lists\entry_list\cli.py", line 30, in do_cli
    entry_list_list(options)
  File "~\GitHub\Flexget\flexget\components\managed_lists\lists\entry_list\cli.py", line 74, in entry_list_list
    console(table)
  File "~\GitHub\Flexget\flexget\terminal.py", line 35, in __call__
    self.print(text, *args, **kwargs)
  File "~\GitHub\Flexget\flexget\terminal.py", line 39, in print
    _patchable_console(*args, **kwargs)
  File "~\GitHub\Flexget\flexget\terminal.py", line 236, in _patchable_console
    console._print(*args, **kwargs)
  File "~\GitHub\Flexget\flexget\terminal.py", line 43, in _print
    super().print(*args, **kwargs)
  File "~\GitHub\Flexget\lib\site-packages\rich\console.py", line 1634, in print
    extend(render(renderable, render_options))
  File "~\GitHub\Flexget\lib\site-packages\rich\console.py", line 1272, in render
    for render_output in iter_render:
  File "~\GitHub\Flexget\flexget\terminal.py", line 160, in __rich_console__
    yield from segments
  File "~\GitHub\Flexget\lib\site-packages\rich\table.py", line 479, in __rich_console__
    console, options.update_width(max_width - extra_width)
  File "~\GitHub\Flexget\lib\site-packages\rich\table.py", line 520, in _calculate_column_widths
    self._measure_column(console, options, column) for column in columns
  File "~\GitHub\Flexget\lib\site-packages\rich\table.py", line 520, in <listcomp>
    self._measure_column(console, options, column) for column in columns
  File "~\GitHub\Flexget\lib\site-packages\rich\table.py", line 723, in _measure_column
    _min, _max = get_render_width(console, options, cell.renderable)
  File "~\GitHub\Flexget\lib\site-packages\rich\measure.py", line 109, in get
    get_console_width(console, options)
  File "~\GitHub\Flexget\lib\site-packages\rich\padding.py", line 132, in __rich_measure__
    measure_min, measure_max = Measurement.get(console, options, self.renderable)
  File "~\GitHub\Flexget\lib\site-packages\rich\measure.py", line 100, in get
    renderable, markup=options.markup, highlight=False
  File "~\GitHub\Flexget\lib\site-packages\rich\console.py", line 1368, in render_str
    emoji_variant=self._emoji_variant,
  File "~\GitHub\Flexget\lib\site-packages\rich\markup.py", line 162, in render
    ) from None
rich.errors.MarkupError: closing tag '[/yuri/ scanlations]' at position 92 doesn't match any open tag
```
</details>

### Additional information:

- FlexGet version: 3.3.15
- Python version: 3.7
- Installation method: git
- Using daemon (yes/no): both
- OS and version: w10


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `flexget/terminal.py`
Content:
```
1 import contextlib
2 import os
3 import threading
4 from textwrap import wrap
5 from typing import Any, Iterator, Optional, TextIO, Union
6 
7 import rich
8 import rich.box
9 import rich.console
10 import rich.rule
11 import rich.segment
12 import rich.table
13 import rich.text
14 
15 from flexget.options import ArgumentParser
16 
17 local_context = threading.local()
18 
19 
20 class _Console(rich.console.Console):
21     def __init__(self, *args, **kwargs):
22         if "PYCHARM_HOSTED" in os.environ:
23             kwargs.setdefault('color_system', 'truecolor')
24         super().__init__(*args, **kwargs)
25 
26     def __call__(self, text: Any, *args, **kwargs) -> None:
27         """
28         Print to console safely. Output is able to be captured by different streams in different contexts.
29 
30         Any plugin wishing to output to the user's console should use this function instead of print so that
31         output can be redirected when FlexGet is invoked from another process.
32 
33         Accepts arguments like the `rich.console.Console.print` function does.
34         """
35         self.print(text, *args, **kwargs)
36 
37     def print(self, *args, **kwargs) -> None:
38         # Also capture calls directly to console.print
39         _patchable_console(*args, **kwargs)
40 
41     def _print(self, *args, **kwargs):
42         """The real parent print function, which can be called internally."""
43         super().print(*args, **kwargs)
44 
45     def rule(
46         self,
47         title: rich.text.TextType = "",
48         *,
49         characters: str = "─",
50         style: Union[str, rich.console.Style] = "rule.line",
51         align: rich.text.AlignMethod = "left",
52         # This is a custom FlexGet argument
53         indent: int = 3,
54     ) -> None:
55         rule = rich.rule.Rule(title, characters=characters, style=style, align=align)
56         if indent and title:
57             if not isinstance(rule.title, rich.text.Text):
58                 rule.title = self.render_str(rule.title, style="rule.text")
59             text = rich.text.Text()
60             if rule.align == 'left':
61                 text = text.append(rule.characters * indent + ' ', style=rule.style)
62             text.append(rule.title)
63             if rule.align == 'right':
64                 text = text.append(' ' + rule.characters * indent, style=rule.style)
65             rule.title = text
66         self.print(rule)
67 
68 
69 # This is used to print (rich) text to the console, as well as expose all the other features of rich's console
70 # Unlike rich, can be called directly to print (for backwards compat purposes.)
71 console = _Console()
72 
73 
74 PORCELAIN_BOX: rich.box.Box = rich.box.Box(
75     """\
76     
77   | 
78     
79   | 
80     
81     
82   | 
83     
84 """,
85     ascii=True,
86 )
87 
88 GITHUB_BOX: rich.box.Box = rich.box.Box(
89     """\
90     
91 | ||
92 |-||
93 | ||
94 |-||
95 |-||
96 | ||
97     
98 """,
99     ascii=True,
100 )
101 
102 
103 class TerminalTable(rich.table.Table):
104     """
105     A data table suited for CLI output, created via its sent parameters. For example::
106 
107         header = ['Col1', 'Col2']
108         table_data = [header]
109         for item in iterable:
110             table_data.append([item.attribute1, item.attribute2])
111         table = TerminalTable('plain', table_data)
112         print table.output
113 
114     Optional values are setting table title, and supplying wrap_columns list and
115     drop_column list. If table does not fit into terminal any columns listed in
116     wrap_columns will be tried to wrap and if resulting columns are below MIN_WIDTH(10)
117     columns listed in drop_column will be removed from output.
118 
119     Example::
120 
121         header = ['Col1', 'Col2']
122         table_data = [header]
123         for item in iterable:
124             table_data.append([item.attribute1, item.attribute2])
125         table = TerminalTable('plain', table_data, 'Table title', wrap_columns=[1,2],
126                               drop_columns=[4,2])
127         print table.output
128 
129     :param table_type: A string matching TABLE_TYPES keys.
130     """
131 
132     # Easy access for our plugins without importing rich
133     Column = rich.table.Column
134 
135     # TODO: Add other new types
136     TABLE_TYPES = {
137         'plain': {'box': rich.box.ASCII},
138         'porcelain': {
139             'box': PORCELAIN_BOX,
140             'show_edge': False,
141             'pad_edge': False,
142             'title': None,
143             'padding': 0,
144         },
145         'single': {'box': rich.box.SQUARE},
146         'double': {'box': rich.box.DOUBLE},
147         'github': {'box': GITHUB_BOX},
148         'heavy-head': {'box': rich.box.HEAVY_HEAD},
149     }
150 
151     def __init__(self, *args, table_type: str = None, **kwargs) -> None:
152         self.table_type = table_type
153         if table_type:
154             kwargs = {**kwargs, **self.TABLE_TYPES[table_type]}
155         super().__init__(*args, **kwargs)
156 
157     def __rich_console__(self, console, options):
158         segments = super().__rich_console__(console, options)
159         if self.table_type not in ['porcelain', 'github']:
160             yield from segments
161             return
162         # Strips out blank lines from our custom types
163         lines = rich.segment.Segment.split_lines(segments)
164         for line in lines:
165             if any(seg.text.strip() for seg in line):
166                 yield from line
167                 yield rich.segment.Segment.line()
168 
169 
170 table_parser = ArgumentParser(add_help=False)
171 table_parser.add_argument(
172     '--table-type',
173     choices=list(TerminalTable.TABLE_TYPES),
174     default='heavy-head',
175     help='Select output table style',
176 )
177 table_parser.add_argument(
178     '--porcelain',
179     dest='table_type',
180     action='store_const',
181     const='porcelain',
182     help='Make the output parseable. Similar to using `--table-type porcelain`',
183 )
184 
185 
186 def word_wrap(text: str, max_length: int) -> str:
187     """A helper method designed to return a wrapped string.
188 
189     :param text: Text to wrap
190     :param max_length: Maximum allowed string length
191     :return: Wrapped text or original text
192     """
193     if len(text) >= max_length:
194         return '\n'.join(wrap(text, max_length))
195     return text
196 
197 
198 def colorize(color: str, text: str) -> str:
199     """
200     A simple override of Color.colorize which sets the default auto colors value to True, since it's the more common
201     use case. When output isn't TTY just return text
202 
203     :param color: Color tag to use
204     :param text: Text to color
205 
206     :return: Colored text or text
207     """
208     return f'[{color}]{text}[/]'
209 
210 
211 def disable_colors():
212     """
213     Disables colors to the terminal.
214     """
215     console.no_color = True
216 
217 
218 @contextlib.contextmanager
219 def capture_console(filelike: TextIO) -> Iterator:
220     old_output = get_console_output()
221     local_context.output = filelike
222     try:
223         yield
224     finally:
225         local_context.output = old_output
226 
227 
228 def get_console_output() -> Optional[TextIO]:
229     return getattr(local_context, 'output', None)
230 
231 
232 def _patchable_console(*args, **kwargs):
233     # Nobody will import this directly, so we can monkeypatch it for IPC calls
234     console.file = get_console_output()
235     try:
236         console._print(*args, **kwargs)
237     finally:
238         console.file = None
239
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/flexget/terminal.py b/flexget/terminal.py
--- a/flexget/terminal.py
+++ b/flexget/terminal.py
@@ -21,6 +21,7 @@
     def __init__(self, *args, **kwargs):
         if "PYCHARM_HOSTED" in os.environ:
             kwargs.setdefault('color_system', 'truecolor')
+        kwargs.setdefault("markup", True)
         super().__init__(*args, **kwargs)
 
     def __call__(self, text: Any, *args, **kwargs) -> None:

{"golden_diff": "diff --git a/flexget/terminal.py b/flexget/terminal.py\n--- a/flexget/terminal.py\n+++ b/flexget/terminal.py\n@@ -21,6 +21,7 @@\n     def __init__(self, *args, **kwargs):\n         if \"PYCHARM_HOSTED\" in os.environ:\n             kwargs.setdefault('color_system', 'truecolor')\n+        kwargs.setdefault(\"markup\", True)\n         super().__init__(*args, **kwargs)\n \n     def __call__(self, text: Any, *args, **kwargs) -> None:\n", "issue": "Console formatting causing crash\n### Expected behaviour:\r\nOutput the entry-list's entries\r\n\r\n### Actual behaviour:\r\nOutput crash traceback\r\n\r\n#### Config:\r\nN\\A\r\n  \r\n#### Log:\r\n<details>\r\n <summary>(click to expand)</summary>\r\n\r\n```\r\n$ flexget entry-list list manga\r\nTraceback (most recent call last):\r\n  File \"C:\\Python37\\Scripts\\flexget-script.py\", line 33, in <module>\r\n    sys.exit(load_entry_point('FlexGet', 'console_scripts', 'flexget')())\r\n  File \"~\\GitHub\\Flexget\\flexget\\__init__.py\", line 44, in main\r\n    manager.start()\r\n  File \"~\\GitHub\\Flexget\\flexget\\manager.py\", line 383, in start\r\n    self.handle_cli()\r\n  File \"~\\GitHub\\Flexget\\flexget\\manager.py\", line 412, in handle_cli\r\n    options.cli_command_callback(self, command_options)\r\n  File \"~\\GitHub\\Flexget\\flexget\\components\\managed_lists\\lists\\entry_list\\cli.py\", line 30, in do_cli\r\n    entry_list_list(options)\r\n  File \"~\\GitHub\\Flexget\\flexget\\components\\managed_lists\\lists\\entry_list\\cli.py\", line 74, in entry_list_list\r\n    console(table)\r\n  File \"~\\GitHub\\Flexget\\flexget\\terminal.py\", line 35, in __call__\r\n    self.print(text, *args, **kwargs)\r\n  File \"~\\GitHub\\Flexget\\flexget\\terminal.py\", line 39, in print\r\n    _patchable_console(*args, **kwargs)\r\n  File \"~\\GitHub\\Flexget\\flexget\\terminal.py\", line 236, in _patchable_console\r\n    console._print(*args, **kwargs)\r\n  File \"~\\GitHub\\Flexget\\flexget\\terminal.py\", line 43, in _print\r\n    super().print(*args, **kwargs)\r\n  File \"~\\GitHub\\Flexget\\lib\\site-packages\\rich\\console.py\", line 1634, in print\r\n    extend(render(renderable, render_options))\r\n  File \"~\\GitHub\\Flexget\\lib\\site-packages\\rich\\console.py\", line 1272, in render\r\n    for render_output in iter_render:\r\n  File \"~\\GitHub\\Flexget\\flexget\\terminal.py\", line 160, in __rich_console__\r\n    yield from segments\r\n  File \"~\\GitHub\\Flexget\\lib\\site-packages\\rich\\table.py\", line 479, in __rich_console__\r\n    console, options.update_width(max_width - extra_width)\r\n  File \"~\\GitHub\\Flexget\\lib\\site-packages\\rich\\table.py\", line 520, in _calculate_column_widths\r\n    self._measure_column(console, options, column) for column in columns\r\n  File \"~\\GitHub\\Flexget\\lib\\site-packages\\rich\\table.py\", line 520, in <listcomp>\r\n    self._measure_column(console, options, column) for column in columns\r\n  File \"~\\GitHub\\Flexget\\lib\\site-packages\\rich\\table.py\", line 723, in _measure_column\r\n    _min, _max = get_render_width(console, options, cell.renderable)\r\n  File \"~\\GitHub\\Flexget\\lib\\site-packages\\rich\\measure.py\", line 109, in get\r\n    get_console_width(console, options)\r\n  File \"~\\GitHub\\Flexget\\lib\\site-packages\\rich\\padding.py\", line 132, in __rich_measure__\r\n    measure_min, measure_max = Measurement.get(console, options, self.renderable)\r\n  File \"~\\GitHub\\Flexget\\lib\\site-packages\\rich\\measure.py\", line 100, in get\r\n    renderable, markup=options.markup, highlight=False\r\n  File \"~\\GitHub\\Flexget\\lib\\site-packages\\rich\\console.py\", line 1368, in render_str\r\n    emoji_variant=self._emoji_variant,\r\n  File \"~\\GitHub\\Flexget\\lib\\site-packages\\rich\\markup.py\", line 162, in render\r\n    ) from None\r\nrich.errors.MarkupError: closing tag '[/yuri/ scanlations]' at position 92 doesn't match any open tag\r\n```\r\n</details>\r\n\r\n### Additional information:\r\n\r\n- FlexGet version: 3.3.15\r\n- Python version: 3.7\r\n- Installation method: git\r\n- Using daemon (yes/no): both\r\n- OS and version: w10\r\n\n", "before_files": [{"content": "import contextlib\nimport os\nimport threading\nfrom textwrap import wrap\nfrom typing import Any, Iterator, Optional, TextIO, Union\n\nimport rich\nimport rich.box\nimport rich.console\nimport rich.rule\nimport rich.segment\nimport rich.table\nimport rich.text\n\nfrom flexget.options import ArgumentParser\n\nlocal_context = threading.local()\n\n\nclass _Console(rich.console.Console):\n    def __init__(self, *args, **kwargs):\n        if \"PYCHARM_HOSTED\" in os.environ:\n            kwargs.setdefault('color_system', 'truecolor')\n        super().__init__(*args, **kwargs)\n\n    def __call__(self, text: Any, *args, **kwargs) -> None:\n        \"\"\"\n        Print to console safely. Output is able to be captured by different streams in different contexts.\n\n        Any plugin wishing to output to the user's console should use this function instead of print so that\n        output can be redirected when FlexGet is invoked from another process.\n\n        Accepts arguments like the `rich.console.Console.print` function does.\n        \"\"\"\n        self.print(text, *args, **kwargs)\n\n    def print(self, *args, **kwargs) -> None:\n        # Also capture calls directly to console.print\n        _patchable_console(*args, **kwargs)\n\n    def _print(self, *args, **kwargs):\n        \"\"\"The real parent print function, which can be called internally.\"\"\"\n        super().print(*args, **kwargs)\n\n    def rule(\n        self,\n        title: rich.text.TextType = \"\",\n        *,\n        characters: str = \"\u2500\",\n        style: Union[str, rich.console.Style] = \"rule.line\",\n        align: rich.text.AlignMethod = \"left\",\n        # This is a custom FlexGet argument\n        indent: int = 3,\n    ) -> None:\n        rule = rich.rule.Rule(title, characters=characters, style=style, align=align)\n        if indent and title:\n            if not isinstance(rule.title, rich.text.Text):\n                rule.title = self.render_str(rule.title, style=\"rule.text\")\n            text = rich.text.Text()\n            if rule.align == 'left':\n                text = text.append(rule.characters * indent + ' ', style=rule.style)\n            text.append(rule.title)\n            if rule.align == 'right':\n                text = text.append(' ' + rule.characters * indent, style=rule.style)\n            rule.title = text\n        self.print(rule)\n\n\n# This is used to print (rich) text to the console, as well as expose all the other features of rich's console\n# Unlike rich, can be called directly to print (for backwards compat purposes.)\nconsole = _Console()\n\n\nPORCELAIN_BOX: rich.box.Box = rich.box.Box(\n    \"\"\"\\\n    \n  | \n    \n  | \n    \n    \n  | \n    \n\"\"\",\n    ascii=True,\n)\n\nGITHUB_BOX: rich.box.Box = rich.box.Box(\n    \"\"\"\\\n    \n| ||\n|-||\n| ||\n|-||\n|-||\n| ||\n    \n\"\"\",\n    ascii=True,\n)\n\n\nclass TerminalTable(rich.table.Table):\n    \"\"\"\n    A data table suited for CLI output, created via its sent parameters. For example::\n\n        header = ['Col1', 'Col2']\n        table_data = [header]\n        for item in iterable:\n            table_data.append([item.attribute1, item.attribute2])\n        table = TerminalTable('plain', table_data)\n        print table.output\n\n    Optional values are setting table title, and supplying wrap_columns list and\n    drop_column list. If table does not fit into terminal any columns listed in\n    wrap_columns will be tried to wrap and if resulting columns are below MIN_WIDTH(10)\n    columns listed in drop_column will be removed from output.\n\n    Example::\n\n        header = ['Col1', 'Col2']\n        table_data = [header]\n        for item in iterable:\n            table_data.append([item.attribute1, item.attribute2])\n        table = TerminalTable('plain', table_data, 'Table title', wrap_columns=[1,2],\n                              drop_columns=[4,2])\n        print table.output\n\n    :param table_type: A string matching TABLE_TYPES keys.\n    \"\"\"\n\n    # Easy access for our plugins without importing rich\n    Column = rich.table.Column\n\n    # TODO: Add other new types\n    TABLE_TYPES = {\n        'plain': {'box': rich.box.ASCII},\n        'porcelain': {\n            'box': PORCELAIN_BOX,\n            'show_edge': False,\n            'pad_edge': False,\n            'title': None,\n            'padding': 0,\n        },\n        'single': {'box': rich.box.SQUARE},\n        'double': {'box': rich.box.DOUBLE},\n        'github': {'box': GITHUB_BOX},\n        'heavy-head': {'box': rich.box.HEAVY_HEAD},\n    }\n\n    def __init__(self, *args, table_type: str = None, **kwargs) -> None:\n        self.table_type = table_type\n        if table_type:\n            kwargs = {**kwargs, **self.TABLE_TYPES[table_type]}\n        super().__init__(*args, **kwargs)\n\n    def __rich_console__(self, console, options):\n        segments = super().__rich_console__(console, options)\n        if self.table_type not in ['porcelain', 'github']:\n            yield from segments\n            return\n        # Strips out blank lines from our custom types\n        lines = rich.segment.Segment.split_lines(segments)\n        for line in lines:\n            if any(seg.text.strip() for seg in line):\n                yield from line\n                yield rich.segment.Segment.line()\n\n\ntable_parser = ArgumentParser(add_help=False)\ntable_parser.add_argument(\n    '--table-type',\n    choices=list(TerminalTable.TABLE_TYPES),\n    default='heavy-head',\n    help='Select output table style',\n)\ntable_parser.add_argument(\n    '--porcelain',\n    dest='table_type',\n    action='store_const',\n    const='porcelain',\n    help='Make the output parseable. Similar to using `--table-type porcelain`',\n)\n\n\ndef word_wrap(text: str, max_length: int) -> str:\n    \"\"\"A helper method designed to return a wrapped string.\n\n    :param text: Text to wrap\n    :param max_length: Maximum allowed string length\n    :return: Wrapped text or original text\n    \"\"\"\n    if len(text) >= max_length:\n        return '\\n'.join(wrap(text, max_length))\n    return text\n\n\ndef colorize(color: str, text: str) -> str:\n    \"\"\"\n    A simple override of Color.colorize which sets the default auto colors value to True, since it's the more common\n    use case. When output isn't TTY just return text\n\n    :param color: Color tag to use\n    :param text: Text to color\n\n    :return: Colored text or text\n    \"\"\"\n    return f'[{color}]{text}[/]'\n\n\ndef disable_colors():\n    \"\"\"\n    Disables colors to the terminal.\n    \"\"\"\n    console.no_color = True\n\n\n@contextlib.contextmanager\ndef capture_console(filelike: TextIO) -> Iterator:\n    old_output = get_console_output()\n    local_context.output = filelike\n    try:\n        yield\n    finally:\n        local_context.output = old_output\n\n\ndef get_console_output() -> Optional[TextIO]:\n    return getattr(local_context, 'output', None)\n\n\ndef _patchable_console(*args, **kwargs):\n    # Nobody will import this directly, so we can monkeypatch it for IPC calls\n    console.file = get_console_output()\n    try:\n        console._print(*args, **kwargs)\n    finally:\n        console.file = None\n", "path": "flexget/terminal.py"}], "after_files": [{"content": "import contextlib\nimport os\nimport threading\nfrom textwrap import wrap\nfrom typing import Any, Iterator, Optional, TextIO, Union\n\nimport rich\nimport rich.box\nimport rich.console\nimport rich.rule\nimport rich.segment\nimport rich.table\nimport rich.text\n\nfrom flexget.options import ArgumentParser\n\nlocal_context = threading.local()\n\n\nclass _Console(rich.console.Console):\n    def __init__(self, *args, **kwargs):\n        if \"PYCHARM_HOSTED\" in os.environ:\n            kwargs.setdefault('color_system', 'truecolor')\n        kwargs.setdefault(\"markup\", True)\n        super().__init__(*args, **kwargs)\n\n    def __call__(self, text: Any, *args, **kwargs) -> None:\n        \"\"\"\n        Print to console safely. Output is able to be captured by different streams in different contexts.\n\n        Any plugin wishing to output to the user's console should use this function instead of print so that\n        output can be redirected when FlexGet is invoked from another process.\n\n        Accepts arguments like the `rich.console.Console.print` function does.\n        \"\"\"\n        self.print(text, *args, **kwargs)\n\n    def print(self, *args, **kwargs) -> None:\n        # Also capture calls directly to console.print\n        _patchable_console(*args, **kwargs)\n\n    def _print(self, *args, **kwargs):\n        \"\"\"The real parent print function, which can be called internally.\"\"\"\n        super().print(*args, **kwargs)\n\n    def rule(\n        self,\n        title: rich.text.TextType = \"\",\n        *,\n        characters: str = \"\u2500\",\n        style: Union[str, rich.console.Style] = \"rule.line\",\n        align: rich.text.AlignMethod = \"left\",\n        # This is a custom FlexGet argument\n        indent: int = 3,\n    ) -> None:\n        rule = rich.rule.Rule(title, characters=characters, style=style, align=align)\n        if indent and title:\n            if not isinstance(rule.title, rich.text.Text):\n                rule.title = self.render_str(rule.title, style=\"rule.text\")\n            text = rich.text.Text()\n            if rule.align == 'left':\n                text = text.append(rule.characters * indent + ' ', style=rule.style)\n            text.append(rule.title)\n            if rule.align == 'right':\n                text = text.append(' ' + rule.characters * indent, style=rule.style)\n            rule.title = text\n        self.print(rule)\n\n\n# This is used to print (rich) text to the console, as well as expose all the other features of rich's console\n# Unlike rich, can be called directly to print (for backwards compat purposes.)\nconsole = _Console()\n\n\nPORCELAIN_BOX: rich.box.Box = rich.box.Box(\n    \"\"\"\\\n    \n  | \n    \n  | \n    \n    \n  | \n    \n\"\"\",\n    ascii=True,\n)\n\nGITHUB_BOX: rich.box.Box = rich.box.Box(\n    \"\"\"\\\n    \n| ||\n|-||\n| ||\n|-||\n|-||\n| ||\n    \n\"\"\",\n    ascii=True,\n)\n\n\nclass TerminalTable(rich.table.Table):\n    \"\"\"\n    A data table suited for CLI output, created via its sent parameters. For example::\n\n        header = ['Col1', 'Col2']\n        table_data = [header]\n        for item in iterable:\n            table_data.append([item.attribute1, item.attribute2])\n        table = TerminalTable('plain', table_data)\n        print table.output\n\n    Optional values are setting table title, and supplying wrap_columns list and\n    drop_column list. If table does not fit into terminal any columns listed in\n    wrap_columns will be tried to wrap and if resulting columns are below MIN_WIDTH(10)\n    columns listed in drop_column will be removed from output.\n\n    Example::\n\n        header = ['Col1', 'Col2']\n        table_data = [header]\n        for item in iterable:\n            table_data.append([item.attribute1, item.attribute2])\n        table = TerminalTable('plain', table_data, 'Table title', wrap_columns=[1,2],\n                              drop_columns=[4,2])\n        print table.output\n\n    :param table_type: A string matching TABLE_TYPES keys.\n    \"\"\"\n\n    # Easy access for our plugins without importing rich\n    Column = rich.table.Column\n\n    # TODO: Add other new types\n    TABLE_TYPES = {\n        'plain': {'box': rich.box.ASCII},\n        'porcelain': {\n            'box': PORCELAIN_BOX,\n            'show_edge': False,\n            'pad_edge': False,\n            'title': None,\n            'padding': 0,\n        },\n        'single': {'box': rich.box.SQUARE},\n        'double': {'box': rich.box.DOUBLE},\n        'github': {'box': GITHUB_BOX},\n        'heavy-head': {'box': rich.box.HEAVY_HEAD},\n    }\n\n    def __init__(self, *args, table_type: str = None, **kwargs) -> None:\n        self.table_type = table_type\n        if table_type:\n            kwargs = {**kwargs, **self.TABLE_TYPES[table_type]}\n        super().__init__(*args, **kwargs)\n\n    def __rich_console__(self, console, options):\n        segments = super().__rich_console__(console, options)\n        if self.table_type not in ['porcelain', 'github']:\n            yield from segments\n            return\n        # Strips out blank lines from our custom types\n        lines = rich.segment.Segment.split_lines(segments)\n        for line in lines:\n            if any(seg.text.strip() for seg in line):\n                yield from line\n                yield rich.segment.Segment.line()\n\n\ntable_parser = ArgumentParser(add_help=False)\ntable_parser.add_argument(\n    '--table-type',\n    choices=list(TerminalTable.TABLE_TYPES),\n    default='heavy-head',\n    help='Select output table style',\n)\ntable_parser.add_argument(\n    '--porcelain',\n    dest='table_type',\n    action='store_const',\n    const='porcelain',\n    help='Make the output parseable. Similar to using `--table-type porcelain`',\n)\n\n\ndef word_wrap(text: str, max_length: int) -> str:\n    \"\"\"A helper method designed to return a wrapped string.\n\n    :param text: Text to wrap\n    :param max_length: Maximum allowed string length\n    :return: Wrapped text or original text\n    \"\"\"\n    if len(text) >= max_length:\n        return '\\n'.join(wrap(text, max_length))\n    return text\n\n\ndef colorize(color: str, text: str) -> str:\n    \"\"\"\n    A simple override of Color.colorize which sets the default auto colors value to True, since it's the more common\n    use case. When output isn't TTY just return text\n\n    :param color: Color tag to use\n    :param text: Text to color\n\n    :return: Colored text or text\n    \"\"\"\n    return f'[{color}]{text}[/]'\n\n\ndef disable_colors():\n    \"\"\"\n    Disables colors to the terminal.\n    \"\"\"\n    console.no_color = True\n\n\n@contextlib.contextmanager\ndef capture_console(filelike: TextIO) -> Iterator:\n    old_output = get_console_output()\n    local_context.output = filelike\n    try:\n        yield\n    finally:\n        local_context.output = old_output\n\n\ndef get_console_output() -> Optional[TextIO]:\n    return getattr(local_context, 'output', None)\n\n\ndef _patchable_console(*args, **kwargs):\n    # Nobody will import this directly, so we can monkeypatch it for IPC calls\n    console.file = get_console_output()\n    try:\n        console._print(*args, **kwargs)\n    finally:\n        console.file = None\n", "path": "flexget/terminal.py"}]}