farsflow 0.1.0__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.
farsflow-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mahdi Hosseini
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, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.4
2
+ Name: farsflow
3
+ Version: 0.1.0
4
+ Summary: Fast, modern, and modular Persian text preprocessing library.
5
+ Author-email: Mahdi Hosseini <mhossza@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/mhhoss/farsflow
8
+ Project-URL: Documentation, https://github.com/mhhoss/farsflow
9
+ Project-URL: Source, https://github.com/mhhoss/farsflow
10
+ Project-URL: Issues, https://github.com/mhhoss/farsflow/issues
11
+ Keywords: farsi,Persian,preprocessing,nlp,text-cleaning,normalization,tokenization,machine-learning,llm
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Text Processing :: Linguistic
23
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
24
+ Requires-Python: >=3.8
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8.3.5; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ # farsflow
32
+
33
+ farsflow is a small library for **Persian text preprocessing**, designed for practical NLP and LLM workflows.
34
+
35
+ ---
36
+
37
+ ## 🚀 Features
38
+
39
+ - Deterministic pipeline (same input -> same output)
40
+ - Safe character normalization
41
+ - Joiner (ZWNJ) fixes
42
+ - Whitespace & punctuation cleanup
43
+ - Modular processors (use only the components you need)
44
+ - Real‑world style sample tests
45
+ - Zero dependencies
46
+
47
+ ---
48
+
49
+ ## 📦 Installation
50
+
51
+ ```bash
52
+ pip install farsflow
53
+ ```
54
+
55
+ ## ✨ Quick Start
56
+
57
+ ```python
58
+ import farsflow as ff
59
+
60
+ text = "سلام دنیا! این یك تست است که می نویسم ۴۵۶"
61
+ cleaned = ff.clean(text)
62
+ print(cleaned)
63
+ ```
64
+ Expected output: سلام دنیا! این یک تست است که می‌نویسم 456
65
+
66
+ ---
67
+
68
+ ## 🧩 Pipeline Components
69
+
70
+ farsflow ships with a set of modular, composable components:
71
+
72
+ - **Normalizer** — safe character normalization
73
+ - **JoinerFixer** — fixes ZWNJ usage without over-correction
74
+ - **SpaceCleaner** — trims redundant whitespace and punctuation spacing
75
+ - **Pipeline** — orchestrates components in a deterministic order
76
+
77
+ You can customize the pipeline:
78
+
79
+ ```python
80
+ from farsflow import Pipeline, Normalizer, JoinerFixer
81
+
82
+ pipeline = Pipeline(
83
+ Normalizer(),
84
+ JoinerFixer(),
85
+ )
86
+
87
+ text = "متن تستی"
88
+ pipeline(text)
89
+ ```
90
+
91
+ ---
92
+
93
+ 🧪 Testing
94
+
95
+ ```bash
96
+ pytest
97
+ # or:
98
+ pytest path/to/test_file.py
99
+ ```
100
+
101
+ ---
102
+
103
+ 🗺 Roadmap (v0.2.0)
104
+
105
+ - [ ] formalize the behavior of "ff.clean" as a safe and deterministic baseline
106
+ - [ ] add optional normalization controls (e.g. digit normalization)
107
+ - [ ] add optional noise-cleaning emoji and url processors
108
+ - [ ] introduce simple profiles (ff.llm.clean, ff.embedding.query, ff.embedding.index) built on top of the same core
109
+ - [ ] expand real-world test cases to ensure stable behavior across informal, mixed, and noisy Persian text
110
+
111
+ ---
112
+
113
+ 📄 License
114
+
115
+ MIT License — see [LICENSE](LICENSE).
116
+
117
+ ---
118
+
119
+ 🤝 Contributing
120
+
121
+ Contributions are welcome.
122
+ Please open an issue or submit a pull request on GitHub.
123
+
124
+ 📝 Changelog
125
+
126
+ See [CHANGELOG](CHANGELOG) for version history.
@@ -0,0 +1,96 @@
1
+ # farsflow
2
+
3
+ farsflow is a small library for **Persian text preprocessing**, designed for practical NLP and LLM workflows.
4
+
5
+ ---
6
+
7
+ ## 🚀 Features
8
+
9
+ - Deterministic pipeline (same input -> same output)
10
+ - Safe character normalization
11
+ - Joiner (ZWNJ) fixes
12
+ - Whitespace & punctuation cleanup
13
+ - Modular processors (use only the components you need)
14
+ - Real‑world style sample tests
15
+ - Zero dependencies
16
+
17
+ ---
18
+
19
+ ## 📦 Installation
20
+
21
+ ```bash
22
+ pip install farsflow
23
+ ```
24
+
25
+ ## ✨ Quick Start
26
+
27
+ ```python
28
+ import farsflow as ff
29
+
30
+ text = "سلام دنیا! این یك تست است که می نویسم ۴۵۶"
31
+ cleaned = ff.clean(text)
32
+ print(cleaned)
33
+ ```
34
+ Expected output: سلام دنیا! این یک تست است که می‌نویسم 456
35
+
36
+ ---
37
+
38
+ ## 🧩 Pipeline Components
39
+
40
+ farsflow ships with a set of modular, composable components:
41
+
42
+ - **Normalizer** — safe character normalization
43
+ - **JoinerFixer** — fixes ZWNJ usage without over-correction
44
+ - **SpaceCleaner** — trims redundant whitespace and punctuation spacing
45
+ - **Pipeline** — orchestrates components in a deterministic order
46
+
47
+ You can customize the pipeline:
48
+
49
+ ```python
50
+ from farsflow import Pipeline, Normalizer, JoinerFixer
51
+
52
+ pipeline = Pipeline(
53
+ Normalizer(),
54
+ JoinerFixer(),
55
+ )
56
+
57
+ text = "متن تستی"
58
+ pipeline(text)
59
+ ```
60
+
61
+ ---
62
+
63
+ 🧪 Testing
64
+
65
+ ```bash
66
+ pytest
67
+ # or:
68
+ pytest path/to/test_file.py
69
+ ```
70
+
71
+ ---
72
+
73
+ 🗺 Roadmap (v0.2.0)
74
+
75
+ - [ ] formalize the behavior of "ff.clean" as a safe and deterministic baseline
76
+ - [ ] add optional normalization controls (e.g. digit normalization)
77
+ - [ ] add optional noise-cleaning emoji and url processors
78
+ - [ ] introduce simple profiles (ff.llm.clean, ff.embedding.query, ff.embedding.index) built on top of the same core
79
+ - [ ] expand real-world test cases to ensure stable behavior across informal, mixed, and noisy Persian text
80
+
81
+ ---
82
+
83
+ 📄 License
84
+
85
+ MIT License — see [LICENSE](LICENSE).
86
+
87
+ ---
88
+
89
+ 🤝 Contributing
90
+
91
+ Contributions are welcome.
92
+ Please open an issue or submit a pull request on GitHub.
93
+
94
+ 📝 Changelog
95
+
96
+ See [CHANGELOG](CHANGELOG) for version history.
@@ -0,0 +1,82 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "farsflow"
7
+ version = "0.1.0"
8
+ description = "Fast, modern, and modular Persian text preprocessing library."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = "MIT"
12
+ authors = [
13
+ { name = "Mahdi Hosseini", email = "mhossza@gmail.com" }
14
+ ]
15
+
16
+ keywords = [
17
+ "farsi",
18
+ "Persian",
19
+ "preprocessing",
20
+ "nlp",
21
+ "text-cleaning",
22
+ "normalization",
23
+ "tokenization",
24
+ "machine-learning",
25
+ "llm",
26
+ ]
27
+
28
+ classifiers = [
29
+ "Development Status :: 3 - Alpha",
30
+ "Intended Audience :: Developers",
31
+ "Programming Language :: Python",
32
+ "Programming Language :: Python :: 3",
33
+ "Programming Language :: Python :: 3.8",
34
+ "Programming Language :: Python :: 3.9",
35
+ "Programming Language :: Python :: 3.10",
36
+ "Programming Language :: Python :: 3.11",
37
+ "Programming Language :: Python :: 3.12",
38
+ "Programming Language :: Python :: 3.13",
39
+ "Topic :: Text Processing :: Linguistic",
40
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
41
+ ]
42
+
43
+ dependencies = [
44
+ # intentionally empty - farsflow is dependency-free
45
+ ]
46
+
47
+ [project.urls]
48
+ Homepage = "https://github.com/mhhoss/farsflow"
49
+ Documentation = "https://github.com/mhhoss/farsflow"
50
+ Source = "https://github.com/mhhoss/farsflow"
51
+ Issues = "https://github.com/mhhoss/farsflow/issues"
52
+
53
+ [tool.setuptools]
54
+ package-dir = {"" = "src"}
55
+
56
+ [tool.setuptools.packages.find]
57
+ where = ["src"]
58
+ include = ["farsflow*"]
59
+
60
+ [tool.setuptools.package-data]
61
+ "farsflow" = ["data/*.txt", "data/*.json"]
62
+
63
+ [tool.black]
64
+ line-length = 88
65
+ target-version = ["py38"]
66
+
67
+ [tool.isort]
68
+ profile = "black"
69
+
70
+ [tool.pytest.ini_options]
71
+ testpaths = ["tests"]
72
+
73
+ [dependency-groups]
74
+ dev = [
75
+ "build>=1.2.2.post1",
76
+ "twine>=6.1.0",
77
+ ]
78
+
79
+ [project.optional-dependencies]
80
+ dev = [
81
+ "pytest>=8.3.5",
82
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,25 @@
1
+ from .pipeline.engine import Pipeline, create_pipeline
2
+ from .processors.normalizer import NormalizerProcessor as Normalizer
3
+ from .processors.joiner_fixer import JoinerFixerProcessor as JoinerFixer
4
+ from .processors.space_cleaner import SpaceCleanerProcessor as SpaceCleaner
5
+
6
+
7
+ def clean(text: str) -> str:
8
+ """
9
+ Run the default farsflow pipeline on the given text.
10
+
11
+ This is a convenience shortcut for simple use cases.
12
+ For more control, use `create_pipeline()` or `Pipeline` directly.
13
+ """
14
+ pipeline = create_pipeline()
15
+ return pipeline.run(text)
16
+
17
+
18
+ __all__ = [
19
+ "Pipeline",
20
+ "create_pipeline",
21
+ "Normalizer",
22
+ "JoinerFixer",
23
+ "SpaceCleaner",
24
+ "clean",
25
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
File without changes
@@ -0,0 +1,54 @@
1
+ from typing import List, Protocol
2
+
3
+ from farsflow.processors.normalizer import NormalizerProcessor
4
+ from farsflow.processors.joiner_fixer import JoinerFixerProcessor
5
+ from farsflow.processors.space_cleaner import SpaceCleanerProcessor
6
+
7
+
8
+ class Processor(Protocol):
9
+ """Interface for all processors in farsflow."""
10
+ def process(self, text: str) -> str:
11
+ ...
12
+
13
+
14
+ class Pipeline:
15
+ """
16
+ A simple, modular pipeline engine for farsflow.
17
+ Executes a sequence of processors on input text.
18
+ """
19
+
20
+ def __init__(self, processors: List[Processor]):
21
+ self.processors = processors
22
+
23
+ def run(self, text: str) -> str:
24
+ """
25
+ Run the pipeline on the given text.
26
+ Each processor receives the output of the previous one.
27
+ """
28
+ for processor in self.processors:
29
+ text = processor.process(text)
30
+ return text
31
+
32
+ def __call__(self, text: str) -> str:
33
+ """
34
+ Allow calling the pipeline instance directly: pipeline(text).
35
+ """
36
+ return self.run(text)
37
+
38
+
39
+ def create_pipeline(processors: List[Processor] = None) -> Pipeline:
40
+ """
41
+ Creates a farsflow text-processing pipeline.
42
+
43
+ If no processors are provided, the default architecture is used:
44
+ Normalizer -> JoinerFixer -> SpaceCleaner
45
+ """
46
+ if processors is None:
47
+ processors = [
48
+ NormalizerProcessor(),
49
+ JoinerFixerProcessor(),
50
+ SpaceCleanerProcessor(),
51
+ ]
52
+
53
+ return Pipeline(processors)
54
+
File without changes
@@ -0,0 +1,86 @@
1
+ import re
2
+
3
+ ZWNJ = "\u200c"
4
+
5
+
6
+ class JoinerFixerProcessor:
7
+ """
8
+ Restores correct Persian half-spaces (ZWNJ) based on safe, rule-based patterns.
9
+
10
+ - Only applies patterns that are highly reliable
11
+ - Does NOT try to be "smart" or guess semantics
12
+ - Keeps behavior deterministic and idempotent
13
+ """
14
+
15
+ def process(self, text: str) -> str:
16
+ # 0) Cleanup existing joiners/spaces to get a stable base
17
+ text = self._cleanup_wrong_joiners(text)
18
+
19
+ # 1) Prefix-based patterns (می / نمی)
20
+ text = self._apply_prefix_rules(text)
21
+
22
+ # 2) Suffix-based patterns (ها، فعل+ام/ای/اند)
23
+ text = self._apply_suffix_rules(text)
24
+
25
+ # 3) Verb conjugation patterns (رفته‌ام، گفته‌ای، دیده‌اند)
26
+ text = self._apply_verb_conjugation_rules(text)
27
+
28
+ # 4) Common, high-confidence compounds
29
+ text = self._apply_common_compounds(text)
30
+
31
+ # 5) Final cleanup (in case new patterns introduced extra spaces)
32
+ text = self._cleanup_wrong_joiners(text)
33
+
34
+ return text
35
+
36
+ def _apply_prefix_rules(self, text: str) -> str:
37
+ # می + فاصله + فعل/کلمه فارسی -> می‌فعل
38
+ text = re.sub(r"\bمی\s+(?=[آ-ی])", "می" + ZWNJ, text)
39
+
40
+ # نمی + فاصله + فعل/کلمه فارسی -> نمی‌فعل
41
+ text = re.sub(r"\bنمی\s+(?=[آ-ی])", "نمی" + ZWNJ, text)
42
+
43
+ return text
44
+
45
+ def _apply_suffix_rules(self, text: str) -> str:
46
+ # جمع "ها" — فقط اگر قبلش حرف/عدد باشد
47
+ text = re.sub(r"([آ-یA-Za-z0-9])\s+ها\b", r"\1" + ZWNJ + "ها", text)
48
+
49
+ # فعل/اسمِ ختم‌شده به «ه» + ام/ای/اند → ه‌ام/ه‌ای/ه‌اند
50
+ # (ضمیر ملکی عمومی مثل "ایران ام" را دست‌کاری نمی‌کنیم)
51
+ text = re.sub(r"([آ-ی]{2,}ه)\s+ام\b", r"\1" + ZWNJ + "ام", text)
52
+ text = re.sub(r"([آ-ی]{2,}ه)\s+ای\b", r"\1" + ZWNJ + "ای", text)
53
+ text = re.sub(r"([آ-ی]{2,}ه)\s+اند\b", r"\1" + ZWNJ + "اند", text)
54
+
55
+ return text
56
+
57
+ def _apply_verb_conjugation_rules(self, text: str) -> str:
58
+ # Note: این تابع فعلاً روی همان الگوهای بالا تکیه دارد؛
59
+ # اگر در آینده الگوهای بیشتری اضافه شود، اینجا گسترش می‌یابد.
60
+ return text
61
+
62
+ def _apply_common_compounds(self, text: str) -> str:
63
+
64
+ # به کارگیری -> به‌کارگیری
65
+ text = re.sub(r"\bبه\s+کارگیری\b", "به" + ZWNJ + "کارگیری", text)
66
+
67
+ # دست خط -> دست‌خط
68
+ text = re.sub(r"\bدست\s+خط\b", "دست" + ZWNJ + "خط", text)
69
+
70
+ # Note: Only includes a few high‑confidence compounds in v0.1.0.
71
+ # This list will expand gradually in future versions.
72
+
73
+ return text
74
+
75
+ def _cleanup_wrong_joiners(self, text: str) -> str:
76
+ # ۱) ZWNJهای تکراری -> یک ZWNJ
77
+ text = re.sub(ZWNJ + r"{2,}", ZWNJ, text)
78
+
79
+ # ۲) حذف فاصله بعد از ZWNJ
80
+ text = re.sub(ZWNJ + r"\s+", ZWNJ, text)
81
+
82
+ # ۳) حذف فاصله قبل از ZWNJ
83
+ text = re.sub(r"\s+" + ZWNJ, ZWNJ, text)
84
+
85
+ return text
86
+
@@ -0,0 +1,62 @@
1
+ class NormalizerProcessor:
2
+ """
3
+ Architecture-level Persian normalizer for farsflow.
4
+
5
+ - Minimal, deterministic, and semantic-safe
6
+ - Converts Arabic letters to Persian equivalents
7
+ - Removes unwanted invisible/control characters
8
+ - Optionally normalizes Persian/Arabic digits to ASCII digits
9
+ - Does NOT touch spacing, punctuation, or ZWNJ
10
+ - Suitable as a shared layer for ML/LLM/RAG/Embedding
11
+ """
12
+
13
+ CHAR_MAP = str.maketrans({
14
+ "ي": "ی",
15
+ "ى": "ی",
16
+ "ك": "ک",
17
+ "ؤ": "و",
18
+ "أ": "ا",
19
+ "إ": "ا",
20
+ "ة": "ه",
21
+ "ۀ": "ه",
22
+ # Note: "آ "هرگز تغییر نمی‌کند
23
+ })
24
+
25
+ # Remove or normalize only harmful/invisible characters
26
+ INVISIBLE_MAP = str.maketrans({
27
+ "\u200d": "", # ZWJ
28
+ "\u200e": "", # LRM
29
+ "\u200f": "", # RLM
30
+ "\ufeff": "", # BOM
31
+ "\u200b": "", # zero-width space
32
+ "\u2060": "", # word joiner
33
+ "\u00a0": " ", # NBSP → normal space
34
+ # Note: ZWNJ (\u200c) حذف یا تبدیل نمیشود
35
+ })
36
+
37
+ # Persian + Arabic digits -> ASCII digits
38
+ DIGIT_MAP = str.maketrans({
39
+ "۰": "0", "۱": "1", "۲": "2", "۳": "3", "۴": "4",
40
+ "۵": "5", "۶": "6", "۷": "7", "۸": "8", "۹": "9",
41
+ "٠": "0", "١": "1", "٢": "2", "٣": "3", "٤": "4",
42
+ "٥": "5", "٦": "6", "٧": "7", "٨": "8", "٩": "9",
43
+ })
44
+
45
+ def __init__(self, normalize_digits: bool = True) -> None:
46
+ self.normalize_digits = normalize_digits
47
+
48
+ def process(self, text: str) -> str:
49
+ # 1) Arabic -> Persian letters
50
+ text = text.translate(self.CHAR_MAP)
51
+
52
+ # 2) Remove/normalize invisible/control characters (NOT ZWNJ)
53
+ text = text.translate(self.INVISIBLE_MAP)
54
+
55
+ # 3) Digit normalization (optional)
56
+ if self.normalize_digits:
57
+ text = text.translate(self.DIGIT_MAP)
58
+
59
+ # 4) SpaceCleanerProcessor handles spacing
60
+ # 5) JoinerFixerProcessor handles ZWNJ logic
61
+
62
+ return text
@@ -0,0 +1,54 @@
1
+ import re
2
+
3
+
4
+ class SpaceCleanerProcessor:
5
+ """
6
+ Cleans and standardizes spacing and punctuation for Persian text.
7
+ - Normalizes spaces
8
+ - Fixes spacing around punctuation
9
+ - Converts English punctuation to Persian equivalents
10
+ - Handles parentheses spacing safely
11
+ - Does NOT touch ZWNJ or semantic structure
12
+ """
13
+
14
+ PUNCT_MAP = str.maketrans({
15
+ ",": "،",
16
+ ";": "؛",
17
+ "?": "؟",
18
+ })
19
+
20
+ # Persian punctuation that must NOT have space before them
21
+ NO_SPACE_BEFORE = r"[،؛:؟!.,]"
22
+
23
+ # Punctuation that must have exactly one space after them
24
+ NEED_SPACE_AFTER = r"[،؛:؟!()]"
25
+
26
+ def process(self, text: str) -> str:
27
+
28
+ # 1) Convert English punctuation to Persian equivalents
29
+ text = text.translate(self.PUNCT_MAP)
30
+
31
+ # 2) Remove spaces before punctuation
32
+ text = re.sub(r"\s+(?=" + self.NO_SPACE_BEFORE + ")", "", text)
33
+
34
+ # 3) Ensure exactly one space after punctuation
35
+ text = re.sub(r"(" + self.NEED_SPACE_AFTER + r")(?=\S)", r"\1 ", text)
36
+
37
+ # 4) Fix spacing inside parentheses
38
+ text = re.sub(r"\(\s+", "(", text)
39
+ text = re.sub(r"\s+\)", ")", text)
40
+
41
+ # 5) Add missing space before "("
42
+ text = re.sub(r"([^\s(])\(", r"\1 (", text)
43
+
44
+ # 6) Add missing space after ")"
45
+ text = re.sub(r"\)([^\s)])", r") \1", text)
46
+
47
+ # 7) Remove space before punctuation after ")"
48
+ text = re.sub(r"\)\s+(?=" + self.NO_SPACE_BEFORE + ")", r")", text)
49
+
50
+ # 8) Collapse multiple spaces
51
+ text = " ".join(text.split())
52
+
53
+ return text
54
+
File without changes
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.4
2
+ Name: farsflow
3
+ Version: 0.1.0
4
+ Summary: Fast, modern, and modular Persian text preprocessing library.
5
+ Author-email: Mahdi Hosseini <mhossza@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/mhhoss/farsflow
8
+ Project-URL: Documentation, https://github.com/mhhoss/farsflow
9
+ Project-URL: Source, https://github.com/mhhoss/farsflow
10
+ Project-URL: Issues, https://github.com/mhhoss/farsflow/issues
11
+ Keywords: farsi,Persian,preprocessing,nlp,text-cleaning,normalization,tokenization,machine-learning,llm
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Text Processing :: Linguistic
23
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
24
+ Requires-Python: >=3.8
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8.3.5; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ # farsflow
32
+
33
+ farsflow is a small library for **Persian text preprocessing**, designed for practical NLP and LLM workflows.
34
+
35
+ ---
36
+
37
+ ## 🚀 Features
38
+
39
+ - Deterministic pipeline (same input -> same output)
40
+ - Safe character normalization
41
+ - Joiner (ZWNJ) fixes
42
+ - Whitespace & punctuation cleanup
43
+ - Modular processors (use only the components you need)
44
+ - Real‑world style sample tests
45
+ - Zero dependencies
46
+
47
+ ---
48
+
49
+ ## 📦 Installation
50
+
51
+ ```bash
52
+ pip install farsflow
53
+ ```
54
+
55
+ ## ✨ Quick Start
56
+
57
+ ```python
58
+ import farsflow as ff
59
+
60
+ text = "سلام دنیا! این یك تست است که می نویسم ۴۵۶"
61
+ cleaned = ff.clean(text)
62
+ print(cleaned)
63
+ ```
64
+ Expected output: سلام دنیا! این یک تست است که می‌نویسم 456
65
+
66
+ ---
67
+
68
+ ## 🧩 Pipeline Components
69
+
70
+ farsflow ships with a set of modular, composable components:
71
+
72
+ - **Normalizer** — safe character normalization
73
+ - **JoinerFixer** — fixes ZWNJ usage without over-correction
74
+ - **SpaceCleaner** — trims redundant whitespace and punctuation spacing
75
+ - **Pipeline** — orchestrates components in a deterministic order
76
+
77
+ You can customize the pipeline:
78
+
79
+ ```python
80
+ from farsflow import Pipeline, Normalizer, JoinerFixer
81
+
82
+ pipeline = Pipeline(
83
+ Normalizer(),
84
+ JoinerFixer(),
85
+ )
86
+
87
+ text = "متن تستی"
88
+ pipeline(text)
89
+ ```
90
+
91
+ ---
92
+
93
+ 🧪 Testing
94
+
95
+ ```bash
96
+ pytest
97
+ # or:
98
+ pytest path/to/test_file.py
99
+ ```
100
+
101
+ ---
102
+
103
+ 🗺 Roadmap (v0.2.0)
104
+
105
+ - [ ] formalize the behavior of "ff.clean" as a safe and deterministic baseline
106
+ - [ ] add optional normalization controls (e.g. digit normalization)
107
+ - [ ] add optional noise-cleaning emoji and url processors
108
+ - [ ] introduce simple profiles (ff.llm.clean, ff.embedding.query, ff.embedding.index) built on top of the same core
109
+ - [ ] expand real-world test cases to ensure stable behavior across informal, mixed, and noisy Persian text
110
+
111
+ ---
112
+
113
+ 📄 License
114
+
115
+ MIT License — see [LICENSE](LICENSE).
116
+
117
+ ---
118
+
119
+ 🤝 Contributing
120
+
121
+ Contributions are welcome.
122
+ Please open an issue or submit a pull request on GitHub.
123
+
124
+ 📝 Changelog
125
+
126
+ See [CHANGELOG](CHANGELOG) for version history.
@@ -0,0 +1,17 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/farsflow/__init__.py
5
+ src/farsflow/__version__.py
6
+ src/farsflow.egg-info/PKG-INFO
7
+ src/farsflow.egg-info/SOURCES.txt
8
+ src/farsflow.egg-info/dependency_links.txt
9
+ src/farsflow.egg-info/requires.txt
10
+ src/farsflow.egg-info/top_level.txt
11
+ src/farsflow/pipeline/__init__.py
12
+ src/farsflow/pipeline/engine.py
13
+ src/farsflow/processors/__init__.py
14
+ src/farsflow/processors/joiner_fixer.py
15
+ src/farsflow/processors/normalizer.py
16
+ src/farsflow/processors/space_cleaner.py
17
+ src/farsflow/profiles/__init__.py
@@ -0,0 +1,3 @@
1
+
2
+ [dev]
3
+ pytest>=8.3.5
@@ -0,0 +1 @@
1
+ farsflow