fetchit-engine 0.3.0__tar.gz → 0.4.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fetchit-engine
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Deterministic text cleanup and AI-writing heuristics. Runs entirely in your process; text never leaves it.
5
5
  Author: Outta Work Solutions
6
6
  License-Expression: Apache-2.0
@@ -16,8 +16,6 @@ Requires-Python: >=3.8
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
18
  License-File: NOTICE
19
- Provides-Extra: spell
20
- Requires-Dist: pyspellchecker>=0.7; extra == "spell"
21
19
  Dynamic: license-file
22
20
 
23
21
  # fetchit-engine
@@ -15,7 +15,7 @@ import json
15
15
  import os
16
16
  import re
17
17
 
18
- ENGINE_VERSION = "0.3.0"
18
+ ENGINE_VERSION = "0.4.0"
19
19
 
20
20
  # --- ruleset (single source of truth, shared with the JS package) -----------
21
21
  _RULESET_PATH = os.path.join(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fetchit-engine
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Deterministic text cleanup and AI-writing heuristics. Runs entirely in your process; text never leaves it.
5
5
  Author: Outta Work Solutions
6
6
  License-Expression: Apache-2.0
@@ -16,8 +16,6 @@ Requires-Python: >=3.8
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
18
  License-File: NOTICE
19
- Provides-Extra: spell
20
- Requires-Dist: pyspellchecker>=0.7; extra == "spell"
21
19
  Dynamic: license-file
22
20
 
23
21
  # fetchit-engine
@@ -5,9 +5,7 @@ pyproject.toml
5
5
  fetchit_engine/__init__.py
6
6
  fetchit_engine/core.py
7
7
  fetchit_engine/ruleset.json
8
- fetchit_engine/spell_core.py
9
8
  fetchit_engine.egg-info/PKG-INFO
10
9
  fetchit_engine.egg-info/SOURCES.txt
11
10
  fetchit_engine.egg-info/dependency_links.txt
12
- fetchit_engine.egg-info/requires.txt
13
11
  fetchit_engine.egg-info/top_level.txt
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fetchit-engine"
7
- version = "0.3.0"
7
+ version = "0.4.0"
8
8
  description = "Deterministic text cleanup and AI-writing heuristics. Runs entirely in your process; text never leaves it."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -25,10 +25,6 @@ Repository = "https://github.com/OuttaWorkSolutions/fetchit-engine"
25
25
  Changelog = "https://github.com/OuttaWorkSolutions/fetchit-engine/blob/main/CHANGELOG.md"
26
26
  Issues = "https://github.com/OuttaWorkSolutions/fetchit-engine/issues"
27
27
 
28
- [project.optional-dependencies]
29
- # Structured spellcheck extracted from the desktop app.
30
- spell = ["pyspellchecker>=0.7"]
31
-
32
28
  [tool.setuptools]
33
29
  packages = ["fetchit_engine"]
34
30
 
@@ -1,117 +0,0 @@
1
- """Structured spellcheck, extracted from the desktop app's spellcheck.py.
2
-
3
- Qt-free: this is the reusable detection and ranking logic with the Qt widget
4
- classes removed, returning misspellings and ranked suggestions as plain data.
5
- The desktop SpellHighlighter / SpellCheckTextEdit can be refactored to consume
6
- this later; the tool web engine does not use it (browsers spellcheck natively).
7
-
8
- Backed by pyspellchecker, which is an optional extra:
9
- pip install "fetchit-engine[spell]"
10
- If it is not installed, get_spellchecker() returns None and the functions here
11
- degrade to empty results, exactly like the desktop app does.
12
-
13
- Offsets in check_text results are CODE POINTS into the text, matching the rest
14
- of the engine (offsetUnit "codePoint"). Results use the same shape as engine
15
- flags: severity "suggest", a ruleId, category, start/end, and text.
16
- """
17
- import re
18
-
19
- WORD_RE = re.compile(r"[A-Za-z']{2,}")
20
-
21
- _spell = None
22
- _spell_load_failed = False
23
-
24
-
25
- def get_spellchecker():
26
- """Lazily load the dictionary (~0.3s once). Returns None if pyspellchecker
27
- is unavailable, so callers keep working without spellcheck."""
28
- global _spell, _spell_load_failed
29
- if _spell is None and not _spell_load_failed:
30
- try:
31
- from spellchecker import SpellChecker
32
-
33
- _spell = SpellChecker()
34
- except Exception:
35
- _spell_load_failed = True
36
- return _spell
37
-
38
-
39
- def is_checkable(word):
40
- """Skip things that are not prose: words with digits, ALL-CAPS acronyms,
41
- and CamelCase / mid-word capitals. Same rule as the desktop app."""
42
- if any(ch.isdigit() for ch in word):
43
- return False
44
- if word.isupper():
45
- return False
46
- if any(ch.isupper() for ch in word[1:]):
47
- return False
48
- return True
49
-
50
-
51
- def suggest(word, max_suggestions=5):
52
- """Ranked replacement suggestions for one word: the best correction first,
53
- then remaining candidates alphabetically, capped. Capitalization of the
54
- original is restored. Returns [] if the word is fine, too long/short, not
55
- checkable, or the dictionary is unavailable. Mirrors the desktop ranking."""
56
- spell = get_spellchecker()
57
- if spell is None:
58
- return []
59
- w = word.strip("'")
60
- # Long garbage strings make candidate search slow; skip them (as the app does).
61
- if not w or not (2 <= len(w) <= 15) or not is_checkable(w):
62
- return []
63
- lw = w.lower()
64
- if lw not in spell.unknown([lw]):
65
- return []
66
- best = spell.correction(lw)
67
- candidates = set(spell.candidates(lw) or set())
68
- candidates.discard(lw)
69
- ordered = []
70
- if best and best != lw:
71
- ordered.append(best)
72
- ordered.extend(sorted(c for c in candidates if c != best))
73
- ordered = ordered[:max_suggestions]
74
- if w[0].isupper():
75
- ordered = [c.capitalize() for c in ordered]
76
- return ordered
77
-
78
-
79
- def check_text(text, with_suggestions=True, max_suggestions=5):
80
- """Find misspelled words in text. Returns a list of records:
81
-
82
- {ruleId, category, severity, start, end, text, suggestions, message}
83
-
84
- start/end are code-point offsets. Every unknown checkable word (2+ letters)
85
- is reported; suggestions are provided for words 2-15 characters long when
86
- with_suggestions is true (longer words are still reported, with []).
87
- Returns [] if the dictionary is unavailable."""
88
- found = []
89
- for m in WORD_RE.finditer(text):
90
- raw = m.group()
91
- word = raw.strip("'")
92
- if word and is_checkable(word):
93
- start = m.start() + raw.index(word)
94
- found.append((start, start + len(word), word))
95
-
96
- spell = get_spellchecker()
97
- if spell is None or not found:
98
- return []
99
-
100
- unknown = spell.unknown({w.lower() for _, _, w in found})
101
- if not unknown:
102
- return []
103
-
104
- results = []
105
- for start, end, word in found:
106
- if word.lower() in unknown:
107
- results.append({
108
- "ruleId": "spell.misspelling",
109
- "category": "spell",
110
- "severity": "suggest",
111
- "start": start,
112
- "end": end,
113
- "text": word,
114
- "suggestions": suggest(word, max_suggestions) if with_suggestions else [],
115
- "message": "Possible misspelling",
116
- })
117
- return results
@@ -1,3 +0,0 @@
1
-
2
- [spell]
3
- pyspellchecker>=0.7
File without changes
File without changes
File without changes
File without changes