Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pyenchant not working within QGIS environment #311

Open
walking-the-talk opened this issue Aug 4, 2023 · 21 comments
Open

pyenchant not working within QGIS environment #311

walking-the-talk opened this issue Aug 4, 2023 · 21 comments
Labels
help-wanted Help wanted

Comments

@walking-the-talk
Copy link

walking-the-talk commented Aug 4, 2023

Environment
Python version: 3.9
Operation system: Windows 10 64 bit

Additional context
QGIS 3.28 - I am trying to import enchant module to spell check data within QGIS. The problem might be QGIS related and I will raise an issue there, but I thought I would start here...

Steps to Reproduce
used pip install --target=path/to/python/QGIS pyenchant

It has installed and is working as expected using the command prompt in Windows
However, when accessing it through the Python Console in QGIS, although it appears to import enchant (it can run the help command), I get the following error when running the test code to enchant.request_dict("EN_US"):

Traceback (most recent call last):
  File "C:\PROGRA~1\QGIS32~1.2\apps\Python39\lib\code.py", line 90, in runcode
    exec(code, self.locals)
  File "<input>", line 1, in <module>
  File "<string>", line 3, in <module>
  File "C:\Users/Chris/AppData/Roaming/QGIS/QGIS3\profiles\default/python\enchant\__init__.py", line 272, in request_dict
    return Dict(tag, self)
  File "C:\Users/Chris/AppData/Roaming/QGIS/QGIS3\profiles\default/python\enchant\__init__.py", line 542, in __init__
    super().__init__()
  File "C:\Users/Chris/AppData/Roaming/QGIS/QGIS3\profiles\default/python\enchant\__init__.py", line 144, in __init__
    self._init_this()
  File "C:\Users/Chris/AppData/Roaming/QGIS/QGIS3\profiles\default/python\enchant\__init__.py", line 549, in _init_this
    this = self._broker._request_dict_data(self.tag)
  File "C:\Users/Chris/AppData/Roaming/QGIS/QGIS3\profiles\default/python\enchant\__init__.py", line 287, in _request_dict_data
    self._raise_error(e_str % (tag,), DictNotFoundError)
  File "C:\Users/Chris/AppData/Roaming/QGIS/QGIS3\profiles\default/python\enchant\__init__.py", line 233, in _raise_error
    raise eclass(default)
enchant.errors.DictNotFoundError: Dictionary for language 'EN_US' could not be found
Please check https://pyenchant.github.io/pyenchant/ for details

No dictionaries / languages are available when I try to list them using the broker (but no errors appear when running that code)

Expected behaviour
just no error would be nice! I have a Python script that needs enchant, but can't get to test that if it can't identify a Dict()

Additional context

import enchant
from pprint import pprint
d = enchant.request_dict("EN_US")  # or an other language
pprint(vars(d))

in Windows console

{'_Dict__describe_result': ('en_US',
                            'aspell',
                            'Aspell Provider',
                            'C:\\Program '
                            'Files\\Inkscape/lib/enchant-2\\enchant_aspell.dll'),
 '_broker': <enchant.Broker object at 0x00000176ac1327c0>,
 '_this': 1609177917168,
 'provider': <Enchant: Aspell Provider>,
 'tag': 'en_US'}

Weirdly that looks like Python is running via Inkscape (there are multiple versions of Python on my machine!) - I tried copying the enchant-2 folder to lib in my python39 folder, but no joy (that was a stab in the dark)

Thanks for any help you can provide (even it is just to point me towards QGIS as the culprit)

@walking-the-talk
Copy link
Author

An update on this issue, which leads me to think it might not be a QGIS problem.

I uninstalled the pyenchant package and then reinstalled a different (QGIS recommended) way. So the installation now sits in 'site packages' folder of Python39.

The same problem occurs within QGIS and when I run the specific copy of Python (3.9) from the command prompt the same behaviour occurs -
import enchant - works; broker - works;
Dict() - same error as before, claiming that no dictionaries are installed - the expected folder structure is on my system...
enchant

Any thoughts on whether this is a bug with pyenchant or my set up?

@dmerejkowsky
Copy link
Member

dmerejkowsky commented Aug 9, 2023

Any thoughts on whether this is a bug with pyenchant or my set up?

No idea :( Is QGIS hard to install ? I'm asking in case anyone wants to try and reproduce the bug

@walking-the-talk
Copy link
Author

QGIS is very easy to install - https://qgis.org/en/site/forusers/download.html - using the LTR - there is and .msi installer that can be used with default settings...

After installation there is a way to install additional python packages using the OSGeo4W shell (should be available on your start menu) - this will run the qgis installed version of python - and then use pip -m install pyenchant

As highlighted above the site-packages are installed in the Roaming folder for Python39 (the version used by QGIS...

Thanks.

I find it hard to believe that QGIS has no inbuilt spell checker and I'm trying to pull together a plugin that will work... I have a way of generating QPlainTextEdit widgets so I'm a step closer...

qgis/QGIS#49934

@pmhahn
Copy link
Collaborator

pmhahn commented Aug 15, 2023

  1. Please follow our installation guid; especially you needs to install the enchant dictionary separately!
  2. The locale is called en_US: lowercase en for English as spoken in the USA (uppercase US)

@walking-the-talk
Copy link
Author

Thanks. I carefully followed the guide and checked that the dictionaries were installed before posting (in the previous screengrab you will see that the hunspell folder exists). I followed the subsequent tutorials,

I have now installed pyenchant on another windows machine with QGIS 3.22 installed and run further tests on the existing installation (without obvious intervention from me, Enchant now works). I can get Enchant to work from within the OSGeoW shell (i.e. it calls the version of Python used by QGIS). However it does not work on either machine when using the Python console within QGIS - using the same command sequence.
Here is a screengrab comparing the two methods - the QGIS Python console (right) shows [ ] to denote that it appears there are no dictionaries, yet the shell window (left) shows the hunspell list and the test spell check completing successfully.

Does this point more clearly to QGIS being the issue? Is part of the package not being 'exposed' to QGIS (I'm not a python expert as you can tell!)

QGIS-enchant

To address your second point, I appear to have used EN_US rather than en_US, but that doesn't seem to be a material issue as far as I can see - enchant appears to pick up en_US when EN_US is requested:

>>> dd=enchant.request_dict('EN_US')
>>> pprint(vars(dd))
{'_Dict__describe_result': ('en_US',
                            'hunspell',
                            'Hunspell Provider',
                            'C:\\Users\\Chris\\AppData\\Roaming\\Python\\Python39\\site-packages\\enchant\\data\\mingw64/lib/enchant-2\\enchant_hunspell.dll'),
 '_broker': <enchant.Broker object at 0x0000029F84AA3610>,
 '_this': 2884108075472,
 'provider': <Enchant: Hunspell Provider>,
 'tag': 'en_US'}

Thanks for your help so far.

@caprieldeluca
Copy link

the site-packages are installed in the Roaming folder for Python39 (the version used by QGIS...

That isn't the QGIS Python install.
Please, include the output of

import sys
print(sys.executable)

in both OSGeo4W Shell and QGIS Python console.


C:\Users\Chris\AppData\Roaming\Python\Python39\site-packages\enchant\...

Don't have a Windows machine at hand, but I think it must say something like:
C:\PROGRA~1\QGIS32~1.2\apps\Python39\lib\site-packages\enchant\...

@walking-the-talk
Copy link
Author

Thanks for following up on your comments from StackExchange.

Agreed that is not the QGIS install, but it is the location that OSGeo4W installs these site packages when using the shell and pip install pyenchant - I have run the shell as user and administrator on my machine and it places the package in the same location - which is the [user]\AppData\Roaming\Python\Python39\site-packages folder. There is conflicting advice about installing python packages for QGIS, but this appeared to be the most straightforward, and kept within the 'ecosystem of QGIS'.

If it were to be installed within the QGIS install (in my case C:\PROGRA~1\QGIS32~1.2\) wouldn't that mean pyenchant needs to be reinstalled on each update of QGIS?

print(sys.executable) from OSGeo4W: C:\PROGRA~1\QGIS32~1.2\bin\python.exe
print(sys.executable) from QGIS console: C:\Program Files\QGIS 3.28.2\bin\qgis-bin.exe

As mentioned at StackExchange (https://gis.stackexchange.com/questions/473944/) I have also checked the available paths using print(sys.path) – both OSGeo4W shell and the QGIS Console include [user]\AppData\Roaming\Python\Python39\site-packages

@caprieldeluca
Copy link

I have run the shell as user and administrator on my machine and it places the package in the same location - which is the [user]\AppData\Roaming\Python\Python39\site-packages folder.

I think that folder is for a system-wide Python install. Maybe the pip version used to install it was a system-wide one.
The only safe way I know to install packages for QGIS in Windows is from the OSGeo4W Setup application, but neither enchant nor python3-pyenchant seems to be packaged there (https://download.osgeo.org/osgeo4w/v2/x86_64/release/).
Maybe "C:\Program Files\QGIS 3.28.2\bin\python" -m pip install pyenchant from OSGeo4W Shell is able to install it in QGIS environment.


If it were to be installed within the QGIS install (in my case C:\PROGRA~1\QGIS32~1.2\) wouldn't that mean pyenchant needs to be reinstalled on each update of QGIS?

Yes. It is a kind of Python virtual environment, and must be in that way.
You can also install through OSGeo4W network installer (https://download.osgeo.org/osgeo4w/v2/) and upgrade QGIS always in the same location (usually C:/OSGeo4W). Each QGIS upgrade will update its Python base install and all packages delivered through OSGeo4W will upgrade to it, but in any case you will need to upgrade custom installed packages.


I have also checked the available paths using print(sys.path) – both OSGeo4W shell and the QGIS Console include [user]\AppData\Roaming\Python\Python39\site-packages

Yes, but the problem doesn't seem to be related with pyenchant but with the enchant C library wrapped in it, since it is not installed inside QGIS environment. I don't know where it is installed but maybe you can make its path available through Windows PATH (not PYTHONPATH) environment variable.

@walking-the-talk
Copy link
Author

Thanks - really appreciate your assistance.

My first attempt to install pyenchant was to try using --target (see 'how to reproduce' in the first post) and I have repeated that using the following:
pip install --target="C:\Program Files\QGIS 3.28.2\apps\Python39\Lib\site-packages" pyenchant

(so it now sits alongside all the main packages such as pandas geopandas etc)

No joy.

I added the folder C:\Users\Chris\AppData\Roaming\Python\Python39\site-packages\enchant\data\mingw64 (which according to the file _enchant.py is where it tries to allocate the ctype wrapper) to my windows path.

No joy.

I tried your suggestion of "C:\Program Files\QGIS 3.28.2\bin\python" -m pip install pyenchant from OSGeo4W Shell - it tried to put it in the existing Roaming folder

No joy.

I'm going way beyond my competence here, but it looks like _enchant.py is the ctypes wrapper and appears to construct the 'connections' to the relevant folders (extract from lines 33-38 of the code):

    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.

I'm not surprised there is no package within OSGeo4W - there has been no apparent interest in spell checking within QGIS anyway (an observation rather than criticism!)

I'm ALMOST relieved that it isn't an obvious error that I've made... but still frustrated that I've fallen over before even being able to get the proof-of-concept to work.

@caprieldeluca
Copy link

caprieldeluca commented Jan 11, 2024

it looks like _enchant.py is the ctypes wrapper and appears to construct the 'connections' to the relevant folders

Yes, the "Dictionary for language '%s' could not be found\n" message is raised because new_dict is None in __init__.py, which must be returned by e.enchant_broker_request_dict in _enchant.py, being e the enchant C library wrapper.

So, try to edit your installed _enchant.py file to include a

print(f'{enchant_lib_path = }')

line between 170 and 171 (before e = ctypes.cdll.LoadLibrary(enchant_lib_path)), to see if the find_c_enchant_lib() function is returning the same path in QGIS Python console than OSGeo4W Shell.

If you can print the path to the library returned in both consoles we can see what the problem is. Also, you will be sure which pyenchant package are they calling.

@walking-the-talk
Copy link
Author

Thanks - your patience with my level of competence is admirable :-) and not even patronising me when I pointed out the ctype wrapper location (which you'd clearly already worked out).

I had previously been going through the code and the thrown errors to see if I could make some sense of it, and I'd spotted the commented code in def from_prefix that indicated no need to modify %PATH% which is one of the (many things) that confused me (implying that it should be able to find the path to the dictionary?).

So the path to the library is exactly the same on both QGIS Python console and OSGeo4W Shell:

enchant_lib_path = 'C:\\Users\\Chris\\AppData\\Roaming\\Python\\Python39\\site-packages\\enchant\\data\\mingw64\\bin\\libenchant-2.dll'

In an attempt to be helpful, I changed line 117 to VERBOSE_FIND = True (I should have thought of that earlier, my apologies)

Which gives the following output:

pyenchant:: looking in C:\Users\Chris\AppData\Roaming\Python\Python39\site-packages\enchant\data\mingw64
pyenchant:: finding from prefix C:\Users\Chris\AppData\Roaming\Python\Python39\site-packages\enchant\data\mingw64
pyenchant:: Prepending C:\Users\Chris\AppData\Roaming\Python\Python39\site-packages\enchant\data\mingw64\bin to %PATH%
pyenchant:: loading library C:\Users\Chris\AppData\Roaming\Python\Python39\site-packages\enchant\data\mingw64\bin\libenchant-2.dll
pyenchant:: setting prefix C:\Users\Chris\AppData\Roaming\Python\Python39\site-packages\enchant\data\mingw64

Again this is the same in both consoles - nothing obviously amiss.

@caprieldeluca
Copy link

Ok, so this is something with QGIS Python interpreter, or just something inside the QGIS Python console interface.

I wrote a QGIS plugin to run Python files without the console. It is called Puentes. Can you install it?

Write a file, anywhere, called something like test_enchant.py, with the following code:

# test_enchant.py

import enchant

b = enchant.Broker()
plog(f'{b.list_dicts() = }')

d = enchant.Dict('en_US')
plog(f'{d.check('enchant') = }')

Open Plugins menu, Puentes, Configure, and open the test_enchant.py file. In the same menu, click on Run.
The plog() function is like a wrapper of print(), to log messages in the Puentes tab of the Log Messages panel.

@walking-the-talk
Copy link
Author

Nice Bridge! If I've installed it correctly, I still have the same problem... I hope the error in line 94 of your Puentes code is caused by enchant failing and not anything I have done?

Puentes Log:

2024-01-12T16:48:08 INFO Configured to run: C:\Users\Chris\AppData\Roaming\QGIS\QGIS3\profiles\default\python\expressions\test_enchant.py
2024-01-12T16:48:22 INFO ----------
2024-01-12T16:48:22 INFO Run: C:\Users\Chris\AppData\Roaming\QGIS\QGIS3\profiles\default\python\expressions\test_enchant.py
2024-01-12T16:48:22 INFO b.list_dicts() = []

So it can't find any dictionaries and throws exactly the same errors as QGIS console in enchant __init__.py when runningDict("en_US") :

Python Error log:

2024-01-12T16:48:23     WARNING    Traceback (most recent call last):
              File "C:\Users/Chris/AppData/Roaming/QGIS/QGIS3\profiles\default/python/plugins\puentes\plugin.py", line 94, in run_command
              runpy.run_path(self.file_path, init_globals={'plog': plog})
              File "C:\PROGRA~1\QGIS32~1.2\apps\Python39\lib\runpy.py", line 268, in run_path
              return _run_module_code(code, init_globals, run_name,
              File "C:\PROGRA~1\QGIS32~1.2\apps\Python39\lib\runpy.py", line 97, in _run_module_code
              _run_code(code, mod_globals, init_globals,
              File "C:\PROGRA~1\QGIS32~1.2\apps\Python39\lib\runpy.py", line 87, in _run_code
              exec(code, run_globals)
              File "C:\Users\Chris\AppData\Roaming\QGIS\QGIS3\profiles\default\python\expressions\test_enchant.py", line 8, in 
              d = enchant.Dict("en_US")
              File "C:\Users\Chris\AppData\Roaming\Python\Python39\site-packages\enchant\__init__.py", line 542, in __init__
              super().__init__()
              File "C:\Users\Chris\AppData\Roaming\Python\Python39\site-packages\enchant\__init__.py", line 144, in __init__
              self._init_this()
              File "C:\Users\Chris\AppData\Roaming\Python\Python39\site-packages\enchant\__init__.py", line 549, in _init_this
              this = self._broker._request_dict_data(self.tag)
              File "C:\Users\Chris\AppData\Roaming\Python\Python39\site-packages\enchant\__init__.py", line 287, in _request_dict_data
              self._raise_error(e_str % (tag,), DictNotFoundError)
              File "C:\Users\Chris\AppData\Roaming\Python\Python39\site-packages\enchant\__init__.py", line 233, in _raise_error
              raise eclass(default)
             enchant.errors.DictNotFoundError: Dictionary for language 'en_US' could not be found
             Please check https://pyenchant.github.io/pyenchant/ for details
             
             During handling of the above exception, another exception occurred:
             
             Traceback (most recent call last):
              File "C:\Users/Chris/AppData/Roaming/QGIS/QGIS3\profiles\default/python/plugins\puentes\plugin.py", line 98, in run_command
              plog(*traceback.format_exception(e, limit=-1))
             TypeError: format_exception() missing 2 required positional arguments: 'value' and 'tb'

[side-note - double quotes are required in test_enchant.py above to avoid SyntaxError: f-string: unmatched '(' so it becomes plog(f'{d.check("enchant") = }')]

Still scratching my head...

@caprieldeluca
Copy link

I still have the same problem...

So it is a not a console problem. The Python interpreter can't find it.

I hope the error in line 94 of your Puentes code is caused by enchant failing and not anything I have done?

The traceback must be logged in the same tab and not raise the exception to QGIS. If you can add a issue including the Python version (from QGIS About window) I can see how to fix the traceback call.

side-note - double quotes are required in test_enchant.py above to avoid SyntaxError: f-string: unmatched '('

Sorry, that error was mine. I don't have a Windows machine and am not testing this. I will see if it work in a Debian machine.

@caprieldeluca
Copy link

Well, it works out-of-the-box in debian, just installing python3-enchant.

Screenshot_2024-01-12_20-41-31

@walking-the-talk
Copy link
Author

Sorry, that error was mine. I don't have a Windows machine and am not testing this.

No problem, no need to apologise for being so helpful and giving a sample script for testing - I was just making sure that anyone else testing this would be able to follow (or not pick up a false error). Hence calling it a side-note :-)

Good that it works on debian - starting to narrow down some avenues of thought. Thanks for confirming that it is a pyenchant / windows / QGIS triumvirate!

@caprieldeluca
Copy link

it is a pyenchant / windows / QGIS triumvirate!

It seems to me strange that both paths are the same but QGIS can't read the dictionaries. The library seems to be loaded.

You can try to set Windows environment variables at hand. See that the last directory in the path is the bin folder.

I don't know how to test enchant steps after the library load.

@walking-the-talk
Copy link
Author

I've added that specific \enchant\data\mingw64\bin folder to path in the Windows Environment Variables.

I have checked that it can be read using os.environ.get('path') and the folder is listed, but still the same errors are thrown.

We appear to have reached a limit here - unless the pyenchant developers can shed some light on testing enchant after the library load, we are stuck. I will see if I can find any answers / ask more useful or directed questions of QGIS developers around the Python interpreter on Windows (given that the enchant package for debian works in QGIS as you have demonstrated).

Thanks for all your help - none of which is wasted, even though we haven't found the prize yet :-)

@walking-the-talk
Copy link
Author

walking-the-talk commented Jan 14, 2024

I wonder... qgis/QGIS#54491

If you can get pyenchant to work on debian version of QGIS, which I think it running a more recent version of python (hence your plugin Puentes needing a back-port to version <3.10), maybe there is an (undocumented?) issue with QGIS python interpreter doing anything more than seeing mingw64 libraries? I know that's arm-waving vague statement, but maybe more food for thought...

@caprieldeluca
Copy link

maybe there is an (undocumented?) issue with QGIS python interpreter doing anything more than loading mingw64 libraries?

Whether due to the interpreter embedded in the QGIS executable, or for some other reason, it was always difficult to link libraries within QGIS in Windows. That's why there is no apparent consensus on how to install them. And therefore, the best solution that I see possible is to include enchant (and even better python3-enchant) package in the OSGeo4W repository.

My recommendation is to complete a feature request ticket in the OSGeo4W trac system (an OSGeo UserID is required), asking about its possible inclusion or requesting help about your current installation.

@walking-the-talk
Copy link
Author

Thanks for this suggestion - seems a prudent move irrespective of the underlying issue - awaiting approval of OSGeo Userid to log the request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help-wanted Help wanted
Projects
None yet
Development

No branches or pull requests

4 participants