-
Notifications
You must be signed in to change notification settings - Fork 995
Consistent ImportError messages #1827
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
Conversation
f51e4a0
to
a5341c1
Compare
import serial | ||
except ImportError: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong, because it demands users to have the dependency installed.
The existing version, allows the user not to have the dependency installed, but of course he/she is then not allowed to use these methods.
We do NOT want to demand users to install e.g. pyserial / aiohttp if they are going to use serial communication / the simulator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what I did earlier to avoid followup errors was to have
except ImportError:
serial = None
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do NOT want to demand users to install e.g. pyserial / aiohttp if they are going to use serial communication / the simulator.
I'm confused. What use is importing the serial client without pyserial
, or importing the HTTP simulator without aiohttp
? They will not run, and fail with some confusing error message.
Are you concerned about importing pymodbus
to use another part? (for instance, the TCP client as in #1128) I checked and this is not a problem.
>>> import serial
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'serial'
>>> from pymodbus.client import AsyncModbusTcpClient
>>>
Last year pymodbus did something like import *
, but now after your refactors it does not. So I'm not sure how the ImportError would be triggered when it shouldn't be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the user does "import pymodbus" and that scans all py files, and it then breaks down.
Try to uninstall pyserial and try for yourself, I had the problem about a month ago. aiohttp was never the problem, but pyserial have caused a couple of issues.
See this:
(pymodbus) jan@MacMini examples % pip uninstall pyserial
...
(pymodbus) jan@MacMini examples % ./client_async.py
Traceback (most recent call last):
File "/Volumes/DiskHomes/home_jan/repos/pymodbus/pymodbus/client/serial.py", line 17, in <module>
import serial
ModuleNotFoundError: No module named 'serial'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Volumes/DiskHomes/home_jan/repos/pymodbus/examples/./client_async.py", line 35, in <module>
from pymodbus.client import (
File "/Volumes/DiskHomes/home_jan/repos/pymodbus/pymodbus/client/__init__.py", line 16, in <module>
from pymodbus.client.serial import AsyncModbusSerialClient, ModbusSerialClient
File "/Volumes/DiskHomes/home_jan/repos/pymodbus/pymodbus/client/serial.py", line 19, in <module>
raise ImportError( # pylint: disable=raise-missing-from
ImportError: Serial client requires pyserial Please install with "pip install pyserial" and try again.
(pymodbus) jan@MacMini examples %
client_async without parameters uses a tcp client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My test was done with this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the user does "import pymodbus" and that scans all py files, and it then breaks down. [...]
Try to uninstall pyserial and try for yourself, I had the problem about a month ago.
(pymodbus) jan@MacMini examples % ./client_async.py
Traceback (most recent call last):
File "/Volumes/DiskHomes/home_jan/repos/pymodbus/pymodbus/client/serial.py", line 17, in <module>
import serial
ModuleNotFoundError: No module named 'serial'
OK, now I understand what I missed. The examples fail, not all of pymodbus. I'll look to see if that can be changed.
Yes, that is my intention as well! Depending on how it's called,
With this PR, the error message is below. I find this much more helpful.
|
Well the code as it is without this PR, do not have problem, nor did it have problems with the "web". But the error message you get is exactly what we try to avoid by making a conditional import. In reality, apart from the error message, there are no reason for your try/except. Clearly the app import pymodbus.server.simulator or instantiate ModbusSerialClient, the dependency must be installed....but if the app does not of that the code should work...see the test I just made above. |
Yes, I agree. The entire point (instead of |
You miss the point, it is a conditional import, so all the users that do not use simulator and/or serial do not need to install these libraries...and as you might already have seen from my test above, uninstalling pyserial and running client_async.py caused problems. |
The solution we have currently on dev allows:
This PR should not change that. |
Yes, understood. I think the answer is to use conditional importing (and/or error suppression) in the examples as well.
|
NO because the examples are building block of applications, and we do not want to force applications to use conditional imports. Apart from the fact that I would not know which import should be conditional...because the application import pymodbus, pymodbus.client etc....the application do not even know (in theory) that we import pysierlal and other dependencies. |
Can I ask you to slow down a bit, please? 😃
Yes, both of these are fine.
Only the examples are problematic, because they import everything (even if unneeded). It is here where I propose to suppress the unused imports. pymodbus/examples/client_async.py Lines 37 to 42 in 4ddb9b1
|
And I politely disagree 😃 The application should be allowed to import everything, but not to instantiate the classes. I am not sure we still have it, but earlier you would get an error message if you did client = AsyncModbusSerialClient() and did not have the library installed. Please do not forget that many users copy our examples (that is sort of the idea) so if we introduce conditionals in the examples it grows into the applications. I really do not understand what we are discussing....this works nicely on dev, so why change it to something less handy ? |
OK, perhaps it's best to check at time of use rather than time of import.
No, it fails silently now.
Well, there are multiple cases that are inconsistent.
I am trying to make them all the same, and helpful. (Also avoid the wrath of strict |
Sorry if the error message dropped out (aiohttp in simulator probably never had it, I am guilty in not being consistent) ! I am all in favour of having a "common" error message if a dependency is missing ... I think the easiest (apart from mypy) is something like:
And then in the relevant base class init():
I have no idea how that works with mypy, but it would work well with users. |
Actually, you did include it and it used to work (but was broken by the pymodbus/pymodbus/server/simulator/http_server.py Lines 120 to 121 in 4ddb9b1
How about this? try:
import x
x_missing = True
except:
x_missing = False
__init__():
if x_missing:
nice error message and exit |
Also, This is why I re-raised an |
Your suggestion is a good compromise, with a small change, because I agree that sys.exit is ugly, how about
That could be used in REPL as well, there are a number of dependencies there. |
Forgot to write using RuntimeError secures the exception is not caught in a wildcard except. I think runtime error is the only exception not catchable. |
See #1818 (comment)
Thanks @MAKOMO