Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
60a95f9
Update setup.py
sam-fakhreddine Oct 17, 2023
51e1b46
Update __init__.py
sam-fakhreddine Oct 17, 2023
477423b
Update setup.py
sam-fakhreddine Oct 17, 2023
9e7fb69
Update setup.py
sam-fakhreddine Oct 17, 2023
c453615
Merge pull request #1 from sam-fakhreddine/patch-1
sam-fakhreddine Oct 17, 2023
63499e6
Update setup.py
sam-fakhreddine Oct 17, 2023
cdac3b9
Create workflows.yml
sam-fakhreddine Oct 17, 2023
6b4b09d
Update setup.py
sam-fakhreddine Oct 17, 2023
1f2e4c3
Update workflows.yml
sam-fakhreddine Oct 17, 2023
1d49df7
Update workflows.yml
sam-fakhreddine Oct 17, 2023
1dc5ffe
Create __init__.py
sam-fakhreddine Oct 17, 2023
33e9481
Delete str2bool directory
sam-fakhreddine Oct 17, 2023
85888e7
Update setup.cfg
sam-fakhreddine Oct 17, 2023
6ab0d87
Update README.md
sam-fakhreddine Oct 17, 2023
f3fc189
Update setup.py
sam-fakhreddine Oct 17, 2023
8f995ff
Update setup.py
sam-fakhreddine Oct 17, 2023
3d3389b
Update README.md
sam-fakhreddine Oct 17, 2023
963fc48
Update README.md
sam-fakhreddine Oct 17, 2023
297b401
fixed some typos
sam-fakhreddine Oct 17, 2023
a7b94e3
Merge pull request #2 from sam-fakhreddine/fix/name
sam-fakhreddine Oct 17, 2023
1e03f34
Update __init__.py
sam-fakhreddine Aug 30, 2024
e2d5425
Update README.md
sam-fakhreddine Aug 30, 2024
7034208
Update __init__.py
sam-fakhreddine Aug 30, 2024
a043350
Create str_utils.py
sam-fakhreddine Aug 30, 2024
0ad4bb5
Update setup.py
sam-fakhreddine Aug 30, 2024
ec3f2c0
Release v1.4.0: improve robustness, add test suite, modernize packaging
claude Apr 13, 2026
d822b04
Fix pyproject.toml build backend causing CI install failure
claude Apr 13, 2026
04b8095
Add on/off/enabled/disabled; refactor to DRY _parse() core
claude Apr 13, 2026
279dac7
Address review comments; add Python 3.13 and 3.14
claude Apr 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions .github/workflows/workflows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Test, Build, and Publish 📦

on:
push:
branches: ["master"]
tags: ["*"]
pull_request:
branches: ["master"]

jobs:
test:
name: Run tests 🧪
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["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@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install pytest
- name: Install package
run: pip install -e .
- name: Run tests
run: pytest tests/ -v

build:
name: Build distribution 📦
runs-on: ubuntu-latest
needs: [test]
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Extract version from git tag
if: startsWith(github.ref, 'refs/tags/')
run: echo "PACKAGE_VERSION=${{ github.ref_name }}" >> $GITHUB_ENV
- name: Install pypa/build
run: python3 -m pip install build --user
- name: Build a binary wheel and a source tarball
run: python3 -m build
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/

publish-to-pypi:
name: Publish Python 🐍 distribution 📦 to PyPI
if: startsWith(github.ref, 'refs/tags/')
needs: [build]
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/str2bool3
permissions:
id-token: write

steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
71 changes: 63 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,75 @@
# str2bool v.1.1
# str2bool3 v1.4.0

## About
Convert string to boolean.
Library recognizes "yes", "true", "y", "t", "1" as True, and "no", "false", "n", "f", "0" as False.
Case insensitive.
Convert a string (or compatible type) to a boolean value.

Recognized **True** values: `yes`, `true`, `t`, `y`, `1`
Recognized **False** values: `no`, `false`, `f`, `n`, `0`

Matching is case-insensitive and leading/trailing whitespace is stripped automatically.

`bool` and `int` inputs are also accepted directly:
- `True` / `False` → returned as-is
- `1` → `True`, `0` → `False`

## Installation

$ pip install str2bool
$ pip install str2bool3

## Examples
Here's a basic example:

>>> from str2bool import str2bool
>>> print(str2bool('Yes'))
Basic usage:

>>> from str2bool3 import str2bool
>>> str2bool('Yes')
True
>>> str2bool('no')
False
>>> str2bool(' TRUE ') # whitespace stripped
True
>>> str2bool(True) # bool pass-through
True
>>> str2bool(1) # int support
True

Unrecognized values return `None` by default (or a custom default):

>>> str2bool('maybe')
None
>>> str2bool('maybe', default=False)
False

Raise an exception for invalid input:

>>> str2bool('maybe', raise_exc=True)
ValueError: Invalid value 'maybe'. Expected one of: 0, 1, f, false, n, no, t, true, y, yes.

Convenience wrapper that always raises on invalid input (including `None`):

>>> from str2bool3 import str2bool_exc
>>> str2bool_exc('invalid')
ValueError: Invalid value 'invalid'. Expected one of: 0, 1, f, false, n, no, t, true, y, yes.
>>> str2bool_exc(None)
ValueError: Cannot convert None to bool. ...

## API

### `str2bool(value, raise_exc=False, default=None)`

| Parameter | Type | Description |
|-----------|------|-------------|
| `value` | `str \| bool \| int \| None` | Value to convert |
| `raise_exc` | `bool` | Raise `ValueError` on unrecognized input (default `False`) |
| `default` | `bool \| None` | Return value when input is unrecognized (default `None`) |

Raises `TypeError` for unsupported types (e.g. `float`, `list`).

### `str2bool_exc(value)`

Shorthand for `str2bool(value, raise_exc=True)`. Always raises `ValueError`
for `None` or unrecognized values.

## License
BSD

Forked from [symonsoft/str2bool](https://github.com/symonsoft/str2bool)
39 changes: 39 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[build-system]
requires = ["setuptools>=61", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "str2bool3"
dynamic = ["version"]
description = "Convert string to boolean (Forked from SymonSoft/str2bool)"
readme = {file = "README.md", content-type = "text/markdown"}
license = {text = "BSD-3-Clause"}
authors = [
{name = "Sam Fakhreddine", email = "sam.fakhreddine@gmail.com"}
]
keywords = ["str2bool", "bool", "boolean", "convert", "yes", "no", "true", "false"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"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",
"Topic :: Utilities",
]
requires-python = ">=3.8"

[project.urls]
Homepage = "https://github.com/sam-fakhreddine/str2bool3"
Repository = "https://github.com/sam-fakhreddine/str2bool3"

[tool.setuptools.packages.find]
where = ["."]
include = ["str2bool3*"]

[tool.pytest.ini_options]
testpaths = ["tests"]
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[metadata]
description-file = README.md
description_file = README.md
30 changes: 6 additions & 24 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,6 @@
from distutils.core import setup


setup(
name='str2bool',
packages=['str2bool'],
version='1.1',
description='Convert string to boolean',
author='SymonSoft',
author_email='symonsoft@gmail.com',
url='https://github.com/symonsoft/str2bool',
download_url='https://github.com/symonsoft/str2bool/tarball/1.1',
keywords=['str2bool', 'bool', 'boolean', 'convert', 'yes', 'no', 'true', 'false'],
classifiers=[
'Development Status :: 5 - Production/Stable',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Topic :: Utilities'
],
)
import os
from setuptools import setup

# Single source of truth: PACKAGE_VERSION env var (set from git tag in CI),
# falling back to the default development version.
setup(version=os.environ.get('PACKAGE_VERSION', '1.4.0'))
21 changes: 0 additions & 21 deletions str2bool/__init__.py

This file was deleted.

3 changes: 3 additions & 0 deletions str2bool3/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .str_utils import FALSE_VALUES, TRUE_VALUES, str2bool, str2bool_exc

__all__ = ["str2bool", "str2bool_exc", "TRUE_VALUES", "FALSE_VALUES"]
92 changes: 92 additions & 0 deletions str2bool3/str_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from typing import Optional, Union

TRUE_VALUES: frozenset = frozenset({'yes', 'true', 't', 'y', '1', 'on', 'enabled'})
FALSE_VALUES: frozenset = frozenset({'no', 'false', 'f', 'n', '0', 'off', 'disabled'})

_VALID: str = ', '.join(sorted(TRUE_VALUES | FALSE_VALUES))
_MISSING = object() # sentinel: value was not recognized

_Input = Optional[Union[str, bool, int]]


def _parse(value: _Input) -> object:
"""Coerce *value* to True, False, or _MISSING.

Raises TypeError for unsupported types so callers never have to.
"""
# bool must come before int — bool is a subclass of int
if isinstance(value, bool):
return value

if isinstance(value, int):
if value == 1:
return True
if value == 0:
return False
return _MISSING

if value is None:
return _MISSING

if not isinstance(value, str):
raise TypeError(
f"Expected str, bool, int, or None; got {type(value).__name__!r}."
)

normalized = value.strip().lower()
if normalized in TRUE_VALUES:
return True
if normalized in FALSE_VALUES:
return False
return _MISSING


def str2bool(
value: _Input,
raise_exc: bool = False,
default: Optional[bool] = None,
) -> Optional[bool]:
"""Convert a value to bool.

Recognized True: yes, true, t, y, 1, on, enabled
Recognized False: no, false, f, n, 0, off, disabled

Matching is case-insensitive; leading/trailing whitespace is stripped.
bool inputs are passed through; int 1/0 map to True/False.

Args:
value: Input to convert. Accepts str, bool, int, or None.
raise_exc: Raise ValueError for unrecognized values (including None).
default: Returned when value is unrecognized and raise_exc is False.

Returns:
True, False, or default.

Raises:
TypeError: If value is not str, bool, int, or None.
ValueError: If value is unrecognized and raise_exc is True.
"""
result = _parse(value)
if result is not _MISSING:
return result # type: ignore[return-value]

if raise_exc:
if value is None:
raise ValueError(f"Cannot convert None to bool. Expected one of: {_VALID}.")
if isinstance(value, int):
raise ValueError(f"Invalid integer {value!r}. Expected 0 or 1.")
raise ValueError(f"Invalid value {value!r}. Expected one of: {_VALID}.")

return default


def str2bool_exc(value: _Input) -> bool:
"""Convert to bool, raising ValueError on any invalid input (including None).

Shorthand for ``str2bool(value, raise_exc=True)``.

Raises:
TypeError: If value is not str, bool, int, or None.
ValueError: If value is None or unrecognized.
"""
return str2bool(value, raise_exc=True)
Empty file added tests/__init__.py
Empty file.
Loading