Say one develops an application that should run on Windows and on Linux and the application uses the pyserial package which provides the type serial.serialposix.Serial
when installed in Linux and the type serial.serialwin32.Serial
when installed in Windows. Is it possible to annotate the variable type, so that the linter knows which type to use based on the OS?
import serial
from serial.serialposix import Serial as PosixSerial
from serial.serialwin32 import Serial as Win32Serial
# linter complains on Windows
serial_port: PosixSerial = serial.Serial('/dev/ttyUSB0')
# linter complains on Linux
serial_port: Win32Serial = serial.Serial('COM1')
# linter complains on both Windows and Linux
serial_port: PosixSerial | Win32Serial = serial.Serial(f"{name}")
There are some constant literals for python installations, two of them are sys.platform
and os.name
.
os.name
is more general and will be nt
(Windows), posix
(linux) or java
. (Check out sys.platform
if you need fine grained options).
The following code creates a platform-aware variable Serial
, Win32Serial
and PosixSerial
are TypeAlias that are identical to Serial
on their matching platform and Never
on the respective other one.
Your type-checker/linter can pick up these constants and will infer only one if
clause as reachable here, hence it will know exactly if Serial
is the windows or linux (or another) variant on your current system or if you change the constant for the linter.
import os
import serial
if os.name == "nt":
from serial.serialwin32 import Serial # only reachable on Windows
Win32Serial: TypeAlias = Serial
PosixSerial = Never
serial_port: Win32Serial = serial.Serial('COM1')
elif os.name == "posix":
from serial.serialposix import Serial # only reachable on Linux
PosixSerial: TypeAlias = Serial
Win32Serial = Never
serial_port: PosixSerial = serial.Serial('/dev/ttyUSB0')
else:
... # maybe lint as SerialBase
# Serial is platform-aware, however statically only one covered at the same time
another_port: Serial = serial.Serial(f"{name}")
The use of Never
is optional, using it allows you to check to never reach linux code on windows (an alternative is also to use typing.assert_never
). For example:
def windows_only(a: Win32Serial):
...
# Windows OK
# Posix: error: Type "Serial" is not assignable to type "Win32Serial" (reportArgumentType)
windows_only(serial_port)