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 +24 -0
- textutils/_validation.py +7 -0
- textutils/counting.py +70 -0
- textutils/transform.py +122 -0
- textutils_hs-0.1.0.dist-info/METADATA +93 -0
- textutils_hs-0.1.0.dist-info/RECORD +9 -0
- textutils_hs-0.1.0.dist-info/WHEEL +5 -0
- textutils_hs-0.1.0.dist-info/licenses/LICENSE +8 -0
- textutils_hs-0.1.0.dist-info/top_level.txt +1 -0
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")
|
textutils/_validation.py
ADDED
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,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
|