425 lines
12 KiB
Python
425 lines
12 KiB
Python
# pyenchant
|
|
#
|
|
# Copyright (C) 2004-2008, Ryan Kelly
|
|
#
|
|
# This library is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
# License as published by the Free Software Foundation; either
|
|
# version 2.1 of the License, or (at your option) any later version.
|
|
#
|
|
# This library is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
# License along with this library; if not, write to the
|
|
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
# Boston, MA 02111-1307, USA.
|
|
#
|
|
# In addition, as a special exception, you are
|
|
# given permission to link the code of this program with
|
|
# non-LGPL Spelling Provider libraries (eg: a MSFT Office
|
|
# spell checker backend) and distribute linked combinations including
|
|
# the two. You must obey the GNU Lesser General Public License in all
|
|
# respects for all of the code used other than said providers. If you modify
|
|
# this file, you may extend this exception to your version of the
|
|
# file, but you are not obligated to do so. If you do not wish to
|
|
# do so, delete this exception statement from your version.
|
|
#
|
|
|
|
"""
|
|
|
|
enchant._enchant: ctypes-based wrapper for enchant C library
|
|
|
|
This module implements the low-level interface to the underlying
|
|
C library for enchant. The interface is based on ctypes and tries
|
|
to do as little as possible while making the higher-level components
|
|
easier to write.
|
|
|
|
The following conveniences are provided that differ from the underlying
|
|
C API:
|
|
|
|
* the "enchant" prefix has been removed from all functions, since
|
|
python has a proper module system
|
|
* callback functions do not take a user_data argument, since
|
|
python has proper closures that can manage this internally
|
|
* string lengths are not passed into functions such as dict_check,
|
|
since python strings know how long they are
|
|
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
import os.path
|
|
import ctypes
|
|
from ctypes import c_char_p, c_int, c_size_t, c_void_p, pointer, CFUNCTYPE, POINTER
|
|
import ctypes.util
|
|
import platform
|
|
import textwrap
|
|
|
|
|
|
def from_prefix(prefix):
|
|
find_message("finding from prefix ", prefix)
|
|
assert os.path.exists(prefix), prefix + " does not exist"
|
|
bin_path = os.path.join(prefix, "bin")
|
|
enchant_dll_path = os.path.join(bin_path, "libenchant-2.dll")
|
|
assert os.path.exists(enchant_dll_path), enchant_dll_path + " does not exist"
|
|
# Make sure all the dlls found next to libenchant-2.dll
|
|
# (libglib-2.0-0.dll, libgmodule-2.0-0.dll, ...) can be
|
|
# used without having to modify %PATH%
|
|
new_path = bin_path + os.pathsep + os.environ["PATH"]
|
|
find_message("Prepending ", bin_path, " to %PATH%")
|
|
os.environ["PATH"] = new_path
|
|
return enchant_dll_path
|
|
|
|
|
|
def from_env_var(library_path):
|
|
find_message("using PYENCHANT_LIBRARY_PATH env var")
|
|
assert os.path.exists(library_path), library_path + " does not exist"
|
|
return library_path
|
|
|
|
|
|
def from_package_resources():
|
|
if sys.platform != "win32":
|
|
return None
|
|
bits, _ = platform.architecture()
|
|
if bits == "64bit":
|
|
subdir = "mingw64" # hopefully this is compatible
|
|
else:
|
|
subdir = "mingw32" # ditto
|
|
this_path = os.path.dirname(os.path.abspath(__file__))
|
|
data_path = os.path.join(this_path, "data", subdir)
|
|
find_message("looking in ", data_path)
|
|
if os.path.exists(data_path):
|
|
return from_prefix(data_path)
|
|
|
|
|
|
def from_system():
|
|
# Note: keep enchant-2 first
|
|
find_message("looking in system")
|
|
candidates = [
|
|
"enchant-2",
|
|
"libenchant-2",
|
|
"enchant",
|
|
"libenchant",
|
|
"enchant-1",
|
|
"libenchant-1",
|
|
]
|
|
|
|
for name in candidates:
|
|
find_message("with name ", name)
|
|
res = ctypes.util.find_library(name)
|
|
if res:
|
|
return res
|
|
|
|
|
|
VERBOSE_FIND = False
|
|
|
|
|
|
def find_message(*args):
|
|
if not VERBOSE_FIND:
|
|
return
|
|
print("pyenchant:: ", *args, sep="")
|
|
|
|
|
|
def find_c_enchant_lib():
|
|
verbose = os.environ.get("PYENCHANT_VERBOSE_FIND")
|
|
if verbose:
|
|
global VERBOSE_FIND
|
|
VERBOSE_FIND = True
|
|
prefix = os.environ.get("PYENCHANT_ENCHANT_PREFIX")
|
|
if prefix:
|
|
return from_prefix(prefix)
|
|
|
|
library_path = os.environ.get("PYENCHANT_LIBRARY_PATH")
|
|
if library_path:
|
|
return from_env_var(library_path)
|
|
|
|
from_package = from_package_resources()
|
|
if from_package:
|
|
return from_package
|
|
|
|
# Last chance
|
|
return from_system()
|
|
|
|
|
|
enchant_lib_path = find_c_enchant_lib()
|
|
|
|
if enchant_lib_path is None:
|
|
msg = textwrap.dedent(
|
|
"""\
|
|
The 'enchant' C library was not found and maybe needs to be installed.
|
|
See https://pyenchant.github.io/pyenchant/install.html
|
|
for details
|
|
"""
|
|
)
|
|
raise ImportError(msg)
|
|
|
|
|
|
find_message("loading library ", enchant_lib_path)
|
|
e = ctypes.cdll.LoadLibrary(enchant_lib_path)
|
|
|
|
# Always assume the found enchant C dll is inside
|
|
# the correct directory layout
|
|
prefix_dir = os.path.dirname(os.path.dirname(enchant_lib_path))
|
|
if hasattr(e, "enchant_set_prefix_dir") and prefix_dir:
|
|
find_message("setting prefix ", prefix_dir)
|
|
e.enchant_set_prefix_dir(prefix_dir.encode())
|
|
|
|
|
|
def callback(restype, *argtypes):
|
|
"""Factory for generating callback function prototypes.
|
|
|
|
This is factored into a factory so I can easily change the definition
|
|
for experimentation or debugging.
|
|
"""
|
|
return CFUNCTYPE(restype, *argtypes)
|
|
|
|
|
|
t_broker_desc_func = callback(None, c_char_p, c_char_p, c_char_p, c_void_p)
|
|
t_dict_desc_func = callback(None, c_char_p, c_char_p, c_char_p, c_char_p, c_void_p)
|
|
|
|
|
|
# Simple typedefs for readability
|
|
|
|
t_broker = c_void_p
|
|
t_dict = c_void_p
|
|
|
|
|
|
# Now we can define the types of each function we are going to use
|
|
|
|
broker_init = e.enchant_broker_init
|
|
broker_init.argtypes = []
|
|
broker_init.restype = t_broker
|
|
|
|
broker_free = e.enchant_broker_free
|
|
broker_free.argtypes = [t_broker]
|
|
broker_free.restype = None
|
|
|
|
broker_request_dict = e.enchant_broker_request_dict
|
|
broker_request_dict.argtypes = [t_broker, c_char_p]
|
|
broker_request_dict.restype = t_dict
|
|
|
|
broker_request_pwl_dict = e.enchant_broker_request_pwl_dict
|
|
broker_request_pwl_dict.argtypes = [t_broker, c_char_p]
|
|
broker_request_pwl_dict.restype = t_dict
|
|
|
|
broker_free_dict = e.enchant_broker_free_dict
|
|
broker_free_dict.argtypes = [t_broker, t_dict]
|
|
broker_free_dict.restype = None
|
|
|
|
broker_dict_exists = e.enchant_broker_dict_exists
|
|
broker_dict_exists.argtypes = [t_broker, c_char_p]
|
|
broker_dict_exists.restype = c_int
|
|
|
|
broker_set_ordering = e.enchant_broker_set_ordering
|
|
broker_set_ordering.argtypes = [t_broker, c_char_p, c_char_p]
|
|
broker_set_ordering.restype = None
|
|
|
|
broker_get_error = e.enchant_broker_get_error
|
|
broker_get_error.argtypes = [t_broker]
|
|
broker_get_error.restype = c_char_p
|
|
|
|
broker_describe1 = e.enchant_broker_describe
|
|
broker_describe1.argtypes = [t_broker, t_broker_desc_func, c_void_p]
|
|
broker_describe1.restype = None
|
|
|
|
|
|
def broker_describe(broker, cbfunc):
|
|
def cbfunc1(*args):
|
|
cbfunc(*args[:-1])
|
|
|
|
broker_describe1(broker, t_broker_desc_func(cbfunc1), None)
|
|
|
|
|
|
broker_list_dicts1 = e.enchant_broker_list_dicts
|
|
broker_list_dicts1.argtypes = [t_broker, t_dict_desc_func, c_void_p]
|
|
broker_list_dicts1.restype = None
|
|
|
|
|
|
def broker_list_dicts(broker, cbfunc):
|
|
def cbfunc1(*args):
|
|
cbfunc(*args[:-1])
|
|
|
|
broker_list_dicts1(broker, t_dict_desc_func(cbfunc1), None)
|
|
|
|
|
|
try:
|
|
broker_get_param = e.enchant_broker_get_param
|
|
except AttributeError:
|
|
# Make the lookup error occur at runtime
|
|
def broker_get_param(broker, name):
|
|
return e.enchant_broker_get_param(broker, name)
|
|
|
|
|
|
else:
|
|
broker_get_param.argtypes = [t_broker, c_char_p]
|
|
broker_get_param.restype = c_char_p
|
|
|
|
try:
|
|
broker_set_param = e.enchant_broker_set_param
|
|
except AttributeError:
|
|
# Make the lookup error occur at runtime
|
|
def broker_set_param(broker, name, value):
|
|
return e.enchant_broker_set_param(broker, name, value)
|
|
|
|
|
|
else:
|
|
broker_set_param.argtypes = [t_broker, c_char_p, c_char_p]
|
|
broker_set_param.restype = None
|
|
|
|
try:
|
|
get_version = e.enchant_get_version
|
|
except AttributeError:
|
|
# Make the lookup error occur at runtime
|
|
def get_version():
|
|
return e.enchant_get_version()
|
|
|
|
|
|
else:
|
|
get_version.argtypes = []
|
|
get_version.restype = c_char_p
|
|
|
|
try:
|
|
set_prefix_dir = e.enchant_set_prefix_dir
|
|
except AttributeError:
|
|
# Make the lookup error occur at runtime
|
|
def set_prefix_dir(path):
|
|
return e.enchant_set_prefix_dir(path)
|
|
|
|
|
|
else:
|
|
set_prefix_dir.argtypes = [c_char_p]
|
|
set_prefix_dir.restype = None
|
|
|
|
try:
|
|
get_user_config_dir = e.enchant_get_user_config_dir
|
|
except AttributeError:
|
|
# Make the lookup error occur at runtime
|
|
def get_user_config_dir():
|
|
return e.enchant_get_user_config_dir()
|
|
|
|
|
|
else:
|
|
get_user_config_dir.argtypes = []
|
|
get_user_config_dir.restype = c_char_p
|
|
|
|
dict_check1 = e.enchant_dict_check
|
|
dict_check1.argtypes = [t_dict, c_char_p, c_size_t]
|
|
dict_check1.restype = c_int
|
|
|
|
|
|
def dict_check(dict, word):
|
|
return dict_check1(dict, word, len(word))
|
|
|
|
|
|
dict_suggest1 = e.enchant_dict_suggest
|
|
dict_suggest1.argtypes = [t_dict, c_char_p, c_size_t, POINTER(c_size_t)]
|
|
dict_suggest1.restype = POINTER(c_char_p)
|
|
|
|
|
|
def dict_suggest(dict, word):
|
|
num_suggs_p = pointer(c_size_t(0))
|
|
suggs_c = dict_suggest1(dict, word, len(word), num_suggs_p)
|
|
suggs = []
|
|
n = 0
|
|
while n < num_suggs_p.contents.value:
|
|
suggs.append(suggs_c[n])
|
|
n = n + 1
|
|
if num_suggs_p.contents.value > 0:
|
|
dict_free_string_list(dict, suggs_c)
|
|
return suggs
|
|
|
|
|
|
dict_add1 = e.enchant_dict_add
|
|
dict_add1.argtypes = [t_dict, c_char_p, c_size_t]
|
|
dict_add1.restype = None
|
|
|
|
|
|
def dict_add(dict, word):
|
|
return dict_add1(dict, word, len(word))
|
|
|
|
|
|
dict_add_to_pwl1 = e.enchant_dict_add
|
|
dict_add_to_pwl1.argtypes = [t_dict, c_char_p, c_size_t]
|
|
dict_add_to_pwl1.restype = None
|
|
|
|
|
|
def dict_add_to_pwl(dict, word):
|
|
return dict_add_to_pwl1(dict, word, len(word))
|
|
|
|
|
|
dict_add_to_session1 = e.enchant_dict_add_to_session
|
|
dict_add_to_session1.argtypes = [t_dict, c_char_p, c_size_t]
|
|
dict_add_to_session1.restype = None
|
|
|
|
|
|
def dict_add_to_session(dict, word):
|
|
return dict_add_to_session1(dict, word, len(word))
|
|
|
|
|
|
dict_remove1 = e.enchant_dict_remove
|
|
dict_remove1.argtypes = [t_dict, c_char_p, c_size_t]
|
|
dict_remove1.restype = None
|
|
|
|
|
|
def dict_remove(dict, word):
|
|
return dict_remove1(dict, word, len(word))
|
|
|
|
|
|
dict_remove_from_session1 = e.enchant_dict_remove_from_session
|
|
dict_remove_from_session1.argtypes = [t_dict, c_char_p, c_size_t]
|
|
dict_remove_from_session1.restype = c_int
|
|
|
|
|
|
def dict_remove_from_session(dict, word):
|
|
return dict_remove_from_session1(dict, word, len(word))
|
|
|
|
|
|
dict_is_added1 = e.enchant_dict_is_added
|
|
dict_is_added1.argtypes = [t_dict, c_char_p, c_size_t]
|
|
dict_is_added1.restype = c_int
|
|
|
|
|
|
def dict_is_added(dict, word):
|
|
return dict_is_added1(dict, word, len(word))
|
|
|
|
|
|
dict_is_removed1 = e.enchant_dict_is_removed
|
|
dict_is_removed1.argtypes = [t_dict, c_char_p, c_size_t]
|
|
dict_is_removed1.restype = c_int
|
|
|
|
|
|
def dict_is_removed(dict, word):
|
|
return dict_is_removed1(dict, word, len(word))
|
|
|
|
|
|
dict_store_replacement1 = e.enchant_dict_store_replacement
|
|
dict_store_replacement1.argtypes = [t_dict, c_char_p, c_size_t, c_char_p, c_size_t]
|
|
dict_store_replacement1.restype = None
|
|
|
|
|
|
def dict_store_replacement(dict, mis, cor):
|
|
return dict_store_replacement1(dict, mis, len(mis), cor, len(cor))
|
|
|
|
|
|
dict_free_string_list = e.enchant_dict_free_string_list
|
|
dict_free_string_list.argtypes = [t_dict, POINTER(c_char_p)]
|
|
dict_free_string_list.restype = None
|
|
|
|
dict_get_error = e.enchant_dict_get_error
|
|
dict_get_error.argtypes = [t_dict]
|
|
dict_get_error.restype = c_char_p
|
|
|
|
dict_describe1 = e.enchant_dict_describe
|
|
dict_describe1.argtypes = [t_dict, t_dict_desc_func, c_void_p]
|
|
dict_describe1.restype = None
|
|
|
|
|
|
def dict_describe(dict, cbfunc):
|
|
def cbfunc1(tag, name, desc, file, data):
|
|
cbfunc(tag, name, desc, file)
|
|
|
|
dict_describe1(dict, t_dict_desc_func(cbfunc1), None)
|