From ea7fa054ed7cdcad7ac6e9b67a6aa13cb183b32a Mon Sep 17 00:00:00 2001 From: qexat Date: Mon, 31 Jul 2023 15:28:33 +0200 Subject: [PATCH] Allow the use of sequence names (#36) * This enables avoiding imports * It is not a breaking change, the old way still works --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- README.md | 11 +++---- examples/coquille_context/__main__.py | 7 ++--- examples/write/__main__.py | 8 ++--- pyproject.toml | 2 +- src/coquille/__init__.py | 2 +- src/coquille/{coquille.py => prelude.py} | 39 +++++++++++++++++------- src/coquille/sequences.py | 22 +++++++++++-- tests/coquille_test.py | 5 ++- 8 files changed, 63 insertions(+), 33 deletions(-) rename src/coquille/{coquille.py => prelude.py} (87%) diff --git a/README.md b/README.md index 59066a0..5449dbc 100644 --- a/README.md +++ b/README.md @@ -21,17 +21,17 @@ Even though the examples are mostly showcasing [SGR escape sequences](https://en ```py from coquille import Coquille -from coquille.sequences import bold, fg_magenta, italic print("Hello World!") # By default, the coquille wraps the standard output -with Coquille.new(fg_magenta, italic) as coquille: +with Coquille.new("fg_magenta", "italic") as coquille: print("Hello World, but in magenta and italic!") - coquille.apply(bold) + coquille.apply("bold") print("Now, with a touch of bold :D") print("Oh, we are back to normal now...") + ``` ![screenshot.png](https://raw.githubusercontent.com/qexat/Coquille/main/examples/coquille_context/screenshot.png) @@ -42,14 +42,13 @@ Source code: [examples/coquille_context/](https://github.com/qexat/Coquille/blob ```py from coquille import write -from coquille.sequences import bold, fg_blue, fg_magenta, italic print("Hello World!") -write("Hello World, but in magenta and italic!", fg_magenta, italic) +write("Hello World, but in magenta and italic!", "fg_magenta", "italic") with open("examples/write/output.txt", "w") as my_file: - write("A pretty Hello World in a file!", fg_blue, bold, file=my_file) + write("A pretty Hello World in a file!", "fg_blue", "bold", file=my_file) ``` diff --git a/examples/coquille_context/__main__.py b/examples/coquille_context/__main__.py index 94a79fd..57930cb 100644 --- a/examples/coquille_context/__main__.py +++ b/examples/coquille_context/__main__.py @@ -1,14 +1,11 @@ from coquille import Coquille -from coquille.sequences import bold -from coquille.sequences import fg_magenta -from coquille.sequences import italic print("Hello World!") # By default, the coquille wraps the standard output -with Coquille.new(fg_magenta, italic) as coquille: +with Coquille.new("fg_magenta", "italic") as coquille: print("Hello World, but in magenta and italic!") - coquille.apply(bold) + coquille.apply("bold") print("Now, with a touch of bold :D") print("Oh, we are back to normal now...") diff --git a/examples/write/__main__.py b/examples/write/__main__.py index 43ceb5e..2b876c4 100644 --- a/examples/write/__main__.py +++ b/examples/write/__main__.py @@ -1,12 +1,8 @@ from coquille import write -from coquille.sequences import bold -from coquille.sequences import fg_blue -from coquille.sequences import fg_magenta -from coquille.sequences import italic print("Hello World!") -write("Hello World, but in magenta and italic!", fg_magenta, italic) +write("Hello World, but in magenta and italic!", "fg_magenta", "italic") with open("examples/write/output.txt", "w") as my_file: - write("A pretty Hello World in a file!", fg_blue, bold, file=my_file) + write("A pretty Hello World in a file!", "fg_blue", "bold", file=my_file) diff --git a/pyproject.toml b/pyproject.toml index 339028c..e322d65 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "Coquille" -version = "1.1.2" +version = "1.2.0" authors = [{ name = "Qexat" }] description = "Coquille is a library that wraps terminal escape sequences as convenient functions." readme = "README.md" diff --git a/src/coquille/__init__.py b/src/coquille/__init__.py index 2cb80c4..0fd14d8 100644 --- a/src/coquille/__init__.py +++ b/src/coquille/__init__.py @@ -1 +1 @@ -from coquille.coquille import * +from coquille.prelude import * diff --git a/src/coquille/coquille.py b/src/coquille/prelude.py similarity index 87% rename from src/coquille/coquille.py rename to src/coquille/prelude.py index a146866..363b354 100644 --- a/src/coquille/coquille.py +++ b/src/coquille/prelude.py @@ -10,9 +10,19 @@ from typing import TYPE_CHECKING from coquille.sequences import EscapeSequence +from coquille.sequences import EscapeSequenceName +from coquille.sequences import get_sequence_from_name from coquille.sequences import soft_reset -__all__ = ["apply", "Coquille", "EscapeSequence", "prepare", "write"] +__all__ = [ + "apply", + "Coquille", + "EscapeSequence", + "EscapeSequenceName", + "prepare", + "write", +] + # ... don't say anything. if TYPE_CHECKING: # pragma: no cover @@ -26,7 +36,9 @@ @overload -def prepare(sequence: EscapeSequence) -> EscapeSequence: # pragma: no cover +def prepare( + sequence: EscapeSequence | EscapeSequenceName, +) -> EscapeSequence: # pragma: no cover pass @@ -40,7 +52,7 @@ def prepare( def prepare( - sequence: EscapeSequence | Callable[P, EscapeSequence], + sequence: EscapeSequence | EscapeSequenceName | Callable[P, EscapeSequence], *args: P.args, **kwargs: P.kwargs, ) -> EscapeSequence: @@ -51,15 +63,17 @@ def prepare( the arguments to the escape sequence factory to construct one. """ - if isinstance(sequence, str): + if isinstance(sequence, EscapeSequence): return sequence + elif isinstance(sequence, str): + return get_sequence_from_name(sequence) return sequence(*args, **kwargs) @overload def apply( - sequence: EscapeSequence, + sequence: EscapeSequence | EscapeSequenceName, file: SupportsWrite[str] | None = None, ) -> None: # pragma: no cover pass @@ -76,7 +90,7 @@ def apply( def apply( - sequence: EscapeSequence | Callable[P, EscapeSequence], + sequence: EscapeSequence | EscapeSequenceName | Callable[P, EscapeSequence], file: SupportsWrite[str] | None = None, *args: P.args, **kwargs: P.kwargs, @@ -109,7 +123,7 @@ class _ContextCoquille: sequences: list[EscapeSequence] file: SupportsWrite[str] | None - def apply(self, sequence: EscapeSequence) -> None: + def apply(self, sequence: EscapeSequence | EscapeSequenceName) -> None: """ Apply an escape sequence in the context manager of a Coquille in live. @@ -155,14 +169,17 @@ class Coquille: @overload @classmethod - def new(cls: type[Self], *sequences: EscapeSequence) -> Self: # pragma: no cover + def new( + cls: type[Self], + *sequences: EscapeSequence | EscapeSequenceName, + ) -> Self: # pragma: no cover pass @overload @classmethod def new( cls: type[Self], - *sequences: EscapeSequence, + *sequences: EscapeSequence | EscapeSequenceName, file: SupportsWrite[str], ) -> Self: # pragma: no cover pass @@ -170,7 +187,7 @@ def new( @classmethod def new( cls: type[Self], - *sequences: EscapeSequence, + *sequences: EscapeSequence | EscapeSequenceName, file: SupportsWrite[str] | None = None, ) -> Self: """ @@ -264,7 +281,7 @@ def __exit__(self, *_) -> None: def write( text: str, - *sequences: EscapeSequence, + *sequences: EscapeSequence | EscapeSequenceName, end: str | None = "\n", file: SupportsWrite[str] | None = None, ) -> None: diff --git a/src/coquille/sequences.py b/src/coquille/sequences.py index bfdb80b..72af5da 100644 --- a/src/coquille/sequences.py +++ b/src/coquille/sequences.py @@ -20,7 +20,6 @@ - [RS] = rarely (never, basically) supported. """ from typing import Literal -from typing import NewType # Constants @@ -39,10 +38,14 @@ # Helper types -EscapeSequence = NewType("EscapeSequence", str) +EscapeSequenceName = str AltFontNumber = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9] +class EscapeSequence(str): + pass + + def escape_sequence( code: str, subcode: str | None = None, @@ -461,3 +464,18 @@ def DCHARSET(subcode: str) -> EscapeSequence: # aliases DECSTR = soft_reset + + +def get_sequence_from_name(name: str) -> EscapeSequence: + module_items = dict(globals()) + sequence_error = ValueError(f"{name!r} is not a valid escape sequence name") + + if name not in module_items: + raise sequence_error + + sequence = module_items[name] + + if not isinstance(sequence, EscapeSequence): + raise sequence_error + + return sequence diff --git a/tests/coquille_test.py b/tests/coquille_test.py index 8b607f6..2bc1984 100644 --- a/tests/coquille_test.py +++ b/tests/coquille_test.py @@ -1,9 +1,10 @@ from io import StringIO import pytest -from coquille.coquille import * +from coquille.prelude import * from coquille.sequences import DEC_save_cursor from coquille.sequences import erase_in_display +from coquille.sequences import fg_black from coquille.sequences import select_graphical_rendition from coquille.sequences import start_of_string @@ -12,6 +13,7 @@ ["args", "output"], [ ((DEC_save_cursor,), DEC_save_cursor), + (("fg_black",), fg_black), ((start_of_string,), "\u001bX"), ((erase_in_display, 2), "\u001b[2J"), ((select_graphical_rendition, 38, 5, 16), "\x1b[38;5;16m"), @@ -26,6 +28,7 @@ def test_prepare(args, output): [ (DEC_save_cursor, (), DEC_save_cursor), (start_of_string, (), "\u001bX"), + ("fg_black", (), fg_black), (erase_in_display, (2,), "\u001b[2J"), (select_graphical_rendition, (38, 5, 16), "\x1b[38;5;16m"), ],