toolify 0.2.0__tar.gz → 1.0.1__tar.gz

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.
Files changed (36) hide show
  1. {toolify-0.2.0 → toolify-1.0.1}/LICENSE +2 -2
  2. toolify-1.0.1/PKG-INFO +141 -0
  3. toolify-1.0.1/README.md +102 -0
  4. toolify-1.0.1/pyproject.toml +68 -0
  5. toolify-1.0.1/setup.py +3 -0
  6. toolify-1.0.1/tests/test_ai.py +223 -0
  7. toolify-1.0.1/tests/test_tools.py +103 -0
  8. toolify-1.0.1/tests/test_youtube.py +190 -0
  9. toolify-1.0.1/toolify/__init__.py +35 -0
  10. toolify-1.0.1/toolify/ai/__init__.py +15 -0
  11. toolify-1.0.1/toolify/ai/ai.py +15 -0
  12. toolify-1.0.1/toolify/ai/huggingface.py +439 -0
  13. toolify-1.0.1/toolify/audio/__init__.py +15 -0
  14. toolify-1.0.1/toolify/audio/audio.py +199 -0
  15. toolify-1.0.1/toolify/plots/__init__.py +7 -0
  16. toolify-1.0.1/toolify/plots/plots.py +75 -0
  17. toolify-1.0.1/toolify/tools/__init__.py +12 -0
  18. toolify-1.0.1/toolify/tools/constants.py +178 -0
  19. toolify-1.0.1/toolify/tools/tools.py +369 -0
  20. toolify-1.0.1/toolify/youtube/__init__.py +21 -0
  21. toolify-1.0.1/toolify/youtube/youtube.py +453 -0
  22. toolify-1.0.1/toolify.egg-info/PKG-INFO +141 -0
  23. toolify-1.0.1/toolify.egg-info/SOURCES.txt +25 -0
  24. toolify-1.0.1/toolify.egg-info/requires.txt +20 -0
  25. toolify-0.2.0/PKG-INFO +0 -107
  26. toolify-0.2.0/README.md +0 -74
  27. toolify-0.2.0/setup.py +0 -33
  28. toolify-0.2.0/toolify/__init__.py +0 -1
  29. toolify-0.2.0/toolify/tools-old.py +0 -234
  30. toolify-0.2.0/toolify/tools.py +0 -292
  31. toolify-0.2.0/toolify.egg-info/PKG-INFO +0 -107
  32. toolify-0.2.0/toolify.egg-info/SOURCES.txt +0 -11
  33. toolify-0.2.0/toolify.egg-info/requires.txt +0 -2
  34. {toolify-0.2.0 → toolify-1.0.1}/setup.cfg +0 -0
  35. {toolify-0.2.0 → toolify-1.0.1}/toolify.egg-info/dependency_links.txt +0 -0
  36. {toolify-0.2.0 → toolify-1.0.1}/toolify.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Amr Abdelsamea
3
+ Copyright (c) 2025 Amr Abdelsamea
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
toolify-1.0.1/PKG-INFO ADDED
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.4
2
+ Name: toolify
3
+ Version: 1.0.1
4
+ Summary: Utilities for terminal output, Arabic text, audio, plotting, Hugging Face, and YouTube.
5
+ Author-email: Amr Abdelsamea <amr.abdelsamee33@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Amr-abdelsamee/toolify
8
+ Project-URL: Source, https://github.com/Amr-abdelsamee/toolify
9
+ Project-URL: Documentation, https://amr-abdelsamee.github.io/toolify/
10
+ Project-URL: Issues, https://github.com/Amr-abdelsamee/toolify/issues
11
+ Project-URL: Changelog, https://github.com/Amr-abdelsamee/toolify/blob/main/CHANGELOG.md
12
+ Keywords: tools,arabic,audio,plotting,youtube,huggingface
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Operating System :: OS Independent
17
+ Requires-Python: >=3.11
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: python-bidi>=0.6.6
21
+ Requires-Dist: arabic-reshaper>=3.0.0
22
+ Requires-Dist: youtube-transcript-api>=1.1.0
23
+ Requires-Dist: yt-dlp>=2025.10.22
24
+ Requires-Dist: tqdm>=4.67.1
25
+ Requires-Dist: matplotlib>=3.11.0
26
+ Requires-Dist: librosa>=0.11.0
27
+ Requires-Dist: numpy>=1.25
28
+ Requires-Dist: soundfile>=0.14.0
29
+ Requires-Dist: huggingface-hub>=1.21.0
30
+ Requires-Dist: hf-transfer>=0.1.9
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest; extra == "dev"
33
+ Requires-Dist: build; extra == "dev"
34
+ Requires-Dist: twine; extra == "dev"
35
+ Provides-Extra: docs
36
+ Requires-Dist: mkdocs-material; extra == "docs"
37
+ Requires-Dist: mkdocstrings[python]; extra == "docs"
38
+ Dynamic: license-file
39
+
40
+ <h1>
41
+ <img
42
+ src="docs/assets/images/toolify-logo.png"
43
+ alt="Toolify logo"
44
+ width="84"
45
+ align="absmiddle"
46
+ >
47
+ Toolify
48
+ </h1>
49
+
50
+ [![PyPI](https://img.shields.io/pypi/v/toolify)](https://pypi.org/project/toolify/)
51
+ [![Python](https://img.shields.io/pypi/pyversions/toolify)](https://pypi.org/project/toolify/)
52
+ [![Documentation](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://amr-abdelsamee.github.io/toolify/)
53
+ [![License](https://img.shields.io/pypi/l/toolify)](https://github.com/Amr-abdelsamee/toolify/blob/main/LICENSE)
54
+
55
+ Toolify is a Python utility library for terminal output, Arabic text handling,
56
+ logging, plotting, audio inspection, Hugging Face downloads, and YouTube media
57
+ downloads.
58
+
59
+ ## Features
60
+
61
+ | Module | Purpose |
62
+ | --- | --- |
63
+ | `toolify.tools` | Colored output, Arabic text, tables, logging, and confirmation |
64
+ | `toolify.plots` | Line-plot generation |
65
+ | `toolify.audio` | Silence detection, spectrograms, and duration helpers |
66
+ | `toolify.ai` | Hugging Face repository inspection and downloads |
67
+ | `toolify.youtube` | YouTube video, transcript, and playlist utilities |
68
+
69
+ ## Installation
70
+
71
+ Install the latest release from PyPI:
72
+
73
+ ```bash
74
+ pip install toolify
75
+ ```
76
+
77
+ Python 3.11 or newer is required. FFmpeg is also required when YouTube video
78
+ and audio streams need to be merged.
79
+
80
+ ### Install FFmpeg
81
+
82
+ FFmpeg is a system application, not a Python package.
83
+
84
+ #### Windows
85
+
86
+ Install it from PowerShell with Windows Package Manager:
87
+
88
+ ```powershell
89
+ winget install --exact --id Gyan.FFmpeg
90
+ ```
91
+
92
+ Close and reopen the terminal after installation. If `winget` is unavailable,
93
+ download a Windows build from the
94
+ [official FFmpeg download page](https://ffmpeg.org/download.html), extract it,
95
+ and add its `bin` directory to the Windows `PATH`.
96
+
97
+ #### Linux
98
+
99
+ Ubuntu, Debian, and Linux Mint:
100
+
101
+ ```bash
102
+ sudo apt update
103
+ sudo apt install ffmpeg
104
+ ```
105
+
106
+
107
+ Verify the installation on either operating system:
108
+
109
+ ```bash
110
+ ffmpeg -version
111
+ ```
112
+
113
+
114
+ ## Documentation
115
+
116
+ The complete guides and API reference are available in the
117
+ [Toolify documentation](https://amr-abdelsamee.github.io/toolify/).
118
+
119
+ - [Installation](https://amr-abdelsamee.github.io/toolify/getting-started/installation/)
120
+ - [Quick start](https://amr-abdelsamee.github.io/toolify/getting-started/quickstart/)
121
+ - [User guides](https://amr-abdelsamee.github.io/toolify/guides/tools/)
122
+ - [API reference](https://amr-abdelsamee.github.io/toolify/reference/tools/)
123
+ - [Changelog](https://github.com/Amr-abdelsamee/toolify/blob/main/CHANGELOG.md)
124
+
125
+ ## Development
126
+
127
+ ```bash
128
+ git clone https://github.com/Amr-abdelsamee/toolify.git
129
+ cd toolify
130
+ pip install -e ".[dev,docs]"
131
+ python -m pytest -m "not integration"
132
+ mkdocs serve
133
+ ```
134
+
135
+ See the [contributing guide](https://amr-abdelsamee.github.io/toolify/development/contributing/)
136
+ and [release guide](https://amr-abdelsamee.github.io/toolify/development/releasing/).
137
+
138
+ ## License
139
+
140
+ Toolify is licensed under the
141
+ [MIT License](https://github.com/Amr-abdelsamee/toolify/blob/main/LICENSE).
@@ -0,0 +1,102 @@
1
+ <h1>
2
+ <img
3
+ src="docs/assets/images/toolify-logo.png"
4
+ alt="Toolify logo"
5
+ width="84"
6
+ align="absmiddle"
7
+ >
8
+ Toolify
9
+ </h1>
10
+
11
+ [![PyPI](https://img.shields.io/pypi/v/toolify)](https://pypi.org/project/toolify/)
12
+ [![Python](https://img.shields.io/pypi/pyversions/toolify)](https://pypi.org/project/toolify/)
13
+ [![Documentation](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://amr-abdelsamee.github.io/toolify/)
14
+ [![License](https://img.shields.io/pypi/l/toolify)](https://github.com/Amr-abdelsamee/toolify/blob/main/LICENSE)
15
+
16
+ Toolify is a Python utility library for terminal output, Arabic text handling,
17
+ logging, plotting, audio inspection, Hugging Face downloads, and YouTube media
18
+ downloads.
19
+
20
+ ## Features
21
+
22
+ | Module | Purpose |
23
+ | --- | --- |
24
+ | `toolify.tools` | Colored output, Arabic text, tables, logging, and confirmation |
25
+ | `toolify.plots` | Line-plot generation |
26
+ | `toolify.audio` | Silence detection, spectrograms, and duration helpers |
27
+ | `toolify.ai` | Hugging Face repository inspection and downloads |
28
+ | `toolify.youtube` | YouTube video, transcript, and playlist utilities |
29
+
30
+ ## Installation
31
+
32
+ Install the latest release from PyPI:
33
+
34
+ ```bash
35
+ pip install toolify
36
+ ```
37
+
38
+ Python 3.11 or newer is required. FFmpeg is also required when YouTube video
39
+ and audio streams need to be merged.
40
+
41
+ ### Install FFmpeg
42
+
43
+ FFmpeg is a system application, not a Python package.
44
+
45
+ #### Windows
46
+
47
+ Install it from PowerShell with Windows Package Manager:
48
+
49
+ ```powershell
50
+ winget install --exact --id Gyan.FFmpeg
51
+ ```
52
+
53
+ Close and reopen the terminal after installation. If `winget` is unavailable,
54
+ download a Windows build from the
55
+ [official FFmpeg download page](https://ffmpeg.org/download.html), extract it,
56
+ and add its `bin` directory to the Windows `PATH`.
57
+
58
+ #### Linux
59
+
60
+ Ubuntu, Debian, and Linux Mint:
61
+
62
+ ```bash
63
+ sudo apt update
64
+ sudo apt install ffmpeg
65
+ ```
66
+
67
+
68
+ Verify the installation on either operating system:
69
+
70
+ ```bash
71
+ ffmpeg -version
72
+ ```
73
+
74
+
75
+ ## Documentation
76
+
77
+ The complete guides and API reference are available in the
78
+ [Toolify documentation](https://amr-abdelsamee.github.io/toolify/).
79
+
80
+ - [Installation](https://amr-abdelsamee.github.io/toolify/getting-started/installation/)
81
+ - [Quick start](https://amr-abdelsamee.github.io/toolify/getting-started/quickstart/)
82
+ - [User guides](https://amr-abdelsamee.github.io/toolify/guides/tools/)
83
+ - [API reference](https://amr-abdelsamee.github.io/toolify/reference/tools/)
84
+ - [Changelog](https://github.com/Amr-abdelsamee/toolify/blob/main/CHANGELOG.md)
85
+
86
+ ## Development
87
+
88
+ ```bash
89
+ git clone https://github.com/Amr-abdelsamee/toolify.git
90
+ cd toolify
91
+ pip install -e ".[dev,docs]"
92
+ python -m pytest -m "not integration"
93
+ mkdocs serve
94
+ ```
95
+
96
+ See the [contributing guide](https://amr-abdelsamee.github.io/toolify/development/contributing/)
97
+ and [release guide](https://amr-abdelsamee.github.io/toolify/development/releasing/).
98
+
99
+ ## License
100
+
101
+ Toolify is licensed under the
102
+ [MIT License](https://github.com/Amr-abdelsamee/toolify/blob/main/LICENSE).
@@ -0,0 +1,68 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.3"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "toolify"
7
+ version = "1.0.1"
8
+ description = "Utilities for terminal output, Arabic text, audio, plotting, Hugging Face, and YouTube."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+
13
+ authors = [
14
+ { name = "Amr Abdelsamea", email = "amr.abdelsamee33@gmail.com" }
15
+ ]
16
+
17
+ keywords = [
18
+ "tools",
19
+ "arabic",
20
+ "audio",
21
+ "plotting",
22
+ "youtube",
23
+ "huggingface",
24
+ ]
25
+
26
+ classifiers = [
27
+ "Programming Language :: Python :: 3",
28
+ "Programming Language :: Python :: 3.11",
29
+ "Programming Language :: Python :: 3.12",
30
+ "Operating System :: OS Independent",
31
+ ]
32
+
33
+ dependencies = [
34
+ "python-bidi>=0.6.6",
35
+ "arabic-reshaper>=3.0.0",
36
+ "youtube-transcript-api>=1.1.0",
37
+ "yt-dlp>=2025.10.22",
38
+ "tqdm>=4.67.1",
39
+ "matplotlib>=3.11.0",
40
+ "librosa>=0.11.0",
41
+ "numpy>=1.25",
42
+ "soundfile>=0.14.0",
43
+ "huggingface-hub>=1.21.0",
44
+ "hf-transfer>=0.1.9",
45
+ ]
46
+
47
+ [project.optional-dependencies]
48
+ dev = [
49
+ "pytest",
50
+ "build",
51
+ "twine",
52
+ ]
53
+
54
+ docs = [
55
+ "mkdocs-material",
56
+ "mkdocstrings[python]",
57
+ ]
58
+
59
+ [project.urls]
60
+ Homepage = "https://github.com/Amr-abdelsamee/toolify"
61
+ Source = "https://github.com/Amr-abdelsamee/toolify"
62
+ Documentation = "https://amr-abdelsamee.github.io/toolify/"
63
+ Issues = "https://github.com/Amr-abdelsamee/toolify/issues"
64
+ Changelog = "https://github.com/Amr-abdelsamee/toolify/blob/main/CHANGELOG.md"
65
+
66
+ [tool.setuptools.packages.find]
67
+ include = ["toolify*"]
68
+ exclude = ["tests*", "docs*"]
toolify-1.0.1/setup.py ADDED
@@ -0,0 +1,3 @@
1
+ from setuptools import setup
2
+
3
+ setup()
@@ -0,0 +1,223 @@
1
+ import os
2
+ from pathlib import Path
3
+ from types import SimpleNamespace
4
+
5
+ import pytest
6
+
7
+ import toolify.ai.huggingface as hf
8
+ from toolify.ai import (
9
+ get_hf_model_size,
10
+ get_hf_dataset_size,
11
+ download_hf_model,
12
+ download_hf_dataset,
13
+ )
14
+
15
+
16
+ GB = 1024**3
17
+
18
+
19
+ def test_ai_public_imports():
20
+ assert callable(get_hf_model_size)
21
+ assert callable(get_hf_dataset_size)
22
+ assert callable(download_hf_model)
23
+ assert callable(download_hf_dataset)
24
+
25
+
26
+ def test_get_hf_model_size_falls_back_when_safetensors_size_is_none(monkeypatch):
27
+ """This test catches the real bug:
28
+ safetensors metadata exists but total size cannot be extracted.
29
+ The fallback must reset total_bytes to 0, not keep it as None.
30
+ """
31
+
32
+ class FakeHfApi:
33
+ def __init__(self, token=None):
34
+ self.token = token
35
+
36
+ def get_safetensors_metadata(self, repo_id, revision=None):
37
+ # No total_size, no metadata["total_size"], no files_metadata sizes.
38
+ return SimpleNamespace()
39
+
40
+ def model_info(self, repo_id, revision=None, files_metadata=True):
41
+ return SimpleNamespace(
42
+ siblings=[
43
+ SimpleNamespace(size=1 * GB),
44
+ SimpleNamespace(size=2 * GB),
45
+ SimpleNamespace(size=None),
46
+ ]
47
+ )
48
+
49
+ monkeypatch.setattr(
50
+ hf,
51
+ "_require_huggingface_hub",
52
+ lambda: (FakeHfApi, None),
53
+ )
54
+
55
+ size = hf.get_hf_model_size("org/test-model", verbose=False)
56
+
57
+ assert size == 3.0
58
+
59
+
60
+ def test_get_hf_model_size_uses_safetensors_when_valid(monkeypatch):
61
+ class FakeHfApi:
62
+ def __init__(self, token=None):
63
+ self.token = token
64
+
65
+ def get_safetensors_metadata(self, repo_id, revision=None):
66
+ return SimpleNamespace(total_size=4 * GB)
67
+
68
+ def model_info(self, *args, **kwargs):
69
+ raise AssertionError("model_info should not be called")
70
+
71
+ monkeypatch.setattr(
72
+ hf,
73
+ "_require_huggingface_hub",
74
+ lambda: (FakeHfApi, None),
75
+ )
76
+
77
+ size = hf.get_hf_model_size("org/test-model", verbose=False)
78
+
79
+ assert size == 4.0
80
+
81
+
82
+ def test_get_hf_dataset_size(monkeypatch):
83
+ class FakeHfApi:
84
+ def __init__(self, token=None):
85
+ self.token = token
86
+
87
+ def dataset_info(self, repo_id, revision=None, expand=None):
88
+ assert expand == ["usedStorage"]
89
+ return SimpleNamespace(usedStorage=5 * GB)
90
+
91
+ monkeypatch.setattr(
92
+ hf,
93
+ "_require_huggingface_hub",
94
+ lambda: (FakeHfApi, None),
95
+ )
96
+
97
+ size = hf.get_hf_dataset_size("org/test-dataset", verbose=False)
98
+
99
+ assert size == 5.0
100
+
101
+
102
+ def test_download_hf_model_uses_local_dir(monkeypatch, tmp_path):
103
+ calls = {}
104
+
105
+ class FakeHfApi:
106
+ pass
107
+
108
+ def fake_snapshot_download(**kwargs):
109
+ calls.update(kwargs)
110
+
111
+ local_dir = Path(kwargs["local_dir"])
112
+ local_dir.mkdir(parents=True, exist_ok=True)
113
+ (local_dir / "README.md").write_text("fake model", encoding="utf-8")
114
+
115
+ return str(local_dir)
116
+
117
+ monkeypatch.setattr(
118
+ hf,
119
+ "_require_huggingface_hub",
120
+ lambda: (FakeHfApi, fake_snapshot_download),
121
+ )
122
+
123
+ target_dir = tmp_path / "custom_model"
124
+
125
+ path = hf.download_hf_model(
126
+ "org/test-model",
127
+ local_dir=target_dir,
128
+ show_size=False,
129
+ verbose=False,
130
+ )
131
+
132
+ assert path == target_dir
133
+ assert path.exists()
134
+ assert (path / "README.md").exists()
135
+ assert calls["repo_id"] == "org/test-model"
136
+ assert calls["repo_type"] == "model"
137
+ assert Path(calls["local_dir"]) == target_dir
138
+
139
+
140
+ def test_download_hf_dataset_uses_base_dir(monkeypatch, tmp_path):
141
+ calls = {}
142
+
143
+ class FakeHfApi:
144
+ pass
145
+
146
+ def fake_snapshot_download(**kwargs):
147
+ calls.update(kwargs)
148
+
149
+ local_dir = Path(kwargs["local_dir"])
150
+ local_dir.mkdir(parents=True, exist_ok=True)
151
+ (local_dir / "README.md").write_text("fake dataset", encoding="utf-8")
152
+
153
+ return str(local_dir)
154
+
155
+ monkeypatch.setattr(
156
+ hf,
157
+ "_require_huggingface_hub",
158
+ lambda: (FakeHfApi, fake_snapshot_download),
159
+ )
160
+
161
+ path = hf.download_hf_dataset(
162
+ "org/test-dataset",
163
+ base_dir=tmp_path / "datasets",
164
+ show_size=False,
165
+ verbose=False,
166
+ )
167
+
168
+ expected = tmp_path / "datasets" / "org_test-dataset"
169
+
170
+ assert path == expected
171
+ assert path.exists()
172
+ assert (path / "README.md").exists()
173
+ assert calls["repo_id"] == "org/test-dataset"
174
+ assert calls["repo_type"] == "dataset"
175
+ assert Path(calls["local_dir"]) == expected
176
+
177
+
178
+ def test_download_hf_repo_rejects_invalid_repo_type(tmp_path):
179
+ with pytest.raises(ValueError):
180
+ hf.download_hf_repo(
181
+ "org/test-repo",
182
+ repo_type="invalid", # type: ignore[arg-type]
183
+ base_dir=tmp_path,
184
+ verbose=False,
185
+ )
186
+
187
+
188
+ @pytest.mark.integration
189
+ def test_integration_get_real_hf_model_size():
190
+ """Real Hugging Face test.
191
+
192
+ Run only when:
193
+ RUN_HF_INTEGRATION=1 python -m pytest tests/test_ai.py -m integration -v
194
+ """
195
+ if os.getenv("RUN_HF_INTEGRATION") != "1":
196
+ pytest.skip("Set RUN_HF_INTEGRATION=1 to run Hugging Face integration tests.")
197
+
198
+ size = hf.get_hf_model_size(
199
+ "Qwen/Qwen3-Reranker-0.6B",
200
+ verbose=True,
201
+ raise_on_error=True,
202
+ )
203
+
204
+ assert size is not None
205
+ assert size > 0
206
+
207
+
208
+ @pytest.mark.integration
209
+ def test_integration_download_model_readme_only(tmp_path):
210
+ """Downloads only tiny files from a real model repo."""
211
+ if os.getenv("RUN_HF_INTEGRATION") != "1":
212
+ pytest.skip("Set RUN_HF_INTEGRATION=1 to run Hugging Face integration tests.")
213
+
214
+ path = hf.download_hf_model(
215
+ "Qwen/Qwen3-Reranker-0.6B",
216
+ local_dir=tmp_path / "qwen_reranker_test",
217
+ allow_patterns=["README.md", "config.json"],
218
+ show_size=False,
219
+ verbose=True,
220
+ )
221
+
222
+ assert path.exists()
223
+ assert any(path.iterdir())
@@ -0,0 +1,103 @@
1
+ import os
2
+ import logging
3
+ import pytest
4
+ from datetime import datetime
5
+
6
+ from toolify.tools import (
7
+ pct,
8
+ print_table,
9
+ setup_logger,
10
+ strip_tashkeel,
11
+ confirm,
12
+ )
13
+
14
+
15
+ def test_strip_tashkeel_removes_arabic_diacritics():
16
+ text = "مُحَمَّدٌ"
17
+ result = strip_tashkeel(text)
18
+
19
+ assert result == "محمد"
20
+
21
+
22
+ def test_strip_tashkeel_removes_special_symbols_by_default():
23
+ text = "ا^ل>س<لام؞"
24
+ result = strip_tashkeel(text)
25
+
26
+ assert result == "السلام"
27
+
28
+
29
+ def test_strip_tashkeel_can_keep_special_symbols():
30
+ text = "ا^ل>س<لام؞"
31
+ result = strip_tashkeel(text, remove_special_symbols=False)
32
+
33
+ assert result == "ا^ل>س<لام؞"
34
+
35
+
36
+ def test_pct_prints_plain_text_when_ec_false(capsys):
37
+ pct("Hello Toolify", ec=False)
38
+
39
+ captured = capsys.readouterr()
40
+
41
+ assert captured.out == "Hello Toolify\n"
42
+
43
+
44
+ def test_pct_prints_with_emoji_when_ec_false(capsys):
45
+ pct("Done", ec=False, emoji="success", end_emoji="fire")
46
+
47
+ captured = capsys.readouterr()
48
+
49
+ assert captured.out == "✅ Done 🔥\n"
50
+
51
+
52
+ def test_pct_custom_end(capsys):
53
+ pct("Hello", ec=False, end="")
54
+
55
+ captured = capsys.readouterr()
56
+
57
+ assert captured.out == "Hello"
58
+
59
+
60
+ def test_print_table_outputs_headers_and_rows(capsys):
61
+ headers = ["Name", "Score"]
62
+ rows = [
63
+ ["Ali", 95],
64
+ ["Sara", 88],
65
+ ]
66
+
67
+ print_table(headers, rows)
68
+
69
+ captured = capsys.readouterr()
70
+
71
+ assert "Name" in captured.out
72
+ assert "Score" in captured.out
73
+ assert "Ali" in captured.out
74
+ assert "Sara" in captured.out
75
+
76
+
77
+ def test_setup_logger_creates_log_file(tmp_path):
78
+ date_str = datetime.now().strftime("%Y_%m_%d")
79
+ log_file = tmp_path /f"test.log"
80
+ logger = setup_logger(__name__, str(log_file))
81
+ log_file = tmp_path /f"test__{date_str}.log"
82
+ logger.info("Hello logger")
83
+
84
+ assert isinstance(logger, logging.Logger)
85
+ assert log_file.exists()
86
+
87
+ content = log_file.read_text(encoding="utf-8")
88
+ assert "Hello logger" in content
89
+
90
+
91
+ def test_confirm_returns_none_when_user_confirms(monkeypatch):
92
+ monkeypatch.setattr("builtins.input", lambda _: "yes")
93
+
94
+ result = confirm(message="Continue? ")
95
+
96
+ assert result is None
97
+
98
+
99
+ def test_confirm_exits_when_user_declines(monkeypatch):
100
+ monkeypatch.setattr("builtins.input", lambda _: "no")
101
+
102
+ with pytest.raises(SystemExit):
103
+ confirm(message="Continue? ")