mirror of
https://github.com/ChristianLight/tutor.git
synced 2024-06-08 17:02:19 +00:00
Merge remote-tracking branch 'origin/master' into nightly
This commit is contained in:
commit
7076b7ca95
|
@ -20,6 +20,11 @@ instructions, because git commits are used to generate release notes:
|
||||||
|
|
||||||
<!-- scriv-insert-here -->
|
<!-- scriv-insert-here -->
|
||||||
|
|
||||||
|
<a id='changelog-15.3.1'></a>
|
||||||
|
## v15.3.1 (2023-02-28)
|
||||||
|
|
||||||
|
- [Bugfix] `patchStrategicMerge` can now be applied to jobs. (by @keithgg)
|
||||||
|
|
||||||
<a id='changelog-15.3.0'></a>
|
<a id='changelog-15.3.0'></a>
|
||||||
## v15.3.0 (2023-02-10)
|
## v15.3.0 (2023-02-10)
|
||||||
|
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
<!--
|
|
||||||
Create a changelog entry for every new user-facing change. Please respect the following instructions:
|
|
||||||
- Indicate breaking changes by prepending an explosion 💥 character.
|
|
||||||
- Prefix your changes with either [Bugfix], [Improvement], [Feature], [Security], [Deprecation].
|
|
||||||
- You may optionally append "(by @<author>)" at the end of the line, where "<author>" is either one (just one)
|
|
||||||
of your GitHub username, real name or affiliated organization. These affiliations will be displayed in
|
|
||||||
the release notes for every release.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- - 💥[Feature] Foobarize the blorginator. This breaks plugins by renaming the `FOO_DO` filter to `BAR_DO`. (by @regisb) -->
|
|
||||||
<!-- - [Improvement] This is a non-breaking change. Life is good. (by @billgates) -->
|
|
||||||
[Bugfix] `patchStrategicMerge` can now be applied to jobs (by @keithgg)
|
|
|
@ -56,17 +56,3 @@ class PluginsTests(unittest.TestCase, TestCommandMixin):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
["all", "alba"], plugins_commands.PluginName(allow_all=True).get_names("al")
|
["all", "alba"], plugins_commands.PluginName(allow_all=True).get_names("al")
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_format_table(self) -> None:
|
|
||||||
rows: t.List[t.Tuple[str, ...]] = [
|
|
||||||
("a", "xyz", "value 1"),
|
|
||||||
("abc", "x", "value 12345"),
|
|
||||||
]
|
|
||||||
formatted = plugins_commands.format_table(rows, separator=" ")
|
|
||||||
self.assertEqual(
|
|
||||||
"""
|
|
||||||
a xyz value 1
|
|
||||||
abc x value 12345
|
|
||||||
""".strip(),
|
|
||||||
formatted,
|
|
||||||
)
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import os
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
from typing import List, Tuple
|
||||||
from unittest.mock import MagicMock, mock_open, patch
|
from unittest.mock import MagicMock, mock_open, patch
|
||||||
|
|
||||||
from tutor import exceptions, utils
|
from tutor import exceptions, utils
|
||||||
|
@ -239,3 +240,17 @@ class UtilsTests(unittest.TestCase):
|
||||||
self.assertFalse(utils.is_http("/home/user/"))
|
self.assertFalse(utils.is_http("/home/user/"))
|
||||||
self.assertFalse(utils.is_http("home/user/"))
|
self.assertFalse(utils.is_http("home/user/"))
|
||||||
self.assertFalse(utils.is_http("http-home/user/"))
|
self.assertFalse(utils.is_http("http-home/user/"))
|
||||||
|
|
||||||
|
def test_format_table(self) -> None:
|
||||||
|
rows: List[Tuple[str, ...]] = [
|
||||||
|
("a", "xyz", "value 1"),
|
||||||
|
("abc", "x", "value 12345"),
|
||||||
|
]
|
||||||
|
formatted = utils.format_table(rows, separator=" ")
|
||||||
|
self.assertEqual(
|
||||||
|
"""
|
||||||
|
a xyz value 1
|
||||||
|
abc x value 12345
|
||||||
|
""".strip(),
|
||||||
|
formatted,
|
||||||
|
)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import os
|
||||||
|
|
||||||
# Increment this version number to trigger a new release. See
|
# Increment this version number to trigger a new release. See
|
||||||
# docs/tutor.html#versioning for information on the versioning scheme.
|
# docs/tutor.html#versioning for information on the versioning scheme.
|
||||||
__version__ = "15.3.0"
|
__version__ = "15.3.1"
|
||||||
|
|
||||||
# The version suffix will be appended to the actual version, separated by a
|
# The version suffix will be appended to the actual version, separated by a
|
||||||
# dash. Use this suffix to differentiate between the actual released version and
|
# dash. Use this suffix to differentiate between the actual released version and
|
||||||
|
|
|
@ -128,7 +128,7 @@ def list_command(show_enabled_only: bool) -> None:
|
||||||
(plugin_info or "").replace("\n", " "),
|
(plugin_info or "").replace("\n", " "),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fmt.echo(format_table(plugins_table))
|
fmt.echo(utils.format_table(plugins_table))
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Enable a plugin")
|
@click.command(help="Enable a plugin")
|
||||||
|
@ -302,7 +302,7 @@ def search(pattern: str) -> None:
|
||||||
plugin.short_description,
|
plugin.short_description,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
print(format_table(results))
|
print(utils.format_table(results))
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
|
@ -408,34 +408,6 @@ def index_remove(context: Context, url: str) -> None:
|
||||||
fmt.echo_alert("Plugin index not present")
|
fmt.echo_alert("Plugin index not present")
|
||||||
|
|
||||||
|
|
||||||
def format_table(rows: t.List[t.Tuple[str, ...]], separator: str = "\t") -> str:
|
|
||||||
"""
|
|
||||||
Format a list of values as a tab-separated table. Column sizes are determined such
|
|
||||||
that row values are vertically aligned.
|
|
||||||
"""
|
|
||||||
formatted = ""
|
|
||||||
if not rows:
|
|
||||||
return formatted
|
|
||||||
columns_count = len(rows[0])
|
|
||||||
# Determine each column size
|
|
||||||
col_sizes = [1] * columns_count
|
|
||||||
for row in rows:
|
|
||||||
for c, value in enumerate(row):
|
|
||||||
col_sizes[c] = max(col_sizes[c], len(value))
|
|
||||||
# Print all values
|
|
||||||
for r, row in enumerate(rows):
|
|
||||||
for c, value in enumerate(row):
|
|
||||||
if c < len(col_sizes) - 1:
|
|
||||||
formatted += f"{value:{col_sizes[c]}}{separator}"
|
|
||||||
else:
|
|
||||||
# The last column is not left-justified
|
|
||||||
formatted += f"{value}"
|
|
||||||
if r < len(rows) - 1:
|
|
||||||
# Append EOL at all lines but the last one
|
|
||||||
formatted += "\n"
|
|
||||||
return formatted
|
|
||||||
|
|
||||||
|
|
||||||
index_command.add_command(index_add)
|
index_command.add_command(index_add)
|
||||||
index_command.add_command(index_list)
|
index_command.add_command(index_list)
|
||||||
index_command.add_command(index_remove)
|
index_command.add_command(index_remove)
|
||||||
|
|
|
@ -345,3 +345,31 @@ def is_http(url: str) -> bool:
|
||||||
Basic test to check whether a string is a web URL. Use only for basic use cases.
|
Basic test to check whether a string is a web URL. Use only for basic use cases.
|
||||||
"""
|
"""
|
||||||
return re.match(r"^https?://", url) is not None
|
return re.match(r"^https?://", url) is not None
|
||||||
|
|
||||||
|
|
||||||
|
def format_table(rows: List[Tuple[str, ...]], separator: str = "\t") -> str:
|
||||||
|
"""
|
||||||
|
Format a list of values as a tab-separated table. Column sizes are determined such
|
||||||
|
that row values are vertically aligned.
|
||||||
|
"""
|
||||||
|
formatted = ""
|
||||||
|
if not rows:
|
||||||
|
return formatted
|
||||||
|
columns_count = len(rows[0])
|
||||||
|
# Determine each column size
|
||||||
|
col_sizes = [1] * columns_count
|
||||||
|
for row in rows:
|
||||||
|
for c, value in enumerate(row):
|
||||||
|
col_sizes[c] = max(col_sizes[c], len(value))
|
||||||
|
# Print all values
|
||||||
|
for r, row in enumerate(rows):
|
||||||
|
for c, value in enumerate(row):
|
||||||
|
if c < len(col_sizes) - 1:
|
||||||
|
formatted += f"{value:{col_sizes[c]}}{separator}"
|
||||||
|
else:
|
||||||
|
# The last column is not left-justified
|
||||||
|
formatted += f"{value}"
|
||||||
|
if r < len(rows) - 1:
|
||||||
|
# Append EOL at all lines but the last one
|
||||||
|
formatted += "\n"
|
||||||
|
return formatted
|
||||||
|
|
Loading…
Reference in New Issue
Block a user