Repository: pytest-dev/pytest-reportlog (language: python)

Issue:
support .bz2/gz filters
this is a nice to have extension and comes as idea from the conventions of https://jsonlines.org/

for large test suites this likely will intensely reduce the amount of reports


Discussion / hints:

Fix patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 3b5b682..7fb5746 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -9,6 +9,12 @@ repos:
     hooks:
       - id: trailing-whitespace
       - id: end-of-file-fixer
+
+  -  repo: https://github.com/pre-commit/mirrors-mypy
+     rev: 'v1.2.0'
+     hooks:
+     -   id: mypy
+
   - repo: local
     hooks:
       - id: rst
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 173ac90..ebd4ca6 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,7 +1,8 @@
-next (tbd)
+Next (tbd)
 -------------------
 
 * `#16 <https://github.com/pytest-dev/pytest-reportlog/issues/15>`_: switch recommended file extension to `jsonl <https://jsonlines.org/>`__.
+* `#17 <https://github.com/pytest-dev/pytest-reportlog/issues/17>`_: add compression support for ``.bz2``/``.gz``/``.xz`` log file extensions.
 
 
 0.3.0 (2023-04-26)
diff --git a/README.rst b/README.rst
index 7baf9ff..7c254d4 100644
--- a/README.rst
+++ b/README.rst
@@ -43,6 +43,11 @@ that kind of report object. For future compatibility, consumers of the file shou
 they don't recognize, as well as ignore unknown properties/keys in JSON objects that they do know,
 as future pytest versions might enrich the objects with more properties/keys.
 
+Compression
+===========
+
+Common compression suffixes like `.gz`, `.bz2` and `.xz` will automatically use the requested compression format.
+The implementations from the python stdlib are used and must be enabled in the python builds.
 
 Example
 -------
diff --git a/tox.ini b/tox.ini
index e37995f..0fe18c4 100644
--- a/tox.ini
+++ b/tox.ini
@@ -4,6 +4,7 @@ envlist = py{37,38,39,310,311},linting
 [testenv]
 deps =
     pytest-xdist
+    typing-extensions # for the python3.7 compat imports in the tests
 commands =
     pytest -n1 tests
 


Test patch:
diff --git a/src/pytest_reportlog/plugin.py b/src/pytest_reportlog/plugin.py
index d03967f..ab9b9ec 100644
--- a/src/pytest_reportlog/plugin.py
+++ b/src/pytest_reportlog/plugin.py
@@ -1,5 +1,5 @@
 import json
-from typing import Dict, Any
+from typing import Dict, Any, TextIO
 
 from _pytest.pathlib import Path
 
@@ -31,13 +31,31 @@ def pytest_unconfigure(config):
         del config._report_log_plugin
 
 
+def _open_filtered_writer(log_path: Path) -> TextIO:
+    if log_path.suffix == ".gz":
+        import gzip
+
+        return gzip.open(log_path, "wt", encoding="UTF-8")
+    elif log_path.suffix == ".bz2":
+        import bz2
+
+        return bz2.open(log_path, "wt", encoding="UTF-8")
+    elif log_path.suffix == ".xz":
+        import lzma
+
+        return lzma.open(log_path, "wt", encoding="UTF-8")
+    else:
+        # line buffer for text mode to ease tail -f
+        return log_path.open("wt", buffering=1, encoding="UTF-8")
+
+
 class ReportLogPlugin:
     def __init__(self, config, log_path: Path):
         self._config = config
         self._log_path = log_path
 
         log_path.parent.mkdir(parents=True, exist_ok=True)
-        self._file = log_path.open("w", buffering=1, encoding="UTF-8")
+        self._file = _open_filtered_writer(log_path)
 
     def close(self):
         if self._file is not None:
diff --git a/tests/test_reportlog.py b/tests/test_reportlog.py
index 7ee0187..72d3813 100644
--- a/tests/test_reportlog.py
+++ b/tests/test_reportlog.py
@@ -1,10 +1,37 @@
 import json
 from collections import defaultdict
-
+from typing import TextIO
+import bz2, gzip, lzma, io
 import pytest
+from pathlib import Path
 from _pytest.reports import BaseReport
 
-from pytest_reportlog.plugin import cleanup_unserializable
+from pytest_reportlog.plugin import cleanup_unserializable, _open_filtered_writer
+
+from typing_extensions import Protocol, Literal
+
+
+class OpenerModule(Protocol):
+    def open(self, path: Path, mode: Literal["rt"]) -> TextIO:
+        ...
+
+
+@pytest.mark.parametrize(
+    "filename, opener_module",
+    [
+        ("test.jsonl", io),
+        ("test.unknown", io),
+        ("test.jsonl.gz", gzip),
+        ("test.jsonl.bz2", bz2),
+        ("test.jsonl.xz", lzma),
+    ],
+)
+def test_open_filtered(filename: str, opener_module: OpenerModule, tmp_path: Path):
+    path = tmp_path / filename
+    with _open_filtered_writer(path) as fp:
+        fp.write("test\n")
+    with opener_module.open(path, "rt") as fp2:
+        assert fp2.read() == "test\n"
 
 
 def test_basics(testdir, tmp_path, pytestconfig):