Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
07d2e8b
ci: rm unused
disafronov Apr 28, 2026
71794b0
chore: sync from template v2.1.0
Apr 28, 2026
6242bd8
ci: major
disafronov Apr 30, 2026
c92f9d8
build: Update Makefile to pin Python version
disafronov May 5, 2026
bace71b
build: Refactor test command to include coverage by default
disafronov May 5, 2026
c973fd7
build: Update Makefile to remove formatting step from all target
disafronov May 5, 2026
1c698ee
chore: Update pre-commit configuration
disafronov May 5, 2026
627999f
build: Update coverage configuration
disafronov May 5, 2026
3091460
test: Add test for cache hit on missing file in schema loader
disafronov May 5, 2026
662abe0
refactor: Optimize schema applier and loader
disafronov May 5, 2026
df2264e
refactor: schema applier to improve dict cleaning
disafronov May 5, 2026
07d1dda
refactor: error handling to use dictionaries instead of JSON strings
disafronov May 5, 2026
6715c92
refactor: Remove unused _is_leaf_node function and tests
disafronov May 5, 2026
d5fb4b4
ci: Update python versions in lint and test workflow
disafronov May 5, 2026
948a88c
build: Bump python version to 3.11
disafronov May 5, 2026
8965236
refactor: Remove inspect fallback for Python < 3.11
disafronov May 5, 2026
44d976f
ci: Update dependencies to latest versions
disafronov May 5, 2026
429759f
chore: Update pre-commit config to exclude helpers.py
disafronov May 5, 2026
f35bfb1
build: Update pre-commit config to fix test exclusion
disafronov May 5, 2026
5148040
build: Add markdownlint ignore file for changelog
disafronov May 5, 2026
208e840
test: Remove obsolete tests for Python 3.11 and earlier
disafronov May 5, 2026
c8ff073
refactor: Remove future annotations from modules
disafronov May 5, 2026
098a125
chore(release): 0.4.2-rc.1
semantic-release-bot May 5, 2026
60558a4
ci: pr
disafronov May 5, 2026
076ae98
test: Improve Test Isolation in Conftest
disafronov May 5, 2026
da60a41
docs: Update schema loader cache key and locking mechanism
disafronov May 5, 2026
79e58c0
docs: tighten inline comments; document _CompiledSchema fields
disafronov May 5, 2026
7ff2c6d
docs: restore os._exit(1) rationale in comment and README
disafronov May 5, 2026
21ba61d
fix: enforce non-empty strings for type and source fields
disafronov May 5, 2026
2b0af23
docs: Update type checking in README
disafronov May 5, 2026
9b041b5
ci: fix auto-pr-description workflow
disafronov May 5, 2026
7e9daf7
chore(release): 0.4.2-rc.2
semantic-release-bot May 5, 2026
1fdd2ec
feat!: promote to stable 1.0.0
semantic-release-bot May 5, 2026
d5e3ef7
chore(release): 1.0.0-rc.1
semantic-release-bot May 5, 2026
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
30 changes: 24 additions & 6 deletions .github/workflows/auto-pr-description.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@ on:
- reopened
branches:
- release
- main

jobs:
force-release-pr:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
if: github.event.pull_request.head.ref == 'main'
if: >-
github.event_name == 'pull_request' &&
!contains(github.event.pull_request.labels.*.name, 'dependencies') && (
(github.event.pull_request.base.ref == 'release' && github.event.pull_request.head.ref == 'main') ||
(github.event.pull_request.base.ref == 'main' && github.event.pull_request.head.ref != 'release')
)

steps:
- uses: actions/checkout@v6
Expand All @@ -27,7 +33,7 @@ jobs:
pull_request_url: ${{ github.event.pull_request.url }}
api_token: ${{ secrets.GITHUB_TOKEN }}

- name: Force overwrite PR title and body
- name: Update PR description and title
uses: actions/github-script@v9
env:
PR_BODY: ${{ steps.gen.outputs.pull_request_description }}
Expand All @@ -36,10 +42,22 @@ jobs:
script: |
const now = new Date().toISOString().slice(0,16).replace('T',' ');
const body = process.env.PR_BODY;
await github.rest.pulls.update({
const base = context.payload.pull_request.base.ref;
const head = context.payload.pull_request.head.ref;
const update = {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
title: `Release PR for ${now}`,
body: body
});
body,
};

// Branch roles come from `.releaserc.cjs`:
// - base `release`: stable releases
// - base `main`: prereleases ("rc")
if (base === 'release') {
update.title = `Release! PR for ${now}`;
} else if (base === 'main') {
update.title = `PR for ${now}`;
}

await github.rest.pulls.update(update);
2 changes: 1 addition & 1 deletion .github/workflows/lint_and_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
contents: read
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
python-version: ["3.11", "3.12", "3.13", "3.14"]
steps:
- name: Checkout
uses: actions/checkout@v6
Expand Down
66 changes: 0 additions & 66 deletions .github/workflows/release-stable.yaml

This file was deleted.

1 change: 1 addition & 0 deletions .github/workflows/semantic.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ name: Semantic
pull_request:
branches:
- main
- release
push:
branches:
- main
Expand Down
1 change: 1 addition & 0 deletions .markdownlintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CHANGELOG.md
13 changes: 6 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ repos:
args: [--allow-multiple-documents]
- id: check-toml
stages: [pre-commit]
- id: check-case-conflict
stages: [pre-commit]
- id: name-tests-test
stages: [pre-commit]
args: [--pytest-test-first]
exclude: ^(.*/)?tests/helpers\.py$

- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.46.0
Expand All @@ -46,13 +52,6 @@ repos:

- repo: local
hooks:
- id: make-format
name: make format (pre-commit)
stages: [pre-commit]
language: system
entry: make format
pass_filenames: false

- id: make-lint
name: make lint (pre-commit)
stages: [pre-commit]
Expand Down
1 change: 1 addition & 0 deletions .releaserc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = {
["@semantic-release/commit-analyzer", {
"preset": "conventionalcommits",
"releaseRules": [
{ "breaking": true, "release": "major" },
{ "type": "feat", "release": "minor" },
{ "type": "fix", "release": "patch" },
{ "type": "perf", "release": "patch" },
Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
## [1.0.0-rc.1](https://github.com/disafronov/python-logging-objects-with-schema/compare/v0.4.2-rc.2...v1.0.0-rc.1) (2026-05-05)

### ⚠ BREAKING CHANGES

* first stable release, no longer considered experimental

Signed-off-by: Dmitrii Safronov <zimniy@cyberbrain.cc>

### Features

* promote to stable 1.0.0 ([1fdd2ec](https://github.com/disafronov/python-logging-objects-with-schema/commit/1fdd2ec40dad460c0bddcf65c6961ef5357c878a))

## [0.4.2-rc.2](https://github.com/disafronov/python-logging-objects-with-schema/compare/v0.4.2-rc.1...v0.4.2-rc.2) (2026-05-05)

### Bug Fixes

* enforce non-empty strings for type and source fields ([21ba61d](https://github.com/disafronov/python-logging-objects-with-schema/commit/21ba61dfc7ca32de848c85f42ad39450f4bbb3cc))

## [0.4.2-rc.1](https://github.com/disafronov/python-logging-objects-with-schema/compare/v0.4.1...v0.4.2-rc.1) (2026-05-05)

## [0.4.1](https://github.com/disafronov/python-logging-objects-with-schema/compare/v0.4.0...v0.4.1) (2026-04-28)

## [0.4.1-rc.1](https://github.com/disafronov/python-logging-objects-with-schema/compare/v0.4.0...v0.4.1-rc.1) (2026-04-28)
Expand Down
14 changes: 7 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Python version is pinned via `.python-version` (used by uv and CI).
PYTHON_VERSION := $(shell tr -d '[:space:]' < .python-version)

# Variables
PYTEST_CMD = uv run python -m pytest -v
COVERAGE_OPTS = --cov --cov-report=term-missing --cov-report=html
Expand All @@ -15,7 +18,8 @@ help: ## Show this help message
# Development
install: ## Install dependencies
@echo "Installing dependencies..."
uv sync
uv python install $(PYTHON_VERSION)
uv sync --python $(PYTHON_VERSION)
@echo "Installing pre-commit hooks..."
uv run pre-commit install

Expand All @@ -33,16 +37,12 @@ dead-code: ## Check for dead code using vulture
uv run vulture

# Testing
test: ## Run tests
@echo "Running tests..."
$(PYTEST_CMD)

test-coverage: ## Run tests with coverage
test: ## Run tests with coverage
@echo "Running tests with coverage..."
$(PYTEST_CMD) $(COVERAGE_OPTS)

# Combined operations
all: format lint test dead-code ## Run format, lint, test, and dead-code check
all: lint test dead-code ## Run lint, test, and dead-code check
@echo "All checks completed successfully!"

# Maintenance
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ systems) can rely on a consistent log structure.
problem.
- Any mismatch between runtime values and the declared types (wrong types,
`None` values, disallowed list elements) is also treated as a data error.
Type checks use `type()`, not `isinstance()`: `bool` is **not** accepted
where `int` is declared, and `int` is **not** accepted where `float` is
declared. There are no implicit type conversions.
- All validation problems are aggregated and logged as a single ERROR message
**after** the log record has been emitted, ensuring 100% compatibility with
standard logger behavior (no exceptions are raised).
Expand Down Expand Up @@ -145,7 +148,9 @@ directory until it finds the file or reaches the filesystem root.
**Important**: If there are any problems with the schema (missing file, broken
JSON, invalid structure, etc.), the application is terminated after logging
schema problems to stderr. Schema validation happens when the first logger
instance is created.
instance is created. Termination uses `os._exit(1)` β€” bypassing `atexit`
handlers and `finally` blocks β€” to prevent any cleanup code from running
against a broken logger that was never fully registered.

**Note**: The schema is compiled once per process and cached. Schema changes
require an application restart to take effect. The library is thread-safe.
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ build-backend = "uv_build"

[project]
name = "logging-objects-with-schema"
version = "0.4.1"
version = "1.0.0rc1"
description = "Proxy logging wrapper that validates extra fields against a JSON schema."
readme = "README.md"
requires-python = ">=3.10"
requires-python = ">=3.11"
dependencies = [ ]
license = "Apache-2.0"
license-files = [ "LICENSE" ]
Expand All @@ -20,7 +20,6 @@ keywords = [
]
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
Expand Down Expand Up @@ -102,6 +101,7 @@ source = [ "src" ]
branch = true

[tool.coverage.report]
fail_under = 100
exclude_lines = [
"def __repr__",
"if self\\.debug",
Expand Down
2 changes: 0 additions & 2 deletions src/logging_objects_with_schema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
according to an application-defined JSON schema.
"""

from __future__ import annotations

from .schema_logger import SchemaLogger

__all__ = [
Expand Down
21 changes: 9 additions & 12 deletions src/logging_objects_with_schema/errors.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""Custom exception types used by logging_objects_with_schema."""

from __future__ import annotations

import json
from dataclasses import dataclass
from typing import Any


@dataclass
Expand Down Expand Up @@ -60,15 +60,12 @@ class _DataProblem:
- Redundant fields (fields not defined in the schema)

Attributes:
message: JSON string containing structured error information. The message
is always a valid JSON object with the following structure:
``{"field": "...", "error": "...", "value": "..."}``
All values are serialized via ``repr()`` for safety and consistency.
Examples:
- ``{"field": "'user_id'", "error": "'has type str, expected int'", "value": "'abc-123'"}``
- ``{"field": "'request_id'", "error": "'is None'", "value": "None"}``
- ``{"field": "'tags'", "error": "'is a list but contains elements with types dict; expected all elements to be of type str'", "value": "[{'key': 'color'}]"}`` # noqa: E501
- ``{"field": "'unknown_field'", "error": "'is not defined in schema'", "value": "'some_value'"}``
data: Dict containing structured error information with keys
``field``, ``error``, and ``value`` (all via ``repr()``).
"""

message: str
data: dict[str, Any]

@property
def message(self) -> str:
return json.dumps(self.data)
Loading
Loading