Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/branch_ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Non-Default Branch Push CI (Python)

on:
push:
branches-ignore: [ "main" ]
paths-ignore: ['README.md']

jobs:
branch-ci:
uses: openclimatefix/.github/.github/workflows/nondefault_branch_push_ci_python.yml@main
secrets: inherit
with:
containerfile: Containerfile
enable_linting: true
enable_typechecking: true
tests_folder: tests

11 changes: 11 additions & 0 deletions .github/workflows/merged_ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: Default Branch PR Merged CI

on:
pull_request:
types: ["closed"]
branches: [ "main" ]

jobs:
merged-ci:
uses: openclimatefix/.github/.github/workflows/default_branch_pr_merged_ci.yml@main
secrets: inherit
20 changes: 0 additions & 20 deletions .pre-commit-config.yaml

This file was deleted.

14 changes: 14 additions & 0 deletions Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM python:3.12-slim-bookworm
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

# Add repository code
WORKDIR /opt/app
COPY src /opt/app/src
COPY pyproject.toml /opt/app
COPY .git /opt/app/.git

RUN uv sync --no-dev

ENTRYPOINT ["uv", "run", "--no-dev"]
CMD ["ocf-template-cli"]

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ classifiers = [
"License :: OSI Approved :: MIT License",
]
dependencies = [
"loguru >= 0.7.3",
"numpy >= 1.23.2",
]

Expand All @@ -39,6 +40,7 @@ dev = [

[project.scripts]
# Put entrypoints in here
ocf-template-cli = "ocf_template.main:main"

[project.urls]
repository = "https://github.com/openclimatefix/ocf-python-template"
Expand Down
54 changes: 53 additions & 1 deletion src/ocf_template/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,53 @@
"""Source File"""
"""Setup logging configuration for the application."""

import json
import sys
import os
import loguru

def development_formatter(record: "loguru.Record") -> str:
"""Format a log record for development."""
return "".join((
"<green>{time:HH:mm:ss.SSS}</green> ",
"<level>{level:<7s}</level> [{file}:{line}] | <level>{message}</level> ",
"<green>{extra}</green>" if record["extra"] else "",
"\n{exception}",
))

def structured_formatter(record: "loguru.Record") -> str:
"""Format a log record as a structured JSON object."""
record["extra"]["serialized"] = json.dumps({
"timestamp": record["time"].strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
"severity": record["level"].name,
"message": record["message"],
"elapsed": record["elapsed"].total_seconds(),
"logging.googleapis.com/labels": {
"python_logger": record["name"],
},
"logging.googleapis.com/sourceLocation": {
"file": record["file"].name,
"line": record["line"],
"function": record["function"],
},
} | record["extra"])
return "{extra[serialized]}\n"

# Define the logging formatter, removing the default one
loguru.logger.remove(0)
if sys.stdout.isatty():
# Simple logging for development
loguru.logger.add(
sys.stdout, format=development_formatter, diagnose=True,
level=os.getenv("LOGLEVEL", "DEBUG"), backtrace=True, colorize=True,
)
else:
# JSON logging for containers
loguru.logger.add(
sys.stdout, format=structured_formatter,
level=os.getenv("LOGLEVEL", "INFO").upper(),
)

# Uncomment and change the list to quieten external libraries
# for logger in ["aiobotocore", "cfgrib"]:
# logging.getLogger(logger).setLevel(logging.WARNING)

47 changes: 47 additions & 0 deletions src/ocf_template/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Entrypoint to the application."""

from importlib.metadata import PackageNotFoundError, version

from loguru import logger as log

try:
__version__ = version(__package__)
except PackageNotFoundError:
__version__ = "v?"

def main() -> None:
"""Entrypoint to the application.

Thanks to the `script` section in `pyproject.toml`, this function is
automatically executed when the package is run as a script via
`ocf-template-cli`. Change name in pyproject to something more suitable
for your package!
"""
log.info("Hello, world!")
log.info("Adding extra context to this log to help with debugging: ", version=__version__)

log.info("You can also contextualize logs in bulk to save having to provide it repeatedly!")
filenames: list[str] = [f"file_{i}.txt" for i in range(5)]
log.debug("Might be some spooky numbers in these files!", num_files=len(filenames))
for filename in filenames:
with log.contextualize(filename=filename):
log.debug("Trying to find spooky numbers in file {filename}")
if "4" in filename:
log.warning("Spooky number found!")
else:
log.debug("No spooky numbers found")

log.info("Different log levels are also nicely formatted")
for i, level in enumerate(["DEBUG", "INFO", "WARNING", "ERROR"]):
log.log(level, f"This is a {level} log message", index=i)

log.info("Consider wrapping your main in a try/except block to handle exceptions gracefully")
try:
y: int = int(3 / 0)
log.warning("This will never be logged", y=y)
except ZeroDivisionError as e:
log.opt(exception=e).error("Caught exception")

log.info("And if you run this in a container, you'll see structured logs instead!")
log.info("Goodbye, world!")

7 changes: 7 additions & 0 deletions tests/test_something.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import unittest


class TestSomething(unittest.TestCase):
def test_something(self) -> None:
self.assertEqual(1, 1)

Loading