From e63f09d2eef008e5cc3b3c65bebcbd3ddd5b88c8 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 11:39:35 +1000 Subject: [PATCH 01/15] chore: fix project URL --- CHANGES.md | 5 +++++ setup.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 365cf81..a71ecf3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,11 @@ # **py2be** Changes +## 0.0.4 - 2nd July 2026 + +* chore: fix project URL; + + ## 0.0.3 - 1st September 2025 * GitHub Actions; diff --git a/setup.py b/setup.py index 39a5c40..a269675 100644 --- a/setup.py +++ b/setup.py @@ -29,6 +29,6 @@ 'examples', 'tests', ], - url='https://github.com/synesissoftware/diagnosticism.Python', + url='https://github.com/synesissoftware/py2be', ) From 9678c524fdfac9353ce8544b30cef39c8be0eecd Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 11:47:24 +1000 Subject: [PATCH 02/15] chore: fix README URL(s); --- CHANGES.md | 3 ++- README.md | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a71ecf3..e73f120 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,7 +3,8 @@ ## 0.0.4 - 2nd July 2026 -* chore: fix project URL; +* fix project URL; +* fix README URL(s); ## 0.0.3 - 1st September 2025 diff --git a/README.md b/README.md index 240da98..b2332c7 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,12 @@ Simple Python library determining whether strings indicate truey or falsy values - [Introduction](#introduction) - [Terminology](#terminology) - [Project Information](#project-information) - - [Where to get help](#where-to-get-help) - - [Contribution guidelines](#contribution-guidelines) - - [Dependencies](#dependencies) - - [Dev Dependencies](#dev-dependencies) - - [Related projects](#related-projects) - - [License](#license) + - [Where to get help](#where-to-get-help) + - [Contribution guidelines](#contribution-guidelines) + - [Dependencies](#dependencies) + - [Dev Dependencies](#dev-dependencies) + - [Related projects](#related-projects) + - [License](#license) ## Terminology @@ -90,7 +90,7 @@ Defect reports, feature requests, and pull requests are welcome on https://githu ### Related projects -* [**to-be**](https://github.com/synesissoftware/to-be) (**C**); +* [**2be**](https://github.com/synesissoftware/2be) (**C**); * [**to_be.Ruby**](https://github.com/synesissoftware/to_be.Ruby); * [**to-be.Rust**](https://github.com/synesissoftware/to-be.Rust); From 74acef36c80c7e31a62887fcf2c0e89543abc290 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 11:53:17 +1000 Subject: [PATCH 03/15] chore: * added top-level `__all__` documenting the public API; * removed `tests` and `examples` from installable packages; --- CHANGES.md | 2 ++ __init__.py | 4 +++- py2be/__init__.py | 10 ++++++++++ setup.py | 6 ++---- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e73f120..5351500 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,8 @@ * fix project URL; * fix README URL(s); +* added top-level `__all__` documenting the public API; +* removed `tests` and `examples` from installable packages; ## 0.0.3 - 1st September 2025 diff --git a/__init__.py b/__init__.py index db720d3..f04f6a1 100644 --- a/__init__.py +++ b/__init__.py @@ -1,2 +1,4 @@ -# This file present to ensure that unittest's discover operates recursively +# This file present to ensure that unittest's discover operates recursively. +# +# It is not exported from the package. diff --git a/py2be/__init__.py b/py2be/__init__.py index 717d952..0ca4ab9 100644 --- a/py2be/__init__.py +++ b/py2be/__init__.py @@ -18,5 +18,15 @@ ) +__all__ = [ + '__version__', + + 'str2bool', + 'string_is_falsey', + 'string_is_truey', + 'string_is_truthy', +] + + # ############################## end of file ############################# # diff --git a/setup.py b/setup.py index a269675..f2907ab 100644 --- a/setup.py +++ b/setup.py @@ -23,12 +23,10 @@ license='BSD-3-Clause', long_description=open('README.md').read(), long_description_content_type="text/markdown", - packages=[ - 'py2be', - 'py2be/internal', + packages=setuptools.find_packages(exclude=[ 'examples', 'tests', - ], + ]), url='https://github.com/synesissoftware/py2be', ) From 7b7cd88900ed3b854290e4e94f35b1de44567fec Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 12:03:42 +1000 Subject: [PATCH 04/15] chore: * aligned **build_dist.sh** with **build_dist_uv.sh** (system Python); * added **build_dist_uv.sh** (build + `twine check`); --- CHANGES.md | 2 ++ TODO.md | 6 ++++++ build_dist.sh | 10 ++++++++-- build_dist_uv.sh | 15 +++++++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100755 build_dist_uv.sh diff --git a/CHANGES.md b/CHANGES.md index 5351500..4f49bf6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,8 @@ * fix README URL(s); * added top-level `__all__` documenting the public API; * removed `tests` and `examples` from installable packages; +* aligned **build_dist.sh** with **build_dist_uv.sh** (system Python); +* added **build_dist_uv.sh** (build + `twine check`); ## 0.0.3 - 1st September 2025 diff --git a/TODO.md b/TODO.md index b83d3a5..bd8842d 100644 --- a/TODO.md +++ b/TODO.md @@ -3,10 +3,16 @@ ## Table of Contents +- [Project improvements](#project-improvements) - [Functional improvements](#functional-improvements) - [Performance improvements](#performance-improvements) +## Project improvements + +* [x] **build_dist.sh** and **build_dist_uv.sh** (build + `twine check`); + + ## Functional improvements * \ diff --git a/build_dist.sh b/build_dist.sh index 138b9e5..07d1e6b 100755 --- a/build_dist.sh +++ b/build_dist.sh @@ -1,4 +1,10 @@ -#!/bin/bash +#! /bin/bash -python3 setup.py sdist +set -e +cd "$(dirname "$0")" + +rm -rf build/ dist/ py2be.egg-info/ + +python3 -m build +python3 -m twine check dist/* diff --git a/build_dist_uv.sh b/build_dist_uv.sh new file mode 100755 index 0000000..00eb85c --- /dev/null +++ b/build_dist_uv.sh @@ -0,0 +1,15 @@ +#! /bin/bash + +set -e + +cd "$(dirname "$0")" + +if [ ! -d .venv ]; then + uv venv +fi + +rm -rf build/ dist/ py2be.egg-info/ + +uv pip install build twine +.venv/bin/python -m build +.venv/bin/twine check dist/* From 5964477125015cafa51c5406efa15a8f6705c2fd Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 12:05:27 +1000 Subject: [PATCH 05/15] chore: updated version --- py2be/__init__.py | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/py2be/__init__.py b/py2be/__init__.py index 0ca4ab9..99b7277 100644 --- a/py2be/__init__.py +++ b/py2be/__init__.py @@ -1,6 +1,6 @@ __author__ = 'Matt Wilson' -__copyright__ = 'Copyright 2025, Synesis Information Systems' +__copyright__ = 'Copyright 2025-2026, Synesis Information Systems' __credits__ = [ 'Matt Wilson', ] @@ -8,7 +8,7 @@ __license__ = 'BSD-3-Clause' __maintainer__ = 'Matt Wilson' __status__ = 'Beta' -__version__ = '0.0.3' +__version__ = '0.0.4' from .truthy import ( str2bool, diff --git a/setup.py b/setup.py index f2907ab..80c44d5 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setuptools.setup( name='py2be', - version='0.0.3', + version='0.0.4', author='Matt Wilson', author_email='matthew@synesis.com.au', From 78387b0e9bf56986d7b2e90d86c66c916dbda0be Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 12:05:59 +1000 Subject: [PATCH 06/15] chore: fix README example(s) --- CHANGES.md | 5 +++-- README.md | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4f49bf6..5fc5a48 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,12 +3,13 @@ ## 0.0.4 - 2nd July 2026 -* fix project URL; -* fix README URL(s); * added top-level `__all__` documenting the public API; * removed `tests` and `examples` from installable packages; * aligned **build_dist.sh** with **build_dist_uv.sh** (system Python); * added **build_dist_uv.sh** (build + `twine check`); +* fix project URL; +* fix README URL(s); +* fix README example(s); ## 0.0.3 - 1st September 2025 diff --git a/README.md b/README.md index b2332c7..ecbad8c 100644 --- a/README.md +++ b/README.md @@ -51,18 +51,18 @@ s3 = "orange" # "no" is validly truthy, and is falsey assert string_is_falsey(s1) -assert !string_is_truey(s1) +assert not string_is_truey(s1) assert string_is_truthy(s1) # "True" is validly truthy, and is truey -assert !string_is_falsey(s2) +assert not string_is_falsey(s2) assert string_is_truey(s2) assert string_is_truthy(s2) # "orange" is not validly truthy, and is neither falsey nor truey -assert !string_is_falsey(s3) -assert !string_is_truey(s3) -assert !string_is_truthy(s3) +assert not string_is_falsey(s3) +assert not string_is_truey(s3) +assert not string_is_truthy(s3) ``` From 59f013e37b1066c7f918edfe98e6ad15b7ba3731 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 12:06:38 +1000 Subject: [PATCH 07/15] chore: consistent .gitignore --- .gitignore | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index c2717b0..07871f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,28 @@ # directories (by name) -build/ -dist/ +/.bin/ +/.venv/ + +/_build/ +/build/ +/dist-old/ +/dist/ +/scratch/ + *.egg-info/ + # directories (by pattern) + # files (by name) .DS_Store +notes.txt + + # files (by pattern) *~ @@ -19,4 +31,5 @@ dist/ *.swo *.swp *.tmp +*.zip From 8ec1dd1aff4097af6e403103038e9d9650c85709 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 12:10:16 +1000 Subject: [PATCH 08/15] chore: * added `python_requires` for Python 2.7 and Python 3.8+; * added **MANIFEST.in**; --- CHANGES.md | 2 ++ MANIFEST.in | 2 ++ TODO.md | 2 ++ setup.py | 9 ++++++++- 4 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 MANIFEST.in diff --git a/CHANGES.md b/CHANGES.md index 5fc5a48..c5be05c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,8 @@ * fix project URL; * fix README URL(s); * fix README example(s); +* added `python_requires` for Python 2.7 and Python 3.8+; +* added **MANIFEST.in**; ## 0.0.3 - 1st September 2025 diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..ee80c36 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +include LICENSE README.md CHANGES.md TODO.md +recursive-include tests *.py diff --git a/TODO.md b/TODO.md index bd8842d..411874b 100644 --- a/TODO.md +++ b/TODO.md @@ -11,6 +11,8 @@ ## Project improvements * [x] **build_dist.sh** and **build_dist_uv.sh** (build + `twine check`); +* [x] **MANIFEST.in**; +* [x] `python_requires` (Python 2.7 and Python 3.8+); ## Functional improvements diff --git a/setup.py b/setup.py index 80c44d5..8c8b716 100644 --- a/setup.py +++ b/setup.py @@ -4,6 +4,7 @@ setuptools.setup( name='py2be', + python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*', version='0.0.4', author='Matt Wilson', @@ -15,8 +16,14 @@ '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", ], description='Simple Python library determining whether strings indicate truey or falsy values', keywords='configuration environment string traits', From c6f947bc71ca102ed129bc1d4841b9f09ee19a80 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 12:23:17 +1000 Subject: [PATCH 09/15] feature: * added **tests/test_package.py**; * added **tests/run_unittest.py** and Python 2.7 compatibility shims in **tests/__init__.py**; * expanded unit tests (`str2bool`, whitespace-padded stock terms); * extended GitHub Actions workflow (Python 2.7 job, Python 3.14, pytest); --- .github/workflows/python-package.yml | 51 ++++++++--- CHANGES.md | 9 +- LICENSE | 2 +- README.md | 3 +- TODO.md | 2 + examples/__init__.py | 1 + tests/__init__.py | 22 +++++ tests/run_unittest.py | 39 +++++++++ tests/test_package.py | 13 +++ tests/test_truthy.py | 121 +++++++++++++++++++++++---- 10 files changed, 233 insertions(+), 30 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/run_unittest.py create mode 100644 tests/test_package.py diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 121beb1..c4b4c57 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -5,9 +5,17 @@ name: Python package on: push: - branches: [ "master", "dev", "gha" ] + branches: + - master + - dev + - gha + - boilerplate pull_request: - branches: [ "master", "dev", "gha" ] + branches: + - master + - dev + - gha + - boilerplate jobs: build: @@ -17,34 +25,53 @@ jobs: fail-fast: false matrix: python-version: - - "3.13" - - "3.12" - - "3.11" - - "3.10" - - "3.9" - - "3.8" + - "3.8" + - "3.9" + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" steps: - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v3 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 + - name: Test with pytest + run: | + pytest + + build-py27: + runs-on: ubuntu-latest + container: + image: python:2.7-slim + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + run: | + python -m pip install --upgrade "pip<21" setuptools mock + - name: Compile library modules + run: | + python -m compileall py2be - name: Test with unittest run: | - python3 -m unittest discover tests + python tests/run_unittest.py + env: + PYTHONPATH: ${{ github.workspace }} diff --git a/CHANGES.md b/CHANGES.md index c5be05c..215b914 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,13 +5,18 @@ * added top-level `__all__` documenting the public API; * removed `tests` and `examples` from installable packages; +* added `python_requires` for Python 2.7 and Python 3.8+; +* added **MANIFEST.in**; * aligned **build_dist.sh** with **build_dist_uv.sh** (system Python); * added **build_dist_uv.sh** (build + `twine check`); +* added **tests/test_package.py**; +* added **tests/run_unittest.py** and Python 2.7 compatibility shims in **tests/__init__.py**; +* expanded unit tests (`str2bool`, whitespace-padded stock terms); +* extended GitHub Actions workflow (Python 2.7 job, Python 3.14, pytest); * fix project URL; * fix README URL(s); * fix README example(s); -* added `python_requires` for Python 2.7 and Python 3.8+; -* added **MANIFEST.in**; +* added CI badge to README; ## 0.0.3 - 1st September 2025 diff --git a/LICENSE b/LICENSE index 16a3327..48182d4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ py2be - BSD 3-Clause License -Copyright (c) 2025, Matthew Wilson and Synesis Information Systems +Copyright (c) 2025-2026, Matthew Wilson and Synesis Information Systems All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index ecbad8c..5482349 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,10 @@ ![Language](https://img.shields.io/badge/Python-3776AB?style=flat&logo=python&logoColor=white) [![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) +[![PyPI version](https://badge.fury.io/py/py2be.svg)](https://badge.fury.io/py/py2be) [![GitHub release](https://img.shields.io/github/v/release/synesissoftware/py2be.svg)](https://github.com/synesissoftware/py2be/releases/latest) [![Last Commit](https://img.shields.io/github/last-commit/synesissoftware/py2be)](https://github.com/synesissoftware/py2be/commits/master) -[![PyPI version](https://badge.fury.io/py/py2be.svg)](https://badge.fury.io/py/py2be) +[![CI](https://github.com/synesissoftware/py2be/actions/workflows/python-package.yml/badge.svg)](https://github.com/synesissoftware/py2be/actions/workflows/python-package.yml) Simple Python library determining whether strings indicate truey or falsy values. diff --git a/TODO.md b/TODO.md index 411874b..2b567bc 100644 --- a/TODO.md +++ b/TODO.md @@ -13,6 +13,8 @@ * [x] **build_dist.sh** and **build_dist_uv.sh** (build + `twine check`); * [x] **MANIFEST.in**; * [x] `python_requires` (Python 2.7 and Python 3.8+); +* [x] Python 2.7 test infrastructure (`tests/__init__.py`, `tests/run_unittest.py`, `tests/test_package.py`); +* [x] expanded unit tests and GitHub Actions (Python 2.7, pytest, Python 3.14); ## Functional improvements diff --git a/examples/__init__.py b/examples/__init__.py index e69de29..2f5dee3 100644 --- a/examples/__init__.py +++ b/examples/__init__.py @@ -0,0 +1 @@ +# This file present to deal with warnings from packager diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..6c47654 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,22 @@ +# This file present to deal with warnings from packager + +import sys + +if sys.version_info[0] < 3: + + import unittest + + try: + + import mock + + sys.modules['unittest.mock'] = mock + except ImportError: + + raise ImportError( + "the 'mock' package is required to run tests on Python 2.7", + ) + + if not hasattr(unittest.TestCase, 'assertRegex'): + + unittest.TestCase.assertRegex = unittest.TestCase.assertRegexpMatches diff --git a/tests/run_unittest.py b/tests/run_unittest.py new file mode 100644 index 0000000..d01e05d --- /dev/null +++ b/tests/run_unittest.py @@ -0,0 +1,39 @@ +#! /usr/bin/env python +""" +unittest entry point. + +Imports the `tests` package first so tests/__init__.py can install +Python 2.7 compatibility shims before test modules load. +""" + +import os +import sys +import unittest + + +def main(): + + root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + if root not in sys.path: + + sys.path.insert(0, root) + + import tests # noqa: F401 + + start_dir = os.path.join(root, 'tests') + loader = unittest.TestLoader() + suite = loader.discover( + start_dir, + pattern='test_*.py', + top_level_dir=root, + ) + runner = unittest.TextTestRunner(verbosity=2) + result = runner.run(suite) + + sys.exit(0 if result.wasSuccessful() else 1) + + +if '__main__' == __name__: + + main() diff --git a/tests/test_package.py b/tests/test_package.py new file mode 100644 index 0000000..35bd0ba --- /dev/null +++ b/tests/test_package.py @@ -0,0 +1,13 @@ +#! /usr/bin/env python + +import unittest + +import py2be + + +class Package_tester(unittest.TestCase): + + def test_all_names_are_defined(self): + + for name in py2be.__all__: + self.assertTrue(hasattr(py2be, name), name) diff --git a/tests/test_truthy.py b/tests/test_truthy.py index d0b564e..1c51c14 100755 --- a/tests/test_truthy.py +++ b/tests/test_truthy.py @@ -1,25 +1,23 @@ -#! /usr/bin/env python3 +#! /usr/bin/env python +# -*- coding: utf-8 -*- -from py2be.truthy import ( +import unittest + +from py2be import ( + str2bool, string_is_falsey, string_is_truey, string_is_truthy, ) -import unittest -from unittest.mock import patch - -import re - -try: - - from StringIO import StringIO -except ImportError: - from io import StringIO +class Truthy_tester(unittest.TestCase): + def _assert_stock_truthy(self, s, truthy, truey, falsey): -class Truthy_tester(unittest.TestCase): + self.assertEqual(truthy, string_is_truthy(s), 'string_is_truthy(%r)' % (s,)) + self.assertEqual(truey, string_is_truey(s), 'string_is_truey(%r)' % (s,)) + self.assertEqual(falsey, string_is_falsey(s), 'string_is_falsey(%r)' % (s,)) def test__string_is_falsey(self): @@ -106,7 +104,102 @@ def test__string_is_truthy(self): self.assertTrue(string_is_truthy("yEs")) + def test__str2bool(self): + + self.assertIsNone(str2bool(None)) + self.assertIsNone(str2bool("")) + self.assertIsNone(str2bool("orange")) + self.assertIsNone(str2bool("Nyet")) + + self.assertIs(False, str2bool("0")) + self.assertIs(False, str2bool("false")) + self.assertIs(False, str2bool("no")) + self.assertIs(False, str2bool("off")) + + self.assertIs(True, str2bool("1")) + self.assertIs(True, str2bool("true")) + self.assertIs(True, str2bool("yes")) + self.assertIs(True, str2bool("on")) + + + def test__whitespace_padded(self): + + self._assert_stock_truthy(" true", True, True, False) + self._assert_stock_truthy(" TRUE", True, True, False) + self._assert_stock_truthy(" True", True, True, False) + self._assert_stock_truthy(" false", True, False, True) + self._assert_stock_truthy(" FALSE", True, False, True) + self._assert_stock_truthy(" False", True, False, True) + self._assert_stock_truthy(" yes", True, True, False) + self._assert_stock_truthy(" YES", True, True, False) + self._assert_stock_truthy(" Yes", True, True, False) + self._assert_stock_truthy(" no", True, False, True) + self._assert_stock_truthy(" NO", True, False, True) + self._assert_stock_truthy(" No", True, False, True) + self._assert_stock_truthy(" on", True, True, False) + self._assert_stock_truthy(" ON", True, True, False) + self._assert_stock_truthy(" On", True, True, False) + self._assert_stock_truthy(" off", True, False, True) + self._assert_stock_truthy(" OFF", True, False, True) + self._assert_stock_truthy(" Off", True, False, True) + self._assert_stock_truthy(" 1", True, True, False) + self._assert_stock_truthy(" 0", True, False, True) + + self._assert_stock_truthy("true ", True, True, False) + self._assert_stock_truthy("TRUE ", True, True, False) + self._assert_stock_truthy("True ", True, True, False) + self._assert_stock_truthy("false ", True, False, True) + self._assert_stock_truthy("FALSE ", True, False, True) + self._assert_stock_truthy("False ", True, False, True) + self._assert_stock_truthy("yes ", True, True, False) + self._assert_stock_truthy("YES ", True, True, False) + self._assert_stock_truthy("Yes ", True, True, False) + self._assert_stock_truthy("no ", True, False, True) + self._assert_stock_truthy("NO ", True, False, True) + self._assert_stock_truthy("No ", True, False, True) + self._assert_stock_truthy("on ", True, True, False) + self._assert_stock_truthy("ON ", True, True, False) + self._assert_stock_truthy("On ", True, True, False) + self._assert_stock_truthy("off ", True, False, True) + self._assert_stock_truthy("OFF ", True, False, True) + self._assert_stock_truthy("Off ", True, False, True) + self._assert_stock_truthy("1 ", True, True, False) + self._assert_stock_truthy("0 ", True, False, True) + + self._assert_stock_truthy(" true ", True, True, False) + self._assert_stock_truthy(" TRUE ", True, True, False) + self._assert_stock_truthy(" True ", True, True, False) + self._assert_stock_truthy(" false ", True, False, True) + self._assert_stock_truthy(" FALSE ", True, False, True) + self._assert_stock_truthy(" False ", True, False, True) + self._assert_stock_truthy(" yes ", True, True, False) + self._assert_stock_truthy(" YES ", True, True, False) + self._assert_stock_truthy(" Yes ", True, True, False) + self._assert_stock_truthy(" no ", True, False, True) + self._assert_stock_truthy(" NO ", True, False, True) + self._assert_stock_truthy(" No ", True, False, True) + self._assert_stock_truthy(" on ", True, True, False) + self._assert_stock_truthy(" ON ", True, True, False) + self._assert_stock_truthy(" On ", True, True, False) + self._assert_stock_truthy(" off ", True, False, True) + self._assert_stock_truthy(" OFF ", True, False, True) + self._assert_stock_truthy(" Off ", True, False, True) + self._assert_stock_truthy(" 1 ", True, True, False) + self._assert_stock_truthy(" 0 ", True, False, True) + + self._assert_stock_truthy("\ttrue\t", True, True, False) + self._assert_stock_truthy("\tFALSE\t", True, False, True) + self._assert_stock_truthy("\t yes \t", True, True, False) + + self._assert_stock_truthy(" tRuE ", True, True, False) + self._assert_stock_truthy(" FaLSe ", True, False, True) + self._assert_stock_truthy(" yEs ", True, True, False) + + self._assert_stock_truthy(" unrecognised ", False, False, False) + self._assert_stock_truthy(" ", False, False, False) + self._assert_stock_truthy("\t", False, False, False) + + if '__main__' == __name__: unittest.main() - From 302c824beb021e4c3df5ad176ba2c116ad03c3f7 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 12:30:52 +1000 Subject: [PATCH 10/15] chore: * README polish (installation, Python version compatibility, Components/API reference, CI badges, examples, URL(s)) --- CHANGES.md | 4 +-- README.md | 86 +++++++++++++++++++++++++++++++++++++++++++++--------- TODO.md | 1 + 3 files changed, 75 insertions(+), 16 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 215b914..3eed49b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,9 +14,7 @@ * expanded unit tests (`str2bool`, whitespace-padded stock terms); * extended GitHub Actions workflow (Python 2.7 job, Python 3.14, pytest); * fix project URL; -* fix README URL(s); -* fix README example(s); -* added CI badge to README; +* README polish (installation, Python version compatibility, Components/API reference, CI badges, examples, URL(s)); ## 0.0.3 - 1st September 2025 diff --git a/README.md b/README.md index 5482349..ff415d8 100644 --- a/README.md +++ b/README.md @@ -4,22 +4,21 @@ [![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![PyPI version](https://badge.fury.io/py/py2be.svg)](https://badge.fury.io/py/py2be) [![GitHub release](https://img.shields.io/github/v/release/synesissoftware/py2be.svg)](https://github.com/synesissoftware/py2be/releases/latest) -[![Last Commit](https://img.shields.io/github/last-commit/synesissoftware/py2be)](https://github.com/synesissoftware/py2be/commits/master) +![Python](https://img.shields.io/badge/Python-2.7%20%7C%203.8+-lightgrey) [![CI](https://github.com/synesissoftware/py2be/actions/workflows/python-package.yml/badge.svg)](https://github.com/synesissoftware/py2be/actions/workflows/python-package.yml) +[![Last Commit](https://img.shields.io/github/last-commit/synesissoftware/py2be)](https://github.com/synesissoftware/py2be/commits/master) -Simple Python library determining whether strings indicate truey or falsy values. - - -## Introduction - -**to-be** is a library providing facilities for determine whether the truthyness of strings. It implemented in several languages: **py2be** is the **Python** implementation. +Simple Python library determining whether strings indicate *truey* or *falsey* values. ## Table of Contents - - [Introduction](#introduction) +- [Installation \& usage](#installation--usage) + - [Python version compatibility](#python-version-compatibility) - [Terminology](#terminology) +- [Components](#components) + - [Functions](#functions) - [Project Information](#project-information) - [Where to get help](#where-to-get-help) - [Contribution guidelines](#contribution-guidelines) @@ -29,11 +28,55 @@ Simple Python library determining whether strings indicate truey or falsy values - [License](#license) +## Introduction + +**to-be** is a library providing facilities for determining whether the truthyness of strings. It is implemented in several languages: **py2be** is the **Python** implementation. + + +## Installation & usage + +Install via **pip** or **pip3**, as in: + +``` +$ pip3 install py2be +``` + +Use via **import**: + +```Python +import py2be +``` + +or import the functions you need: + +```Python +from py2be import ( + str2bool, + string_is_falsey, + string_is_truey, + string_is_truthy, +) +``` + + +### Python version compatibility + +**py2be** is intended to run on **Python 2.7** and **Python 3.8+**. GitHub Actions exercises **Python 2.7** and **Python 3.8–3.14**. + +| Requirement | Applies to | +| ----------- | ---------- | +| Python **2.7** or **3.8+** | All public APIs (`str2bool`, `string_is_falsey`, `string_is_truey`, `string_is_truthy`) | + +Inputs are expected to be text strings (`str` on Python 3; `str` or `unicode` on Python 2.7). `bytes` are not supported. Passing `None` to `str2bool()` yields `None`; the boolean predicate functions treat unclassified input as `False`. + +The public API surface is listed in `py2be.__all__`. + + ## Terminology The term "*truthy*" is an unhelpfully overloaded term in the programming world, insofar as it is used to refer to the notion of "truthyness" - whether something can be _deemed to be_ interpretable as truth - and also the true side of that interpretation. In this library, the former interpretation is used, leaving us with the following terms: -* "*truthy*" - whether something can be can be _deemed to be_ interpretable as having truth; +* "*truthy*" - whether something can be _deemed to be_ interpretable as having truth (and, thus, will be *falsey* or *truey*); * "*falsey*" - whether an object can be _deemed to be_ interpretable as being false; * "*truey*" - whether an object can be _deemed to be_ interpretable as being true; @@ -41,9 +84,9 @@ For example, consider the following **Python** program: ```Python from py2be import ( - string_is_falsey, - string_is_truey, - string_is_truthy, + string_is_falsey, + string_is_truey, + string_is_truthy, ) s1 = "no" @@ -67,6 +110,24 @@ assert not string_is_truthy(s3) ``` +## Components + +### Functions + +The following public functions are defined in the current version: + +| Function | Purpose | +| -------- | ------- | +| `str2bool(s)` | Classifies `s` as unrecognised (`None`), falsey (`False`), or truey (`True`). | +| `string_is_falsey(s)` | Indicates that `s`, when trimmed, is classified as truthy and is deemed falsey. | +| `string_is_truey(s)` | Indicates that `s`, when trimmed, is classified as truthy and is deemed truey. | +| `string_is_truthy(s)` | Indicates that `s`, when trimmed, is classified as truthy (and is deemed either falsey or truey). | + +**NOTE:** `string_is_falsey(x) == not string_is_truey(x)` is **not** guaranteed (for example, when `x` is not classified as truthy, both predicates return `False`). + +Stock falsey terms (after optional trimming and case folding) include `0`, `false`, `no`, and `off`. Stock truey terms include `1`, `true`, `yes`, and `on`. Several common capitalisations and mixtures of case are recognised without lower-casing first. + + ## Project Information ### Where to get help @@ -102,4 +163,3 @@ Defect reports, feature requests, and pull requests are welcome on https://githu - diff --git a/TODO.md b/TODO.md index 2b567bc..5ff565f 100644 --- a/TODO.md +++ b/TODO.md @@ -15,6 +15,7 @@ * [x] `python_requires` (Python 2.7 and Python 3.8+); * [x] Python 2.7 test infrastructure (`tests/__init__.py`, `tests/run_unittest.py`, `tests/test_package.py`); * [x] expanded unit tests and GitHub Actions (Python 2.7, pytest, Python 3.14); +* [x] README polish (installation, compatibility table, API reference); ## Functional improvements From 89712523dc2a2bd10e946ec4e3b45c8c5b5a5653 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 12:35:05 +1000 Subject: [PATCH 11/15] fix: fixed pytest import failure (removed repo-root `__init__.py`; `pip install -e .` in CI; **pytest.ini**) --- .github/workflows/python-package.yml | 5 ++++- CHANGES.md | 1 + __init__.py | 4 ---- pytest.ini | 2 ++ run_all_unit_tests.sh | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) delete mode 100644 __init__.py create mode 100644 pytest.ini diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index c4b4c57..0f6439f 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -44,6 +44,9 @@ jobs: python -m pip install --upgrade pip python -m pip install flake8 pytest if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Install package + run: | + python -m pip install -e . - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names @@ -52,7 +55,7 @@ jobs: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with unittest run: | - python3 -m unittest discover + python tests/run_unittest.py - name: Test with pytest run: | pytest diff --git a/CHANGES.md b/CHANGES.md index 3eed49b..079a53e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ * extended GitHub Actions workflow (Python 2.7 job, Python 3.14, pytest); * fix project URL; * README polish (installation, Python version compatibility, Components/API reference, CI badges, examples, URL(s)); +* fixed pytest import failure (removed repo-root `__init__.py`; `pip install -e .` in CI; **pytest.ini**); ## 0.0.3 - 1st September 2025 diff --git a/__init__.py b/__init__.py deleted file mode 100644 index f04f6a1..0000000 --- a/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# This file present to ensure that unittest's discover operates recursively. -# -# It is not exported from the package. - diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..5ee6477 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +testpaths = tests diff --git a/run_all_unit_tests.sh b/run_all_unit_tests.sh index e91c1e5..280a5fb 100755 --- a/run_all_unit_tests.sh +++ b/run_all_unit_tests.sh @@ -56,7 +56,7 @@ Dir="$(cd -P "$( dirname "$Source" )" && pwd)" # 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" +python3 "$Dir/tests/run_unittest.py" # ############################## end of file ############################# # From 41ca3aae29f1d293ec1ca6e9d76d0c7b1c3f83ee Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 12:38:21 +1000 Subject: [PATCH 12/15] chore: README: explicit Python 2.7 compatibility claim; documented `unicode` / `bytes` input policy --- CHANGES.md | 1 + README.md | 31 ++++++++++++++++++++++++++++--- TODO.md | 1 + 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 079a53e..4b800d9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,7 @@ * fix project URL; * README polish (installation, Python version compatibility, Components/API reference, CI badges, examples, URL(s)); * fixed pytest import failure (removed repo-root `__init__.py`; `pip install -e .` in CI; **pytest.ini**); +* README: explicit Python 2.7 compatibility claim; documented `unicode` / `bytes` input policy; ## 0.0.3 - 1st September 2025 diff --git a/README.md b/README.md index ff415d8..3751b11 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Simple Python library determining whether strings indicate *truey* or *falsey* v - [Introduction](#introduction) - [Installation \& usage](#installation--usage) - [Python version compatibility](#python-version-compatibility) + - [Text string inputs (`unicode` / `bytes`)](#text-string-inputs-unicode--bytes) - [Terminology](#terminology) - [Components](#components) - [Functions](#functions) @@ -32,6 +33,8 @@ Simple Python library determining whether strings indicate *truey* or *falsey* v **to-be** is a library providing facilities for determining whether the truthyness of strings. It is implemented in several languages: **py2be** is the **Python** implementation. +**py2be** explicitly supports **Python 2.7** — not other Python 2.x releases — and **Python 3.8+**. This is enforced at install time via `python_requires` in **setup.py** and exercised in GitHub Actions on **Python 2.7** and **Python 3.8–3.14**. + ## Installation & usage @@ -61,17 +64,39 @@ from py2be import ( ### Python version compatibility -**py2be** is intended to run on **Python 2.7** and **Python 3.8+**. GitHub Actions exercises **Python 2.7** and **Python 3.8–3.14**. +**py2be** is intended to run on **Python 2.7** and **Python 3.8+** only. Versions in the Python 3.0–3.7 range are excluded by `python_requires`. + +| Python version | Support | +| -------------- | ------- | +| **2.7** | Supported (the only Python 2 release supported) | +| **3.0 – 3.7** | Not supported | +| **3.8+** | Supported | | Requirement | Applies to | | ----------- | ---------- | | Python **2.7** or **3.8+** | All public APIs (`str2bool`, `string_is_falsey`, `string_is_truey`, `string_is_truthy`) | -Inputs are expected to be text strings (`str` on Python 3; `str` or `unicode` on Python 2.7). `bytes` are not supported. Passing `None` to `str2bool()` yields `None`; the boolean predicate functions treat unclassified input as `False`. - The public API surface is listed in `py2be.__all__`. +### Text string inputs (`unicode` / `bytes`) + +All public functions take a single string argument `s`. The library classifies **text** only; binary buffers are not accepted. + +| Input | Python 2.7 | Python 3.8+ | +| ----- | ------------ | ----------- | +| `unicode` / text `str` | Supported | Supported (`str`) | +| `str` (byte string) | Supported for ASCII stock terms (e.g. `"true"`, `"no"`) | N/A — `str` is text | +| `bytes`, `bytearray`, and other non-text types | Not supported — behaviour is undefined; decode to text first | Not supported — behaviour is undefined; decode to text first | +| `None` | `str2bool(None)` returns `None`; the boolean predicates return `False` | Same | + +On **Python 3**, passing `bytes` (for example `b"true"`) will not match stock terms and is not a supported use case. Decode explicitly before calling, for example `s.decode("utf-8")` or `s.decode("ascii")`. + +On **Python 2.7**, prefer `unicode` literals (e.g. `u"true"`) for non-ASCII configuration values. ASCII `str` literals work for the stock vocabulary because they match the internal comparison tables directly. + +Trimming uses `str.strip()` / `unicode.strip()`; only leading and trailing whitespace is removed before lower-case matching. + + ## Terminology The term "*truthy*" is an unhelpfully overloaded term in the programming world, insofar as it is used to refer to the notion of "truthyness" - whether something can be _deemed to be_ interpretable as truth - and also the true side of that interpretation. In this library, the former interpretation is used, leaving us with the following terms: diff --git a/TODO.md b/TODO.md index 5ff565f..582d88e 100644 --- a/TODO.md +++ b/TODO.md @@ -16,6 +16,7 @@ * [x] Python 2.7 test infrastructure (`tests/__init__.py`, `tests/run_unittest.py`, `tests/test_package.py`); * [x] expanded unit tests and GitHub Actions (Python 2.7, pytest, Python 3.14); * [x] README polish (installation, compatibility table, API reference); +* [x] README: explicit Python 2.7 claim; `unicode` / `bytes` policy; ## Functional improvements From 54aa8fa37c93a1a5aec332a201b511c7d8da16c7 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 12:42:47 +1000 Subject: [PATCH 13/15] feature: added **examples/truthy_strings.py** and **EXAMPLES.md** --- CHANGES.md | 1 + EXAMPLES.md | 8 +++++++ MANIFEST.in | 3 ++- README.md | 12 +++++++++++ TODO.md | 1 + examples/truthy_strings.py | 43 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 EXAMPLES.md create mode 100644 examples/truthy_strings.py diff --git a/CHANGES.md b/CHANGES.md index 4b800d9..6dbb9fc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,7 @@ * README polish (installation, Python version compatibility, Components/API reference, CI badges, examples, URL(s)); * fixed pytest import failure (removed repo-root `__init__.py`; `pip install -e .` in CI; **pytest.ini**); * README: explicit Python 2.7 compatibility claim; documented `unicode` / `bytes` input policy; +* added **examples/truthy_strings.py** and **EXAMPLES.md**; ## 0.0.3 - 1st September 2025 diff --git a/EXAMPLES.md b/EXAMPLES.md new file mode 100644 index 0000000..88405be --- /dev/null +++ b/EXAMPLES.md @@ -0,0 +1,8 @@ +# py2be Examples + +| Name | Source | Summary | +| ---- | ------ | ------- | +| **truthy_strings** | [examples/truthy_strings.py](./examples/truthy_strings.py) | Classify stock and padded strings with `str2bool()`, `string_is_falsey()`, `string_is_truey()`, and `string_is_truthy()` | + + + diff --git a/MANIFEST.in b/MANIFEST.in index ee80c36..0dc986e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ -include LICENSE README.md CHANGES.md TODO.md +include LICENSE README.md CHANGES.md EXAMPLES.md TODO.md +recursive-include examples *.py recursive-include tests *.py diff --git a/README.md b/README.md index 3751b11..3a51f93 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Simple Python library determining whether strings indicate *truey* or *falsey* v - [Terminology](#terminology) - [Components](#components) - [Functions](#functions) +- [Examples](#examples) - [Project Information](#project-information) - [Where to get help](#where-to-get-help) - [Contribution guidelines](#contribution-guidelines) @@ -153,6 +154,17 @@ The following public functions are defined in the current version: Stock falsey terms (after optional trimming and case folding) include `0`, `false`, `no`, and `off`. Stock truey terms include `1`, `true`, `yes`, and `on`. Several common capitalisations and mixtures of case are recognised without lower-casing first. +## Examples + +Examples are provided in the `examples` directory. A detailed list of them is provided in [EXAMPLES.md](./EXAMPLES.md). + +To run the stock string classification example: + +``` +$ python examples/truthy_strings.py +``` + + ## Project Information ### Where to get help diff --git a/TODO.md b/TODO.md index 582d88e..ea00e5d 100644 --- a/TODO.md +++ b/TODO.md @@ -17,6 +17,7 @@ * [x] expanded unit tests and GitHub Actions (Python 2.7, pytest, Python 3.14); * [x] README polish (installation, compatibility table, API reference); * [x] README: explicit Python 2.7 claim; `unicode` / `bytes` policy; +* [x] **EXAMPLES.md** and **examples/truthy_strings.py**; ## Functional improvements diff --git a/examples/truthy_strings.py b/examples/truthy_strings.py new file mode 100644 index 0000000..eef60da --- /dev/null +++ b/examples/truthy_strings.py @@ -0,0 +1,43 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +""" +Example: classify strings with py2be free functions. + +Run from the repository root (with py2be on PYTHONPATH or installed): + + python examples/truthy_strings.py +""" + +from __future__ import print_function + +from py2be import ( + str2bool, + string_is_falsey, + string_is_truey, + string_is_truthy, +) + + +def classify(label, s): + + print("%s:" % label) + print(" input = %r" % (s,)) + print(" str2bool() = %r" % (str2bool(s),)) + print(" string_is_falsey = %r" % (string_is_falsey(s),)) + print(" string_is_truey = %r" % (string_is_truey(s),)) + print(" string_is_truthy = %r" % (string_is_truthy(s),)) + print("") + + +def main(): + + classify("falsey (stock)", "no") + classify("truey (stock)", "True") + classify("unrecognised", "orange") + classify("whitespace-padded truey", " YES ") + classify("none", None) + + +if '__main__' == __name__: + + main() From 5a0ddd90313d957dc7f7dcc81f50899052de9d94 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 12:44:25 +1000 Subject: [PATCH 14/15] feature: added `Programming Language :: Python :: 3.14` classifier --- CHANGES.md | 1 + setup.py | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 6dbb9fc..a6a6003 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -18,6 +18,7 @@ * fixed pytest import failure (removed repo-root `__init__.py`; `pip install -e .` in CI; **pytest.ini**); * README: explicit Python 2.7 compatibility claim; documented `unicode` / `bytes` input policy; * added **examples/truthy_strings.py** and **EXAMPLES.md**; +* added `Programming Language :: Python :: 3.14` classifier; ## 0.0.3 - 1st September 2025 diff --git a/setup.py b/setup.py index 8c8b716..1acd45d 100644 --- a/setup.py +++ b/setup.py @@ -24,6 +24,7 @@ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ], description='Simple Python library determining whether strings indicate truey or falsy values', keywords='configuration environment string traits', From fbc16bafc8bd628d4d01627d9e364fbc7e564215 Mon Sep 17 00:00:00 2001 From: Matt Wilson Date: Thu, 2 Jul 2026 12:50:23 +1000 Subject: [PATCH 15/15] feature: added **tests/test_bytes_rejection.py** (bytes / `bytearray` not classified on Python 3; Py2.7 text inputs) --- CHANGES.md | 1 + TODO.md | 1 + tests/test_bytes_rejection.py | 68 +++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 tests/test_bytes_rejection.py diff --git a/CHANGES.md b/CHANGES.md index a6a6003..4b9b7e1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -19,6 +19,7 @@ * README: explicit Python 2.7 compatibility claim; documented `unicode` / `bytes` input policy; * added **examples/truthy_strings.py** and **EXAMPLES.md**; * added `Programming Language :: Python :: 3.14` classifier; +* added **tests/test_bytes_rejection.py** (bytes / `bytearray` not classified on Python 3; Py2.7 text inputs); ## 0.0.3 - 1st September 2025 diff --git a/TODO.md b/TODO.md index ea00e5d..9f76640 100644 --- a/TODO.md +++ b/TODO.md @@ -18,6 +18,7 @@ * [x] README polish (installation, compatibility table, API reference); * [x] README: explicit Python 2.7 claim; `unicode` / `bytes` policy; * [x] **EXAMPLES.md** and **examples/truthy_strings.py**; +* [x] **tests/test_bytes_rejection.py** (documented `unicode` / `bytes` policy); ## Functional improvements diff --git a/tests/test_bytes_rejection.py b/tests/test_bytes_rejection.py new file mode 100644 index 0000000..c44d087 --- /dev/null +++ b/tests/test_bytes_rejection.py @@ -0,0 +1,68 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +import unittest + +from py2be import ( + str2bool, + string_is_falsey, + string_is_truey, + string_is_truthy, +) + + +class Bytes_rejection_tester(unittest.TestCase): + + def _assert_unclassified(self, s): + + self.assertIsNone(str2bool(s), 'str2bool(%r)' % (s,)) + self.assertFalse(string_is_falsey(s), 'string_is_falsey(%r)' % (s,)) + self.assertFalse(string_is_truey(s), 'string_is_truey(%r)' % (s,)) + self.assertFalse(string_is_truthy(s), 'string_is_truthy(%r)' % (s,)) + + + @unittest.skipIf(sys.version_info[0] < 3, 'bytes is text str on Python 2.7') + def test__bytes_are_not_classified(self): + + self._assert_unclassified(b'') + self._assert_unclassified(b'true') + self._assert_unclassified(b'True') + self._assert_unclassified(b'false') + self._assert_unclassified(b'no') + self._assert_unclassified(b'yes') + self._assert_unclassified(b' YES ') + + + @unittest.skipIf(sys.version_info[0] < 3, 'bytes is text str on Python 2.7') + def test__bytearray_is_not_classified(self): + + self._assert_unclassified(bytearray(b'')) + self._assert_unclassified(bytearray(b'true')) + self._assert_unclassified(bytearray(b'false')) + self._assert_unclassified(bytearray(b' YES ')) + + + def test__text_inputs_remain_supported(self): + + self.assertIs(True, str2bool('true')) + self.assertTrue(string_is_truey('true')) + self.assertTrue(string_is_truthy('true')) + + self.assertIs(False, str2bool('no')) + self.assertTrue(string_is_falsey('no')) + self.assertTrue(string_is_truthy('no')) + + + @unittest.skipIf(sys.version_info[0] >= 3, 'Python 2.7 byte str and unicode') + def test__ascii_str_and_unicode_work_on_py2(self): + + self.assertIs(True, str2bool('true')) + self.assertIs(True, str2bool(u'true')) + self.assertIs(False, str2bool('no')) + self.assertIs(False, str2bool(u'no')) + + +if '__main__' == __name__: + + unittest.main()