File indexing completed on 2024-04-21 05:44:46

0001 # -*- coding: iso-8859-2 -*-
0002 # Aspell interface using ctypes.
0003 # $Date: 2007-04-07 14:27:33 $, $Revision: 1.3 $
0004 #
0005 # This is straightforward translation of my
0006 # aspell-python, C extension.
0007 #
0008 # License: BSD
0009 #
0010 # author: Wojciech Muła
0011 # e-mail: wojciech_mula@poczta.onet.pl
0012 # www   : http://wmula.republika.pl/proj/aspell-python/
0013 #
0014 # TODO: add method to get/change **current** speller's config
0015 
0016 try:
0017     import ctypes
0018     import ctypes.util
0019 except ImportError:
0020     raise ImportError("ctypes library is needed")
0021 
0022 
0023 class AspellError(Exception): pass
0024 class AspellConfigError(AspellError): pass
0025 class AspellSpellerError(AspellError): pass
0026 
0027 
0028 class AspellLinux(object):
0029     """
0030     Aspell speller object.  Allows to check spelling, get suggested
0031     spelling list, manage user dictionarias, and other.
0032     
0033     Must be closed with 'close' method, or one may experience
0034     problems, like segfaults.
0035     """
0036 
0037     def __init__(self, configkeys=None, libname=None):
0038         """
0039         Parameters:
0040         * configkeys - list of configuration parameters;
0041           each element is a pair key & value (both strings)
0042           if None, then default configuration is used
0043         * libname - explicity set aspell library name;
0044           if None then default name is used
0045         """
0046         if libname is None:
0047             libname = ctypes.util.find_library('aspell')
0048         self.__lib = ctypes.CDLL(libname)
0049 
0050         # Initialize speller
0051 
0052         # 1. create configuration
0053         config = self.__lib.new_aspell_config()
0054         if config == None:
0055             raise AspellError("Can't create aspell config object")
0056 
0057         # 2. parse configkeys arg.
0058         if configkeys is not None:
0059             assert type(configkeys) in [tuple, list], "Tuple or list expeced"
0060             if len(configkeys) == 2 and \
0061                type(configkeys[0]) is str and \
0062                type(configkeys[1]) is str:
0063                 configkeys = [configkeys]
0064 
0065             for key, value in configkeys:
0066                 assert type(key) is str, "Key must be string"
0067                 assert type(value) is str, "Value must be string"
0068                 if not self.__lib.aspell_config_replace(config, key, value):
0069                     raise self._aspell_config_error(config)
0070 
0071         # 3. create speller
0072         possible_error = self.__lib.new_aspell_speller(config)
0073         self.__lib.delete_aspell_config(config)
0074 
0075         if self.__lib.aspell_error_number(possible_error) != 0:
0076             self.__lib.delete_aspell_can_have_error(possible_error)
0077             raise AspellError("Can't create speller object")
0078 
0079         self.__speller = self.__lib.to_aspell_speller(possible_error)
0080 
0081 
0082     def check(self, word):
0083         """
0084         Check if word is present in main, personal or session
0085         dictionary.  Boolean value is returned
0086         """
0087         if type(word) is str:
0088             return bool(
0089                 self.__lib.aspell_speller_check(
0090                     self.__speller,
0091                     word,
0092                     len(word)
0093                 ))
0094         else:
0095             raise TypeError("String expeced")
0096 
0097 
0098     def suggest(self, word):
0099         """
0100         Return list of spelling suggestions of given word.
0101         Works even if word is correct.
0102         """
0103         if type(word) is str:
0104             return self._aspellwordlist(
0105                 self.__lib.aspell_speller_suggest(
0106                     self.__speller,
0107                     word,
0108                     len(word)
0109                 ))
0110         else:
0111             raise TypeError("String expeced")
0112 
0113 
0114     def personal_dict(self, word=None):
0115         """
0116         Aspell's personal dictionary is a user defined, persistent
0117         list of word (saved in certain file).
0118 
0119         If 'word' is not given, then method returns list of words stored in
0120         dict.  If 'word' is given, then is added to personal dict.  New words
0121         are not saved automatically, method 'save_all' have to be call.
0122         """
0123         if word is not None:
0124             # add new word
0125             assert type(word) is str, "String expeced"
0126             self.__lib.aspell_speller_add_to_personal(
0127                 self.__speller,
0128                 word,
0129                 len(word)
0130             )
0131             self._aspell_check_error()
0132         else:
0133             # return list of words from personal dictionary
0134             return self._aspellwordlist(
0135                 self.__lib.aspell_speller_personal_word_list(self.__speller)
0136             )
0137     
0138 
0139     def session_dict(self, word=None, clear=False):
0140         """
0141         Aspell's session dictionary is a user defined, volatile
0142         list of word, that is destroyed with aspell object.
0143 
0144         If 'word' is None, then list of words from session dictionary
0145         is returned.  If 'word' is present, then is added to dict.
0146         If 'clear' is True, then session dictionary is cleared.
0147         """
0148         if clear:
0149             self.__lib.aspell_speller_clear_session(self.__speller)
0150             self._aspell_check_error()
0151             return
0152 
0153 
0154         if word is not None:
0155             # add new word
0156             assert type(word) is str, "String expeced"
0157             self.__lib.aspell_speller_add_to_session(
0158                 self.__speller,
0159                 word,
0160                 len(word)
0161             )
0162             self._aspell_check_error()
0163         else:
0164             # return list of words from personal dictionary
0165             return self._aspellwordlist(
0166                 self.__lib.aspell_speller_session_word_list(self.__speller)
0167             )
0168     
0169 
0170     def add_replacement_pair(self, misspelled, correct):
0171         """
0172         Add replacement pair, i.e. pair of misspelled and correct
0173         word.  It affects on order of words appear on list returned
0174         by 'suggest' method.
0175         """
0176         assert type(misspelled) is str, "String is required"
0177         assert type(correct) is str, "String is required"
0178 
0179         self.__lib.aspell_speller_store_replacement(
0180             self.__speller,
0181             misspelled,
0182             len(misspelled),
0183             correct,
0184             len(correct)
0185         )
0186         self._aspell_check_error()
0187     
0188 
0189     def save_all(self):
0190         """
0191         Saves all words added to personal or session dictionary to
0192         the apell's defined file.
0193         """
0194         self.__lib.spell_speller_save_all_word_lists(self.__speller)
0195         self._aspell_check_error()
0196     
0197 
0198     def configkeys(self):
0199         """
0200         Returns list of all available config keys that can be passed
0201         to contructor.
0202         
0203         List contains a 3-tuples:
0204         1. key name
0205         2. default value of type:
0206            * bool
0207            * int
0208            * string
0209            * list of string
0210         3. short description
0211            if None, then this key is undocumented is should not
0212            be used, unless one know what really do
0213         """
0214         
0215         config = self.__lib.aspell_speller_config(self.__speller)
0216         if config is None:
0217             raise AspellConfigError("Can't get speller's config")
0218 
0219         keys_enum = self.__lib.aspell_config_possible_elements(config, 1)
0220         if keys_enum is None:
0221             raise AspellError("Can't get list of config keys")
0222 
0223         class KeyInfo(ctypes.Structure):
0224             _fields_ = [
0225                 ("name",    ctypes.c_char_p),
0226                 ("type",    ctypes.c_int),
0227                 ("default", ctypes.c_char_p),
0228                 ("desc",    ctypes.c_char_p),
0229                 ("flags",   ctypes.c_int),
0230                 ("other_data", ctypes.c_int),
0231             ]
0232 
0233         key_next = self.__lib.aspell_key_info_enumeration_next
0234         key_next.restype = ctypes.POINTER(KeyInfo)
0235 
0236         list = []
0237         while True:
0238             key_info = key_next(keys_enum)
0239             if not key_info:
0240                 break
0241             else:
0242                 key_info = key_info.contents
0243 
0244             if key_info.type == 0:
0245                 # string
0246                 list.append((
0247                     key_info.name,
0248                     key_info.default,
0249                     key_info.desc,
0250                 ))
0251 
0252             elif key_info.type == 1:
0253                 # integer
0254                 list.append((
0255                     key_info.name,
0256                     int(key_info.default),
0257                     key_info.desc,
0258                 ))
0259             elif key_info.type == 2:
0260                 # boolean
0261                 if key_info.default.lower() == 'true':
0262                     list.append((
0263                         key_info.name,
0264                         True,
0265                         key_info.desc,
0266                     ))
0267                 else:
0268                     list.append((
0269                         key_info.name,
0270                         False,
0271                         key_info.desc,
0272                     ))
0273             elif key_info.type == 3:
0274                 # list
0275                 list.append((
0276                     key_info.name,
0277                     key_info.default.split(),
0278                     key_info.desc,
0279                     ))
0280 
0281         self.__lib.delete_aspell_key_info_enumeration(keys_enum)
0282         return list
0283 
0284 
0285     def close(self):
0286         """
0287         Close aspell speller object.
0288         """
0289         self.__lib.delete_aspell_speller(self.__speller)
0290     
0291 
0292     # XXX: internal function, do not call directly
0293     def _aspellwordlist(self, wordlist_id):
0294         """
0295         XXX: internal function
0296 
0297         Converts aspell list into python list.
0298         """
0299         elements = self.__lib.aspell_word_list_elements(wordlist_id)
0300         list = []
0301         while True:
0302             wordptr = self.__lib.aspell_string_enumeration_next(elements)
0303             if not wordptr:
0304                 break
0305             else:
0306                 word = ctypes.c_char_p(wordptr)
0307                 list.append(word.value)
0308 
0309         self.__lib.delete_aspell_string_enumeration(elements)
0310         return list
0311     
0312 
0313     def _aspell_config_error(self, config):
0314         """
0315         XXX: internal function
0316 
0317         Raise excpetion if operation of speller config
0318         caused an error.  Additionaly destroy config object.
0319         """
0320         # make exception object & copy error msg 
0321         exc = AspellConfigError(
0322             ctypes.c_char_p(
0323                 self.__lib.aspell_config_error_message(config)
0324             ).value
0325         )
0326     
0327         # then destroy config objcet
0328         self.__lib.delete_aspell_config(config)
0329 
0330         # and then raise exception
0331         raise exc
0332 
0333 
0334     def _aspell_check_error(self):
0335         """
0336         XXX: internal function
0337 
0338         Raise exception if previous speller operation
0339         caused an error.
0340         """
0341         if self.__lib.aspell_speller_error(self.__speller) != 0:
0342             msg = self.__lib.aspell_speller_error_message(self.__speller)
0343             raise AspellSpellerError(msg)
0344 #class
0345 
0346 Aspell = AspellLinux
0347 
0348 
0349 if __name__ == '__main__':
0350     # TODO: more test cases
0351     a = Aspell(("lang", "en"))
0352     print(a.check("when"))
0353     print(a.suggest("wehn"))
0354     a.add_replacement_pair("wehn", "ween")
0355     print(a.suggest("wehn"))
0356 
0357     print(a.session_dict())
0358     print(a.check("pyaspell"))
0359     a.session_dict("pyaspell")
0360     print(a.session_dict())
0361     print(a.check("pyaspell"))
0362     a.session_dict(clear=True)
0363     print(a.session_dict())
0364     
0365     a.close()
0366 
0367 # vim: ts=4 sw=4