Skip to content

Commit

Permalink
chore: Run mypy on the EDK (#49)
Browse files Browse the repository at this point in the history
* chore: Run mypy on the EDK

* Add quotes to `3.10`
  • Loading branch information
edgarrmondragon authored Jan 17, 2023
1 parent 4de38d6 commit bfcca27
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 22 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,27 @@ jobs:
- name: Test with pytest
run: |
poetry run pytest
mypy:
name: Static type checking with mypy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v4
with:
python-version: "3.10"

- name: Install Poetry
run: |
pipx install poetry
poetry --version
- name: Install dependencies
run: |
poetry env use 3.10
poetry install
- name: Run mypy
run: |
poetry run mypy --namespace-packages -p meltano.edk
2 changes: 1 addition & 1 deletion meltano/edk/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def describe(self) -> models.Describe:
"""
pass

def describe_formatted(
def describe_formatted( # type: ignore[return]
self, output_format: DescribeFormat = DescribeFormat.text
) -> str:
"""Return a formatted description of the extensions commands and capabilities.
Expand Down
56 changes: 45 additions & 11 deletions meltano/edk/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
import os
import sys
from typing import Callable

import structlog

Expand All @@ -18,7 +19,36 @@
DEFAULT_LEVEL = "info"


def parse_log_level(log_level: dict[str, int]) -> int:
def strtobool(val: str) -> bool:
"""Convert a string representation of truth to true (1) or false (0).
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
'val' is anything else.
Case is ignored in string comparisons.
Re-implemented from distutils.util.strtobool to avoid importing distutils.
Args:
val: The string to convert to a boolean.
Returns:
True if the string represents a truthy value, False otherwise.
Raises:
ValueError: If the string is not a valid representation of a boolean.
"""
val = val.lower()
if val in {"y", "yes", "t", "true", "on", "1"}:
return True
elif val in {"n", "no", "f", "false", "off", "0"}:
return False

raise ValueError(f"invalid truth value {val!r}")


def parse_log_level(log_level: str) -> int:
"""Parse a level descriptor into an logging level.
Args:
Expand All @@ -44,12 +74,18 @@ def default_logging_config(
levels: include levels in the log.
json_format: if True, use JSON format, otherwise use human-readable format.
"""
processors = []
processors: list[Callable] = []
if timestamps:
processors.append(structlog.processors.TimeStamper(fmt="iso"))
if levels:
processors.append(structlog.processors.add_log_level)

renderer: structlog.processors.JSONRenderer | structlog.dev.ConsoleRenderer = (
structlog.processors.JSONRenderer()
if json_format
else structlog.dev.ConsoleRenderer(colors=False)
)

processors.extend(
[
# If log level is too low, abort pipeline and throw away log entry.
Expand All @@ -65,9 +101,7 @@ def default_logging_config(
structlog.processors.UnicodeDecoder(),
structlog.processors.ExceptionPrettyPrinter(),
# Render the final event dict as JSON.
structlog.processors.JSONRenderer()
if json_format
else structlog.dev.ConsoleRenderer(colors=False),
renderer,
]
)

Expand Down Expand Up @@ -100,13 +134,13 @@ def pass_through_logging_config() -> None:
and MELTANO_LOG_JSON env vars.
"""
log_level = os.environ.get("LOG_LEVEL", "INFO")
log_timestamps = os.environ.get("LOG_TIMESTAMPS", False)
log_levels = os.environ.get("LOG_LEVELS", False)
meltano_log_json = os.environ.get("MELTANO_LOG_JSON", False)
log_timestamps = os.environ.get("LOG_TIMESTAMPS", "False")
log_levels = os.environ.get("LOG_LEVELS", "False")
meltano_log_json = os.environ.get("MELTANO_LOG_JSON", "False")

default_logging_config(
level=parse_log_level(log_level),
timestamps=log_timestamps,
levels=log_levels,
json_format=meltano_log_json,
timestamps=strtobool(log_timestamps),
levels=strtobool(log_levels),
json_format=strtobool(meltano_log_json),
)
26 changes: 17 additions & 9 deletions meltano/edk/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
import asyncio
import os
import subprocess
from typing import IO, Any
from typing import IO, Any, Union

import structlog

log = structlog.get_logger()
_ExecArg = Union[str, bytes]


def log_subprocess_error(
Expand Down Expand Up @@ -37,8 +38,8 @@ class Invoker:
def __init__(
self,
bin: str,
cwd: str = None,
env: dict[str, any] | None = None,
cwd: str | None = None,
env: dict[str, Any] | None = None,
) -> None:
"""Minimal invoker for running subprocesses.
Expand All @@ -53,7 +54,7 @@ def __init__(

def run(
self,
*args: str | bytes | os.PathLike[str] | os.PathLike[bytes],
*args: _ExecArg,
stdout: None | int | IO = subprocess.PIPE,
stderr: None | int | IO = subprocess.PIPE,
text: bool = True,
Expand Down Expand Up @@ -113,9 +114,9 @@ async def _log_stdio(reader: asyncio.streams.StreamReader) -> None:
async def _exec(
self,
sub_command: str | None = None,
*args: str | bytes | os.PathLike[str] | os.PathLike[bytes],
*args: _ExecArg,
) -> asyncio.subprocess.Process:
popen_args = []
popen_args: list[_ExecArg] = []
if sub_command:
popen_args.append(sub_command)
if args:
Expand All @@ -130,9 +131,16 @@ async def _exec(
env=self.popen_env,
)

streams: list[asyncio.streams.StreamReader] = []

if p.stderr:
streams.append(p.stderr)

if p.stdout:
streams.append(p.stdout)

results = await asyncio.gather(
asyncio.create_task(self._log_stdio(p.stderr)),
asyncio.create_task(self._log_stdio(p.stdout)),
*[asyncio.create_task(self._log_stdio(stream)) for stream in streams],
return_exceptions=True,
)

Expand All @@ -146,7 +154,7 @@ async def _exec(
def run_and_log(
self,
sub_command: str | None = None,
*args: str | bytes | os.PathLike[str] | os.PathLike[bytes],
*args: _ExecArg,
) -> None:
"""Run a subprocess and stream the output to the logger.
Expand Down
Empty file added meltano/py.typed
Empty file.
14 changes: 13 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ flake8-docstrings = "^1.6.0"

[tool.poetry.group.dev.dependencies]
copier = "^6.2.0"
types-pyyaml = "^6.0.12.2"

[tool.isort]
profile = "black"
Expand Down

0 comments on commit bfcca27

Please sign in to comment.