├── .gitignore ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── aspell.2.c ├── aspell.c ├── pyaspell.py ├── pyaspell ├── __init__.py └── pyaspell.py ├── pypi-update.sh ├── setup.2.py ├── setup.3.py ├── setup.ctypes.py ├── test ├── config.py ├── test.py └── unittests.py └── windows.rst /.gitignore: -------------------------------------------------------------------------------- 1 | setup.py 2 | MANIFEST 3 | dist 4 | build 5 | *.so 6 | *.pyc 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | addons: 3 | apt: 4 | packages: 5 | - python3 6 | - python3-dev 7 | - aspell 8 | - aspell-en 9 | - libaspell-dev 10 | 11 | matrix: 12 | include: 13 | - compiler: gcc 14 | - compiler: clang 15 | 16 | script: 17 | - make 18 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006-2019 Wojciech Muła 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or 5 | without modification, are permitted provided that the following 6 | conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following 12 | disclaimer in the documentation and/or other materials 13 | provided with the distribution. 14 | * Neither the name of the Wojciech Muła nor the names of its 15 | contributors may be used to endorse or promote products 16 | derived from this software without specific prior written 17 | permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 20 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 21 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 22 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 24 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 27 | USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 | THE POSSIBILITY OF SUCH DAMAGE. 32 | 33 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include LICENSE 3 | include test/*.py 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: 2 | .PHONY: work test test_py2 test_py3 clean 3 | 4 | export PYTHONPATH := .:$(PYTHONPATH):$(PATH) 5 | 6 | test: test_py2 test_py3 7 | 8 | test_py3: 9 | python3 setup.3.py build_ext --inplace 10 | python3 test/unittests.py 11 | 12 | test_py2: 13 | python2 setup.2.py build_ext --inplace 14 | python2 test/unittests.py 15 | 16 | clean: 17 | rm -f *.so 18 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | aspell-python - Python bindings for GNU aspell 3 | ======================================================================== 4 | 5 | .. image:: https://travis-ci.org/WojciechMula/aspell-python.svg?branch=master 6 | :target: https://travis-ci.org/WojciechMula/aspell-python 7 | 8 | .. contents:: 9 | 10 | 11 | Introduction 12 | ============ 13 | 14 | `GNU Aspell`__ is a leading spelling engine, fast, with many 15 | dictionaries available. Take a look at `Python Cookbook`__ --- 16 | Ryan Kelly have collected links to all python bindings for spellers. 17 | 18 | **aspell-python** is a Python wrapper for GNU Aspell, there 19 | are two variants: 20 | 21 | * ``pyaspell.py`` --- Python library, that utilize ctypes__ 22 | module; compatible with python3; 23 | * ``aspell-python`` --- C extension, two versions are available, 24 | one for Python 2.x, and Python 3.x. 25 | 26 | C exension exist in two versions: one compatible with Python 2.x 27 | and other with Python 3.x. 28 | 29 | Version for Py2 has been tested with Python 2.1, Python 2.3.4 30 | and Python 2.4.1. Probably it works fine with all Python versions 31 | not older than 2.0. Version for Py3 has been tested with Python 3.2. 32 | 33 | __ http://docs.python.org/library/ctypes.html 34 | __ http://aspell.net 35 | __ http://code.activestate.com/recipes/117221/ 36 | 37 | 38 | License 39 | ======= 40 | 41 | Both libraries are licensed under BSD license 42 | 43 | 44 | Author 45 | ====== 46 | 47 | Wojciech Muła, wojciech_mula@poczta.onet.pl 48 | 49 | Thanks to: 50 | 51 | * **Adam Karpierz** for conviencing me to change license from GPL to BSD 52 | and for compiling early versions of C extension under Windows 53 | 54 | * **Gora Mohanty** for reporting a bug. 55 | 56 | 57 | Installation 58 | ============ 59 | 60 | To build & install module for python2.x please use script setup.2.py, i.e.:: 61 | 62 | $ python setup.2.py build 63 | $ python setup.2.py install 64 | 65 | Module for python3.x is build with setup.3.py:: 66 | 67 | $ python3 setup.3.py build 68 | $ python3 setup.3.py install 69 | 70 | Note ``python3`` name. Many Linux distributions ship both Python 2 and 3, 71 | and use the different name to distinguish versions. 72 | 73 | 74 | Details 75 | ------- 76 | 77 | You need to have libaspell headers installed, Debian package is called 78 | ``libaspell-dev``, other distributions should have similar package. 79 | 80 | In order to install **aspell-python** for all users, you must be a root. 81 | If you are, type following command:: 82 | 83 | $ python setup.py install 84 | 85 | It builds package and installs ``aspell.so`` in directory 86 | ``/usr/lib/{python}/site-packages``. 87 | 88 | If you don't have root login, you can append ``--user`` to the install 89 | command, to install it for the current user in 90 | ``~/.local/lib/{python}/site-packages``. 91 | 92 | 93 | Windows issues 94 | -------------- 95 | 96 | To correctly install aspell's dictionaries in Windows some additional 97 | work is needed. `Eric Woudenberg`__ has prepared detailed step-by-step 98 | instruction avaiable in file `windows.rst `_. 99 | 100 | __ http://www.woudy.org/ 101 | 102 | 103 | API 104 | === 105 | 106 | **Aspell-python** module is seen in python under name ``aspell``. So, 107 | **aspell-python** module is imported in following way:: 108 | 109 | import aspell 110 | 111 | The module provides Speller_ class, two methods, and three types 112 | of exceptions --- all described below. 113 | 114 | 115 | Methods 116 | ------- 117 | 118 | 119 | .. _ConfigKeysMeth: 120 | 121 | ConfigKeys() => dictionary 122 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 123 | 124 | Method returns a dictionary, where keys are names of configuration 125 | item, values are 3-tuples: 126 | 127 | * key type (``string``, ``integer``, ``boolean``, ``list``) 128 | * default value for the key 129 | * short description - "internal" means that aspell doesn't provide 130 | any description of item and you shouldn't set/change it, unless 131 | you know what you do 132 | 133 | 134 | Aspell's documentation covers in details all of keys and their meaning. 135 | Below is a list of most useful and obvious options (it is a filtered 136 | output of ``ConfigKeys``). 137 | 138 | :: 139 | 140 | ('data-dir', 'string', '/usr/lib/aspell-0.60', 'location of language data files') 141 | ('dict-dir', 'string', '/usr/lib/aspell-0.60', 'location of the main word list') 142 | ('encoding', 'string', 'ISO-8859-2', 'encoding to expect data to be in') 143 | ('home-dir', 'string', '/home/wojtek', 'location for personal files') 144 | ('ignore', 'integer', 1, 'ignore words <= n chars') 145 | ('ignore-accents', 'boolean', False, 'ignore accents when checking words -- CURRENTLY IGNORED') 146 | ('ignore-case', 'boolean', False, 'ignore case when checking words') 147 | ('ignore-repl', 'boolean', False, 'ignore commands to store replacement pairs') 148 | ('keyboard', 'string', 'standard', 'keyboard definition to use for typo analysis') 149 | ('lang', 'string', 'pl_PL', 'language code') 150 | ('master', 'string', 'pl_PL', 'base name of the main dictionary to use') 151 | ('personal-path', 'string', '/home/wojtek/.aspell.pl_PL.pws', 'internal') 152 | ('repl-path', 'string', '/home/wojtek/.aspell.pl_PL.prepl', 'internal') 153 | ('run-together', 'boolean', False, 'consider run-together words legal') 154 | ('save-repl', 'boolean', True, 'save replacement pairs on save all') 155 | ('warn', 'boolean', True, 'enable warnings') 156 | ('backup', 'boolean', True, 'create a backup file by appending ".bak"') 157 | ('reverse', 'boolean', False, 'reverse the order of the suggest list') 158 | ('suggest', 'boolean', True, 'suggest possible replacements') 159 | 160 | 161 | Classes 162 | ------- 163 | 164 | _`Speller`\ () 165 | ~~~~~~~~~~~~~~ 166 | 167 | Method creates an AspellSpeller_ object which is an interface to the GNU 168 | Aspell. 169 | 170 | ``Speller`` called with no parameters creates speller using default 171 | configuration. If you want to change or set some parameter you can pass 172 | pair of strings: key and it's value. One can get available keys using 173 | ConfigKeys_. 174 | 175 | >>> aspell.Speller("key", "value") 176 | 177 | If you want to set more than one pair of key&value, pass the list 178 | of pairs to the Speller(). 179 | 180 | >>> aspell.Speller( ("k1","v1"), ("k2","v2"), ("k3","v3") ) 181 | 182 | 183 | Exceptions 184 | ---------- 185 | 186 | Module defines following errors: 187 | 188 | * AspellConfigError_, 189 | * AspellModuleError_ and 190 | * AspellSpellerError_. 191 | 192 | Additionally ``TypeError`` is raised when you pass wrong parameters to 193 | method. 194 | 195 | _`AspellConfigError` 196 | ~~~~~~~~~~~~~~~~~~~~ 197 | 198 | Error is reported by methods Speller_ and ConfigKeys_. The most common 199 | error is passing unknown key. 200 | 201 | >>> s = aspell.Speller('python', '2.3') 202 | Traceback (most recent call last): 203 | File "", line 1, in ? 204 | aspell.AspellConfigError: The key "python" is unknown. 205 | >>> 206 | 207 | 208 | _`AspellModuleError` 209 | ~~~~~~~~~~~~~~~~~~~~ 210 | 211 | Error is reported when module can't allocate aspell structures. 212 | 213 | 214 | _`AspellSpellerError` 215 | ~~~~~~~~~~~~~~~~~~~~~ 216 | 217 | 218 | Error is reported by ``libaspell``. 219 | 220 | >>> # we set master dictionary file, the file doesn't exist 221 | >>> s = Speller('master', '/home/dictionary.rws') 222 | Traceback (most recent call last): 223 | File "", line 1, in ? 224 | aspell.AspellSpellerError: The file "/home/dictionary.rws" can not be opened for reading. 225 | >>> 226 | 227 | _`AspellSpeller` Object 228 | ----------------------- 229 | 230 | The AspellSpeller object provides interface to the aspell. It has 231 | several methods, described below. 232 | 233 | * ConfigKeys_ 234 | * check_ 235 | * suggest_ 236 | * addReplacement_ 237 | * addtoPersonal_ 238 | * saveAllwords_ 239 | * addtoSession_ 240 | * clearSession_ 241 | * getPersonalwordlist_ 242 | * getSessionwordlist_ 243 | * getMainwordlist_ 244 | 245 | In examples the assumption is that following code has been executed 246 | earlier: 247 | 248 | >>> import aspell 249 | >>> s = aspell.Speller('lang', 'en') 250 | >>> s 251 | 252 | >>> 253 | 254 | 255 | _`ConfigKeys`\ () => dictionary 256 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 257 | 258 | **New in version 1.1, changed in 1.13.** 259 | 260 | Method returns current configuration of speller. 261 | 262 | Result has the same meaning as ``ConfigKeys()`` procedure. 263 | 264 | 265 | _`setConfigKey`\ (key, value) 266 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 267 | 268 | **New in version 1.14** 269 | 270 | Method alters configuration value. Note that depending on key's type value 271 | is expected to be: string, boolean or integer. 272 | 273 | Although setting all keys is possible, changes to some of them have no 274 | effect. For example changing **lang** doesn't change current language, 275 | it's an aspell limitation (feature). 276 | 277 | 278 | _`check`\ (word) => boolean 279 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 280 | 281 | Method checks spelling of given ``word``. If ``word`` is present in 282 | the main or personal (see addtoPersonal_) or session dictionary 283 | (see addtoSession_) returns True, otherwise False. 284 | 285 | >>> s.check('word') # correct word 286 | True 287 | >>> s.check('wrod') # incorrect 288 | False 289 | >>> 290 | 291 | **New in version 1.13.** 292 | 293 | It's possible to use operator ``in`` or ``not in`` instead 294 | of ``check()``. 295 | 296 | >>> 'word' in s 297 | True 298 | >>> 'wrod' in s 299 | False 300 | >>> 301 | 302 | 303 | _`suggest` (word) => list of suggestions 304 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 305 | 306 | Method returns a list of suggested spellings for given word. Even if 307 | word is correct, i.e. method check_ returned 1, action is performed. 308 | 309 | >>> s.suggest('wrod') # we made mistake, what aspell suggests? 310 | ['word', 'Rod', 'rod', 'Brod', 'prod', 'trod', 'Wood', 'wood', 'wried'] 311 | >>> 312 | 313 | **Warning!** ``suggest()`` in aspell 0.50 is very, very slow. I 314 | recommend caching it's results if program calls the function several 315 | times with the same argument. 316 | 317 | 318 | _`addReplacement`\ (incorrect, correct) => None 319 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 320 | 321 | Adds a replacement pair, it affects order of words in suggest_ result. 322 | 323 | >>> # we choose 7th word from previous result 324 | >>> s.addReplacement('wrod', 'trod') 325 | 326 | >>> # and the selected word appears at the 1st position 327 | >>> s.suggest('word') 328 | ['trod', 'word', 'Rod', 'rod', 'Brod', 'prod', 'Wood', 'wood', 'wried'] 329 | 330 | If config key ``save-repl`` is ``true`` method saveAllwords_ saves 331 | the replacement pairs to file ``~/.aspell.{lang_code}.prepl``. 332 | 333 | 334 | _`addtoPersonal`\ (word) => None 335 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 336 | 337 | Adds word to the personal dictionary, which is stored in file 338 | ``~./.aspell.{lang_code}.pws``. The added words are available for 339 | AspellSpeller object, but they remain unsaved until method saveAllwords_ 340 | is called. 341 | 342 | :: 343 | 344 | # personal dictionary is empty now 345 | $ cat ~/.aspell.en.pws 346 | personal_ws-1.1 en 0 347 | 348 | $ python 349 | >>> import aspell 350 | >>> s = aspell.Speller('lang', 'en') 351 | # word 'aspell' doesn't exist 352 | >>> s.check('aspell') 353 | 0 354 | 355 | # we add it to the personal dictionary 356 | >>> s.addtoPersonal('aspell') 357 | 358 | # and now aspell knows it 359 | >>> s.check('aspell') 360 | 1 361 | 362 | # we save personal dictionary 363 | >>> s.saveAllwords() 364 | 365 | # new word appeared in the file 366 | $ cat ~/.aspell.en.pws 367 | personal_ws-1.1 en 1 368 | aspell 369 | 370 | # check it once again 371 | $ python 372 | >>> import aspell 373 | >>> s = aspell.Speller('lang', 'en') 374 | 375 | # aspell still knows it's own name 376 | >>> s.check('aspell') 377 | 1 378 | 379 | >>> s.check('aaa') 380 | 0 381 | >>> s.check('bbb') 382 | 0 383 | # add incorrect words, they shouldn't be saved 384 | >>> s.addtoPersonal('aaa') 385 | >>> s.addtoPersonal('bbb') 386 | >>> s.check('aaa') 387 | 1 388 | >>> s.check('bbb') 389 | 1 390 | 391 | # we've exit without saving, words 'aaa' and 'bbb' doesn't exists 392 | $ cat ~/.aspell.en.pws 393 | personal_ws-1.1 en 1 394 | aspell 395 | $ 396 | 397 | 398 | _`addtoSession`\ (word) => None 399 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 400 | 401 | Adds word to the session dictionary. The session dictionary is 402 | volatile, it is not saved to any file. It is destroyed with 403 | AspellSpeller_ object or when method clearSession_ is called. 404 | 405 | 406 | _`saveAllwords`\ () => None 407 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 408 | 409 | Save all words from personal dictionary. 410 | 411 | 412 | _`clearSession`\ () => None 413 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 414 | 415 | Clears session dictionary. 416 | 417 | >>> import aspell 418 | >>> s = aspell.Speller('lang', 'en') 419 | >>> s.check('linux') 420 | 0 421 | >>> s.addtoSession('linux') 422 | >>> s.check('linux') 423 | 1 424 | >>> s.clearSession() 425 | >>> s.check('linux') 426 | 0 427 | 428 | _`getPersonalwordlist`\ () => [list of strings] 429 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 430 | 431 | Returns list of words from personal dictionary. 432 | 433 | _`getSessionwordlist`\ () => [list of strings] 434 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 435 | 436 | Returns list of words from session dictionary. 437 | 438 | >>> s.addtoSession('aaa') 439 | >>> s.addtoSession('bbb') 440 | >>> s.getSessionwordlist() 441 | ['aaa', 'bbb'] 442 | >>> s.clearSession() 443 | >>> s.getSessionwordlist() 444 | [] 445 | >>> 446 | 447 | 448 | _`getMainwordlist`\ () => [list of strings] 449 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 450 | 451 | Returns list of words from the main dictionary. 452 | 453 | 454 | Known problems 455 | ============== 456 | 457 | All version of aspell I've tested have the same error - calling method 458 | getMainwordlist_ produces ``SIGKILL``. It is aspell problem and if you 459 | really need a full list of words, use external program 460 | ``word-list-compress``. 461 | 462 | 463 | .. list-table:: 464 | 465 | * - method 466 | - aspell 0.50.5 467 | - aspell 0.60.2 468 | - aspell 0.60.3 469 | 470 | * - ConfigKeys_ 471 | - ok 472 | - ok 473 | - ok 474 | 475 | * - Speller_ 476 | - ok 477 | - ok 478 | - ok 479 | 480 | * - check_ 481 | - ok 482 | - ok 483 | - ok 484 | 485 | * - suggest_ 486 | - ok 487 | - ok 488 | - ok 489 | 490 | * - addReplacement_ 491 | - ok 492 | - ok 493 | - ok 494 | 495 | * - addtoPersonal_ 496 | - ok 497 | - ok 498 | - ok 499 | 500 | * - saveAllwords_ 501 | - ok 502 | - ok 503 | - ok 504 | 505 | * - addtoSession_ 506 | - ok 507 | - ok 508 | - ok 509 | 510 | * - clearSession_ 511 | - ok 512 | - AspellSpellerError_ 513 | - ok 514 | 515 | * - getPersonalwordlist_ 516 | - ok 517 | - **SIGKILL** 518 | - ok 519 | 520 | * - getSessionwordlist_ 521 | - ok 522 | - **SIGKILL** 523 | - ok 524 | 525 | * - getMainwordlist_ 526 | - **SIGKILL** 527 | - **SIGKILL** 528 | - **SIGKILL** 529 | 530 | Character encoding 531 | ================== 532 | 533 | Aspell uses 8-bit encoding. The encoding depend on dictionary setting and 534 | is stored in key ``encoding``. One can obtain this key using speller's 535 | ConfigKeys_. 536 | 537 | If your application uses other encoding than aspell, the translation is 538 | needed. Here is a sample session (polish dictionary is used). 539 | 540 | >>> import aspell 541 | >>> s=aspell.Speller('lang', 'pl') 542 | >>> 543 | >>> s.ConfigKeys()['encoding'] 544 | ('string', u'iso-8859-1', 'encoding to expect data to be in') 545 | >>> enc =s.ConfigKeys()['encoding'][1] 546 | >>> enc # dictionary encoding 547 | 'iso-8859-1' 548 | >>> word # encoding of word is utf8 549 | # 'gżegżółka' means in some polish dialects 'cuckoo' 550 | 'g\xc5\xbceg\xc5\xbc\xc3\xb3\xc5\x82ka' 551 | >>> s.check(word) 552 | 0 553 | >>> s.check( unicode(word, 'utf-8').encode(enc) ) 554 | 1 555 | 556 | -------------------------------------------------------------------------------- /aspell.2.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WojciechMula/aspell-python/864d326c1dfb04aaea3cc476459f4ba6f4425670/aspell.2.c -------------------------------------------------------------------------------- /aspell.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | Python wrapper for aspell, version 1.12 4 | 5 | Tested with: 6 | * aspell 0.60.2 & python 3.2 7 | 8 | Released under BSD license 9 | 10 | Wojciech Muła 11 | wojciech_mula@poczta.onet.pl 12 | 13 | History: 14 | - 2011-03-31: 15 | * added __contains__ method 16 | 17 | - 2011-03-xx: 18 | * ConfigKeys returns dictionary 19 | 20 | - 2011-02-21, 2011-03-06: 21 | * compliance with py3k 22 | 23 | - 2006-04-xx: 24 | * license is BSD now 25 | 26 | - 18.08.2005: 27 | * fixed method ConfigKeys - now works with aspell 0.60 28 | thanks to Gora Mohanty for note 29 | * fixed stupid bug in Speller 30 | - 29.01.2005: 31 | * added method ConfigKeys() 32 | - 30.12.2004: 33 | * new() constructor replaced with Speller() 34 | * constructor accepts now much simpler syntax for passing 35 | multiple arguments 36 | * removed methods releated to configuratinon from AspellSpeller 37 | object 38 | * global method ConfigKeys() 39 | - 7.10.2004: 40 | * fixed saveAllwords method 41 | patch by Helmut Jarausch 42 | - 28.08.2004: 43 | * tested with python 2.3.4 44 | * now aspell.new accepts list of config keys 45 | (see typescript2.txt for examples) 46 | - 20-22.08.2004: 47 | * first version of module 48 | 49 | $Id$ 50 | ******************************************************************************/ 51 | 52 | #include 53 | #include 54 | 55 | #define Speller(pyobject) (((aspell_AspellObject*)pyobject)->speller) 56 | #define Encoding(pyobject) (((aspell_AspellObject*)pyobject)->encoding) 57 | 58 | static char* DefaultEncoding = "ascii"; 59 | 60 | static PyTypeObject aspell_AspellType; 61 | 62 | /* error reported by speller */ 63 | static PyObject* _AspellSpellerException; 64 | 65 | /* error reported by speller's config */ 66 | static PyObject* _AspellConfigException; 67 | 68 | /* error reported by module */ 69 | static PyObject* _AspellModuleException; 70 | 71 | typedef struct { 72 | PyObject_HEAD 73 | char* encoding; /* internal encoding */ 74 | AspellSpeller* speller; /* the speller */ 75 | } aspell_AspellObject; 76 | 77 | 78 | /* helper function: converts an aspell word list into python list */ 79 | static PyObject* AspellWordList2PythonList(PyObject* self, const AspellWordList* wordlist) { 80 | PyObject* list; 81 | PyObject* elem; 82 | 83 | AspellStringEnumeration* elements; 84 | const char* word; 85 | 86 | list = PyList_New(0); 87 | if (!list) { 88 | PyErr_SetString(PyExc_Exception, "can't create new list"); 89 | return NULL; 90 | } 91 | 92 | elements = aspell_word_list_elements(wordlist); 93 | while ( (word=aspell_string_enumeration_next(elements)) != 0) { 94 | elem = PyUnicode_Decode(word, strlen(word), Encoding(self), NULL); 95 | 96 | if (elem == 0) { 97 | delete_aspell_string_enumeration(elements); 98 | Py_DECREF(list); 99 | return NULL; 100 | } 101 | 102 | if (PyList_Append(list, elem) == -1) { 103 | delete_aspell_string_enumeration(elements); 104 | Py_DECREF(elem); 105 | Py_DECREF(list); 106 | return NULL; 107 | } 108 | } 109 | 110 | delete_aspell_string_enumeration(elements); 111 | return list; 112 | } 113 | 114 | /* helper function: converts an aspell string list into python list */ 115 | static PyObject* AspellStringList2PythonList(const AspellStringList* wordlist) { 116 | PyObject* list; 117 | AspellStringEnumeration* elements; 118 | const char* word; 119 | 120 | list = PyList_New(0); 121 | if (!list) { 122 | PyErr_SetString(PyExc_Exception, "can't create new list"); 123 | return NULL; 124 | } 125 | 126 | elements = aspell_string_list_elements(wordlist); 127 | while ( (word=aspell_string_enumeration_next(elements)) != 0) 128 | if (PyList_Append(list, Py_BuildValue("s", word)) == -1) { 129 | PyErr_SetString(PyExc_Exception, "It is almost impossible, but happend! Can't append element to the list."); 130 | delete_aspell_string_enumeration(elements); 131 | Py_DECREF(list); 132 | return NULL; 133 | } 134 | delete_aspell_string_enumeration(elements); 135 | return list; 136 | } 137 | 138 | 139 | static PyObject* get_single_arg_string( 140 | PyObject* self, // [in] 141 | PyObject* obj, // [in] 142 | char** word, // [out] 143 | Py_ssize_t* size // [out] 144 | ); 145 | 146 | /* Create a new speller *******************************************************/ 147 | static PyObject* new_speller(PyTypeObject* self, PyObject* args, PyObject* kwargs) { 148 | aspell_AspellObject* newobj; 149 | 150 | AspellSpeller* speller = 0; 151 | AspellConfig* config; 152 | AspellCanHaveError* possible_error; 153 | 154 | int i; 155 | int n; /* arg count */ 156 | char *key, *value; 157 | char *encoding; 158 | 159 | config = new_aspell_config(); 160 | if (config == NULL) { 161 | PyErr_SetString(_AspellModuleException, "can't create config"); 162 | return NULL; 163 | } 164 | 165 | /* check constructor arguments */ 166 | n = PyTuple_Size(args); 167 | switch (n) { 168 | case 0: /* no arguments passed */ 169 | break; 170 | 171 | case 2: /* constructor is called with single pair: key & value */ 172 | if (PyArg_ParseTuple(args, "ss", &key, &value)) { 173 | if (!aspell_config_replace(config, key, value)) { 174 | PyErr_SetString(_AspellConfigException, aspell_config_error_message(config)); 175 | goto arg_error; 176 | } 177 | break; 178 | } 179 | PyErr_Clear(); 180 | default: /* list of tuples key&value */ 181 | for (i=0; ispeller = speller; 228 | newobj->encoding = encoding; 229 | 230 | return (PyObject*)newobj; 231 | 232 | /* argument error: before return NULL we need to 233 | delete speller's config we've created */ 234 | arg_error: 235 | delete_aspell_config(config); 236 | return NULL; 237 | } 238 | 239 | /* Delete speller *************************************************************/ 240 | static void speller_dealloc(PyObject* self) { 241 | if (Encoding(self) != DefaultEncoding) 242 | free(Encoding(self)); 243 | 244 | delete_aspell_speller( Speller(self) ); 245 | PyObject_Del(self); 246 | } 247 | 248 | 249 | static PyObject* configkeys_helper(PyObject* self) { 250 | AspellConfig* config; 251 | AspellKeyInfoEnumeration *keys_enumeration; 252 | AspellStringList* lst; 253 | AspellMutableContainer* amc; 254 | const AspellKeyInfo *key_info; 255 | 256 | PyObject *dict = 0, *obj = 0, *value = 0; 257 | const char* string; 258 | unsigned int integer; 259 | unsigned int boolean; 260 | 261 | char *key_type = 0; 262 | 263 | if (self) 264 | config = aspell_speller_config(Speller(self)); 265 | else 266 | config = new_aspell_config(); 267 | 268 | if (config == NULL) { 269 | PyErr_SetString(_AspellModuleException, "can't create config"); 270 | return NULL; 271 | } 272 | 273 | keys_enumeration = aspell_config_possible_elements(config, 1); 274 | if (!keys_enumeration) { 275 | if (!self) delete_aspell_config(config); 276 | PyErr_SetString(_AspellConfigException, "can't get list of config keys"); 277 | return NULL; 278 | } 279 | 280 | dict = PyDict_New(); 281 | if (dict == NULL) { 282 | if (!self) delete_aspell_config(config); 283 | return NULL; 284 | } 285 | 286 | while ((key_info = aspell_key_info_enumeration_next(keys_enumeration))) { 287 | 288 | /* key type -> string */ 289 | switch (key_info->type) { 290 | case AspellKeyInfoString: 291 | key_type = "string"; 292 | string = aspell_config_retrieve(config, key_info->name); 293 | if (aspell_config_error(config) != NULL) goto config_get_error; 294 | obj = PyUnicode_FromString( string ); 295 | break; 296 | case AspellKeyInfoInt: 297 | key_type = "integer"; 298 | integer = aspell_config_retrieve_int(config, key_info->name); 299 | if (aspell_config_error(config) != NULL) goto config_get_error; 300 | obj = PyLong_FromLong( integer ); 301 | break; 302 | case AspellKeyInfoBool: 303 | key_type = "boolean"; 304 | boolean = aspell_config_retrieve_bool(config, key_info->name); 305 | if (aspell_config_error(config) != NULL) goto config_get_error; 306 | obj = PyBool_FromLong( boolean ); 307 | break; 308 | case AspellKeyInfoList: 309 | key_type = "list"; 310 | lst = new_aspell_string_list(); 311 | amc = aspell_string_list_to_mutable_container(lst); 312 | aspell_config_retrieve_list(config, key_info->name, amc); 313 | if (aspell_config_error(config) != NULL) goto config_get_error; 314 | 315 | obj = AspellStringList2PythonList(lst); 316 | delete_aspell_string_list(lst); 317 | break; 318 | } 319 | 320 | /* value */ 321 | value = Py_BuildValue("(sOs)", 322 | key_type, 323 | obj, 324 | key_info->desc ? key_info->desc : "internal" 325 | ); 326 | 327 | if (value) { 328 | if (PyDict_SetItemString(dict, key_info->name, value)) { 329 | goto python_error; 330 | } 331 | else 332 | Py_DECREF(value); 333 | } 334 | else 335 | goto python_error; 336 | } 337 | 338 | delete_aspell_key_info_enumeration(keys_enumeration); 339 | if (!self) delete_aspell_config(config); 340 | return dict; 341 | 342 | config_get_error: 343 | PyErr_SetString(_AspellConfigException, aspell_config_error_message(config)); 344 | python_error: 345 | delete_aspell_key_info_enumeration(keys_enumeration); 346 | if (!self) delete_aspell_config(config); 347 | Py_DECREF(dict); 348 | return NULL; 349 | } 350 | 351 | /* ConfigKeys *****************************************************************/ 352 | static PyObject* configkeys(PyObject* _) { 353 | return configkeys_helper(NULL); 354 | } 355 | 356 | /* method:ConfigKeys **********************************************************/ 357 | static PyObject* m_configkeys(PyObject* self, PyObject* args) { 358 | return configkeys_helper(self); 359 | } 360 | 361 | /* method:setConfigKey ********************************************************/ 362 | static PyObject* m_set_config_key(PyObject* self, PyObject* args) { 363 | AspellConfig* config; 364 | const AspellKeyInfo* info; 365 | char* key; 366 | char* string; 367 | Py_ssize_t length; 368 | long number; 369 | char buffer[32]; 370 | 371 | PyObject* arg1; 372 | PyObject* value; 373 | 374 | if (PyTuple_Size(args) != 2) { 375 | PyErr_Format(PyExc_TypeError, "expected two arguments"); 376 | return NULL; 377 | } 378 | 379 | if (!PyArg_ParseTuple(args, "sO", &key, &arg1)) { 380 | PyErr_Format(PyExc_TypeError, "first argument must be a string"); 381 | return NULL; 382 | } 383 | 384 | 385 | config = aspell_speller_config(Speller(self)); 386 | info = aspell_config_keyinfo(config, key); 387 | if (aspell_config_error(config) != 0) { 388 | PyErr_SetString(_AspellConfigException, aspell_config_error_message(config)); 389 | return NULL; 390 | } 391 | 392 | switch (info->type) { 393 | case AspellKeyInfoList: 394 | // Can't figure out the splitting char for lists, 395 | // it seems to be the ':', but doesn't work. 396 | case AspellKeyInfoString: 397 | value = get_single_arg_string(self, arg1, &string, &length); 398 | if (value == NULL) { 399 | PyErr_Format(PyExc_TypeError, "second argument have to be string"); 400 | return NULL; 401 | } 402 | 403 | aspell_config_replace(config, key, string); 404 | Py_DECREF(value); 405 | break; 406 | 407 | case AspellKeyInfoInt: 408 | number = PyLong_AsLong(arg1); 409 | if (number == -1 && PyErr_Occurred()) { 410 | return NULL; 411 | } 412 | 413 | snprintf(buffer, 32, "%ld", number); 414 | aspell_config_replace(config, key, buffer); 415 | break; 416 | 417 | case AspellKeyInfoBool: 418 | if (PyBool_Check(arg1)) { 419 | aspell_config_replace(config, key, (arg1 == Py_True) ? "true" : "false"); 420 | } else { 421 | PyErr_Format(PyExc_TypeError, "second argument have to be boolean"); 422 | return NULL; 423 | } 424 | break; 425 | 426 | default: 427 | PyErr_Format(_AspellModuleException, "unsupported aspell config item type"); 428 | return NULL; 429 | } 430 | 431 | if (aspell_config_error(config) != 0) { 432 | PyErr_SetString(_AspellConfigException, aspell_config_error_message(config)); 433 | return NULL; 434 | } 435 | 436 | Py_RETURN_NONE; 437 | } 438 | 439 | 440 | static PyObject* get_single_arg_string( 441 | PyObject* self, // [in] 442 | PyObject* obj, // [in] 443 | char** word, // [out] 444 | Py_ssize_t* size // [out] 445 | ) { 446 | PyObject* buf; 447 | 448 | /* unicode */ 449 | if (PyUnicode_Check(obj)) 450 | /* convert to buffer */ 451 | buf = PyUnicode_AsEncodedString(obj, Encoding(self), "strict"); 452 | else 453 | /* buffer */ 454 | if (PyBytes_Check(obj)) { 455 | buf = obj; 456 | Py_INCREF(buf); // PyTuple_GetItem returns borrowed reference 457 | } 458 | else { 459 | PyErr_SetString(PyExc_TypeError, "string of bytes required"); 460 | return NULL; 461 | } 462 | 463 | /* unpack buffer */ 464 | if (buf) { 465 | if (PyBytes_AsStringAndSize(buf, word, size) != -1) { 466 | return buf; 467 | } 468 | else { 469 | Py_DECREF(buf); 470 | return NULL; 471 | } 472 | } 473 | else 474 | return NULL; 475 | } 476 | 477 | 478 | static PyObject* get_arg_string( 479 | PyObject* self, // [in] 480 | PyObject* args, // [in] 481 | const int index,// [in] args[index] 482 | char** word, // [out] 483 | Py_ssize_t* size // [out] 484 | ) { 485 | PyObject* obj; 486 | 487 | obj = PyTuple_GetItem(args, index); 488 | if (obj) 489 | return get_single_arg_string(self, obj, word, size); 490 | else 491 | return NULL; 492 | } 493 | 494 | 495 | /* method:__contains__ ********************************************************/ 496 | static int 497 | m_contains(PyObject* self, PyObject* args) { 498 | char* word; 499 | Py_ssize_t length; 500 | PyObject* buf; 501 | 502 | buf = get_single_arg_string(self, args, &word, &length); 503 | if (buf != NULL) 504 | switch (aspell_speller_check(Speller(self), word, length)) { 505 | case 0: 506 | Py_DECREF(buf); 507 | return 0; 508 | 509 | case 1: 510 | Py_DECREF(buf); 511 | return 1; 512 | 513 | default: 514 | Py_DECREF(buf); 515 | PyErr_SetString(_AspellSpellerException, aspell_speller_error_message(Speller(self))); 516 | return -1; 517 | } // switch 518 | else 519 | return -1; 520 | } 521 | 522 | 523 | /* method:check ***************************************************************/ 524 | static PyObject* m_check(PyObject* self, PyObject* args) { 525 | PyObject* word; 526 | 527 | word = PyTuple_GetItem(args, 0); 528 | if (word) 529 | switch (m_contains(self, word)) { 530 | case 0: 531 | Py_RETURN_FALSE; 532 | 533 | case 1: 534 | Py_RETURN_TRUE; 535 | 536 | default: 537 | return NULL; 538 | } 539 | else 540 | return NULL; 541 | } 542 | 543 | /* method:suggest ************************************************************/ 544 | static PyObject* m_suggest(PyObject* self, PyObject* args) { 545 | char* word; 546 | Py_ssize_t length; 547 | PyObject* buf; 548 | PyObject* list; 549 | 550 | buf = get_arg_string(self, args, 0, &word, &length); 551 | if (buf) { 552 | list = AspellWordList2PythonList( 553 | self, 554 | aspell_speller_suggest(Speller(self), 555 | word, 556 | length) 557 | ); 558 | Py_DECREF(buf); 559 | return list; 560 | } 561 | else 562 | return NULL; 563 | } 564 | 565 | /* method:getMainwordlist *****************************************************/ 566 | static PyObject* m_getMainwordlist(PyObject* self, PyObject* args) { 567 | return AspellWordList2PythonList(self, aspell_speller_main_word_list(Speller(self))); 568 | } 569 | 570 | /* method:getPersonalwordlist *************************************************/ 571 | static PyObject* m_getPersonalwordlist(PyObject* self, PyObject* args) { 572 | return AspellWordList2PythonList(self, aspell_speller_personal_word_list(Speller(self))); 573 | } 574 | 575 | /* method:getSessionwordlist **************************************************/ 576 | static PyObject* m_getSessionwordlist(PyObject* self, PyObject* args) { 577 | return AspellWordList2PythonList(self, aspell_speller_session_word_list(Speller(self))); 578 | } 579 | 580 | /* check for any aspell error after a lib call 581 | and either raises exception one or returns none */ 582 | static PyObject* AspellCheckError(PyObject* self) { 583 | if (aspell_speller_error(Speller(self)) != 0) { 584 | PyErr_SetString(_AspellSpellerException, aspell_speller_error_message(Speller(self))); 585 | return NULL; 586 | } 587 | else 588 | Py_RETURN_NONE; 589 | } 590 | 591 | /* method:addtoPersonal *******************************************************/ 592 | static PyObject* m_addtoPersonal(PyObject* self, PyObject* args) { 593 | char *word; 594 | Py_ssize_t length; 595 | 596 | if (!PyArg_ParseTuple(args, "s#", &word, &length)) { 597 | PyErr_SetString(PyExc_TypeError, "a string is required"); 598 | return NULL; 599 | } 600 | 601 | aspell_speller_add_to_personal(Speller(self), word, length); 602 | return AspellCheckError(self); 603 | } 604 | 605 | /* method:addtoSession ********************************************************/ 606 | static PyObject* m_addtoSession(PyObject* self, PyObject* args) { 607 | char *word; 608 | Py_ssize_t length; 609 | PyObject* buf; 610 | 611 | buf = get_arg_string(self, args, 0, &word, &length); 612 | if (buf) { 613 | aspell_speller_add_to_session(Speller(self), word, length); 614 | Py_DECREF(buf); 615 | return AspellCheckError(self); 616 | } 617 | else 618 | return NULL; 619 | 620 | } 621 | 622 | /* method:clearsession ********************************************************/ 623 | static PyObject* m_clearsession(PyObject* self, PyObject* args) { 624 | aspell_speller_clear_session(Speller(self)); 625 | return AspellCheckError(self); 626 | } 627 | 628 | /* method:saveallwords ********************************************************/ 629 | static PyObject* m_saveallwords(PyObject* self, PyObject* args) { 630 | aspell_speller_save_all_word_lists(Speller(self)); 631 | return AspellCheckError(self); 632 | } 633 | 634 | /* method:addReplacement ******************************************************/ 635 | static PyObject* m_addReplacement(PyObject* self, PyObject* args) { 636 | char *mis; Py_ssize_t ml; 637 | char *cor; Py_ssize_t cl; 638 | PyObject* Mbuf; 639 | PyObject* Cbuf; 640 | 641 | Mbuf = get_arg_string(self, args, 0, &mis, &ml); 642 | if (Mbuf == NULL) { 643 | PyErr_SetString(PyExc_TypeError, "first argument have to be a string or bytes"); 644 | return NULL; 645 | } 646 | 647 | Cbuf = get_arg_string(self, args, 1, &cor, &cl); 648 | if (Cbuf == NULL) { 649 | Py_DECREF(Mbuf); 650 | PyErr_SetString(PyExc_TypeError, "second argument have to be a string or bytes"); 651 | return NULL; 652 | } 653 | 654 | aspell_speller_store_replacement(Speller(self), mis, ml, cor, cl); 655 | Py_DECREF(Mbuf); 656 | Py_DECREF(Cbuf); 657 | return AspellCheckError(self); 658 | } 659 | 660 | /* AspellSpeller methods table */ 661 | static PyMethodDef aspell_object_methods[] = { 662 | { 663 | "ConfigKeys", 664 | (PyCFunction)m_configkeys, 665 | METH_VARARGS, 666 | "ConfigKeys() => dictionary of config keys\n" 667 | "Keys are string, values are 3-touple:\n" 668 | "\t1. key type={string|integer|boolean|list}\n" 669 | "\t2. current value\n" 670 | "\t3. description (if 'internal' no description available)" 671 | }, 672 | { 673 | "setConfigKey", 674 | (PyCFunction)m_set_config_key, 675 | METH_VARARGS, 676 | "changeConfig(key, value)\n" 677 | "Sets a new config value for given key" 678 | }, 679 | { 680 | "check", 681 | (PyCFunction)m_check, 682 | METH_VARARGS, 683 | "check(word) => bool\n" 684 | "Checks spelling of word.\n" 685 | "Returns if word is correct." 686 | }, 687 | { 688 | "suggest", 689 | (PyCFunction)m_suggest, 690 | METH_VARARGS, 691 | "suggest(word) => list of words\n" 692 | "Returns a list of suggested spelling for given word.\n" 693 | "Even if word is correct (i.e. check(word) returned 1) aspell performs action." 694 | }, 695 | { 696 | "getMainwordlist", 697 | (PyCFunction)m_getMainwordlist, 698 | METH_VARARGS, 699 | "getMainwordlist() => list of words\n" 700 | "Return a list of words stored in the main dictionary." 701 | 702 | }, 703 | { 704 | "getPersonalwordlist", 705 | (PyCFunction)m_getPersonalwordlist, 706 | METH_VARARGS, 707 | "getPersonalwordlist() => list of words\n" 708 | "Return a list of words stored in the personal dictionary." 709 | }, 710 | { 711 | "getSessionwordlist", 712 | (PyCFunction)m_getSessionwordlist, 713 | METH_VARARGS, 714 | "getSessionwordlist() => list of words\n" 715 | "Return a list of words stored in the session dictionary." 716 | }, 717 | { 718 | "clearSession", 719 | (PyCFunction)m_clearsession, 720 | METH_VARARGS, 721 | "clearSession() => None\n" 722 | "Clear current session, i.e. erases all words added thru addtoSession method since last saveallwords() call." 723 | }, 724 | { 725 | "saveAllwords", 726 | (PyCFunction)m_saveallwords, 727 | METH_VARARGS, 728 | "saveAllwords() => None\n" 729 | "Save all words added thru addtoPersonal() and addtoSession() methods." 730 | }, 731 | { 732 | "addtoPersonal", 733 | (PyCFunction)m_addtoPersonal, 734 | METH_VARARGS, 735 | "addtoPersonal(word) => None\n" 736 | "Add word to the personal dictionary" 737 | }, 738 | { 739 | "addtoSession", 740 | (PyCFunction)m_addtoSession, 741 | METH_VARARGS, 742 | "addtoSession(word) => None\n" 743 | "Add word to the session dictionary" 744 | }, 745 | { 746 | "addReplacement", 747 | (PyCFunction)m_addReplacement, 748 | METH_VARARGS, 749 | "addReplacement(misspeled word, correct word) => None\n" 750 | "Add a replacement pair, i.e. a misspeled and correct words.\n" 751 | "For example 'teh' and 'the'." 752 | }, 753 | {NULL, NULL, 0, NULL} 754 | }; 755 | 756 | static PyTypeObject aspell_AspellType = { 757 | PyVarObject_HEAD_INIT(&PyType_Type, 0) 758 | "AspellSpeller", /* tp_name */ 759 | sizeof(aspell_AspellObject), /* tp_size */ 760 | 0, /* tp_itemsize? */ 761 | (destructor)speller_dealloc, /* tp_dealloc */ 762 | 0, /* tp_print */ 763 | 0, /* tp_getattr */ 764 | 0, /* tp_setattr */ 765 | 0, /* tp_reserved */ 766 | 0, /* tp_repr */ 767 | 0, /* tp_as_number */ 768 | 0, /* tp_as_sequence */ 769 | 0, /* tp_as_mapping */ 770 | 0, /* tp_hash */ 771 | 0, /* tp_call */ 772 | 0, /* tp_str */ 773 | PyObject_GenericGetAttr, /* tp_getattro */ 774 | 0, /* tp_setattro */ 775 | 0, /* tp_as_buffer */ 776 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 777 | 0, /* tp_doc */ 778 | 0, /* tp_traverse */ 779 | 0, /* tp_clear */ 780 | 0, /* tp_richcompare */ 781 | 0, /* tp_weaklistoffset */ 782 | 0, /* tp_iter */ 783 | 0, /* tp_iternext */ 784 | aspell_object_methods, /* tp_methods */ 785 | 0, /* tp_members */ 786 | 0, /* tp_getset */ 787 | 0, /* tp_base */ 788 | 0, /* tp_dict */ 789 | 0, /* tp_descr_get */ 790 | 0, /* tp_descr_set */ 791 | 0, /* tp_dictoffset */ 792 | 0, /* tp_init */ 793 | 0, /* tp_alloc */ 794 | new_speller, /* tp_new */ 795 | }; 796 | 797 | 798 | static PySequenceMethods speller_as_sequence; 799 | 800 | static PyMethodDef aspell_module_methods[] = { 801 | { 802 | "ConfigKeys", 803 | (PyCFunction)configkeys, 804 | METH_VARARGS, 805 | "ConfigKeys() => dictionary of config keys\n" 806 | "Keys are string, values are 3-touple:\n" 807 | "\t1. key type={string|integer|boolean|list}\n" 808 | "\t2. current value\n" 809 | "\t3. description (if 'internal' no description available)" 810 | }, 811 | {NULL, NULL, 0, NULL} 812 | }; 813 | 814 | static PyModuleDef aspellmodule = { 815 | PyModuleDef_HEAD_INIT, 816 | "aspell", 817 | "aspell wrapper", 818 | -1, 819 | aspell_module_methods, 820 | }; 821 | 822 | PyMODINIT_FUNC 823 | PyInit_aspell(void) { 824 | PyObject *module; 825 | 826 | module = PyModule_Create(&aspellmodule); 827 | if (module == NULL) 828 | return NULL; 829 | 830 | speller_as_sequence.sq_contains = m_contains; 831 | aspell_AspellType.tp_as_sequence = &speller_as_sequence; 832 | 833 | if (PyType_Ready(&aspell_AspellType) < 0) { 834 | Py_DECREF(module); 835 | return NULL; 836 | } 837 | else 838 | PyModule_AddObject(module, "Speller", (PyObject*)&aspell_AspellType); 839 | 840 | _AspellSpellerException = PyErr_NewException("aspell.AspellSpellerError", NULL, NULL); 841 | _AspellModuleException = PyErr_NewException("aspell.AspellModuleError", NULL, NULL); 842 | _AspellConfigException = PyErr_NewException("aspell.AspellConfigError", NULL, NULL); 843 | 844 | PyModule_AddObject(module, "AspellSpellerError", _AspellSpellerException); 845 | PyModule_AddObject(module, "AspellModuleError", _AspellModuleException); 846 | PyModule_AddObject(module, "AspellConfigError", _AspellConfigException); 847 | 848 | return module; 849 | } 850 | 851 | /* 852 | vim:ts=2 sw=2 853 | */ 854 | -------------------------------------------------------------------------------- /pyaspell.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WojciechMula/aspell-python/864d326c1dfb04aaea3cc476459f4ba6f4425670/pyaspell.py -------------------------------------------------------------------------------- /pyaspell/__init__.py: -------------------------------------------------------------------------------- 1 | from pyaspell import * 2 | -------------------------------------------------------------------------------- /pyaspell/pyaspell.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WojciechMula/aspell-python/864d326c1dfb04aaea3cc476459f4ba6f4425670/pyaspell/pyaspell.py -------------------------------------------------------------------------------- /pypi-update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | options="--format=bztar --metadata-check upload" 6 | pydist="python setup.py sdist $options" 7 | 8 | function py3() { 9 | echo preparing package for Python 3 extension 10 | rm -f setup.py 11 | ln setup.3.py setup.py 12 | 13 | $pydist 14 | } 15 | 16 | function py2() { 17 | echo preparing package for Python 2 extension 18 | rm -f setup.py 19 | ln setup.2.py setup.py 20 | 21 | $pydist 22 | } 23 | 24 | function ctypes() { 25 | echo preparing package for ctypes module 26 | rm -f setup.py 27 | ln setup.ctypes.py setup.py 28 | 29 | $pydist 30 | } 31 | 32 | py3 33 | py2 34 | ctypes 35 | -------------------------------------------------------------------------------- /setup.2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from distutils.core import setup, Extension 3 | 4 | def get_include_dirs(): 5 | import sys 6 | if sys.platform.startswith('darwin'): 7 | return ['/opt/local/include'] # dir used by MacPorts 8 | else: 9 | return [] 10 | 11 | module = Extension('aspell', 12 | libraries = ['aspell'], 13 | library_dirs = ['/usr/local/lib/'], 14 | include_dirs = get_include_dirs(), 15 | sources = ['aspell.2.c'] 16 | ) 17 | 18 | setup (name = 'aspell-python-py2', 19 | version = '1.15', 20 | ext_modules = [module], 21 | 22 | description = "Wrapper around GNU Aspell for Python 2", 23 | author = "Wojciech Muła", 24 | author_email = "wojciech_mula@poczta.onet.pl", 25 | maintainer = "Wojciech Muła", 26 | maintainer_email = "wojciech_mula@poczta.onet.pl", 27 | url = "http://github.com/WojciechMula/aspell-python", 28 | platforms = ["Linux", "Windows"], 29 | license = "BSD (3 clauses)", 30 | long_description = "aspell-python - C extension for Python 2", 31 | classifiers = [ 32 | "Development Status :: 5 - Production/Stable", 33 | "License :: OSI Approved :: BSD License", 34 | "Programming Language :: C", 35 | "Topic :: Software Development :: Libraries", 36 | "Topic :: Text Editors :: Text Processing", 37 | ], 38 | ) 39 | -------------------------------------------------------------------------------- /setup.3.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from distutils.core import setup, Extension 3 | 4 | def get_include_dirs(): 5 | import sys 6 | if sys.platform.startswith('darwin'): 7 | return ['/opt/local/include'] # dir used by MacPorts 8 | else: 9 | return [] 10 | 11 | module = Extension('aspell', 12 | libraries = ['aspell'], 13 | library_dirs = ['/usr/local/lib/'], 14 | include_dirs = get_include_dirs(), 15 | sources = ['aspell.c'] 16 | ) 17 | 18 | setup (name = 'aspell-python-py3', 19 | version = '1.15', 20 | ext_modules = [module], 21 | 22 | description = "Wrapper around GNU Aspell for Python 3", 23 | author = "Wojciech Muła", 24 | author_email = "wojciech_mula@poczta.onet.pl", 25 | maintainer = "Wojciech Muła", 26 | maintainer_email = "wojciech_mula@poczta.onet.pl", 27 | url = "http://github.com/WojciechMula/aspell-python", 28 | platforms = ["Linux", "Windows"], 29 | license = "BSD (3 clauses)", 30 | long_description = "aspell-python - C extension for Python 3", 31 | classifiers = [ 32 | "Development Status :: 5 - Production/Stable", 33 | "License :: OSI Approved :: BSD License", 34 | "Programming Language :: C", 35 | "Topic :: Software Development :: Libraries", 36 | "Topic :: Text Editors :: Text Processing", 37 | ], 38 | ) 39 | -------------------------------------------------------------------------------- /setup.ctypes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from distutils.core import setup 3 | 4 | setup (name = 'aspell-python-ctypes', 5 | version = '1.13', 6 | packages = ['pyaspell'], 7 | description = "ctypes-based wrapper around GNU Aspell", 8 | author = "Wojciech Muła", 9 | author_email = "wojciech_mula@poczta.onet.pl", 10 | maintainer = "Wojciech Muła", 11 | maintainer_email = "wojciech_mula@poczta.onet.pl", 12 | url = "http://github.com/WojciechMula/aspell-python", 13 | platforms = ["Linux", "Windows"], 14 | license = "BSD (3 clauses)", 15 | long_description = "aspell-python - module for Python 2 & 3", 16 | keywords = [ 17 | "spellcheck", 18 | "aspell", 19 | ], 20 | classifiers = [ 21 | "Development Status :: 5 - Production/Stable", 22 | "License :: OSI Approved :: BSD License", 23 | "Programming Language :: Python", 24 | "Topic :: Software Development :: Libraries", 25 | "Topic :: Text Editors :: Text Processing", 26 | ], 27 | ) 28 | -------------------------------------------------------------------------------- /test/config.py: -------------------------------------------------------------------------------- 1 | # This is simple script that tests aspell-python module. 2 | 3 | # import module 4 | import aspell 5 | 6 | #speller = aspell.Speller() 7 | 8 | for item in aspell.ConfigKeys(): 9 | print item 10 | -------------------------------------------------------------------------------- /test/test.py: -------------------------------------------------------------------------------- 1 | # This is simple script that tests aspell-python module. 2 | 3 | # import module 4 | import aspell 5 | 6 | # select english dictionary 7 | speller = aspell.Speller('lang', 'en') 8 | 9 | # get current configuration of the speller, and create dictionary 10 | speller_config = dict( [(name, value) for name, type, value in speller.ConfigKeys()]) 11 | 12 | ################################################## 13 | print "="*60 14 | print "check method:" 15 | 16 | words = [ 17 | 'word', 18 | 'flower', 19 | 'misteke', # mistake 20 | 'mistake', 21 | 'tree', 22 | 'ruck', 23 | 'rock', 24 | 'rack', 25 | 'zo' # zoo 26 | ] 27 | 28 | def pretty_print(wordlist): 29 | for word in wordlist: 30 | print "* '%s':" % word, 31 | if speller.check(word): 32 | print "ok (1)" 33 | else: 34 | print "wrong (0)" 35 | 36 | pretty_print(words) 37 | 38 | ################################################## 39 | print "="*60 40 | print "suggest method:" 41 | 42 | words = [ 43 | 'wrod', 44 | 'tre', 45 | 'zo', 46 | ] 47 | 48 | for word in words: 49 | print "* '%s': %s" % (word, ', '.join(speller.suggest(word))) 50 | 51 | ################################################## 52 | print "="*60 53 | print "addReplacement method:" 54 | 55 | wrong_word = 'tre' 56 | correct_word = 'tree' 57 | print "default order of word list returned by suggest:" 58 | print "* '%s': %s" % (wrong_word, ', '.join(speller.suggest(wrong_word))) 59 | print "order after calling addReplacement('%s', '%s'):" % (wrong_word, correct_word) 60 | 61 | speller.addReplacement(wrong_word, correct_word) 62 | print "* '%s': %s" % (wrong_word, ', '.join(speller.suggest(wrong_word))) 63 | 64 | ################################################## 65 | print "="*60 66 | print "addtoSession method:" 67 | 68 | unknown_but_correct_words = ['linux', 'aspell', 'emacs', 'ocaml', 'python'] 69 | 70 | print "aspell dosn't know any of these words:" 71 | pretty_print(unknown_but_correct_words) 72 | 73 | print "now, we add them to session dictionary" 74 | for word in unknown_but_correct_words: 75 | speller.addtoSession(word) 76 | print "and check once again" 77 | pretty_print(unknown_but_correct_words) 78 | 79 | ################################################## 80 | print "="*60 81 | print "getSessionwordlist method:" 82 | 83 | print "let see the words we've added earlier:", speller.getSessionwordlist() 84 | 85 | ################################################## 86 | print "="*60 87 | print "clearSession method:" 88 | 89 | print "now, we clean session dictionary:" 90 | speller.clearSession() 91 | pretty_print(unknown_but_correct_words) 92 | 93 | ################################################## 94 | print "="*60 95 | print "addtoPersonal method:" 96 | 97 | print "aspell was forgot words, but now we use personal dictionary" 98 | for word in unknown_but_correct_words: 99 | speller.addtoPersonal(word) 100 | 101 | pretty_print(unknown_but_correct_words) 102 | 103 | ################################################## 104 | print "="*60 105 | print "saveAllwords method:" 106 | 107 | def cat(filename): 108 | try: 109 | file = open(filename) 110 | except: 111 | print "Can't open file '%s'" % filename 112 | else: 113 | print "--- %s ---" % filename 114 | for line in open(filename): 115 | print line, 116 | print "--- eof ---" 117 | file.close() 118 | 119 | print "aspell saves personal dictionary in: '%s'" % \ 120 | speller_config['personal-path'], 121 | 122 | print "let see what words are stored in the file:" 123 | cat(speller_config['personal-path']) 124 | 125 | print "now we save personal dictionary, and see again contents of the file" 126 | speller.saveAllwords() 127 | cat(speller_config['personal-path']) 128 | 129 | ################################################## 130 | print "="*60 131 | print "getPersonalwordlist method:" 132 | 133 | speller.addtoPersonal('TheEnd') 134 | print "method returns:", speller.getPersonalwordlist() 135 | -------------------------------------------------------------------------------- /test/unittests.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | import sys 4 | 5 | # import tested module 6 | arg = '--ctypes-module' 7 | if arg in sys.argv: 8 | import pyaspell as aspell 9 | else: 10 | import aspell 11 | 12 | 13 | class TestBase(unittest.TestCase): 14 | def setUp(self): 15 | # select english dictionary and set name of personal word list 16 | self.speller = aspell.Speller( 17 | ('lang', 'en'), 18 | ('personal', '__unittest__.rws'), 19 | ) 20 | 21 | # get current speller configuration 22 | config = self.speller.ConfigKeys() 23 | 24 | self.config = dict((name, value) for name, (type, value, desc) in config.items()) 25 | 26 | # polish words (cat, tree, spring) not existing in english dict 27 | self.polish_words = ['kot', 'drzewo', 'wiosna'] 28 | 29 | 30 | class TestCheckMethod(TestBase): 31 | "test check method" 32 | 33 | def test_ok(self): 34 | words = ['word', 'flower', 'tree', 'rock', 'cat', 'winter'] 35 | for word in words: 36 | self.assertTrue(self.speller.check(word)) 37 | 38 | def test_false(self): 39 | words = ['misteke', 'zo', 'tre', 'bicyle'] 40 | for word in words: 41 | self.assertFalse(self.speller.check(word)) 42 | 43 | 44 | def test_in(self): 45 | words = ['word', 'flower', 'tree', 'rock', 'cat', 'winter'] 46 | for word in words: 47 | self.assertTrue(word in self.speller) 48 | 49 | def test_notin(self): 50 | words = ['misteke', 'zo', 'tre', 'bicyle'] 51 | for word in words: 52 | self.assertFalse(word in self.speller) 53 | self.assertTrue(word not in self.speller) 54 | 55 | 56 | class TestSuggestMethod(TestBase): 57 | def test(self): 58 | pairs = { 59 | 'wrod' : 'word', 60 | 'tre' : 'tree', 61 | 'xoo' : 'zoo', 62 | } 63 | 64 | for incorrect, correct in pairs.items(): 65 | sug = self.speller.suggest(incorrect) 66 | self.assertTrue(correct in sug) 67 | 68 | 69 | class TestAddReplacementMethod(TestBase): 70 | def test(self): 71 | "addReplacement affects on order of words returing by suggest" 72 | 73 | wrong = 'wrod' 74 | correct_def = 'word' # first suggestion in default order 75 | correct = 'trod' # first suggestion after altering order 76 | 77 | sug = self.speller.suggest(wrong) 78 | self.assertEqual(sug[0], correct_def) 79 | 80 | self.speller.addReplacement(wrong, correct) 81 | 82 | sug = self.speller.suggest(wrong) 83 | self.assertEqual(sug[0], correct) 84 | 85 | 86 | class TestaddtoSession(TestBase): 87 | def test(self): 88 | 89 | # aspell dosn't know any of these words 90 | for word in self.polish_words: 91 | self.assertFalse(self.speller.check(word)) 92 | 93 | # now, we add them to session dictionary 94 | for word in self.polish_words: 95 | self.speller.addtoSession(word) 96 | 97 | # and check once again 98 | for word in self.polish_words: 99 | self.assertTrue(self.speller.check(word)) 100 | 101 | 102 | class TestSessionwordlist(TestBase): 103 | def all_correct(self): 104 | for word in self.polish_words: 105 | self.assertTrue(self.speller.check(word)) 106 | 107 | def all_incorrect(self): 108 | for word in self.polish_words: 109 | self.assertFalse(self.speller.check(word)) 110 | 111 | def test1(self): 112 | "by default session dict is empty" 113 | swl = self.speller.getSessionwordlist() 114 | self.assertEqual(swl, []) 115 | 116 | def test2(self): 117 | "fill session dict with some words, then clear" 118 | 119 | # fill 120 | for word in self.polish_words: 121 | self.speller.addtoSession(word) 122 | 123 | # test - all correct 124 | swl = self.speller.getSessionwordlist() 125 | self.assertEqual(set(swl), set(self.polish_words)) 126 | 127 | # clear 128 | self.speller.clearSession() 129 | swl = self.speller.getSessionwordlist() 130 | 131 | # empty - none correct 132 | self.assertEqual(set(swl), set()) 133 | 134 | def test3(self): 135 | self.all_incorrect() 136 | 137 | for word in self.polish_words: 138 | self.speller.addtoSession(word) 139 | 140 | self.all_correct() 141 | 142 | self.speller.clearSession() 143 | 144 | self.all_incorrect() 145 | 146 | 147 | class TestPersonalwordlist(TestBase): 148 | 149 | def setUp(self): 150 | TestBase.setUp(self) 151 | self._clear_personal() 152 | 153 | def _read_personal(self): 154 | path = self.config['personal-path'] 155 | with open(path, 'rt') as f: 156 | L = f.readlines() 157 | return [line.rstrip() for line in L[1:]] 158 | 159 | def _clear_personal(self): 160 | "clear personal dictionary - remove a file" 161 | path = self.config['personal-path'] 162 | try: 163 | os.remove(path) 164 | except OSError: 165 | pass 166 | 167 | def test_add(self): 168 | "addtoPersonal" 169 | 170 | for word in self.polish_words: 171 | self.assertFalse(self.speller.check(word)) 172 | 173 | for word in self.polish_words: 174 | self.speller.addtoPersonal(word) 175 | 176 | for word in self.polish_words: 177 | self.assertTrue(self.speller.check(word)) 178 | 179 | def test_get(self): 180 | "getPersonalwordlist" 181 | 182 | pwl = self.speller.getPersonalwordlist() 183 | self.assertEqual(set(pwl), set()) 184 | 185 | for word in self.polish_words: 186 | self.speller.addtoPersonal(word) 187 | 188 | pwl = self.speller.getPersonalwordlist() 189 | self.assertEqual(set(pwl), set(self.polish_words)) 190 | 191 | def test_saveall(self): 192 | "saveAllwords" 193 | 194 | for word in self.polish_words: 195 | self.speller.addtoPersonal(word) 196 | 197 | pwl = self.speller.getPersonalwordlist() 198 | self.assertEqual(set(pwl), set(self.polish_words)) 199 | 200 | self.speller.saveAllwords() 201 | saved_wl = self._read_personal() 202 | self.assertEqual(set(pwl), set(self.polish_words)) 203 | 204 | self._clear_personal() 205 | 206 | 207 | class TestSetConfigKey(unittest.TestCase): 208 | def setUp(self): 209 | self.speller = aspell.Speller(('lang', 'en')) 210 | 211 | 212 | def get_config(self): 213 | config = self.speller.ConfigKeys(); 214 | return dict((name, value) for name, (type, value, desc) in config.items()) 215 | 216 | 217 | def test_string_value(self): 218 | 219 | self.speller.setConfigKey('sug-mode', "normal"); 220 | sug1 = self.speller.suggest('rutter') 221 | self.speller.setConfigKey('sug-mode', "bad-spellers"); 222 | sug2 = self.speller.suggest('rutter') 223 | 224 | self.assertTrue(sug1 != sug2) 225 | 226 | 227 | def test_bool_value(self): 228 | key = 'clean-affixes' 229 | 230 | self.speller.setConfigKey(key, True) 231 | self.assertEqual(self.get_config()[key], True) 232 | self.speller.setConfigKey(key, False) 233 | self.assertEqual(self.get_config()[key], False) 234 | 235 | 236 | def test_int_value(self): 237 | key = 'run-together-min' 238 | value = 123 239 | self.speller.setConfigKey(key, value) 240 | self.assertEqual(self.get_config()[key], value) 241 | 242 | @unittest.skip("not implemented") 243 | def test_list_value(self): 244 | key = 'filter' 245 | value = ['foo', 'bar', 'baz'] 246 | self.speller.setConfigKey(key, value) 247 | self.assertEqual(self.get_config()[key], value) 248 | 249 | if __name__ == '__main__': 250 | try: 251 | del sys.argv[sys.argv.index(arg)] 252 | except: 253 | pass 254 | 255 | unittest.main() 256 | 257 | # vim: ts=4 sw=4 nowrap noexpandtab 258 | -------------------------------------------------------------------------------- /windows.rst: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | Windows troubleshooting 3 | ======================================================================== 4 | 5 | :Author: **Eric Woudenberg** 6 | :Date: 2015-08-29 7 | :Updated: 2016-03-17 8 | 9 | .. contents:: 10 | 11 | The problem 12 | ------------------------------------------------------------------------ 13 | 14 | Prerequisites 15 | ~~~~~~~~~~~~~ 16 | 17 | Cygwin Aspell 0.60 or some other Windows Aspell 0.60 installed. 18 | Python 2.6 or 2.7. 19 | 20 | Steps to reproduce problem 21 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 22 | 23 | 1) Run pre-packaged Windows aspell-python ".whl" installer (from: 24 | http://www.lfd.uci.edu/~gohlke/pythonlibs/#aspell-python) with e.g. "pip 25 | install aspell_python‑1.13‑cp27‑none‑win32.whl" 26 | 27 | 2) Try to instantiate Speller, e.g.:: 28 | 29 | $ python -c "import aspell; aspell.Speller()" 30 | Traceback (most recent call last): 31 | File "", line 1, in 32 | aspell.AspellSpellerError: No word lists can be found for the language "en_US". 33 | 34 | 3) Get error "No word lists can be found". 35 | 36 | The problem (that took me so long to figure out) is that all the aspell 37 | dictionary installers don't seem to know about where aspell-python 38 | expects to find the dictionaries and simply assume you're installing the 39 | dictionary for the command line aspell. 40 | 41 | The solution 42 | ------------------------------------------------------------------------ 43 | 44 | 1) Get modern 0.60 dictionaries from e.g. 45 | ftp://ftp.gnu.org/gnu/aspell/dict/en/aspell6-en-2015.04.24-0.tar.bz2 46 | 47 | 2) Unpack and run the configure script. Make a note of the location it 48 | finds for the **Dictionary** and **Data** dirs:: 49 | 50 | $ ./configure 51 | Finding Dictionary file location ... /usr/lib/aspell-0.60 52 | Finding Data file location ... /usr/lib/aspell-0.60 53 | 54 | 3) Run the dictionary's "make" and "make install" scripts:: 55 | 56 | $ make 57 | /usr/bin/prezip-bin -d < en-common.cwl | /usr/bin/aspell --lang=en create master ./en-common.rws 58 | /usr/bin/prezip-bin -d < en-variant_0.cwl | /usr/bin/aspell --lang=en create master ./en-variant_0.rws 59 | /usr/bin/prezip-bin -d < en-variant_1.cwl | /usr/bin/aspell --lang=en create master ./en-variant_1.rws 60 | /usr/bin/prezip-bin -d < en-variant_2.cwl | /usr/bin/aspell --lang=en create master ./en-variant_2.rws 61 | /usr/bin/prezip-bin -d < en-w_accents-only.cwl | /usr/bin/aspell --lang=en create master ./en-w_accents-only.rws 62 | /usr/bin/prezip-bin -d < en-wo_accents-only.cwl | /usr/bin/aspell --lang=en create master ./en-wo_accents-only.rws 63 | /usr/bin/prezip-bin -d < en_CA-variant_0.cwl | /usr/bin/aspell --lang=en create master ./en_CA-variant_0.rws 64 | /usr/bin/prezip-bin -d < en_CA-variant_1.cwl | /usr/bin/aspell --lang=en create master ./en_CA-variant_1.rws 65 | /usr/bin/prezip-bin -d < en_CA-w_accents-only.cwl | /usr/bin/aspell --lang=en create master ./en_CA-w_accents-only.rws 66 | /usr/bin/prezip-bin -d < en_CA-wo_accents-only.cwl | /usr/bin/aspell --lang=en create master ./en_CA-wo_accents-only.rws 67 | /usr/bin/prezip-bin -d < en_GB-ise-w_accents-only.cwl | /usr/bin/aspell --lang=en create master ./en_GB-ise-w_accents-only.rws 68 | /usr/bin/prezip-bin -d < en_GB-ise-wo_accents-only.cwl | /usr/bin/aspell --lang=en create master ./en_GB-ise-wo_accents-only.rws 69 | /usr/bin/prezip-bin -d < en_GB-ize-w_accents-only.cwl | /usr/bin/aspell --lang=en create master ./en_GB-ize-w_accents-only.rws 70 | /usr/bin/prezip-bin -d < en_GB-ize-wo_accents-only.cwl | /usr/bin/aspell --lang=en create master ./en_GB-ize-wo_accents-only.rws 71 | /usr/bin/prezip-bin -d < en_GB-variant_0.cwl | /usr/bin/aspell --lang=en create master ./en_GB-variant_0.rws 72 | /usr/bin/prezip-bin -d < en_GB-variant_1.cwl | /usr/bin/aspell --lang=en create master ./en_GB-variant_1.rws 73 | /usr/bin/prezip-bin -d < en_US-w_accents-only.cwl | /usr/bin/aspell --lang=en create master ./en_US-w_accents-only.rws 74 | /usr/bin/prezip-bin -d < en_US-wo_accents-only.cwl | /usr/bin/aspell --lang=en create master ./en_US-wo_accents-only.rws 75 | 76 | $ make install 77 | mkdir -p /usr/lib/aspell-0.60/ 78 | cp en-common.rws en-variant_0.rws en-variant_1.rws en-variant_2.rws 79 | en-w_accents-only.rws en-wo_accents-only.rws en_CA-variant_0.rws 80 | en_CA-variant_1.rws en_CA-w_accents-only.rws en_CA-wo_acc 81 | ents-only.rws en_GB-ise-w_accents-only.rws 82 | en_GB-ise-wo_accents-only.rws en_GB-ize-w_accents-only.rws 83 | en_GB-ize-wo_accents-only.rws en_GB-variant_0.rws en_GB-variant_1.rws 84 | en_US-w_accents-on 85 | ly.rws en_US-wo_accents-only.rws american.alias 86 | american-variant_0.alias american-variant_1.alias 87 | american-w_accents.alias american-wo_accents.alias british.alias 88 | british-ise.alias british-i 89 | se-w_accents.alias british-ise-wo_accents.alias british-ize.alias 90 | british-ize-w_accents.alias british-ize-wo_accents.alias 91 | british-variant_0.alias british-variant_1.alias british-w_accents.a 92 | lias british-wo_accents.alias canadian.alias canadian-variant_0.alias 93 | canadian-variant_1.alias canadian-w_accents.alias 94 | canadian-wo_accents.alias en.multi en-variant_0.multi en-variant_1.mul 95 | ti en-variant_2.multi en-w_accents.multi en-wo_accents.multi 96 | en_CA.multi en_CA-variant_0.multi en_CA-variant_1.multi 97 | en_CA-w_accents.multi en_CA-wo_accents.multi en_GB.multi en_GB-ise.multi 98 | en_GB-ise-w_accents.multi en_GB-ise-wo_accents.multi en_GB-ize.multi 99 | en_GB-ize-w_accents.multi en_GB-ize-wo_accents.multi 100 | en_GB-variant_0.multi en_GB-variant_1.multi en_GB-w_accents.multi en 101 | _GB-wo_accents.multi en_US.multi en_US-variant_0.multi 102 | en_US-variant_1.multi en_US-w_accents.multi en_US-wo_accents.multi 103 | english.alias english-variant_0.alias english-variant_1.alias englis 104 | h-variant_2.alias english-w_accents.alias english-wo_accents.alias 105 | /usr/lib/aspell-0.60/ 106 | cd /usr/lib/aspell-0.60/ && chmod 644 en-common.rws en-variant_0.rws 107 | en-variant_1.rws en-variant_2.rws en-w_accents-only.rws 108 | en-wo_accents-only.rws en_CA-variant_0.rws en_CA-variant_1.rws en 109 | _CA-w_accents-only.rws en_CA-wo_accents-only.rws 110 | en_GB-ise-w_accents-only.rws en_GB-ise-wo_accents-only.rws 111 | en_GB-ize-w_accents-only.rws en_GB-ize-wo_accents-only.rws 112 | en_GB-variant_0.rws en_ 113 | GB-variant_1.rws en_US-w_accents-only.rws en_US-wo_accents-only.rws 114 | american.alias american-variant_0.alias american-variant_1.alias 115 | american-w_accents.alias american-wo_accents.alias britis 116 | h.alias british-ise.alias british-ise-w_accents.alias 117 | british-ise-wo_accents.alias british-ize.alias 118 | british-ize-w_accents.alias british-ize-wo_accents.alias 119 | british-variant_0.alias british- 120 | variant_1.alias british-w_accents.alias british-wo_accents.alias 121 | canadian.alias canadian-variant_0.alias canadian-variant_1.alias 122 | canadian-w_accents.alias canadian-wo_accents.alias en.multi 123 | en-variant_0.multi en-variant_1.multi en-variant_2.multi 124 | en-w_accents.multi en-wo_accents.multi en_CA.multi 125 | en_CA-variant_0.multi en_CA-variant_1.multi en_CA-w_accents.multi 126 | en_CA-wo_accents 127 | .multi en_GB.multi en_GB-ise.multi en_GB-ise-w_accents.multi 128 | en_GB-ise-wo_accents.multi en_GB-ize.multi en_GB-ize-w_accents.multi 129 | en_GB-ize-wo_accents.multi en_GB-variant_0.multi en_GB-varia 130 | nt_1.multi en_GB-w_accents.multi en_GB-wo_accents.multi en_US.multi 131 | en_US-variant_0.multi en_US-variant_1.multi en_US-w_accents.multi 132 | en_US-wo_accents.multi english.alias english-variant_0.a 133 | lias english-variant_1.alias english-variant_2.alias 134 | english-w_accents.alias english-wo_accents.alias 135 | mkdir -p /usr/lib/aspell-0.60/ 136 | cp en.dat en_phonet.dat en_affix.dat /usr/lib/aspell-0.60/ 137 | cd /usr/lib/aspell-0.60/ && chmod 644 en.dat en_phonet.dat en_affix.dat 138 | 139 | 4) Get the location where aspell-python is expecting the dictionaries to 140 | be installed:: 141 | 142 | $ python -c "import aspell, sys; [sys.stdout.write(i[0] + ' ' + 143 | str(i[2]) + '\n') for i in aspell.ConfigKeys()]" | grep -e -dir 144 | 145 | actual-dict-dir C:\Documents and Settings\All Users\Application Data\Aspell\Dictionaries/ 146 | conf-dir C:\Documents and Settings\All Users\Application Data\Aspell\ 147 | data-dir C:\Documents and Settings\All Users\Application Data\Aspell\Data 148 | dict-dir C:\Documents and Settings\All Users\Application > Data\Aspell\Dictionaries 149 | home-dir C:\Documents and Settings\All Users\Application Data\Aspell\ 150 | local-data-dir C:\Documents and Settings\All Users\Application Data\Aspell\Dictionaries/ 151 | personal-dir C:\Documents and Settings\All Users\Application Data\Aspell\Personal 152 | 153 | 5) Make the target directory and copy all the freshly installed 154 | directories there:: 155 | 156 | $ mkdir 'C:\Documents and Settings\All Users\Application Data\Aspell' 157 | $ cp -r /usr/lib/aspell-0.60 'C:\Documents and Settings\All Users\Application Data\Aspell'/Data 158 | $ cp -r /usr/lib/aspell-0.60 'C:\Documents and Settings\All Users\Application Data\Aspell'/Dictionaries 159 | 160 | 6) Now the aspell-python package will correctly instantiate a Speller:: 161 | 162 | $ python -c "import aspell; aspell.Speller()" 163 | 164 | --------------------------------------------------------------------------------