-
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
Showing
13 changed files
with
243 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from pueblo.util.environ import getenvpass | ||
from pueblo.util.logging import setup_logging |
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,25 @@ | ||
import logging | ||
|
||
import click | ||
from click_aliases import ClickAliasedGroup | ||
|
||
from pueblo.util.cli import boot_click | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def help_pueblo(): | ||
""" | ||
Pueblo - a Python toolbox library. | ||
pueblo --version | ||
""" # noqa: E501 | ||
|
||
|
||
@click.group(cls=ClickAliasedGroup) | ||
@click.version_option(package_name="pueblo") | ||
@click.option("--verbose", is_flag=True, required=False, help="Turn on logging") | ||
@click.option("--debug", is_flag=True, required=False, help="Turn on logging with debug level") | ||
@click.pass_context | ||
def cli(ctx: click.Context, verbose: bool, debug: bool): | ||
return boot_click(ctx, verbose, debug) |
Empty file.
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,49 @@ | ||
import typing as t | ||
|
||
import pytest | ||
|
||
|
||
def monkeypatch_pytest_notebook_treat_cell_exit_as_notebook_skip(): | ||
""" | ||
Patch `pytest-notebook`, in fact `nbclient.client.NotebookClient`, | ||
to propagate cell-level `pytest.exit()` invocations as signals | ||
to mark the whole notebook as skipped. | ||
In order not to be too intrusive, the feature only skips notebooks | ||
when being explicitly instructed, by adding `[skip-notebook]` at the | ||
end of the `reason` string. Example: | ||
import pytest | ||
if "ACME_API_KEY" not in os.environ: | ||
pytest.exit("ACME_API_KEY not given [skip-notebook]") | ||
https://github.com/chrisjsewell/pytest-notebook/issues/43 | ||
""" | ||
from nbclient.client import NotebookClient | ||
from nbclient.exceptions import CellExecutionError | ||
from nbformat import NotebookNode | ||
|
||
async_execute_cell_dist = NotebookClient.async_execute_cell | ||
|
||
async def async_execute_cell( | ||
self, | ||
cell: NotebookNode, | ||
cell_index: int, | ||
execution_count: t.Optional[int] = None, | ||
store_history: bool = True, | ||
) -> NotebookNode: | ||
try: | ||
return await async_execute_cell_dist( | ||
self, | ||
cell, | ||
cell_index, | ||
execution_count=execution_count, | ||
store_history=store_history, | ||
) | ||
except CellExecutionError as ex: | ||
if ex.ename == "Exit" and ex.evalue.endswith("[skip-notebook]"): | ||
raise pytest.skip(ex.evalue) | ||
else: | ||
raise | ||
|
||
NotebookClient.async_execute_cell = async_execute_cell |
Empty file.
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,46 @@ | ||
import logging | ||
import textwrap | ||
import typing as t | ||
|
||
from pueblo.util.logging import setup_logging | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def boot_click(ctx: "click.Context", verbose: bool = False, debug: bool = False): | ||
""" | ||
Bootstrap the CLI application. | ||
""" | ||
|
||
# Adjust log level according to `verbose` / `debug` flags. | ||
log_level = logging.INFO | ||
if debug: | ||
log_level = logging.DEBUG | ||
|
||
# Setup logging, according to `verbose` / `debug` flags. | ||
setup_logging(level=log_level, verbose=verbose) | ||
|
||
|
||
def docstring_format_verbatim(text: t.Optional[str]) -> str: | ||
""" | ||
Format docstring to be displayed verbatim as a help text by Click. | ||
- https://click.palletsprojects.com/en/8.1.x/documentation/#preventing-rewrapping | ||
- https://github.com/pallets/click/issues/56 | ||
""" | ||
text = text or "" | ||
text = textwrap.dedent(text) | ||
lines = [line if line.strip() else "\b" for line in text.splitlines()] | ||
return "\n".join(lines) | ||
|
||
|
||
def make_command(cli, name, helpfun, aliases=None): | ||
""" | ||
Convenience shortcut for creating a subcommand. | ||
""" | ||
return cli.command( | ||
name, | ||
help=docstring_format_verbatim(helpfun.__doc__), | ||
context_settings={"max_content_width": 100}, | ||
aliases=aliases, | ||
) |
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 os | ||
import getpass | ||
from dotenv import load_dotenv, find_dotenv | ||
|
||
|
||
def getenvpass(env_var: str, prompt: str, skip_pytest_notebook: bool = True) -> str: | ||
""" | ||
Read variable from environment or `.env` file. | ||
If it is not defined, prompt interactively. | ||
FIXME: Needs a patch to make it work with `pytest-notebook`, | ||
see https://github.com/chrisjsewell/pytest-notebook/issues/43. | ||
""" | ||
load_dotenv(find_dotenv()) | ||
if env_var not in os.environ: | ||
if "PYTEST_CURRENT_TEST" in os.environ and skip_pytest_notebook: | ||
import pytest | ||
pytest.exit(f"{env_var} not given [skip-notebook]") | ||
else: | ||
os.environ[env_var] = getpass.getpass(prompt) | ||
return os.environ.get(env_var) |
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,32 @@ | ||
import importlib | ||
import logging | ||
import sys | ||
|
||
|
||
def setup_logging(level=logging.INFO, verbose: bool = False): | ||
try: | ||
importlib.import_module("colorlog") | ||
setup_logging_colorlog(level=level, verbose=verbose) | ||
except ImportError: | ||
setup_logging_standard(level=level, verbose=verbose) | ||
|
||
|
||
def setup_logging_standard(level=logging.INFO, verbose: bool = False): | ||
log_format = "%(asctime)-15s [%(name)-35s] %(levelname)-8s: %(message)s" | ||
logging.basicConfig(format=log_format, stream=sys.stderr, level=level) | ||
|
||
|
||
def setup_logging_colorlog(level=logging.INFO, verbose: bool = False): | ||
import colorlog | ||
from colorlog.escape_codes import escape_codes | ||
reset = escape_codes["reset"] | ||
log_format = f"%(asctime)-15s [%(name)-36s] %(log_color)s%(levelname)-8s:{reset} %(message)s" | ||
|
||
handler = colorlog.StreamHandler() | ||
handler.setFormatter(colorlog.ColoredFormatter(log_format)) | ||
|
||
logging.basicConfig(format=log_format, level=level, handlers=[handler]) | ||
|
||
|
||
def tweak_log_levels(): | ||
logging.getLogger("urllib3.connectionpool").setLevel(level=logging.INFO) |
Empty file.
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,17 @@ | ||
from click.testing import CliRunner | ||
|
||
from pueblo.cli import cli | ||
|
||
|
||
def test_version(): | ||
""" | ||
CLI test: Invoke `pueblo --version` | ||
""" | ||
runner = CliRunner() | ||
|
||
result = runner.invoke( | ||
cli, | ||
args="--version", | ||
catch_exceptions=False, | ||
) | ||
assert result.exit_code == 0 |
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,30 @@ | ||
import os | ||
from unittest import mock | ||
|
||
import pytest | ||
|
||
from pueblo.util.environ import getenvpass | ||
|
||
|
||
@pytest.fixture(scope="function", autouse=True) | ||
def init_testcase(): | ||
""" | ||
Make sure each test case uses a blank canvas. | ||
""" | ||
if "ACME_API_KEY" in os.environ: | ||
del os.environ["ACME_API_KEY"] | ||
|
||
|
||
def test_getenvpass_prompt(mocker): | ||
mocker.patch("getpass.getpass", return_value="foobar") | ||
retval = getenvpass("ACME_API_KEY", "really?", skip_pytest_notebook=False) | ||
assert retval == "foobar" | ||
|
||
|
||
def test_getenvpass_environ(): | ||
with \ | ||
mock.patch("getpass.getpass"), \ | ||
mock.patch("pytest.exit") as exit_mock: | ||
retval = getenvpass("ACME_API_KEY", "really?") | ||
assert retval is None | ||
exit_mock.assert_called_once_with("ACME_API_KEY not given [skip-notebook]",) |
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,16 @@ | ||
import sys | ||
|
||
from pueblo.util.logging import setup_logging, tweak_log_levels | ||
|
||
|
||
def test_setup_logging_default(): | ||
setup_logging() | ||
|
||
|
||
def test_setup_logging_no_colorlog(mocker): | ||
mocker.patch.dict(sys.modules, {"colorlog": None}) | ||
setup_logging() | ||
|
||
|
||
def test_tweak_log_levels(): | ||
tweak_log_levels() |
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 @@ | ||
from pueblo.testing.notebook import monkeypatch_pytest_notebook_treat_cell_exit_as_notebook_skip | ||
|
||
|
||
def test_monkeypatch_pytest_notebook_treat_cell_exit_as_notebook_skip(): | ||
monkeypatch_pytest_notebook_treat_cell_exit_as_notebook_skip() |