diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..9f0feb1 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,3 @@ +[run] +source = + checklog_odoo diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..60a0edb --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,46 @@ +--- +name: CI + +on: + push: + branches: ["main"] + tags: ["*"] + pull_request: + +jobs: + tests: + name: "Python ${{ matrix.python-version }}" + runs-on: ${{ matrix.machine }} + strategy: + fail-fast: false + matrix: + include: + - python-version: "3.7" + machine: ubuntu-22.04 + - python-version: "3.8" + machine: ubuntu-22.04 + - python-version: "3.9" + machine: ubuntu-22.04 + - python-version: "3.10" + machine: ubuntu-22.04 + - python-version: "3.11" + machine: ubuntu-22.04 + - python-version: "3.12" + machine: ubuntu-22.04 + steps: + - uses: "actions/checkout@v4" + - uses: "actions/setup-python@v4" + with: + python-version: "${{ matrix.python-version }}" + allow-prereleases: true + - name: "Install dependencies" + run: | + set -xe + python -VV + python -m site + python -m pip install --upgrade pip wheel + python -m pip install --upgrade tox tox-gh-actions + - name: "Run tox targets for ${{ matrix.python-version }}" + run: | + python -m tox + - uses: codecov/codecov-action@v3 diff --git a/.gitignore b/.gitignore index 68bc17f..10cc536 100644 --- a/.gitignore +++ b/.gitignore @@ -56,7 +56,6 @@ cover/ *.pot # Django stuff: -*.log local_settings.py db.sqlite3 db.sqlite3-journal diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..c0bd724 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,16 @@ +default_language_version: + python: python3 +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-toml + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.4.2 + hooks: + - id: ruff + - id: ruff-format diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 01b3519..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 baimont - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/checklog-odoo/LICENSE.txt b/LICENSE.txt similarity index 93% rename from checklog-odoo/LICENSE.txt rename to LICENSE.txt index 9f48452..ed47cb8 100644 --- a/checklog-odoo/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023-present Benoit Aimont +Copyright (c) 2017-present ACSONE SA/NV Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md index a8a1f43..5b2028a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,69 @@ # checklog-odoo + +[![PyPI - Version](https://img.shields.io/pypi/v/checklog-odoo.svg)](https://pypi.org/project/checklog-odoo) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/checklog-odoo.svg)](https://pypi.org/project/checklog-odoo) + +----- + + + Check if an odoo log file contains error, with the possibility to ignore some errors based on regular expressions. + +This project replaces the [acsoo](https://pypi.org/project/acsoo)'s checklog command. + + + +**Table of Contents** + +- [Installation](#installation) +- [Usage](#usage) +- [License](#license) + +## Installation + +```console +pipx install checklog-odoo +``` + +## Use examples + +```console +checklog-odoo odoo.log +unbuffer odoo -d mydb -i base --stop-after-init | checklog-odoo +checklog-odoo --ignore "WARNING.*blah" odoo.log +``` + +## Usage + +```console +Usage: checklog-odoo [OPTIONS] [FILENAME] + + Check an odoo log file for errors. When no filename or - is provided, read + from stdin. + +Options: + -i, --ignore REGEX Regular expression of log records to ignore. + --echo / --no-echo Echo the input file (default when reading + from stdin). + --err-if-empty / --no-err-if-empty + Exit with an error code if no log record is + found (default). + -v, --verbose + -c, --config FILE Configuration file [default: checklog-odoo.cfg] + --help Show this message and exit. +``` + +## Example of config file: + +The configuration file use the `ini` format: + +```ini +[checklog-odoo] +ignore= + WARNING + ERROR:.*registry +``` + +## License + +`checklog-odoo` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. diff --git a/checklog-odoo/README.md b/checklog-odoo/README.md deleted file mode 100644 index 97c07be..0000000 --- a/checklog-odoo/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# checklog-odoo - -[![PyPI - Version](https://img.shields.io/pypi/v/checklog-odoo.svg)](https://pypi.org/project/checklog-odoo) -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/checklog-odoo.svg)](https://pypi.org/project/checklog-odoo) - ------ - -**Table of Contents** - -- [Installation](#installation) -- [License](#license) - -## Installation - -```console -pip install checklog-odoo -``` - -## License - -`checklog-odoo` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. diff --git a/checklog-odoo/src/checklog_odoo/__about__.py b/checklog-odoo/src/checklog_odoo/__about__.py deleted file mode 100644 index 7fdf20b..0000000 --- a/checklog-odoo/src/checklog_odoo/__about__.py +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-FileCopyrightText: 2023-present Benoit Aimont -# -# SPDX-License-Identifier: MIT -__version__ = "0.0.1" diff --git a/checklog-odoo/src/checklog_odoo/__init__.py b/checklog-odoo/src/checklog_odoo/__init__.py deleted file mode 100644 index f750b9e..0000000 --- a/checklog-odoo/src/checklog_odoo/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-FileCopyrightText: 2023-present Benoit Aimont -# -# SPDX-License-Identifier: MIT diff --git a/checklog-odoo/src/checklog_odoo/__main__.py b/checklog-odoo/src/checklog_odoo/__main__.py deleted file mode 100644 index a6e828f..0000000 --- a/checklog-odoo/src/checklog_odoo/__main__.py +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-FileCopyrightText: 2023-present Benoit Aimont -# -# SPDX-License-Identifier: MIT -import sys - -if __name__ == "__main__": - from checklog_odoo.cli.checklog import checklog_odoo - - sys.exit(checklog_odoo()) diff --git a/checklog-odoo/src/checklog_odoo/cli/__init__.py b/checklog-odoo/src/checklog_odoo/cli/__init__.py deleted file mode 100644 index c484d21..0000000 --- a/checklog-odoo/src/checklog_odoo/cli/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-FileCopyrightText: 2023-present Benoit Aimont -# -# SPDX-License-Identifier: MIT - -from . import checklog - diff --git a/checklog-odoo/tests/__init__.py b/checklog-odoo/tests/__init__.py deleted file mode 100644 index f750b9e..0000000 --- a/checklog-odoo/tests/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-FileCopyrightText: 2023-present Benoit Aimont -# -# SPDX-License-Identifier: MIT diff --git a/checklog-odoo/tests/data/odoo/__init__.py b/checklog-odoo/tests/data/odoo/__init__.py deleted file mode 100644 index 5284146..0000000 --- a/checklog-odoo/tests/data/odoo/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__import__("pkg_resources").declare_namespace(__name__) diff --git a/checklog-odoo/tests/data/odoo/addons/__init__.py b/checklog-odoo/tests/data/odoo/addons/__init__.py deleted file mode 100644 index 5284146..0000000 --- a/checklog-odoo/tests/data/odoo/addons/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__import__("pkg_resources").declare_namespace(__name__) diff --git a/checklog-odoo/tests/data/odoo/addons/addon1/__manifest__.py b/checklog-odoo/tests/data/odoo/addons/addon1/__manifest__.py deleted file mode 100644 index 49eb6d3..0000000 --- a/checklog-odoo/tests/data/odoo/addons/addon1/__manifest__.py +++ /dev/null @@ -1,7 +0,0 @@ -# -*- coding: utf-8 -*- -{ - "author": "ACSONE SA/NV", - "version": "", - # TODO ... - "depends": ["base"], -} diff --git a/checklog-odoo/tests/data/odoo/addons/addon2/__init__.py b/checklog-odoo/tests/data/odoo/addons/addon2/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/checklog-odoo/tests/data/odoo/addons/addon2/__manifest__.py b/checklog-odoo/tests/data/odoo/addons/addon2/__manifest__.py deleted file mode 100644 index 825a34e..0000000 --- a/checklog-odoo/tests/data/odoo/addons/addon2/__manifest__.py +++ /dev/null @@ -1,2 +0,0 @@ -# -*- coding: utf-8 -*- -{"author": "ACSONE SA/NV", "license": "AGPL-3", "version": "", "depends": ["base"]} diff --git a/checklog-odoo/tests/data/test_checklog.cfg b/checklog-odoo/tests/data/test_checklog.cfg deleted file mode 100644 index 191ccf7..0000000 --- a/checklog-odoo/tests/data/test_checklog.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[checklog] -ignore=ERROR \ No newline at end of file diff --git a/checklog-odoo/tests/test_checklog.py b/checklog-odoo/tests/test_checklog.py deleted file mode 100644 index cc2a3cf..0000000 --- a/checklog-odoo/tests/test_checklog.py +++ /dev/null @@ -1,70 +0,0 @@ -# SPDX-FileCopyrightText: 2023-present Benoit Aimont -# SPDX-FileCopyrightText: 2023-present ACSONE -# -# SPDX-License-Identifier: MIT - -import os -import unittest - -from click.testing import CliRunner - -from checklog_odoo.cli.checklog import checklog_odoo - -DATA_DIR = os.path.join(os.path.dirname(__file__), "data") - - -class TestChecklog(unittest.TestCase): - def test1(self): - runner = CliRunner() - res = runner.invoke(checklog_odoo, [os.path.join(DATA_DIR, "test1.log")]) - assert res.exit_code != 0 - expected = "errors that caused failure (2):" - assert expected in res.output - - def test2(self): - runner = CliRunner() - res = runner.invoke( - checklog_odoo, ["--ignore", " ERROR ", os.path.join(DATA_DIR, "test1.log")] - ) - assert res.exit_code != 0 - expected = "errors that caused failure (1):" - assert expected in res.output - expected = "errors that did not cause failure (1):" - assert expected in res.output - - def test3(self): - runner = CliRunner() - res = runner.invoke( - checklog_odoo, - ["-i", " ERROR ", "-i", " CRITICAL ", os.path.join(DATA_DIR, "test1.log")], - ) - assert res.exit_code == 0 - expected = "errors that did not cause failure (2):" - assert expected in res.output - - def test4(self): - runner = CliRunner() - res = runner.invoke( - checklog_odoo, - [ - "-c", - os.path.join(DATA_DIR, "test_checklog.cfg"), - os.path.join(DATA_DIR, "test1.log"), - ], - ) - assert res.exit_code != 0 - expected = "errors that caused failure (1):" - assert expected in res.output - expected = "errors that did not cause failure (1):" - assert expected in res.output - - def test_empty(self): - runner = CliRunner() - res = runner.invoke(checklog_odoo, [os.path.join(DATA_DIR, "empty.log")]) - assert res.exit_code != 0 - expected = "No Odoo log record found in input." - assert expected in res.output - res = runner.invoke( - checklog_odoo, ["--no-err-if-empty", os.path.join(DATA_DIR, "empty.log")] - ) - assert res.exit_code == 0 diff --git a/checklog-odoo/pyproject.toml b/pyproject.toml similarity index 51% rename from checklog-odoo/pyproject.toml rename to pyproject.toml index 0540456..3e83214 100644 --- a/checklog-odoo/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,11 @@ [build-system] -requires = ["hatchling"] +requires = ["hatchling", "hatch-vcs"] build-backend = "hatchling.build" [project] name = "checklog-odoo" dynamic = ["version"] -description = '' +description = 'Check if an odoo log file contains error, with the possibility to ignore some errors based on regular expressions' readme = "README.md" requires-python = ">=3.7" license = "MIT" @@ -21,76 +21,42 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ] dependencies = [ "click", - "tomli", + "tomli ; python_version < '3.11'", ] [project.urls] -Documentation = "https://github.com/unknown/checklog-odoo#readme" -Issues = "https://github.com/unknown/checklog-odoo/issues" -Source = "https://github.com/unknown/checklog-odoo" +Documentation = "https://github.com/acsone/checklog-odoo#readme" +Issues = "https://github.com/acsone/checklog-odoo/issues" +Source = "https://github.com/acsone/checklog-odoo" [project.scripts] -checklog-odoo = "checklog_odoo.cli.checklog:checklog_odoo" +checklog-odoo = "checklog_odoo.checklog:checklog_odoo" -[tool.hatch.version] -path = "src/checklog_odoo/__about__.py" - -[tool.hatch.envs.default] -dependencies = [ - "coverage[toml]>=6.5", - "pytest", -] -[tool.hatch.envs.default.scripts] -test = "pytest {args:tests}" -test-cov = "coverage run -m pytest {args:tests}" -cov-report = [ - "- coverage combine", - "coverage report", -] -cov = [ - "test-cov", - "cov-report", +[project.optional-dependencies] +test = [ + "pytest", + "coverage[toml]", ] -[[tool.hatch.envs.all.matrix]] -python = ["3.7", "3.8", "3.9", "3.10", "3.11"] - -[tool.hatch.envs.lint] -detached = true -dependencies = [ - "black>=23.1.0", - "mypy>=1.0.0", - "ruff>=0.0.243", -] -[tool.hatch.envs.lint.scripts] -typing = "mypy --install-types --non-interactive {args:src/checklog_odoo tests}" -style = [ - "ruff {args:.}", - "black --check --diff {args:.}", -] -fmt = [ - "black {args:.}", - "ruff --fix {args:.}", - "style", -] -all = [ - "style", - "typing", -] +[tool.hatch.version] +source = "vcs" -[tool.black] -target-version = ["py37"] -line-length = 120 -skip-string-normalization = true +################################################################################### +# ruff +# [tool.ruff] target-version = "py37" line-length = 120 +fix = true + +[tool.ruff.lint] select = [ "A", "ARG", @@ -136,28 +102,23 @@ unfixable = [ [tool.ruff.isort] known-first-party = ["checklog_odoo"] -[tool.ruff.flake8-tidy-imports] -ban-relative-imports = "all" - [tool.ruff.per-file-ignores] # Tests can use magic values, assertions, and relative imports -"tests/**/*" = ["PLR2004", "S101", "TID252"] +"tests/**/*" = ["PLR2004", "S101"] + +################################################################################### +# coverage +# [tool.coverage.run] -source_pkgs = ["checklog_odoo", "tests"] branch = true -parallel = true -omit = [ - "src/checklog_odoo/__about__.py", -] +source_pkgs = ["checklog-odoo"] [tool.coverage.paths] -checklog_odoo = ["src/checklog_odoo", "*/checklog-odoo/src/checklog_odoo"] -tests = ["tests", "*/checklog-odoo/tests"] +source = ["src", ".tox/*/site-packages"] [tool.coverage.report] +show_missing = true exclude_lines = [ - "no cov", - "if __name__ == .__main__.:", - "if TYPE_CHECKING:", + "pragma: no cover", ] diff --git a/src/checklog_odoo/__init__.py b/src/checklog_odoo/__init__.py new file mode 100644 index 0000000..88b46b8 --- /dev/null +++ b/src/checklog_odoo/__init__.py @@ -0,0 +1,2 @@ +# +# SPDX-License-Identifier: MIT diff --git a/src/checklog_odoo/__main__.py b/src/checklog_odoo/__main__.py new file mode 100644 index 0000000..319051f --- /dev/null +++ b/src/checklog_odoo/__main__.py @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: 2023-present ACSONE +# +# SPDX-License-Identifier: MIT +import sys + +if __name__ == "__main__": + from checklog_odoo.checklog import checklog_odoo + + sys.exit(checklog_odoo()) diff --git a/checklog-odoo/src/checklog_odoo/cli/checklog.py b/src/checklog_odoo/checklog.py similarity index 66% rename from checklog-odoo/src/checklog_odoo/cli/checklog.py rename to src/checklog_odoo/checklog.py index 24afc6e..240d35e 100644 --- a/checklog-odoo/src/checklog_odoo/cli/checklog.py +++ b/src/checklog_odoo/checklog.py @@ -1,16 +1,14 @@ -# SPDX-FileCopyrightText: 2023-present Benoit Aimont +# SPDX-FileCopyrightText: 2023-present ACSONE # # SPDX-License-Identifier: MIT -from checklog_odoo.__about__ import __version__ - import logging import re import sys import click -from checklog_odoo.config import ChecklogConfig +from checklog_odoo.config import DEFAULT_CONFIG_FILE, ChecklogConfig _logger = logging.getLogger(__name__) @@ -32,9 +30,7 @@ def _render_errors(error_records, ignored_error_records): if ignored_error_records: msg.append( click.style( - "\nerrors that did not cause failure ({}):\n".format( - len(ignored_error_records) - ), + f"\nerrors that did not cause failure ({len(ignored_error_records)}):\n", bold=True, ) ) @@ -42,7 +38,7 @@ def _render_errors(error_records, ignored_error_records): if error_records: msg.append( click.style( - "\nerrors that caused failure ({}):\n".format(len(error_records)), + f"\nerrors that caused failure ({len(error_records)}):\n", bold=True, ) ) @@ -50,7 +46,7 @@ def _render_errors(error_records, ignored_error_records): return "".join(msg) -def do_checklog(filename, ignore, echo, err_if_empty=True): +def do_checklog(filename, ignore, echo, *, err_if_empty=True): ignore = [i for i in ignore if not i.startswith("#")] _logger.debug("ignored regular expressions:\n%s", "\n".join(ignore)) ignore_regexes = [re.compile(i, re.MULTILINE) for i in ignore] @@ -80,45 +76,44 @@ def _process_cur_rec(): if echo: click.echo(line, nl=False, color=True) sys.stdout.flush() - line = ANSI_CSI_RE.sub("", line) # strip ANSI colors - mo = LOG_START_RE.match(line) + line_nocolor = ANSI_CSI_RE.sub("", line) # strip ANSI colors + mo = LOG_START_RE.match(line_nocolor) if mo: reccount += 1 _process_cur_rec() cur_rec_mo = mo - cur_rec = [line] + cur_rec = [line_nocolor] else: - cur_rec.append(line) + cur_rec.append(line_nocolor) _process_cur_rec() # last record if not reccount and err_if_empty: - raise click.ClickException("No Odoo log record found in input.") + msg = "No Odoo log record found in input." + raise click.ClickException(msg) if error_records or ignored_error_records: msg = _render_errors(error_records, ignored_error_records) click.echo(msg) if error_records: - raise click.ClickException("Errors detected in log.") + msg = "Errors detected in log." + raise click.ClickException(msg) -class ColoredFormatter(logging.Formatter): - COLORS = { - "DEBUG": dict(dim=True), - "INFO": dict(), - "WARNING": dict(fg="yellow"), - "ERROR": dict(fg="red"), - "CRITICAL": dict(fg="white", bg="red"), +class ColoredFormatter(logging.Formatter): + COLORS = { # noqa: RUF012 + "DEBUG": {"dim": True}, + "INFO": {}, + "WARNING": {"fg": "yellow"}, + "ERROR": {"fg": "red"}, + "CRITICAL": {"fg": "white", "bg": "red"}, } def format(self, record): - res = super(ColoredFormatter, self).format(record) + res = super().format(record) return click.style(res, **self.COLORS[record.levelname]) -@click.command( - help="Check an odoo log file for errors. When no filename " - "or - is provided, read from stdin." -) -@click.version_option(version=__version__, prog_name="checklog-odoo") + +@click.command(help="Check an odoo log file for errors. When no filename or - is provided, read from stdin.") @click.option( "--ignore", "-i", @@ -134,26 +129,26 @@ def format(self, record): @click.option( "--err-if-empty/--no-err-if-empty", default=True, - help="Exit with an error code if no log record is found " "(default).", + help="Exit with an error code if no log record is found (default).", ) @click.option("-v", "--verbose", count=True) @click.option( "-c", "--config", type=click.Path(dir_okay=False, exists=True), - help="Configuration file (default: ./checklog.cfg).", + help=f"Configuration file [default: {DEFAULT_CONFIG_FILE}].", ) @click.argument("filename", type=click.Path(dir_okay=False), default="-") @click.pass_context def checklog_odoo(ctx, filename, config, ignore, verbose, echo, err_if_empty): config = ChecklogConfig(config) - ctx.obj = dict(config=config) + ctx.obj = {"config": config} ctx.default_map = config.get_default_map() - checklog_config = ctx.default_map.get('checklog') - default_ignore = checklog_config.get('ignore') + checklog_config = ctx.default_map.get("checklog") + default_ignore = checklog_config.get("ignore") if not ignore and default_ignore: ignore = default_ignore @@ -170,15 +165,16 @@ def checklog_odoo(ctx, filename, config, ignore, verbose, echo, err_if_empty): logger.setLevel(level) logger.addHandler(channel) - do_checklog(filename, ignore, echo, err_if_empty) + do_checklog(filename, ignore, echo, err_if_empty=err_if_empty) def _read_defaults(config): - section = "checklog" - defaults = dict( - ignore=config.getlist(section, "ignore", []), - echo=config.getboolean(section, "echo", None), - ) - return dict(checklog=defaults) - -ChecklogConfig.add_default_map_reader(_read_defaults) \ No newline at end of file + section = "checklog-odoo" + defaults = { + "ignore": config.getlist(section, "ignore", []), + "echo": config.getboolean(section, "echo", None), + } + return {"checklog": defaults} + + +ChecklogConfig.add_default_map_reader(_read_defaults) diff --git a/src/checklog_odoo/compat.py b/src/checklog_odoo/compat.py new file mode 100644 index 0000000..a983a17 --- /dev/null +++ b/src/checklog_odoo/compat.py @@ -0,0 +1,9 @@ +import sys + +__all__ = ["tomllib"] + + +if sys.version_info >= (3, 11): + import tomllib +else: + import tomli as tomllib diff --git a/checklog-odoo/src/checklog_odoo/config.py b/src/checklog_odoo/config.py similarity index 78% rename from checklog-odoo/src/checklog_odoo/config.py rename to src/checklog_odoo/config.py index adf2988..19d76c5 100644 --- a/checklog-odoo/src/checklog_odoo/config.py +++ b/src/checklog_odoo/config.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023-present Benoit Aimont +# SPDX-FileCopyrightText: 2023-present ACSONE # # SPDX-License-Identifier: MIT @@ -7,9 +7,10 @@ from pathlib import Path import click -import tomli -DEFAULT_CONFIG_FILE = "checklog.cfg" +from .compat import tomllib + +DEFAULT_CONFIG_FILE = "checklog-odoo.cfg" SECTION = "checklog" @@ -17,10 +18,9 @@ def _split_multiline(s): return [i.strip() for i in s.splitlines() if i.strip()] -class ChecklogConfig(object): - +class ChecklogConfig: # list of callables returning dictionaries to update default_map - default_map_readers = [] + default_map_readers = [] # noqa: RUF012 def __init__(self, filename): self.__cfg = RawConfigParser() @@ -28,15 +28,14 @@ def __init__(self, filename): filename = DEFAULT_CONFIG_FILE if filename: if not os.path.isfile(filename): - raise click.ClickException( - "Configuration file {} not found.".format(filename) - ) + msg = f"Configuration file {filename} not found." + raise click.ClickException(msg) self.__cfgfile = filename self.__cfg.read(filename) pyproject_path = Path("pyproject.toml") self.__pyproject = {} if pyproject_path.is_file(): - self.__pyproject = tomli.loads(pyproject_path.read_text()) + self.__pyproject = tomllib.loads(pyproject_path.read_text()) @staticmethod def add_default_map_reader(reader): @@ -48,7 +47,7 @@ def get_default_map(self): default_map.update(reader(self)) return default_map - def get(self, section, option, default=None, flatten=False): + def get(self, section, option, default=None, *, flatten=False): try: r = self.__cfg.get(section, option) if flatten: diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..d2d41bd --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2023-present ACSONE +# +# SPDX-License-Identifier: MIT diff --git a/checklog-odoo/tests/data/odoo/addons/addon1/__init__.py b/tests/data/empty.log similarity index 100% rename from checklog-odoo/tests/data/odoo/addons/addon1/__init__.py rename to tests/data/empty.log diff --git a/checklog-odoo/tests/data/test1.cfg b/tests/data/test1.cfg similarity index 100% rename from checklog-odoo/tests/data/test1.cfg rename to tests/data/test1.cfg diff --git a/tests/data/test1.log b/tests/data/test1.log new file mode 100644 index 0000000..ec13dd9 --- /dev/null +++ b/tests/data/test1.log @@ -0,0 +1,13 @@ +2017-05-21 14:01:49,686 8038 INFO ? odoo: Odoo version 10.0 +2017-05-21 14:01:49,687 8038 INFO ? odoo: addons paths: [u'/home/nobody/odoo-10.0/addons', u'/home/nobody/odoo/odoo/addons', u'/home/nobody/odoo/src/odoo10-addon-mail-environment/setup/mail_environment/odoo/addons', u'/home/nobody/odoo/src/odoo10-addon-mis-builder/setup/mis_builder/odoo/addons', u'/home/nobody/odoo/src/odoo10-addon-mis-builder-operating-unit/setup/mis_builder_operating_unit/odoo/addons', u'/home/nobody/odoo/src/odoo10-addon-server-environment-ir-config-parameter/setup/server_environment_ir_config_parameter/odoo/addons', u'/home/nobody/.virtualenvs/odoo-bscw/local/lib/python2.7/site-packages/odoo/addons', u'/home/nobody/.virtualenvs/odoo-bscw/lib/python2.7/site-packages/odoo/addons', '/home/nobody/odoo/src/odoo/odoo/addons'] +2017-05-21 14:01:49,687 8038 INFO ? odoo: database: default@default:default +2017-05-21 14:01:50,496 8038 INFO ? odoo.modules.loading: init db +2017-05-21 14:01:50,637 8038 ERROR ? odoo.modules.registry: Failed to load registry +Traceback (most recent call last): + ... +OSError: [Errno 2] No such file or directory: '/home/nobody/odoo-10.0/addons' +2017-05-21 14:01:50,637 8038 CRITICAL ? odoo.service.server: Failed to initialize database `odoo-bscw-testsuite-1`. +Traceback (most recent call last): + ... +2017-05-21 14:01:50,638 8038 INFO ? odoo.service.server: Initiating shutdown +2017-05-21 14:01:50,638 8038 INFO ? odoo.service.server: Hit CTRL-C again or send a second signal to force the shutdown. diff --git a/tests/data/test_checklog.cfg b/tests/data/test_checklog.cfg new file mode 100644 index 0000000..6f38941 --- /dev/null +++ b/tests/data/test_checklog.cfg @@ -0,0 +1,2 @@ +[checklog-odoo] +ignore=ERROR diff --git a/tests/test_checklog.py b/tests/test_checklog.py new file mode 100644 index 0000000..107fe42 --- /dev/null +++ b/tests/test_checklog.py @@ -0,0 +1,67 @@ +# SPDX-FileCopyrightText: 2023-present ACSONE +# +# SPDX-License-Identifier: MIT + +import os + +from click.testing import CliRunner + +from checklog_odoo.checklog import checklog_odoo + +DATA_DIR = os.path.join(os.path.dirname(__file__), "data") + + +def test1(): + runner = CliRunner() + res = runner.invoke(checklog_odoo, [os.path.join(DATA_DIR, "test1.log")]) + assert res.exit_code != 0 + expected = "errors that caused failure (2):" + assert expected in res.output + + +def test2(): + runner = CliRunner() + res = runner.invoke(checklog_odoo, ["--ignore", " ERROR ", os.path.join(DATA_DIR, "test1.log")]) + assert res.exit_code != 0 + expected = "errors that caused failure (1):" + assert expected in res.output + expected = "errors that did not cause failure (1):" + assert expected in res.output + + +def test3(): + runner = CliRunner() + res = runner.invoke( + checklog_odoo, + ["-i", " ERROR ", "-i", " CRITICAL ", os.path.join(DATA_DIR, "test1.log")], + ) + assert res.exit_code == 0 + expected = "errors that did not cause failure (2):" + assert expected in res.output + + +def test4(): + runner = CliRunner() + res = runner.invoke( + checklog_odoo, + [ + "-c", + os.path.join(DATA_DIR, "test_checklog.cfg"), + os.path.join(DATA_DIR, "test1.log"), + ], + ) + assert res.exit_code != 0 + expected = "errors that caused failure (1):" + assert expected in res.output + expected = "errors that did not cause failure (1):" + assert expected in res.output + + +def test_empty(): + runner = CliRunner() + res = runner.invoke(checklog_odoo, [os.path.join(DATA_DIR, "empty.log")]) + assert res.exit_code != 0 + expected = "No Odoo log record found in input." + assert expected in res.output + res = runner.invoke(checklog_odoo, ["--no-err-if-empty", os.path.join(DATA_DIR, "empty.log")]) + assert res.exit_code == 0 diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..5e93ed1 --- /dev/null +++ b/tox.ini @@ -0,0 +1,29 @@ +[gh-actions] +python = + 3.7: py37 + 3.8: py38 + 3.9: py39 + 3.10: py310 + 3.11: py311 + 3.12: py312 + +[tox] +isolated_build = True +use_develop = True +envlist = + py37 + py38 + py39 + py310 + py311 + py312 + +[testenv] +skip_missing_interpreters = True +extras = + test + metadata +commands = + coverage run -m pytest + coverage html + coverage xml