textutils-hs 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
textutils/__init__.py ADDED
@@ -0,0 +1,24 @@
1
+ """textutils: small, dependency-free helpers for common text tasks.
2
+
3
+ Public API:
4
+ ``word_count``, ``character_count``, ``reverse``, ``capitalize_words``,
5
+ ``slugify``.
6
+
7
+ Everything else (modules or names starting with ``_``) is internal and may
8
+ change without notice.
9
+ """
10
+
11
+ from importlib.metadata import version
12
+
13
+ from .counting import character_count, word_count
14
+ from .transform import capitalize_words, reverse, slugify
15
+
16
+ __all__ = [
17
+ "capitalize_words",
18
+ "character_count",
19
+ "reverse",
20
+ "slugify",
21
+ "word_count",
22
+ ]
23
+
24
+ __version__ = version("textutils-hs")
@@ -0,0 +1,7 @@
1
+ """Private input-validation helpers for textutils."""
2
+
3
+
4
+ def _ensure_str(text: object) -> None:
5
+ """Raise a ``TypeError`` if *text* is not a :class:`str`."""
6
+ if not isinstance(text, str):
7
+ raise TypeError(f"text must be str, not {type(text).__name__}")
textutils/counting.py ADDED
@@ -0,0 +1,70 @@
1
+ """Word-counting helpers for textutils."""
2
+
3
+ from ._validation import _ensure_str
4
+
5
+ __all__ = ["word_count", "character_count"]
6
+
7
+
8
+ def word_count(text: str) -> int:
9
+ """Count the words in *text*.
10
+
11
+ Words are separated by any amount of whitespace, so runs of spaces,
12
+ tabs, and newlines all act as a single separator.
13
+
14
+ Parameters
15
+ ----------
16
+ text : str
17
+ The string to count words in.
18
+
19
+ Returns
20
+ -------
21
+ int
22
+ The number of words in *text*. An empty string returns ``0``.
23
+
24
+ Raises
25
+ ------
26
+ TypeError
27
+ If *text* is not a :class:`str`.
28
+
29
+ Examples
30
+ --------
31
+ >>> word_count("hello world")
32
+ 2
33
+ >>> word_count(" spaced out ")
34
+ 2
35
+ >>> word_count("")
36
+ 0
37
+ """
38
+ _ensure_str(text)
39
+ return len(text.split())
40
+
41
+
42
+ def character_count(text: str) -> int:
43
+ """Count the characters in *text*, spaces included.
44
+
45
+ Parameters
46
+ ----------
47
+ text : str
48
+ The string to count characters in.
49
+
50
+ Returns
51
+ -------
52
+ int
53
+ The number of characters in *text*. An empty string returns ``0``.
54
+
55
+ Raises
56
+ ------
57
+ TypeError
58
+ If *text* is not a :class:`str`.
59
+
60
+ Examples
61
+ --------
62
+ >>> character_count("hello")
63
+ 5
64
+ >>> character_count("hi there")
65
+ 8
66
+ >>> character_count("")
67
+ 0
68
+ """
69
+ _ensure_str(text)
70
+ return len(text)
textutils/transform.py ADDED
@@ -0,0 +1,122 @@
1
+ """String-transforming helpers for textutils."""
2
+
3
+ import re
4
+
5
+ from ._validation import _ensure_str
6
+
7
+ __all__ = ["reverse", "capitalize_words", "slugify"]
8
+
9
+ _WORD_START = re.compile(r"(?<!\S)\w")
10
+
11
+
12
+ def reverse(text: str) -> str:
13
+ """Return *text* with its characters in reverse order.
14
+
15
+ Parameters
16
+ ----------
17
+ text : str
18
+ The string to reverse.
19
+
20
+ Returns
21
+ -------
22
+ str
23
+ A new string holding the characters of *text* in reverse order. An
24
+ empty string returns an empty string.
25
+
26
+ Raises
27
+ ------
28
+ TypeError
29
+ If *text* is not a :class:`str`.
30
+
31
+ Examples
32
+ --------
33
+ >>> reverse("stressed")
34
+ 'desserts'
35
+ >>> reverse("abc")
36
+ 'cba'
37
+ >>> reverse("")
38
+ ''
39
+ """
40
+ _ensure_str(text)
41
+ return text[::-1]
42
+
43
+
44
+ def capitalize_words(text: str) -> str:
45
+ """Uppercase the first character of every word in *text*.
46
+
47
+ Only the first character of each word is changed; the rest of each word
48
+ is left exactly as it was. Whitespace, including newlines, is preserved.
49
+
50
+ Parameters
51
+ ----------
52
+ text : str
53
+ The string to transform.
54
+
55
+ Returns
56
+ -------
57
+ str
58
+ A new string with the first character of each word uppercased. An
59
+ empty string returns an empty string.
60
+
61
+ Raises
62
+ ------
63
+ TypeError
64
+ If *text* is not a :class:`str`.
65
+
66
+ Examples
67
+ --------
68
+ >>> capitalize_words("hello world")
69
+ 'Hello World'
70
+ >>> capitalize_words("hello WORLD")
71
+ 'Hello WORLD'
72
+ >>> capitalize_words("it's fine")
73
+ "It's Fine"
74
+ >>> capitalize_words("")
75
+ ''
76
+ """
77
+ _ensure_str(text)
78
+ return _WORD_START.sub(lambda match: match.group(0).upper(), text)
79
+
80
+
81
+ def slugify(text: str) -> str:
82
+ """Turn *text* into a URL-friendly slug.
83
+
84
+ The input is lowercased, every run of characters outside ``a-z0-9``
85
+ (spaces, punctuation, underscores, accents, ...) is replaced by a single
86
+ hyphen, and leading/trailing hyphens are removed. Existing hyphens are
87
+ kept as separators, so ``"well-known"`` stays ``"well-known"`` while
88
+ ``"foo_bar"`` becomes ``"foo-bar"``.
89
+
90
+ Parameters
91
+ ----------
92
+ text : str
93
+ The string to slugify.
94
+
95
+ Returns
96
+ -------
97
+ str
98
+ A slug containing only lowercase ASCII letters, digits, and internal
99
+ hyphens. An empty string, or a string with no letters/digits,
100
+ returns ``""``.
101
+
102
+ Raises
103
+ ------
104
+ TypeError
105
+ If *text* is not a :class:`str`.
106
+
107
+ Examples
108
+ --------
109
+ >>> slugify("Hello, World!")
110
+ 'hello-world'
111
+ >>> slugify(" Hello, World! Open Source ")
112
+ 'hello-world-open-source'
113
+ >>> slugify("Python --- is fun")
114
+ 'python-is-fun'
115
+ >>> slugify("Version 2.0 released")
116
+ 'version-2-0-released'
117
+ >>> slugify("!!!")
118
+ ''
119
+ """
120
+ _ensure_str(text)
121
+ slug = re.sub(r"[^a-z0-9]+", "-", text.lower())
122
+ return slug.strip("-")
@@ -0,0 +1,93 @@
1
+ Metadata-Version: 2.4
2
+ Name: textutils-hs
3
+ Version: 0.1.0
4
+ Summary: Small, dependency-free helpers for common text tasks.
5
+ Author: Hamza Fikri
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/hamzasinko/textutils
8
+ Project-URL: Repository, https://github.com/hamzasinko/textutils
9
+ Project-URL: Issues, https://github.com/hamzasinko/textutils/issues
10
+ Project-URL: Changelog, https://github.com/hamzasinko/textutils/blob/main/CHANGELOG.md
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Text Processing
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=8.0; extra == "dev"
26
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
27
+ Requires-Dist: black>=24.0; extra == "dev"
28
+ Requires-Dist: ruff>=0.5; extra == "dev"
29
+ Requires-Dist: pre-commit>=3.7; extra == "dev"
30
+ Requires-Dist: build>=1.2; extra == "dev"
31
+ Requires-Dist: twine>=5.0; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ # textutils
35
+
36
+ Small, dependency-free Python helpers for common text tasks: counting words,
37
+ counting characters, reversing a string, and capitalizing each word. Every
38
+ function has type hints, a full docstring, and tests.
39
+
40
+ ## Features
41
+
42
+ - `word_count(text)` — number of words in a string.
43
+ - `character_count(text)` — number of characters in a string.
44
+ - `reverse(text)` — the string reversed.
45
+ - `capitalize_words(text)` — first letter of each word capitalized.
46
+ - `slugify(text)` — a URL-friendly slug (lowercase, hyphen-separated).
47
+
48
+ Edge cases are handled: empty strings return empty results, and non-string
49
+ input raises `TypeError`.
50
+
51
+ ## Installation
52
+
53
+ Requires Python 3.9 or newer. No third-party runtime dependencies.
54
+
55
+ Clone and install in editable mode with the development tools:
56
+
57
+ ```bash
58
+ git clone https://github.com/hamzasinko/textutils.git
59
+ cd textutils
60
+ python -m pip install -e ".[dev]"
61
+ ```
62
+
63
+ Run the tests:
64
+
65
+ ```bash
66
+ python -m pytest
67
+ ```
68
+
69
+ ## Usage
70
+
71
+ ```python
72
+ from textutils import word_count, character_count, reverse, capitalize_words, slugify
73
+
74
+ word_count("hello world") # 2
75
+ character_count("hello") # 5
76
+ reverse("stressed") # "desserts"
77
+ capitalize_words("hello world") # "Hello World"
78
+ slugify("Hello, World!") # "hello-world"
79
+ ```
80
+
81
+ ## Contributing
82
+
83
+ Contributions are welcome.
84
+
85
+ 1. Fork the repository on GitHub.
86
+ 2. Create a branch: `git checkout -b feature/my-change`.
87
+ 3. Make your change, keeping commits small and focused.
88
+ 4. Run the tests: `python -m pytest`.
89
+ 5. Push your branch and open a pull request.
90
+
91
+ ## License
92
+
93
+ Released under the MIT License. See [LICENSE](LICENSE) for details.
@@ -0,0 +1,9 @@
1
+ textutils/__init__.py,sha256=LXivX6RwU0wIAjiZTUZQnkEnlra4HdawYSxL81BIDXg,575
2
+ textutils/_validation.py,sha256=CGK773yl57Kr2WMwK2eUuTmvMAZvAHF22HtCKaC0bwo,266
3
+ textutils/counting.py,sha256=hxY56bllMU-ySe2g4QtlbCg4YJCUgRehQCH3lLi4W4M,1353
4
+ textutils/transform.py,sha256=aRnaKBwLD3RK38bbVTOCh_eiudqHdl9H_hFYAHaw7qE,2873
5
+ textutils_hs-0.1.0.dist-info/licenses/LICENSE,sha256=YoV42jzb_U2WLhGFmPAGJeEZlqCQU160UxqBffMmSdQ,306
6
+ textutils_hs-0.1.0.dist-info/METADATA,sha256=U5CGl_JTkeclCWl56sL36p-UaZeRsHsYkV7iSvbD0Ps,2989
7
+ textutils_hs-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
8
+ textutils_hs-0.1.0.dist-info/top_level.txt,sha256=amWgGsYwaWzVol7zPaWIjxj2Of6hEptj7rE9MR3bp_E,10
9
+ textutils_hs-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,8 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hamza Fikri
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction. See the full MIT License text at
8
+ https://opensource.org/license/mit.
@@ -0,0 +1 @@
1
+ textutils