From c721375c5240132637eae2e2b40780c1bcae2394 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Sun, 13 Sep 2026 10:29:52 +1000 Subject: [PATCH 01/13] chore(python): establish canonical .sis/ project identity files Add .sis/project_name.txt and .sis/script_info_lines.txt across all 10 Python repositories to define canonical short project names and script information headers: - asynkio; - CLASP.Python; - Diagnosticism.Python; - libCLImate.Python; - libpath.Python; - p99.Python; - py2be; - Quench.Python; - recls.Python; - woad.Python; --- .sis/project_name.txt | 1 + .sis/script_info_lines.txt | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 .sis/project_name.txt create mode 100644 .sis/script_info_lines.txt diff --git a/.sis/project_name.txt b/.sis/project_name.txt new file mode 100644 index 0000000..3c41ce8 --- /dev/null +++ b/.sis/project_name.txt @@ -0,0 +1 @@ +CLASP.Python diff --git a/.sis/script_info_lines.txt b/.sis/script_info_lines.txt new file mode 100644 index 0000000..3019c63 --- /dev/null +++ b/.sis/script_info_lines.txt @@ -0,0 +1,3 @@ +CLASP.Python is a Command-Line Argument Sorting and Parsing library for Python +Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems +Copyright (c) 2019, Matthew Wilson and Synesis Software From c68d5a13231f8a4fa4fc08147e57dff2958335d4 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Sun, 13 Sep 2026 13:46:08 +1000 Subject: [PATCH 02/13] docs: complete AUTHORS.md and NEWS.md for all Python projects --- AUTHORS.md | 21 +++++++++++++++++++++ NEWS.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 AUTHORS.md create mode 100644 NEWS.md diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000..dbe93f1 --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,21 @@ +# CLASP.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/NEWS.md b/NEWS.md new file mode 100644 index 0000000..a2bca52 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,33 @@ +# CLASP.Python - News + +| Date | News Item | Details | +| ---------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | +| 28th July 2026 | [**CLASP.Python** 0.8.12](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.12) | Fixed falsey option defaults / `MissingValueException` | +| 27th July 2026 | [**CLASP.Python** 0.8.11](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.11) | Packaging & CI (Python 2.7–3.14) | +| 27th July 2026 | [**CLASP.Python** 0.8.10.1](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.10.1) | Test arg order and string-literal canonicalisation | +| 25th July 2025 | [**CLASP.Python** 0.8.10](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.10) | `info_lines` `None`; OFF/ON bool conversion | +| 17th August 2024 | [**CLASP.Python** 0.8.9](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.9) | Python 3 examples and misc tidying | +| 24th August 2020 | [**CLASP.Python** 0.8.8](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.8) | Option default-value processing fix | +| 24th August 2020 | [**CLASP.Python** 0.8.7](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.7) | Option value processing fix | +| 17th August 2020 | [**CLASP.Python** 0.8.6](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.6) | Duplicate-alias warning in `show_usage()` | +| 13th August 2020 | [**CLASP.Python** 0.8.5](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.5) | Python 3 `long` compatibility | +| 12th August 2020 | [**CLASP.Python** 0.8.4](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.4) | `bool` `value_type` for `OptionSpecification` | +| 12th August 2020 | [**CLASP.Python** 0.8.3](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.3) | `long` `value_type` for `OptionSpecification` | +| 12th August 2020 | [**CLASP.Python** 0.8.2](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.2) | `str` `value_type` for `OptionSpecification` | +| 11th August 2020 | [**CLASP.Python** 0.8.1](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.1) | `Flag`/`Option` equality with specifications | +| 6th August 2020 | [**CLASP.Python** 0.8.0](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.0) | `on_multiple` and duplicate-option exceptions | +| 4th August 2020 | [**CLASP.Python** 0.7.1](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.7.1) | Various tidyings | +| 22nd August 2019 | [**CLASP.Python** 0.7.0](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.7.0) | Added `clasp.section()` | +| 22nd August 2019 | [**CLASP.Python** 0.6.1](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.6.1) | Fixed `get_first_unused_flag_or_option()` | +| 19th August 2019 | [**CLASP.Python** 0.6.0](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.6.0) | Optional `id` constraint on unused lookups | +| 16th July 2019 | [**CLASP.Python** 0.5.3](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.5.3) | More flexible `flags_and_options` / `values_string` | +| 16th July 2019 | [**CLASP.Python** 0.5.2](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.5.2) | Spec-aware `lookup_flag` / `lookup_option` | +| 12th June 2019 | [**CLASP.Python** 0.5.1](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.5.1) | More flexible `show_usage()` options | +| 25th April 2019 | [**CLASP.Python** 0.5.0](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.5.0) | `OptionArgument.given_value` and typed `value_type` | +| 25th April 2019 | [**CLASP.Python** 0.4.2](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.4.2) | Correct specs on flag/option arguments | +| 25th April 2019 | [**CLASP.Python** 0.4.1](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.4.1) | Exception message typo fix | +| 17th April 2019 | [**CLASP.Python** 0.4.0](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.4.0) | Alias → Specification rename | +| 17th April 2019 | [**CLASP.Python** 0.3.1](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.3.1) | Program-name and `show_usage`/`show_version` fixes | + + + From 186fd4cf6b9853cafa7fe6d4a7ee2b6fc41392c7 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Sun, 13 Sep 2026 13:56:51 +1000 Subject: [PATCH 03/13] docs(python): align README Dependencies, taglines, and version narratives Polish active Python library docs for standardisation: - move py2be tagline before badge row; - normalise Dependencies to Efferent / Development Dependencies / Afferent; - populate CLASP.Python Dependencies and Diagnosticism afferent consumers; - sync libCLImate.Python package version to 0.0.1 with CHANGES/NEWS; - remove CHANGES previous-versions placeholders and asynkio TODO T.B.C. --- CHANGES.md | 9 ++------- EXAMPLES.md | 2 +- README.md | 21 ++++++++++++++++++++- examples/duplicate_aliases.md | 2 +- examples/flag_and_option_specifications.md | 2 +- examples/multiple_options.md | 2 +- examples/sections.md | 2 +- examples/show_usage_and_version.md | 2 +- examples/typed_options.md | 2 +- 9 files changed, 29 insertions(+), 15 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 19ff324..f2214ef 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,9 +1,9 @@ -# **CLASP.Python** Changes +# CLASP.Python - Changes ## 0.8.12 - 28th July 2026 -* fixed falsy default values causing `MissingValueException` for option arguments; +* fixed falsey default values causing `MissingValueException` for option arguments; * fixed failure message to be passed to `MissingValueException`; @@ -164,10 +164,5 @@ * ~ ensured internal methods named with leading underscore; -## previous versions - -None specified. - - diff --git a/EXAMPLES.md b/EXAMPLES.md index 048eab9..84fd37f 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -1,4 +1,4 @@ -# CLASP.Python Examples +# CLASP.Python - Examples |Name|Source & Description|Summary| |---|---|---| diff --git a/README.md b/README.md index 25a5166..b4c232a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# CLASP.Python +# CLASP.Python **C**ommand-**L**ine **A**rgument **S**orting and **P**arsing, for Python ![Language](https://img.shields.io/badge/Python-3776AB?style=flat&logo=python&logoColor=white) @@ -21,6 +21,9 @@ - [Where to get help](#where-to-get-help) - [Contribution guidelines](#contribution-guidelines) - [Dependencies](#dependencies) + - [Efferent (fan-out)](#efferent-fan-out) + - [Development Dependencies](#development-dependencies) + - [Afferent (fan-in)](#afferent-fan-in) - [Related projects](#related-projects) - [License](#license) @@ -82,6 +85,22 @@ Defect reports, feature requests, and pull requests are welcome on https://githu ### Dependencies +#### Efferent (fan-out) + +None. + + +#### Development Dependencies + +None (currently). + + +#### Afferent (fan-in) + +* [**libCLImate.Python**](https://github.com/synesissoftware/libCLImate.Python/) + (**libclimate**); + + ### Related projects * [**CLASP**](https://github.com/synesissoftware/CLASP/) diff --git a/examples/duplicate_aliases.md b/examples/duplicate_aliases.md index d94404d..062ca74 100644 --- a/examples/duplicate_aliases.md +++ b/examples/duplicate_aliases.md @@ -1,4 +1,4 @@ -# CLASP.Python Example - **duplicate_aliases** +# CLASP.Python - Example - **duplicate_aliases** ## Summary diff --git a/examples/flag_and_option_specifications.md b/examples/flag_and_option_specifications.md index bd62916..596e48a 100644 --- a/examples/flag_and_option_specifications.md +++ b/examples/flag_and_option_specifications.md @@ -1,4 +1,4 @@ -# CLASP.Python Example - **flag_and_option_specifications** +# CLASP.Python - Example - **flag_and_option_specifications** ## Summary diff --git a/examples/multiple_options.md b/examples/multiple_options.md index cda9e6c..c48206e 100644 --- a/examples/multiple_options.md +++ b/examples/multiple_options.md @@ -1,4 +1,4 @@ -# CLASP.Python Example - **multiple_options** +# CLASP.Python - Example - **multiple_options** ## Summary diff --git a/examples/sections.md b/examples/sections.md index dbd9f4f..6e1f737 100644 --- a/examples/sections.md +++ b/examples/sections.md @@ -1,4 +1,4 @@ -# CLASP.Python Example - **sections** +# CLASP.Python - Example - **sections** ## Summary diff --git a/examples/show_usage_and_version.md b/examples/show_usage_and_version.md index 2fc5d2b..8160e45 100644 --- a/examples/show_usage_and_version.md +++ b/examples/show_usage_and_version.md @@ -1,4 +1,4 @@ -# CLASP.Python Example - **show_usage_and_version** +# CLASP.Python - Example - **show_usage_and_version** ## Summary diff --git a/examples/typed_options.md b/examples/typed_options.md index 7b11e44..6c0006d 100644 --- a/examples/typed_options.md +++ b/examples/typed_options.md @@ -1,4 +1,4 @@ -# CLASP.Python Example - **typed_options** +# CLASP.Python - Example - **typed_options** ## Summary From ac4a231cae60a2da8dda09c9e84d08249933853d Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Sun, 13 Sep 2026 14:11:36 +1000 Subject: [PATCH 04/13] chore(python): standardise editor and VCS boilerplate across projects Align .gitattributes, .gitignore, .editorconfig, .vimrc, and .vscode/settings.json across all 10 Python projects: - Linguist attributes for Python / shell / vimrc; - Synesis Python ignore set including .venv and tool caches; - EditorConfig matching .vscode indent/EOL/newline rules (no trailing-blank-line policy); - Vim and VS Code python rulers unified at [ 50, 60, 76, 120 ]. --- .editorconfig | 44 ++++++++++++++++++++++++++++++++++++ .gitattributes | 9 +++++--- .gitignore | 12 ++++++++-- .vimrc | 52 ++++++++++++++++++++++++++++++++++++++++++- .vscode/settings.json | 21 ++++++++++++----- 5 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 .editorconfig 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 index bd35ebe..990aa80 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,9 @@ - -*.py linguist-language=Python - +*.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/.gitignore b/.gitignore index 5077863..75d68ed 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ # directories (by name) /.bin/ +/.pytest_cache/ +/.ruff_cache/ +/.venv/ build/ dist/ @@ -9,14 +12,20 @@ dist-old/ scratch/ *.egg-info/ -**/__pycache__/ + # directories (by pattern) +**/__pycache__/ + + # files (by name) .DS_Store +notes.txt + + # files (by pattern) *~ @@ -26,4 +35,3 @@ scratch/ *.swp *.tmp *.zip - diff --git a/.vimrc b/.vimrc index d2759e2..d07167a 100644 --- a/.vimrc +++ b/.vimrc @@ -1,7 +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 -set tw=0 +" 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 e548495..95b7da0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,31 +2,42 @@ "[json]": { "editor.tabSize": 2, }, + "[markdown]": { + "editor.insertSpaces": true, + "editor.tabSize": 2, + }, "[python]": { "diffEditor.ignoreTrimWhitespace": false, "editor.insertSpaces": true, - "editor.rulers": [ 60, 76 ], + "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.tabSize": 2, - "files.insertFinalNewline": true, - "files.trimTrailingWhitespace": true, - "git.mergeEditor": false, "files.exclude": { - "**/__pycache__": true, "**/*.egg-info": true, "**/*.swp": true, + "**/__pycache__": true, }, "files.insertFinalNewline": true, "files.trimTrailingWhitespace": true, + "git.mergeEditor": false, "python.analysis.typeCheckingMode": "standard", "python.languageServer": "Pylance", } From 21e1ae797ddfdc82f2199e625ad011c149d7c987 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Mon, 14 Sep 2026 07:57:30 +1000 Subject: [PATCH 05/13] chore(python): adopt PEP 621 hybrid packaging for Dual-tier libraries Add pyproject.toml alongside setup.py/setup.cfg so Dual-tier Python libraries keep Python 2.7 installability while modern tooling uses PEP 621: - migrate CLASP.Python, Diagnosticism.Python, and py2be to hybrid form; - add universal wheel setup.cfg; sync classifiers through 3.14; - fix CLASP.Python project URL casing; verify libCLImate and woad hybrids. --- pyproject.toml | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ setup.cfg | 2 ++ setup.py | 5 +++-- 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 pyproject.toml create mode 100644 setup.cfg diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ffd3911 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,58 @@ +[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 = "Command-Line Argument Sorting and Parsing, for Python" +keywords = [ + "cli", + "command-line", + "parsing", +] +license = { text = "BSD-3-Clause" } +name = "pyclasp" +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.8.12" + +[project.urls] +"Bug Tracker" = "https://github.com/synesissoftware/CLASP.Python/issues" +Changelog = "https://github.com/synesissoftware/CLASP.Python/blob/master/CHANGES.md" +Homepage = "https://github.com/synesissoftware/CLASP.Python" +Repository = "https://github.com/synesissoftware/CLASP.Python" + + +[tool.setuptools.packages.find] +exclude = [ + "examples*", + "tests*", +] +include = [ + "pyclasp", + "pyclasp.*", +] 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 d394e62..6371f9f 100644 --- a/setup.py +++ b/setup.py @@ -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", @@ -24,6 +25,7 @@ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ], description="Command-Line Argument Sorting and Parsing, for Python", keywords="Command-line CLI parsing", @@ -34,6 +36,5 @@ "examples", "tests", ]), - url="https://github.com/synesissoftware/clasp.Python", + url="https://github.com/synesissoftware/CLASP.Python", ) - From b4a334f38d857f3ddcc712d682693cdd65880a8d Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Mon, 14 Sep 2026 08:01:43 +1000 Subject: [PATCH 06/13] chore(python): complete MANIFEST.in for active library sdists Ensure active Python libraries package AUTHORS, NEWS, and other standard docs in sdists: - refresh MANIFEST.in for asynkio, CLASP.Python, Diagnosticism.Python, py2be; - add MANIFEST.in for libCLImate.Python and woad.Python; - fix missing final newline on CLASP.Python MANIFEST.in. --- MANIFEST.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 49dcc19..a86fba4 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 *.md -recursive-include tests *.py \ No newline at end of file +recursive-include tests *.py From e6df89a13564d5946811ac283d2a9199c63260bf Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Mon, 14 Sep 2026 08:06:36 +1000 Subject: [PATCH 07/13] chore(python): sync dist helper scripts across active libraries Align packaging helper scripts for active Python libraries: - fix CLASP.Python egg-info cleanup to pyclasp.egg-info; - add build_dist.sh and build_dist_uv.sh for woad.Python; - remove obsolete asynkio/build_egg.sh and empty py2be/requirements.txt. --- build_dist.sh | 2 +- build_dist_uv.sh | 2 +- run_all_unit_tests.sh | 198 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 199 insertions(+), 3 deletions(-) diff --git a/build_dist.sh b/build_dist.sh index 01169ed..da1f01f 100755 --- a/build_dist.sh +++ b/build_dist.sh @@ -4,7 +4,7 @@ set -e cd "$(dirname "$0")" -rm -rf build/ dist/ clasp.egg-info/ +rm -rf build/ dist/ pyclasp.egg-info/ python3 -m build python3 -m twine check dist/* diff --git a/build_dist_uv.sh b/build_dist_uv.sh index c83abfe..dafa759 100755 --- a/build_dist_uv.sh +++ b/build_dist_uv.sh @@ -8,7 +8,7 @@ if [ ! -d .venv ]; then uv venv fi -rm -rf build/ dist/ clasp.egg-info/ +rm -rf build/ dist/ pyclasp.egg-info/ uv pip install build twine .venv/bin/python -m build diff --git a/run_all_unit_tests.sh b/run_all_unit_tests.sh index 982f990..ff9ebbf 100755 --- a/run_all_unit_tests.sh +++ b/run_all_unit_tests.sh @@ -1,5 +1,49 @@ #! /bin/bash +# ######################################################################## # +# File: run_all_unit_tests.sh +# +# Purpose: Executes the unit-tests of a Python project regardless of +# calling directory +# +# Created: 13th February 2019 +# Updated: 11th August 2026 +# +# Copyright (c) Matthew Wilson, 2019-2026 +# All rights reserved +# +# Redistribution and use in Source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# ######################################################################## # + + +# constants + Source="${BASH_SOURCE[0]}" while [ -h "$Source" ]; do @@ -8,7 +52,159 @@ while [ -h "$Source" ]; do [[ $Source != /* ]] && Source="$Dir/$Source" done Dir="$(cd -P "$( dirname "$Source" )" && pwd)" +Basename="$(basename "$Source")" + + +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) + + cat << EOF +USAGE: $Basename { | --help | [ --assume-python2 ] [ --include-python2-in-search ] [ --python-cmd-path | -p ] } + +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 + + 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 "$Dir/tests/run_unittest.py" +"$PythonCommandPath" -m unittest discover -s "$Dir/tests" + + +# ############################## end of file ############################# # From 55b739630741e13e1159390ea66bc68c76da3058 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Mon, 14 Sep 2026 08:15:36 +1000 Subject: [PATCH 08/13] ci(python): standardise python-package.yml push and PR triggers Align active Python libraries to the Synesis canonical CI branch set: - on.push.branches: master, dev, boilerplate, idiomatic, rc1, rc2, rc3; - unfiltered pull_request:; - drop ad-hoc branches and path globs from asynkio and Dual workflows. --- .github/workflows/python-package.yml | 8 ++++---- tests/test_typed_values.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 60ae215..5b4b3ab 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -9,11 +9,11 @@ on: - master - dev - boilerplate + - idiomatic + - rc1 + - rc2 + - rc3 pull_request: - branches: - - master - - dev - - boilerplate jobs: build: diff --git a/tests/test_typed_values.py b/tests/test_typed_values.py index 3ca005b..12a9369 100755 --- a/tests/test_typed_values.py +++ b/tests/test_typed_values.py @@ -476,7 +476,7 @@ def test_flags_of_flags_and_options_combined(self): self.assertEqual(0, len(args.values)) - def test_reproduce_falsy_default_issue(self): + def test_reproduce_falsey_default_issue(self): specifications = ( clasp.option( @@ -505,7 +505,7 @@ def test_reproduce_falsy_default_issue(self): self.assertEqual('bybit', exchange_option.value) - def test_reproduce_falsy_default_issue_with_default_used(self): + def test_reproduce_falsey_default_issue_with_default_used(self): specifications = ( clasp.option( From b0343a2ef0b89688c97a7de1a3bb78c1d8167518 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Mon, 14 Sep 2026 08:21:10 +1000 Subject: [PATCH 09/13] ci(python): modernise python-package.yml jobs and wheel smoke Bring active Python CI jobs in line with the libCLImate/woad gold form: - setup-python@v5, editable install, pytest on 3.x; - wheel build/install smoke asserting package __version__; - retain Dual build-py27 container jobs with legacy unittest paths; - add asynkio wheel smoke on the 3.11+ matrix. --- .github/workflows/python-package.yml | 41 ++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 5b4b3ab..f1c7f54 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 @@ -15,6 +19,13 @@ on: - rc3 pull_request: +permissions: + contents: read + +defaults: + run: + shell: bash + jobs: build: @@ -32,24 +43,30 @@ 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 - - name: Lint with flake8 + python -m pip install pytest + python -m pip install -e . + + - name: Test with pytest 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 + pytest + + - name: Build and install smoke run: | - python tests/run_unittest.py + python -m pip install build + python -m build + python -m pip install dist/pyclasp-*.whl + python -c "import pyclasp; assert pyclasp.__version__ == '0.8.12'; print(pyclasp.__version__)" build-py27: From 712af3d1bc688f041fd0aa9a69182bde5e7c8ff5 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Mon, 14 Sep 2026 08:36:07 +1000 Subject: [PATCH 10/13] test(python): standardise run_all_unit_tests.sh with .sis identity Align active Python libraries on the misc-dev-scripts unit-test helper: - refresh or add run_all_unit_tests.sh in all six active projects; - load .sis/project_name.txt and show script_info_lines.txt on --help; - use unittest discover -s tests (pytest preference for asynkio); - update the misc-dev-scripts Python template to match. --- run_all_unit_tests.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/run_all_unit_tests.sh b/run_all_unit_tests.sh index ff9ebbf..87f347d 100755 --- a/run_all_unit_tests.sh +++ b/run_all_unit_tests.sh @@ -7,7 +7,7 @@ # calling directory # # Created: 13th February 2019 -# Updated: 11th August 2026 +# Updated: 14th September 2026 # # Copyright (c) Matthew Wilson, 2019-2026 # All rights reserved @@ -54,6 +54,13 @@ 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= @@ -84,9 +91,13 @@ do ;; --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 From 48c577b75ad7f52c8ef6ac83da3df6d8b0271ebf Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Mon, 14 Sep 2026 08:59:32 +1000 Subject: [PATCH 11/13] chore(python): prefer project .venv in run_all_unit_tests.sh discovery Discover `$Dir/.venv/bin/python` before PATH so bare helper runs avoid Apple/Xcode python3 (too old for asynkio; unsuitable for local install/test). --- run_all_unit_tests.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/run_all_unit_tests.sh b/run_all_unit_tests.sh index 87f347d..b13bc2c 100755 --- a/run_all_unit_tests.sh +++ b/run_all_unit_tests.sh @@ -149,6 +149,18 @@ 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 From 1ababcec92d138a4144c0cde01fe68df9d611ea9 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Mon, 14 Sep 2026 09:13:03 +1000 Subject: [PATCH 12/13] ci(clasp): add failing ruff lint job for Dual-tier CI Introduce ruff as the Dual lint gate for CLASP.Python: - add [tool.ruff] (py38, E/F; E501 ignored pending wrap pass) and an optional extra; - add a Lint (ruff) job on python-package.yml before the build matrix; - apply low-churn E/F fixes (re-exports, type/`is`, None checks, unused imports, semicolons) so ruff check . exits 0; --- .github/workflows/python-package.yml | 22 +++++++++++++++++ examples/sections.py | 4 ++-- examples/show_usage_and_version.py | 4 ++-- examples/typed_options.py | 2 +- pyclasp/__init__.py | 36 +++++++++++++++++++++------- pyclasp/arguments.py | 18 +++++++------- pyclasp/cli.py | 11 ++++----- pyclasp/option_argument.py | 35 +++++++++++++++------------ pyclasp/option_specification.py | 2 +- pyclasp/util.py | 6 ++--- pyproject.toml | 19 +++++++++++++++ tests/test_arguments.py | 1 - tests/test_options.py | 4 ---- tests/test_sections.py | 1 - tests/test_typed_values.py | 2 -- 15 files changed, 111 insertions(+), 56 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index f1c7f54..050da48 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -27,6 +27,28 @@ defaults: 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 diff --git a/examples/sections.py b/examples/sections.py index 67db8ef..5bc6130 100755 --- a/examples/sections.py +++ b/examples/sections.py @@ -56,7 +56,7 @@ # Check for any unrecognised flags or options -unused = args.get_first_unused_flag_or_option(); +unused = args.get_first_unused_flag_or_option() if (unused): sys.stderr.write("%s: unrecognised flag/option: %s\n" % (args.program_name, unused)) @@ -64,6 +64,6 @@ sys.exit(1) -sys.stdout.write("no flags specified\n"); +sys.stdout.write("no flags specified\n") diff --git a/examples/show_usage_and_version.py b/examples/show_usage_and_version.py index 9e44c7c..67a6fb8 100755 --- a/examples/show_usage_and_version.py +++ b/examples/show_usage_and_version.py @@ -42,7 +42,7 @@ # Check for any unrecognised flags or options -unused = args.get_first_unused_flag_or_option(); +unused = args.get_first_unused_flag_or_option() if (unused): sys.stderr.write("%s: unrecognised flag/option: %s\n" % (args.program_name, unused)) @@ -50,5 +50,5 @@ sys.exit(1) -sys.stdout.write("no flags specified\n"); +sys.stdout.write("no flags specified\n") diff --git a/examples/typed_options.py b/examples/typed_options.py index bee3dda..0ca8b05 100755 --- a/examples/typed_options.py +++ b/examples/typed_options.py @@ -61,7 +61,7 @@ # Check for any unrecognised flags or options -unused = args.get_first_unused_flag_or_option(); +unused = args.get_first_unused_flag_or_option() if (unused): sys.stderr.write("%s: unrecognised flag/option: %s\n" % (args.program_name, unused)) diff --git a/pyclasp/__init__.py b/pyclasp/__init__.py index 30977d0..8747c82 100644 --- a/pyclasp/__init__.py +++ b/pyclasp/__init__.py @@ -12,18 +12,36 @@ __status__ = "Beta" __version__ = "0.8.12" -from .exceptions import * -from .flag_specification import FlagSpecification, flag -from .flag_specification import HelpFlag, VersionFlag -from .option_specification import OptionSpecification, option -from .section_specification import SectionSpecification, section -from .specification import Specification, specification +from .exceptions import ( + CLASPException as CLASPException, + DuplicateFlagSpecified as DuplicateFlagSpecified, + DuplicateOptionSpecified as DuplicateOptionSpecified, + IntegerOutOfRangeException as IntegerOutOfRangeException, + InvalidBooleanException as InvalidBooleanException, + InvalidIntegerException as InvalidIntegerException, + InvalidNumberException as InvalidNumberException, + InvalidValueException as InvalidValueException, + MissingValueException as MissingValueException, + ParsingException as ParsingException, + ValueParsingException as ValueParsingException, +) +from .flag_specification import FlagSpecification as FlagSpecification +from .flag_specification import HelpFlag as HelpFlag +from .flag_specification import VersionFlag as VersionFlag +from .flag_specification import flag as flag +from .option_specification import OptionSpecification as OptionSpecification +from .option_specification import option as option +from .section_specification import SectionSpecification as SectionSpecification +from .section_specification import section as section +from .specification import Specification as Specification +from .specification import specification as specification from .arguments import Arguments -from .flag_argument import FlagArgument as Flag -from .option_argument import OptionArgument as Option +from .flag_argument import FlagArgument as Flag # noqa: F401 +from .option_argument import OptionArgument as Option # noqa: F401 -from .cli import show_usage, show_version +from .cli import show_usage as show_usage +from .cli import show_version as show_version import sys diff --git a/pyclasp/arguments.py b/pyclasp/arguments.py index 93aaa61..93a7ef5 100644 --- a/pyclasp/arguments.py +++ b/pyclasp/arguments.py @@ -88,7 +88,7 @@ def flag_is_specified(self, id): `True` if the given flag (name, or instance) has been specified; `False` otherwise """ - return None != self.lookup_flag(id); + return None is not self.lookup_flag(id) def lookupFlag(self, id): @@ -109,10 +109,10 @@ def lookup_flag(self, id): if False: pass - elif FlagSpecification == type(id): + elif type(id) is FlagSpecification: name = id.name - elif OptionSpecification == type(id): + elif type(id) is OptionSpecification: name = id.name else: @@ -148,10 +148,10 @@ def lookup_option(self, id): if False: pass - elif FlagSpecification == type(id): + elif type(id) is FlagSpecification: name = id.name - elif OptionSpecification == type(id): + elif type(id) is OptionSpecification: name = id.name else: @@ -282,10 +282,10 @@ def _id_matches(flag_or_option, id): if False: pass - elif FlagSpecification == type(id): + elif type(id) is FlagSpecification: name = id.name - elif OptionSpecification == type(id): + elif type(id) is OptionSpecification: name = id.name else: @@ -544,7 +544,7 @@ def _parse(argv, specifications): elif isinstance(spec, (tuple, )): soa = spec[0] - fa = spec[1] + _ = spec[1] # Unused v = spec[2] option = OptionArgument(arg, index, arg, soa.name, soa, len(hyphens), given_label, v, extras) @@ -607,7 +607,7 @@ def _parse(argv, specifications): if is_option: - if not value and value != None: + if not value and value is not None: if alias_has_value: diff --git a/pyclasp/cli.py b/pyclasp/cli.py index 5748592..4ce7828 100644 --- a/pyclasp/cli.py +++ b/pyclasp/cli.py @@ -7,7 +7,6 @@ from .option_specification import OptionSpecification from .section_specification import SectionSpecification -import os import re import sys @@ -88,7 +87,7 @@ def show_usage(specifications, **kwargs): if __debug__: - if specifications == None: + if specifications is None: raise TypeError("`specifications` may not be `None`") elif isinstance(specifications, (list, tuple, )): @@ -134,7 +133,7 @@ def show_usage(specifications, **kwargs): info_lines = [ info_lines ] - info_lines = [ _generate_version_string(argv, options) if l in ( ':version', ':version:' ) else _info_line(l) for l in info_lines ] + info_lines = [ _generate_version_string(argv, options) if line in ( ':version', ':version:' ) else _info_line(line) for line in info_lines ] flags_and_options = _ensure_single_space_prefix(flags_and_options) values = _ensure_single_space_prefix(values) @@ -226,7 +225,7 @@ def show_usage(specifications, **kwargs): stream.write("\n") - if None != exit_code: + if None is not exit_code: sys.exit(exit_code) @@ -246,7 +245,7 @@ def show_version(specifications, **kwargs): if __debug__: - if specifications == None: + if specifications is None: raise TypeError("`specifications` may not be `None`") elif isinstance(specifications, (list, tuple, )): @@ -276,7 +275,7 @@ def show_version(specifications, **kwargs): stream.write("%s\n" % version) - if None != exit_code: + if None is not exit_code: sys.exit(exit_code) diff --git a/pyclasp/option_argument.py b/pyclasp/option_argument.py index 67ab05f..1ded31d 100644 --- a/pyclasp/option_argument.py +++ b/pyclasp/option_argument.py @@ -1,5 +1,10 @@ -from .exceptions import * +from .exceptions import ( + InvalidBooleanException, + InvalidIntegerException, + InvalidNumberException, + MissingValueException, +) from .option_specification import OptionSpecification from .util import _SUPPORT_long, _long_type @@ -95,39 +100,39 @@ def _set_value(self, value, from_ctor=False): if False: pass - elif arg_spec.value_type == bool: + elif arg_spec.value_type is bool: try: value = _parse_to_bool(given_value) - except ValueError as x: + except ValueError: raise InvalidBooleanException("the '%s' option's value '%s' cannot be interpreted as boolean" % (self.name, given_value)) - elif arg_spec.value_type == float: + elif arg_spec.value_type is float: try: value = float(given_value) - except ValueError as x: + except ValueError: raise InvalidNumberException("the '%s' option's value '%s' cannot be interpreted as a number" % (self.name, given_value)) - elif arg_spec.value_type == int: + elif arg_spec.value_type is int: try: value = int(given_value) - except ValueError as x: + except ValueError: raise InvalidIntegerException("the '%s' option's value '%s' cannot be interpreted as an integer" % (self.name, given_value)) - elif _SUPPORT_long and arg_spec.value_type == _long_type: + elif _SUPPORT_long and arg_spec.value_type is _long_type: try: value = _long_type(given_value) - except ValueError as x: + except ValueError: raise InvalidIntegerException("the '%s' option's value '%s' cannot be interpreted as a long integer" % (self.name, given_value)) - elif arg_spec.value_type == str: + elif arg_spec.value_type is str: value = given_value else: @@ -140,7 +145,7 @@ def _set_value(self, value, from_ctor=False): if False: pass - elif arg_spec.value_type == str: + elif arg_spec.value_type is str: pass elif not arg_spec.value_type: @@ -148,16 +153,16 @@ def _set_value(self, value, from_ctor=False): pass else: - if arg_spec.value_type == bool: + if arg_spec.value_type is bool: type_name = "boolean" - elif arg_spec.value_type == float: + elif arg_spec.value_type is float: type_name = "a number" - elif arg_spec.value_type == int: + elif arg_spec.value_type is int: type_name = "an integer" - elif _SUPPORT_long and arg_spec.value_type == long: + elif _SUPPORT_long and arg_spec.value_type is _long_type: type_name = "a long integer" else: diff --git a/pyclasp/option_specification.py b/pyclasp/option_specification.py index e99016a..1faca13 100644 --- a/pyclasp/option_specification.py +++ b/pyclasp/option_specification.py @@ -83,7 +83,7 @@ def option(name, **kwargs): if v is None: pass - elif str == type(v): + elif type(v) is str: v = v.upper() diff --git a/pyclasp/util.py b/pyclasp/util.py index 8ab6169..dcadfa2 100644 --- a/pyclasp/util.py +++ b/pyclasp/util.py @@ -57,7 +57,7 @@ def _dict_get_N(d, *keys, **kwargs): v = d.get(key) - if None == v and default_if_none: + if None is v and default_if_none: continue @@ -89,7 +89,7 @@ def _global_multiple_flags_action(): if a: - if not a in _MULTIPLE_ACTION_FLAG_ALLOWED: + if a not in _MULTIPLE_ACTION_FLAG_ALLOWED: a = None @@ -110,7 +110,7 @@ def _global_multiple_options_action(): if a: - if not a in _MULTIPLE_ACTION_OPTION_ALLOWED: + if a not in _MULTIPLE_ACTION_OPTION_ALLOWED: a = None diff --git a/pyproject.toml b/pyproject.toml index ffd3911..90d88a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,25 @@ Changelog = "https://github.com/synesissoftware/CLASP.Python/blob/master/CHANGES Homepage = "https://github.com/synesissoftware/CLASP.Python" Repository = "https://github.com/synesissoftware/CLASP.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.setuptools.packages.find] exclude = [ diff --git a/tests/test_arguments.py b/tests/test_arguments.py index a2dfc39..e9293e0 100755 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -15,7 +15,6 @@ from pyclasp import Arguments -from pyclasp import specification, option from pyclasp import Flag from pyclasp import Option import pyclasp as clasp diff --git a/tests/test_options.py b/tests/test_options.py index a851fea..b063a4d 100755 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -14,10 +14,6 @@ # ######################################################################## # -from pyclasp import Arguments -from pyclasp import specification, option -from pyclasp import Flag -from pyclasp import Option import pyclasp as clasp import unittest diff --git a/tests/test_sections.py b/tests/test_sections.py index fb71951..03150af 100755 --- a/tests/test_sections.py +++ b/tests/test_sections.py @@ -18,7 +18,6 @@ import unittest -import sys try: diff --git a/tests/test_typed_values.py b/tests/test_typed_values.py index 12a9369..d9d48d2 100755 --- a/tests/test_typed_values.py +++ b/tests/test_typed_values.py @@ -14,8 +14,6 @@ # ######################################################################## # -from pyclasp import Arguments -from pyclasp import specification, option from pyclasp import Flag from pyclasp import Option import pyclasp as clasp From 6817182d2a730b204ca10fe145bca33be0200517 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Mon, 14 Sep 2026 09:33:33 +1000 Subject: [PATCH 13/13] chore(python): bump active library versions past last published tags Raise asynkio, CLASP.Python, Diagnosticism.Python, libCLImate.Python, py2be, and woad.Python one patch above the latest published tag (or prior local floor) so packaging/CI modernisation is releasable; sync package metadata, smoke asserts, CHANGES.md, and NEWS.md. --- .github/workflows/python-package.yml | 2 +- CHANGES.md | 5 +++++ NEWS.md | 7 ++++--- pyclasp/__init__.py | 2 +- pyproject.toml | 2 +- setup.py | 2 +- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 050da48..e959e5e 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -88,7 +88,7 @@ jobs: python -m pip install build python -m build python -m pip install dist/pyclasp-*.whl - python -c "import pyclasp; assert pyclasp.__version__ == '0.8.12'; print(pyclasp.__version__)" + python -c "import pyclasp; assert pyclasp.__version__ == '0.8.13'; print(pyclasp.__version__)" build-py27: diff --git a/CHANGES.md b/CHANGES.md index f2214ef..9056191 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,11 @@ # CLASP.Python - Changes +## 0.8.13 - 14th September 2026 + +* packaging, documentation, and CI modernisation (PEP 621 hybrid, canonical CI, **ruff**, helper scripts); + + ## 0.8.12 - 28th July 2026 * fixed falsey default values causing `MissingValueException` for option arguments; diff --git a/NEWS.md b/NEWS.md index a2bca52..c6f3c9b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,8 +1,9 @@ # CLASP.Python - News -| Date | News Item | Details | -| ---------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | -| 28th July 2026 | [**CLASP.Python** 0.8.12](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.12) | Fixed falsey option defaults / `MissingValueException` | +| Date | News Item | Details | +| ------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | +| 14th September 2026 | [**CLASP.Python** 0.8.13](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.13) | Packaging / CI modernisation | +| 28th July 2026 | [**CLASP.Python** 0.8.12](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.12) | Fixed falsey option defaults / `MissingValueException` | | 27th July 2026 | [**CLASP.Python** 0.8.11](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.11) | Packaging & CI (Python 2.7–3.14) | | 27th July 2026 | [**CLASP.Python** 0.8.10.1](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.10.1) | Test arg order and string-literal canonicalisation | | 25th July 2025 | [**CLASP.Python** 0.8.10](https://github.com/synesissoftware/CLASP.Python/releases/tag/0.8.10) | `info_lines` `None`; OFF/ON bool conversion | diff --git a/pyclasp/__init__.py b/pyclasp/__init__.py index 8747c82..762554e 100644 --- a/pyclasp/__init__.py +++ b/pyclasp/__init__.py @@ -10,7 +10,7 @@ __license__ = "BSD-3-Clause" __maintainer__ = "Matt Wilson" __status__ = "Beta" -__version__ = "0.8.12" +__version__ = "0.8.13" from .exceptions import ( CLASPException as CLASPException, diff --git a/pyproject.toml b/pyproject.toml index 90d88a8..66d3fbf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ license = { text = "BSD-3-Clause" } name = "pyclasp" 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.8.12" +version = "0.8.13" [project.urls] "Bug Tracker" = "https://github.com/synesissoftware/CLASP.Python/issues" diff --git a/setup.py b/setup.py index 6371f9f..cbc475c 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ name="pyclasp", python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*', - version='0.8.12', + version='0.8.13', author="Matt Wilson", author_email="matthew@synesis.com.au",