-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
dc4839e
commit cfbac50
Showing
25 changed files
with
307 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
"""<your project name here> | ||
<your project description here> | ||
""" | ||
|
||
# Local | ||
from typing import Any | ||
|
||
from . import _exceptions, _types, cli, utils | ||
from .__metadata__ import __author__, __description__, __license__, __title__ | ||
from .__version__ import __version__ | ||
|
||
_deprecated: dict[str, Any] = {} | ||
|
||
|
||
def __getattr__(name: str) -> Any: | ||
if name in _deprecated: | ||
import warnings | ||
|
||
real = _deprecated[name] | ||
warnings.warn( | ||
f"{name} is deprecated, please use {real.__name__} instead", | ||
DeprecationWarning, | ||
stacklevel=2, | ||
) | ||
return real | ||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}") | ||
|
||
|
||
# Public Re-Exports | ||
__all__ = [ | ||
"cli", | ||
"utils", | ||
"_exceptions", | ||
"_types", | ||
"__title__", | ||
"__description__", | ||
"__version__", | ||
"__author__", | ||
"__license__", | ||
] | ||
|
||
__all__.extend(_deprecated) |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
"""project version""" | ||
|
||
__version__ = "1.0.0" | ||
__version__: str = "1.0.0" | ||
__full_version__ = (0, 0, 0) |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from typing import NamedTuple | ||
|
||
|
||
class MyClass(NamedTuple): | ||
name: str | ||
secret: str | None = None | ||
|
||
def __rich__(self) -> str: | ||
lines: list[str] = [] | ||
lines.append(f"[primary]name[/] = {self.name}") | ||
if self.secret is not None: | ||
lines.append(f"[error]secret[/] = {self.secret}") | ||
return "\n".join(lines) | ||
|
||
|
||
# if TYPE_CHECKING: | ||
if True: | ||
from typing import Any, Protocol, TypeVar | ||
|
||
class RichProtocol(Protocol): | ||
def __rich__(self) -> str: | ||
... | ||
|
||
SpinnerT = TypeVar("SpinnerT", bound="Spinner") | ||
|
||
class Spinner(Protocol): | ||
def update(self, text: str) -> None: | ||
... | ||
|
||
def __enter__(self: SpinnerT) -> SpinnerT: | ||
... | ||
|
||
def __exit__(self, *args: Any) -> None: | ||
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import logging | ||
|
||
from ..utils.logging import setup_logger | ||
from .cli import CLI | ||
|
||
__title__ = "cli" | ||
__description__ = """ | ||
This is the cli module of the python_template_repo package. | ||
""" | ||
|
||
|
||
def main() -> int: | ||
"""Main entry point for the application.""" | ||
LOG = setup_logger(logger_name=__name__, log_level=logging.DEBUG) | ||
LOG.info("Starting cli...") | ||
cli = CLI() | ||
cli.echo("hello world") | ||
return 0 | ||
|
||
|
||
__all__ = ["CLI", "main"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import sys | ||
|
||
from . import main | ||
|
||
sys.exit(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import contextlib | ||
import logging | ||
import warnings | ||
from tempfile import mktemp | ||
from typing import Any | ||
from collections.abc import Iterator | ||
|
||
from rich.console import Console | ||
from rich.progress import Progress, ProgressColumn | ||
|
||
from .._types import RichProtocol, Spinner | ||
from .config import LOG_LEVELS, Verbosity, _console, _err_console | ||
from .spinner import SPINNER, DummySpinner | ||
|
||
logger = logging.getLogger(__name__) | ||
logger.setLevel(logging.DEBUG) | ||
logger.addHandler(logging.NullHandler()) | ||
|
||
|
||
def is_interactive(console: Console | None = None) -> bool: | ||
"""Check if the terminal is run under interactive mode""" | ||
if console is None: | ||
console = _console | ||
return console.is_interactive | ||
|
||
|
||
class CLI: | ||
"""Terminal UI object""" | ||
|
||
def __init__(self, verbosity: Verbosity = Verbosity.NORMAL) -> None: | ||
self.verbosity = verbosity | ||
|
||
def set_verbosity(self, verbosity: int) -> None: | ||
self.verbosity = Verbosity(verbosity) | ||
if self.verbosity == Verbosity.QUIET: | ||
warnings.simplefilter("ignore", FutureWarning, append=True) | ||
|
||
def echo( | ||
self, | ||
message: str | RichProtocol = "", | ||
err: bool = False, | ||
verbosity: Verbosity = Verbosity.QUIET, | ||
**kwargs: Any, | ||
) -> None: | ||
"""print message using rich console | ||
:param message: message with rich markup, defaults to "". | ||
:param err: if true print to stderr, defaults to False. | ||
:param verbosity: verbosity level, defaults to QUIET. | ||
""" | ||
if self.verbosity >= verbosity: | ||
console = _err_console if err else _console | ||
if not console.is_interactive: | ||
kwargs.setdefault("crop", False) | ||
kwargs.setdefault("overflow", "ignore") | ||
console.print(message, **kwargs) | ||
|
||
@contextlib.contextmanager | ||
def logging(self, type_: str = "install") -> Iterator[logging.Logger]: | ||
"""A context manager that opens a file for logging when verbosity is NORMAL or | ||
print to the stdout otherwise. | ||
""" | ||
file_name: str | None = None | ||
if self.verbosity >= Verbosity.DETAIL: | ||
handler: logging.Handler = logging.StreamHandler() | ||
handler.setLevel(LOG_LEVELS[self.verbosity]) | ||
else: | ||
file_name = mktemp(".log") | ||
handler = logging.FileHandler(file_name, encoding="utf-8") | ||
handler.setLevel(logging.DEBUG) | ||
handler.setFormatter(logging.Formatter("%(name)s: %(message)s")) | ||
logger.addHandler(handler) | ||
|
||
try: | ||
yield logger | ||
except Exception: | ||
if self.verbosity < Verbosity.DETAIL: | ||
logger.exception("Error occurs") | ||
self.echo( | ||
f"See [warning]{file_name}[/] for detailed debug log.", | ||
style="error", | ||
err=True, | ||
) | ||
raise | ||
# else: | ||
# atexit.register(cleanup) | ||
finally: | ||
logger.removeHandler(handler) | ||
handler.close() | ||
|
||
def open_spinner(self, title: str) -> Spinner: | ||
"""Open a spinner as a context manager.""" | ||
if self.verbosity >= Verbosity.DETAIL or not is_interactive(): | ||
return DummySpinner(title) | ||
else: | ||
return _err_console.status(title, spinner=SPINNER, spinner_style="primary") # type: ignore | ||
|
||
def make_progress(self, *columns: str | ProgressColumn, **kwargs: Any) -> Progress: | ||
"""create a progress instance for indented spinners""" | ||
return Progress( | ||
*columns, | ||
console=_console, | ||
disable=self.verbosity >= Verbosity.DETAIL, | ||
**kwargs, | ||
) | ||
|
||
def info(self, message: str, verbosity: Verbosity = Verbosity.QUIET) -> None: | ||
"""Print a message to stdout.""" | ||
self.echo(f"[info]INFO:[/] [dim]{message}[/]", err=True, verbosity=verbosity) | ||
|
||
def warn(self, message: str, verbosity: Verbosity = Verbosity.QUIET) -> None: | ||
"""Print a message to stdout.""" | ||
self.echo(f"[warning]WARNING:[/] {message}", err=True, verbosity=verbosity) | ||
|
||
def error(self, message: str, verbosity: Verbosity = Verbosity.QUIET) -> None: | ||
"""Print a message to stdout.""" | ||
self.echo(f"[error]WARNING:[/] {message}", err=True, verbosity=verbosity) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import enum | ||
import logging | ||
|
||
from rich.console import Console | ||
from rich.theme import Theme | ||
|
||
logger = logging.getLogger(__name__) | ||
logger.setLevel(logging.DEBUG) | ||
logger.addHandler(logging.NullHandler()) | ||
|
||
|
||
DEFAULT_THEME: dict[str, str] = { | ||
"primary": "cyan", | ||
"success": "green", | ||
"warning": "yellow", | ||
"error": "red", | ||
"info": "blue", | ||
"req": "bold green", | ||
} | ||
|
||
_console = Console(highlight=False, theme=Theme(DEFAULT_THEME)) | ||
_err_console = Console(stderr=True, theme=Theme(DEFAULT_THEME)) | ||
|
||
|
||
class Verbosity(enum.IntEnum): | ||
QUIET = -1 | ||
NORMAL = 0 | ||
DETAIL = 1 | ||
DEBUG = 2 | ||
|
||
|
||
LOG_LEVELS = { | ||
Verbosity.NORMAL: logging.WARN, | ||
Verbosity.DETAIL: logging.INFO, | ||
Verbosity.DEBUG: logging.DEBUG, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from typing import Any | ||
|
||
from .._types import SpinnerT | ||
from .config import _err_console | ||
|
||
SPINNER = "dots" | ||
|
||
|
||
class DummySpinner: | ||
"""A dummy spinner class implementing needed interfaces. | ||
But only display text onto screen. | ||
""" | ||
|
||
def __init__(self, text: str) -> None: | ||
self.text = text | ||
|
||
def _show(self) -> None: | ||
_err_console.print(f"[primary]STATUS:[/] {self.text}") | ||
|
||
def update(self, text: str) -> None: | ||
self.text = text | ||
self._show() | ||
|
||
def __enter__(self: SpinnerT) -> SpinnerT: | ||
self._show() # type: ignore[attr-defined] | ||
return self | ||
|
||
def __exit__(self, *args: Any) -> None: | ||
pass | ||
|
||
|
||
class SilentSpinner(DummySpinner): | ||
def _show(self) -> None: | ||
pass |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from python_template_repo.cli import CLI | ||
|
||
|
||
def test_app(): | ||
a = CLI() | ||
a.echo("hello world") | ||
assert True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from __future__ import annotations | ||
|
||
|
||
def test_version() -> None: | ||
from python_template_repo import __version__ | ||
|
||
assert __version__ |
Empty file.
Empty file.
Oops, something went wrong.