diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..762ee86 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,44 @@ +root = true + +# Language indent/EOL/charset overlap with .vscode/settings.json. +# Rulers, renderWhitespace, detectIndentation, mergeEditor, and +# language-server settings cannot be expressed here — they stay in +# settings.json. + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = tab +insert_final_newline = true +trim_trailing_whitespace = true + +# [json] +[*.json] +indent_size = 2 +indent_style = space + +# [markdown] +[*.md] +indent_size = 2 +indent_style = space + +# [python] +[*.py] +indent_size = 4 +indent_style = space + +# [shellscript] +[*.{bash,sh,zsh}] +indent_size = 2 +indent_style = space + +# [toml] +[*.toml] +indent_size = 2 +indent_style = tab + +# [yaml] +[*.{yaml,yml}] +indent_size = 2 +indent_style = space diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..990aa80 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +*.c linguist-language=C +*.cpp linguist-language=C++ +*.h linguist-language=C +*.hpp linguist-language=C++ +*.html linguist-detectable=false +*.py linguist-language=Python +*.sh linguist-detectable=false +*.vimrc linguist-detectable=false +makefile linguist-detectable=false diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 5c0eddc..c67dc9c 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -1,5 +1,9 @@ -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python +# This workflow will install Python dependencies, run tests, and smoke +# the sdist/wheel on a range of Python versions. +# +# Created: 14th September 2026 +# Updated: 14th September 2026 +# name: Python package @@ -8,16 +12,43 @@ on: branches: - master - dev - - gha - - time_str + - boilerplate + - idiomatic + - rc1 + - rc2 + - rc3 pull_request: - branches: - - master - - dev - - gha - - time_str + +permissions: + contents: read + +defaults: + run: + shell: bash jobs: + lint: + name: Lint (ruff) + runs-on: ubuntu-latest + + steps: + - name: Checking out code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install ruff + + - name: Lint with ruff + run: | + ruff check . + build: runs-on: ubuntu-latest @@ -34,29 +65,31 @@ jobs: - "3.14" steps: - - uses: actions/checkout@v4 + - name: Checking out code + uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install flake8 pytest - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - name: Lint with flake8 - run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Test with unittest - run: | - python3 -m unittest discover + python -m pip install pytest + python -m pip install -e . + - name: Test with pytest run: | pytest + - name: Build and install smoke + run: | + python -m pip install build + python -m build + python -m pip install dist/diagnosticism-*.whl + python -c "import diagnosticism; assert diagnosticism.__version__ == '0.18.1'; print(diagnosticism.__version__)" + build-py27: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 529ded2..75d68ed 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ # directories (by name) /.bin/ +/.pytest_cache/ +/.ruff_cache/ +/.venv/ build/ dist/ @@ -13,6 +16,8 @@ scratch/ # directories (by pattern) +**/__pycache__/ + # files (by name) @@ -30,4 +35,3 @@ notes.txt *.swp *.tmp *.zip - diff --git a/.sis/project_name.txt b/.sis/project_name.txt new file mode 100644 index 0000000..f16d210 --- /dev/null +++ b/.sis/project_name.txt @@ -0,0 +1 @@ +Diagnosticism.Python diff --git a/.sis/script_info_lines.txt b/.sis/script_info_lines.txt new file mode 100644 index 0000000..489731b --- /dev/null +++ b/.sis/script_info_lines.txt @@ -0,0 +1,3 @@ +Diagnosticism.Python provides basic diagnostic facilities for Python +Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems +Copyright (c) 2019, Matthew Wilson and Synesis Software diff --git a/.vimrc b/.vimrc index cca1736..d07167a 100644 --- a/.vimrc +++ b/.vimrc @@ -1,5 +1,57 @@ +" Synesis Python project .vimrc — aligned with .vscode/settings.json (Python) +set nocompatible +filetype indent plugin on +syntax enable +set autoindent +set backspace=indent,eol,start +set hlsearch +set incsearch +set number + +" files.insertFinalNewline +set eol +set fixeol + +" editor.renderWhitespace: all +set list +set listchars=tab:->,trail:-,extends:>,precedes:<,nbsp:+ + +" editor.detectIndentation: false — global defaults match [python] (editor.tabSize: 4, insertSpaces: true) +set colorcolumn=76 set expandtab set shiftwidth=4 +set softtabstop=4 set tabstop=4 +" colorcolumn draws a full-column tint in Vim (not a VS Code-style 1px line). +" Keep it subtle via the ColorColumn highlight group; reapply after colorscheme changes. +if has('termguicolors') + " set termguicolors +endif + +function! s:ConfigureColorColumn() abort + highlight ColorColumn ctermbg=236 guibg=#2a2a2a cterm=NONE gui=NONE +endfunction + +call s:ConfigureColorColumn() +autocmd ColorScheme * call s:ConfigureColorColumn() + +" files.trimTrailingWhitespace +autocmd BufWritePre * %s/\s\+$//e + +augroup sis_python + autocmd! + + " [json] / [markdown] / [yaml] + autocmd FileType json,markdown,yaml setlocal expandtab tabstop=2 shiftwidth=2 softtabstop=2 + + " [python] — match .vscode [python] tabSize 4 / insertSpaces true / rulers 50,60,76,120 + autocmd FileType python setlocal expandtab tabstop=4 shiftwidth=4 softtabstop=4 colorcolumn=50,60,76,120 + + " [shellscript] + autocmd FileType sh,bash,zsh setlocal expandtab tabstop=2 shiftwidth=2 softtabstop=2 colorcolumn=60,76 + + " [toml] + autocmd FileType toml setlocal noexpandtab tabstop=2 shiftwidth=2 softtabstop=2 +augroup END diff --git a/.vscode/settings.json b/.vscode/settings.json index e10f900..95b7da0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,13 +1,43 @@ { + "[json]": { + "editor.tabSize": 2, + }, + "[markdown]": { + "editor.insertSpaces": true, + "editor.tabSize": 2, + }, "[python]": { - "diffEditor.ignoreTrimWhitespace": false, - "editor.insertSpaces": true, - "editor.rulers": [ 51, 61, 76 ], - "editor.tabSize": 4, + "diffEditor.ignoreTrimWhitespace": false, + "editor.insertSpaces": true, + "editor.rulers": [ 50, 60, 76, 120 ], + "editor.tabSize": 4, + }, + "[shellscript]": { + "editor.insertSpaces": true, + "editor.rulers": [ 60, 76 ], + "editor.tabSize": 2, }, + "[toml]": { + "editor.insertSpaces": false, + "editor.tabSize": 2, + }, + "[yaml]": { + "editor.insertSpaces": true, + "editor.tabSize": 2, + }, + "editor.detectIndentation": false, "editor.insertSpaces": false, "editor.renderWhitespace": "all", - "editor.rulers": [ 76 ], + "editor.rulers": [ 76 ], "editor.tabSize": 2, + "files.exclude": { + "**/*.egg-info": true, + "**/*.swp": true, + "**/__pycache__": true, + }, + "files.insertFinalNewline": true, "files.trimTrailingWhitespace": true, + "git.mergeEditor": false, + "python.analysis.typeCheckingMode": "standard", + "python.languageServer": "Pylance", } diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000..8d930df --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,21 @@ +# Diagnosticism.Python - Authors + + +## Major Contributors + +| Name | GitHub | +| ----------- | --------------------------------- | +| Matt Wilson | [mwsis](https://github.com/mwsis) | + + +## Defect reports, fixes and suggestions (for which we are very grateful) + +| Name | GitHub | +| ------- | ------ | +| \ | | + + +Contributions are welcomed. + + + diff --git a/CHANGES.md b/CHANGES.md index 0a00cf2..3a86312 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,10 @@ -# **Diagnosticism.Python** Changes +# Diagnosticism.Python - Changes + + +## 0.18.1 - 14th September 2026 + +* packaging, documentation, and CI modernisation (PEP 621 hybrid, canonical CI, **ruff**, helper scripts); + ## 0.18.0 - 6th August 2026 @@ -201,10 +207,5 @@ * + project boilerplate -## previous versions - -None specified. - - diff --git a/EXAMPLES.md b/EXAMPLES.md index 869287c..5fb6169 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -1,4 +1,4 @@ -# Diagnosticism.Python Examples +# Diagnosticism.Python - Examples | Name | Source | Summary | | ---- | ------ | ------- | diff --git a/LICENSE b/LICENSE index c6ef6c3..ee64e2c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -BSD 3-Clause License - Diagnosticism.Python +Diagnosticism.Python - BSD 3-Clause License -Copyright (c) 2019-2025, Matthew Wilson and Synesis Information Systems +Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems All rights reserved. diff --git a/MANIFEST.in b/MANIFEST.in index 0dc986e..2aecab3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,3 @@ -include LICENSE README.md CHANGES.md EXAMPLES.md TODO.md +include AUTHORS.md CHANGES.md EXAMPLES.md LICENSE NEWS.md README.md TODO.md recursive-include examples *.py recursive-include tests *.py diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..12ef8c9 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,44 @@ +# Diagnosticism.Python - News + +| Date | News Item | Details | +| ------------------- | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | +| 14th September 2026 | [**Diagnosticism.Python** 0.18.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.18.1) | Packaging / CI modernisation | +| 6th August 2026 | [**Diagnosticism.Python** 0.18.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.18.0) | `function()` alias for `func()` | +| 10th July 2026 | [**Diagnosticism.Python** 0.17.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.17.0) | `DOOMGram` `to_mmm` / `to_nmmm` summaries | +| 27th June 2026 | [**Diagnosticism.Python** 0.16.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.16.0) | Added `nanoseconds_to_string()` | +| 27th June 2026 | [**Diagnosticism.Python** 0.15.3](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.15.3) | Public API, packaging, Python 2.7 restore | +| 27th August 2025 | [**Diagnosticism.Python** 0.15.2](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.15.2) | Project boilerplate and documentation | +| 24th August 2025 | [**Diagnosticism.Python** 0.15.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.15.1) | Python 3.8+ compatibility and CI | +| 13th August 2025 | [**Diagnosticism.Python** 0.15.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.15.0) | Added `asynctracefunc` decorator | +| 31st July 2025 | [**Diagnosticism.Python** 0.14.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.14.1) | Fixed `tracefunc` | +| 28th July 2025 | [**Diagnosticism.Python** 0.14.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.14.0) | Added `tracefunc` decorator | +| 25th July 2025 | [**Diagnosticism.Python** 0.13.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.13.0) | `file=` on `abort`/`log`/`report`/`trace`/`warn` | +| 25th July 2025 | [**Diagnosticism.Python** 0.12.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.12.1) | `DOOMScope` uses `perf_counter_ns()` | +| 20th July 2025 | [**Diagnosticism.Python** 0.12.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.12.0) | Added `DOOMGram` and `DOOMScope` | +| 19th July 2025 | [**Diagnosticism.Python** 0.11.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.11.1) | Fixed `warn()` when logging enabled | +| 18th July 2025 | [**Diagnosticism.Python** 0.11.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.11.0) | Env-var forms of `enable_logging` / `enable_tracing` | +| 17th July 2025 | [**Diagnosticism.Python** 0.10.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.10.1) | Fixed package error | +| 14th July 2025 | [**Diagnosticism.Python** 0.10.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.10.0) | Added `file`/`line`/`func` helpers and `dbg()` | +| 6th July 2025 | [**Diagnosticism.Python** 0.9.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.9.1) | Fixed `trace()` in module context | +| 20th August 2024 | [**Diagnosticism.Python** 0.9.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.9.0) | Added `parse_severity()` | +| 17th August 2024 | [**Diagnosticism.Python** 0.8.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.8.0) | Added `is_severity_logged()` | +| 16th August 2024 | [**Diagnosticism.Python** 0.7.3](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.7.3) | Fixed filter logic | +| 16th August 2024 | [**Diagnosticism.Python** 0.7.2](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.7.2) | ANSI sequences on Windows 11+ | +| 15th August 2024 | [**Diagnosticism.Python** 0.7.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.7.1) | `set_log_filter` export; Win10+ ANSI | +| 14th August 2024 | [**Diagnosticism.Python** 0.7.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.7.0) | Renamed `diagnosticism.log` → `logging` | +| 28th August 2020 | [**Diagnosticism.Python** 0.6.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.6.1) | Fixed `enable_tracing()` defect | +| 28th August 2020 | [**Diagnosticism.Python** 0.6.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.6.0) | Added `set_log_filter()` | +| 28th August 2020 | [**Diagnosticism.Python** 0.5.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.5.1) | More docs and parameter assertions | +| 13th August 2020 | [**Diagnosticism.Python** 0.5.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.5.0) | `log()` accepts message lambda | +| 13th August 2020 | [**Diagnosticism.Python** 0.4.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.4.1) | Added `warn()` and usage-prompt default | +| 11th July 2020 | [**Diagnosticism.Python** 0.4.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.4.0) | Renamed to **Diagnosticism.Python** | +| 8th July 2019 | [**Diagnosticism.Python** 0.3.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.3.1) | `abort()` stringifies message | +| 7th July 2019 | [**Diagnosticism.Python** 0.3.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.3.0) | Added `log` and `severity` modules | +| 7th July 2019 | [**Diagnosticism.Python** 0.2.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.2.1) | Fixed missing `report` export | +| 7th July 2019 | [**Diagnosticism.Python** 0.2.0](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.2.0) | Added `program_name` and `conrep` modules | +| 12th June 2019 | [**Diagnosticism.Python** 0.1.2](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.1.2) | Enhanced `trace()` for methods | +| 11th June 2019 | [**Diagnosticism.Python** 0.1.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.1.1) | Added `trace()` | +| 12th February 2019 | [**Diagnosticism.Python** 0.0.1](https://github.com/synesissoftware/Diagnosticism.Python/releases/tag/0.0.1) | Project boilerplate | + + + diff --git a/README.md b/README.md index a242c20..02a5c52 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Diagnosticism.Python +# Diagnosticism.Python Diagnosticism, for Python @@ -39,7 +39,7 @@ Diagnosticism, for Python - [Contribution guidelines](#contribution-guidelines) - [Dependencies](#dependencies) - [Efferent (fan-out)](#efferent-fan-out) - - [Development Dependencies](#development-dependencies) + - [Development Dependencies](#development-dependencies) - [Afferent (fan-in)](#afferent-fan-in) - [Related projects](#related-projects) - [License](#license) @@ -327,26 +327,21 @@ Defect reports, feature requests, and pull requests are welcome on https://githu ### Dependencies -**Diagnosticism.Python** has no (non-development) runtime dependencies. - #### Efferent (fan-out) -Libraries upon which **Diagnosticism.Python** depends: - None. -##### Development Dependencies +#### Development Dependencies * [**mock**](https://pypi.org/project/mock/) — required for running the unit-test suite on **Python 2.7**; #### Afferent (fan-in) -Projects that depend on **Diagnosticism.Python**: - * [**asynkio**](https://github.com/synesissoftware/asynkio/); +* [**libCLImate.Python**](https://github.com/synesissoftware/libCLImate.Python/); * [**libpath.Python**](https://github.com/synesissoftware/libpath.Python/); diff --git a/TODO.md b/TODO.md index 2d0a62c..06523d0 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,4 @@ -# TODO +# Diagnosticism.Python - TODO * [x] `DOOMGram` and `DOOMScope`; @@ -6,7 +6,7 @@ * [ ] Implicit and explicit facilities for inferring/obtaining logging/tracing levels from the environment; * [ ] Logging/tracing interacting with existing logging libraries; * [x] Separate out the file/line/function facilities from `#trace()`; -* [ ] Use **pyproject.toml**; +* [x] ~~~Use **pyproject.toml**~~~ - ✅; * [x] `warn()` can take multiple strings (a la **Ruby**'s `warn()`); * [ ] `set_log_filter()` to allow filter to be callable; * [ ] `log()` to work with standard logger; diff --git a/diagnosticism/__init__.py b/diagnosticism/__init__.py index 7dba055..3b90d8a 100644 --- a/diagnosticism/__init__.py +++ b/diagnosticism/__init__.py @@ -1,6 +1,6 @@ __author__ = 'Matt Wilson' -__copyright__ = 'Copyright 2019-2025, Synesis Information Systems, Copyright 2019, Synesis Software' +__copyright__ = 'Copyright 2019-2026, Synesis Information Systems, Copyright 2019, Synesis Software' __credits__ = [ 'Garth Lancaster', 'Matt Wilson', @@ -9,7 +9,7 @@ __license__ = 'BSD-3-Clause' __maintainer__ = 'Matt Wilson' __status__ = 'Beta' -__version__ = '0.18.0' +__version__ = '0.18.1' import sys @@ -67,15 +67,15 @@ fileline, filelinefunc, func, - function, + function as function, is_tracing_enabled, line, trace, ) if sys.version_info[:2] >= (3, 9): from .tracing import ( - asynctracefunc, - tracefunc, + asynctracefunc as asynctracefunc, + tracefunc as tracefunc, ) from .warning import warn @@ -114,6 +114,7 @@ 'fileline', 'filelinefunc', 'func', + 'function', 'get_program_name', 'is_logging_enabled', 'is_severity_logged', diff --git a/diagnosticism/internal/__init__.py b/diagnosticism/internal/__init__.py index 0e23ad6..e2cc8d0 100644 --- a/diagnosticism/internal/__init__.py +++ b/diagnosticism/internal/__init__.py @@ -1,6 +1,5 @@ import os -import platform import sys import time diff --git a/diagnosticism/severity.py b/diagnosticism/severity.py index 1563631..de40720 100644 --- a/diagnosticism/severity.py +++ b/diagnosticism/severity.py @@ -190,20 +190,20 @@ def _parse_verbosity( s = str(v) severity = _STRINGS_RECOGNISABLE_AS_SEVERITY_LEVELS.get(s, None) - if severity == None and not strict_case_comparison: + if severity is None and not strict_case_comparison: s_upper = s.upper() severity = _STRINGS_RECOGNISABLE_AS_SEVERITY_LEVELS.get(s_upper, None) - if severity != None: + if severity is not None: return severity try: return int(s) - except ValueError as x: + except ValueError: raise ValueError("could not recognise value '%s' as a severity" % (v)) diff --git a/diagnosticism/time_format/__init__.py b/diagnosticism/time_format/__init__.py index c36d888..32c52d1 100644 --- a/diagnosticism/time_format/__init__.py +++ b/diagnosticism/time_format/__init__.py @@ -6,7 +6,7 @@ # Purpose: Time formatting utilities. # # Created: 24th August 2025 -# Updated: 27th June 2026 +# Updated: 14th September 2026 # # Author: Matthew Wilson # @@ -47,7 +47,7 @@ _to_nmmm, ) else: - from ._fmt_py2 import ( + from ._fmt_py2 import ( # noqa: F401 _fmt, _to_mmm, _to_nmmm, @@ -90,17 +90,17 @@ def _scale_index(n): return (11, _SCALES[11]) - l = 0 - h = 11 + lo = 0 + hi = 11 count = 0 - while l <= h: + while lo <= hi: count += 1 assert count < 5, "too many loops while trying to scale %s" % n - m = (h + l) // 2 + m = (hi + lo) // 2 b = _SCALES[m] @@ -108,7 +108,7 @@ def _scale_index(n): return (m, b) if n < b: - h = m + hi = m continue @@ -117,12 +117,16 @@ def _scale_index(n): if n < b * 10: return (m, b) - l = m + lo = m return (11, _SCALES[11]) -from .nanoseconds import nanoseconds_to_string +# Import after `_fmt` / `_scale_index` / `_SUFFIXES` are bound: +# nanoseconds.py does `from . import (_fmt, _scale_index, _SUFFIXES)`, so a +# top-of-file import here would circular-import while this module is still +# partially initialised. +from .nanoseconds import nanoseconds_to_string as nanoseconds_to_string # noqa: E402 # ############################## end of file ############################# # diff --git a/diagnosticism/tracing.py b/diagnosticism/tracing.py index ede54d4..d024a93 100644 --- a/diagnosticism/tracing.py +++ b/diagnosticism/tracing.py @@ -12,7 +12,6 @@ ) import inspect -import sys _tracingEnabled = False @@ -54,10 +53,8 @@ def _dbg( file_name = code.co_filename line_number = fr.f_lineno - vnames = code.co_varnames - params = fr.f_locals - kwnames = list(kwargs) + list(kwargs) if 0 != len(args): s0 = ", ".join(["(%s)=%s" % (type(arg).__name__, arg) for arg in args]) diff --git a/diagnosticism/warning.py b/diagnosticism/warning.py index b291ddd..b372be6 100644 --- a/diagnosticism/warning.py +++ b/diagnosticism/warning.py @@ -10,7 +10,6 @@ ) from . import severity -import sys def _warn( diff --git a/examples/dbg.py b/examples/dbg.py index e9d76a4..d340227 100755 --- a/examples/dbg.py +++ b/examples/dbg.py @@ -6,7 +6,6 @@ enable_tracing, ) -import sys def f(): diff --git a/examples/trace.py b/examples/trace.py index 3aaad82..a020ebd 100755 --- a/examples/trace.py +++ b/examples/trace.py @@ -2,7 +2,6 @@ import diagnosticism as d -import sys def func1(): diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9663a1a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,92 @@ +[build-system] +build-backend = "setuptools.build_meta" +requires = [ + "setuptools>=61.0", +] + + +[project] +authors = [ + { email = "matthew@synesis.com.au", name = "Matt Wilson" }, +] +classifiers = [ + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", +] +dependencies = [ +] +description = "Basic diagnostic facilities, for Python" +keywords = [ + "diagnostic", + "diagnostics", + "logging", + "stopwatch", + "trace", + "tracing", +] +license = { text = "BSD-3-Clause" } +name = "diagnosticism" +readme = "README.md" +requires-python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*" +version = "0.18.1" + +[project.urls] +"Bug Tracker" = "https://github.com/synesissoftware/Diagnosticism.Python/issues" +Changelog = "https://github.com/synesissoftware/Diagnosticism.Python/blob/master/CHANGES.md" +Homepage = "https://github.com/synesissoftware/Diagnosticism.Python" +Repository = "https://github.com/synesissoftware/Diagnosticism.Python" + +[project.optional-dependencies] +dev = [ + "pytest", + "ruff>=0.4.0", +] + + +[tool.ruff] +line-length = 76 +target-version = "py38" + +[tool.ruff.lint] +ignore = [ + "E501", +] +select = [ + "E", + "F", +] + +[tool.ruff.lint.per-file-ignores] +"diagnosticism/contingent_reporting.py" = [ + "E711", + "E712", +] +"diagnosticism/logging.py" = [ + "E712", +] +"diagnosticism/tracing.py" = [ + "E712", +] + +[tool.setuptools.packages.find] +exclude = [ + "examples*", + "tests*", +] +include = [ + "diagnosticism", + "diagnosticism.*", +] diff --git a/run_all_unit_tests.sh b/run_all_unit_tests.sh index e91c1e5..b13bc2c 100755 --- a/run_all_unit_tests.sh +++ b/run_all_unit_tests.sh @@ -7,9 +7,9 @@ # calling directory # # Created: 13th February 2019 -# Updated: 2nd April 2024 +# Updated: 14th September 2026 # -# Copyright (c) Matthew Wilson, 2019-2024 +# Copyright (c) Matthew Wilson, 2019-2026 # All rights reserved # # Redistribution and use in Source and binary forms, with or without @@ -52,12 +52,182 @@ while [ -h "$Source" ]; do [[ $Source != /* ]] && Source="$Dir/$Source" done Dir="$(cd -P "$( dirname "$Source" )" && pwd)" +Basename="$(basename "$Source")" + +ProjectNameFile="$Dir/.sis/project_name.txt" +if [ -f "$ProjectNameFile" ]; then + ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +else + ProjectName=$(basename "$Dir") +fi + + +AssumePython2= +IncludePython2InSearch= +PythonCommandPath= + + +# regular command-line handling + + +while [[ $# -gt 0 ]] +do + + case "$1" in + + --assume-python2) + + AssumePython2=1 + ;; + --include-python2-in-search) + + IncludePython2InSearch=1 + ;; + --python-cmd-path|-p) + + shift + + PythonCommandPath=$1 + ;; + --help) + + [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" + echo + cat << EOF +USAGE: $Basename { | --help | [ --assume-python2 ] [ --include-python2-in-search ] [ --python-cmd-path | -p ] } + +${ProjectName} unit tests + +flags/options: + + --help + shows this help and terminates + + --assume-python2 + uses the python2 command when no -p / --python-cmd-path is given + + --include-python2-in-search + includes python2 in automatic interpreter discovery (after python3 and python) + + -p + --python-cmd-path + specifies explicitly the path of the Python command to be executed (rather than discover it) +EOF + + exit 0 + ;; + *) + + >&2 echo "unrecognised argument; use --help for usage" + + exit 1 + ;; + esac + + shift +done + + +# validate / discover python executable path + +if [ "x_$PythonCommandPath" = "x_" ] && [ ! -z "$AssumePython2" ]; then + + PythonCommandPath=python2 +fi + +if [ "x_$PythonCommandPath" != "x_" ]; then + + # check the given command + + if ! { [ -x "$PythonCommandPath" ] || command -v "$PythonCommandPath" > /dev/null; }; then + + >&2 echo "given python-cmd-path '$PythonCommandPath' is not executable" + + exit 1 + fi +else + + # try and find a suitable command + + # Prefer a project-local venv when present (avoids Apple/Xcode python3 on + # PATH, which must not be used for local install/test). + if [ "x_$PythonCommandPath" = "x_" ]; then + + if [ -x "$Dir/.venv/bin/python" ]; then + + PythonCommandPath="$Dir/.venv/bin/python" + + echo "found project venv python '$PythonCommandPath'" + fi + fi + + if [ "x_$PythonCommandPath" = "x_" ]; then + + if [ "y_$PYTHON_COMMAND_PATH" != "y_" ]; then + + if command -v "$PYTHON_COMMAND_PATH" > /dev/null; then + + PythonCommandPath=$PYTHON_COMMAND_PATH + fi + fi + fi + + if [ "x_$PythonCommandPath" = "x_" ]; then + + if [ "y_$PYTHON_CMD_PATH" != "y_" ]; then + + if command -v "$PYTHON_CMD_PATH" > /dev/null; then + + PythonCommandPath=$PYTHON_CMD_PATH + fi + fi + fi + + if [ "x_$PythonCommandPath" = "x_" ]; then + + PossiblePythonCommands=(python3 python) + + if [ ! -z "$IncludePython2InSearch" ]; then + + PossiblePythonCommands+=(python2) + fi + + for p in "${PossiblePythonCommands[@]}" + do + + if command -v "$p" > /dev/null; then + + PythonCommandPath=$p + + echo "found valid python command '$p'" + + break + fi + done + fi + + if [ "x_$PythonCommandPath" = "x_" ]; then + + if [ -z "$IncludePython2InSearch" ] && [ -z "$AssumePython2" ] && command -v python2 > /dev/null; then + + >&2 echo "only python2 was found on PATH; pass --include-python2-in-search to allow it during discovery, or --assume-python2 to use python2 explicitly" + + exit 1 + fi + + >&2 echo "no valid python command path discovered" + + exit 1 + fi +fi + + +# executing tests # This will operate recursively as long as each subdirectory of $Dir/tests # contains an __init__.py file (which may be empty) -python3 -m unittest discover "$Dir/tests" +"$PythonCommandPath" -m unittest discover -s "$Dir/tests" # ############################## end of file ############################# # - diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..2a9acf1 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 diff --git a/setup.py b/setup.py index f22c51b..de2ced1 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ name='diagnosticism', python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*', - version='0.18.0', + version='0.18.1', author='Matt Wilson', author_email='matthew@synesis.com.au', @@ -16,6 +16,7 @@ 'Natural Language :: English', "Operating System :: OS Independent", "Programming Language :: Python", + "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.8", @@ -35,6 +36,5 @@ 'examples', 'tests', ]), - url='https://github.com/synesissoftware/diagnosticism.Python', + url='https://github.com/synesissoftware/Diagnosticism.Python', ) - diff --git a/tests/test_log.py b/tests/test_log.py index 58c2c7f..8030e66 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -6,7 +6,16 @@ set_log_filter, ) from diagnosticism.program_name import set_program_name -from diagnosticism.severity import * +from diagnosticism.severity import ( + ALERT, + CRITICAL, + DEBUG0, + FAILURE, + INFORMATIONAL, + NOTICE, + VIOLATION, + WARNING, +) import unittest from unittest.mock import patch @@ -102,7 +111,7 @@ def test_threshold_filter(self): with patch('sys.stderr', new=StringIO()) as fake_stderr: - logging = enable_logging(True) + enable_logging(True) log_filter = set_log_filter(WARNING) @@ -136,7 +145,7 @@ def test_dict_filter_others_action_False(self): with patch('sys.stderr', new=StringIO()) as fake_stderr: - logging = enable_logging(True) + enable_logging(True) filter_dict = { @@ -178,7 +187,7 @@ def test_dict_filter_others_action_True(self): with patch('sys.stderr', new=StringIO()) as fake_stderr: - logging = enable_logging(True) + enable_logging(True) filter_dict = { diff --git a/tests/test_parse_severity.py b/tests/test_parse_severity.py index ebd9213..c4d0c1c 100644 --- a/tests/test_parse_severity.py +++ b/tests/test_parse_severity.py @@ -1,19 +1,11 @@ #! /usr/bin/env python3 -from diagnosticism.severity import * +from diagnosticism.severity import ( + WARNING, + parse_verbosity, +) import unittest -from unittest.mock import patch - -import re -import sys - -try: - - from StringIO import StringIO -except ImportError: - - from io import StringIO INTEGER_LEVELS = [ -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, ] @@ -63,9 +55,9 @@ def test_integer_levels_as_str_with_padding(self): def test_named_levels(self): - for (s, l) in NAMED_LEVELS.items(): + for (s, level) in NAMED_LEVELS.items(): - expected = l + expected = level actual = parse_verbosity(s) self.assertEqual(expected, actual) @@ -73,9 +65,9 @@ def test_named_levels(self): def test_named_levels_with_padding(self): - for (s, l) in NAMED_LEVELS.items(): + for (s, level) in NAMED_LEVELS.items(): - expected = l + expected = level actual = parse_verbosity("\t%s " % (s)) self.assertEqual(expected, actual) diff --git a/tests/test_trace.py b/tests/test_trace.py index 9cce851..c5bda12 100755 --- a/tests/test_trace.py +++ b/tests/test_trace.py @@ -14,7 +14,6 @@ import unittest from unittest.mock import patch -import re try: diff --git a/tests/test_warn.py b/tests/test_warn.py index 01d272f..bc639d4 100755 --- a/tests/test_warn.py +++ b/tests/test_warn.py @@ -24,7 +24,6 @@ set_program_name, ) -import re import unittest from unittest.mock import patch