Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Conversation

@nickrbogdanov
Copy link

Commit 251dbc1 (core: implement Device.__eq__") had the side effect of making Device objects unhashable, so they could no longer be included in a set(). This was a regression from v1.1.1. Add a corresponding __hash__() method so that Device objects are hashable again.

Fixes eblot/pyftdi#256

Commit 251dbc1 ("core: implement Device.__eq__") had the side
effect of making Device objects unhashable, so they could no longer be
included in a set().  This was a regression from v1.1.1.  Add a
corresponding __hash__() method so that Device objects are hashable
again.

Fixes eblot/pyftdi#256
@jonasmalacofilho
Copy link
Member

The pyftdi code and comments after the call that raises that exception makes me worry that we may instead need to remove Device.__eq__() altogether:

if sys.platform == 'win32':
    # ugly kludge for a boring OS:
    # on Windows, the USB stack may enumerate the very same
    # devices several times: a real device with N interface
    # appears also as N device with as single interface.
    # We only keep the "device" that declares the most
    # interface count and discard the "virtual" ones.
    filtered_devs = dict()
    for dev in devs:
        vid = dev.idVendor
        pid = dev.idProduct
        ifc = max([cfg.bNumInterfaces for cfg in dev])
        k = (vid, pid, dev.bus, dev.address)
        if k not in filtered_devs:
            filtered_devs[k] = dev
        else:
            fdev = filtered_devs[k]
            fifc = max([cfg.bNumInterfaces for cfg in fdev])
            if fifc < ifc:
                filtered_devs[k] = dev
    devs = set(filtered_devs.values())

https://github.com/eblot/pyftdi/blob/845bc7944fe1/pyftdi/usbtools.py#L615-L635

The notable part is that, apparently, multiple Device instances with the same (backed, bus, address) tuple may be returned [by a single call to find()] on Windows. This wouldn't be a problem if all of those instances were interchangeable, but if Windows does indeed have that behavior, it makes me wonder if it isn't because they aren't actually interchangeable.

@jonasmalacofilho
Copy link
Member

Pinging @mcuee: have you seen this behavior on libusb and Windows (or any other platform)?

@mcuee
Copy link
Member

mcuee commented Jul 6, 2021

By default Windows will enumerate different interfaces of a USB composite device as different devices. That is one of the major challenges for libusb Windows backend. Sometimes we actually have to asked the user to use WinUSB to replace the USB Composite Device parent driver (usbccgp.sys).

@mcuee mcuee added the core label Jul 6, 2021
@mcuee
Copy link
Member

mcuee commented Jul 6, 2021

On the other hand, at one particular run (without hotplug), I believe libusb_get_port_numbers() for a device should be unique. With hotplug that is another story since libusb does not support hotplug under Windows.

Example: I purposely use different drivers for the two interfaces of FT2232H (USB Composite), one with libusbk driver and one with WinUSB driver, and the parent device is using usbccgp.sys. Still libusb lists it as one device.

https://github.com/libusb/libusb/blob/master/examples/listdevs.c

/c/work/libusb/libusb
$ ./examples/listdevs.exe
0424:5744 (bus 3, device 16) path: 4.3.2
0bda:5413 (bus 3, device 5) path: 2.3
0bda:0413 (bus 3, device 3) path: 4.3
0424:2740 (bus 3, device 46) path: 2.3.2.5
413c:b06e (bus 3, device 6) path: 2.5
046d:c534 (bus 1, device 1) path: 1
8087:0aaa (bus 1, device 4) path: 14
0424:2744 (bus 3, device 15) path: 2.3.2
0bda:5487 (bus 3, device 2) path: 2
0bda:0487 (bus 3, device 1) path: 4
047f:c056 (bus 3, device 45) path: 2.3.2.2
0bda:402e (bus 3, device 44) path: 2.3.4
8086:a36d (bus 1, device 0)
413c:2107 (bus 3, device 18) path: 2.3.2.3
0a5c:5842 (bus 1, device 2) path: 10
8086:15f0 (bus 3, device 0)
8086:15db (bus 2, device 0)
0bda:8153 (bus 3, device 4) path: 4.4
413c:b06f (bus 3, device 10) path: 2.3.5
046d:c077 (bus 3, device 19) path: 2.3.2.4
0403:6010 (bus 3, device 47) path: 2.3.2.1
0bda:58fd (bus 1, device 3) path: 11

@mcuee
Copy link
Member

mcuee commented Jul 6, 2021

As for the pyftdi issue, yes I can reproduce the issue.

/c/work/libusb/pyftdi
$ ftdi_urls.py
Traceback (most recent call last):
  File "C:/msys64/mingw64/bin/ftdi_urls.py", line 4, in <module>
    __import__('pkg_resources').run_script('pyftdi==0.53.1', 'ftdi_urls.py')
  File "C:/msys64/mingw64/lib/python3.8/site-packages/pkg_resources/__init__.py", line 651, in run_script
    self.require(requires)[0].run_script(script_name, ns)
  File "C:/msys64/mingw64/lib/python3.8/site-packages/pkg_resources/__init__.py", line 1455, in run_script
    exec(script_code, namespace, namespace)
  File "C:/msys64/mingw64/lib/python3.8/site-packages/pyftdi-0.53.1-py3.8.egg/EGG-INFO/scripts/ftdi_urls.py", line 74, in <module>
  File "C:/msys64/mingw64/lib/python3.8/site-packages/pyftdi-0.53.1-py3.8.egg/EGG-INFO/scripts/ftdi_urls.py", line 62, in main
  File "C:/msys64/mingw64/lib/python3.8/site-packages/pyftdi-0.53.1-py3.8.egg/pyftdi/ftdi.py", line 387, in show_devices
  File "C:/msys64/mingw64/lib/python3.8/site-packages/pyftdi-0.53.1-py3.8.egg/pyftdi/usbtools.py", line 274, in list_devices
  File "C:/msys64/mingw64/lib/python3.8/site-packages/pyftdi-0.53.1-py3.8.egg/pyftdi/usbtools.py", line 417, in enumerate_candidates
  File "C:/msys64/mingw64/lib/python3.8/site-packages/pyftdi-0.53.1-py3.8.egg/pyftdi/usbtools.py", line 95, in find_all
  File "C:/msys64/mingw64/lib/python3.8/site-packages/pyftdi-0.53.1-py3.8.egg/pyftdi/usbtools.py", line 614, in _find_devices
TypeError: unhashable type: 'Device'

@mcuee
Copy link
Member

mcuee commented Jul 6, 2021

With the proposed fix, the issue goes away.

/c/work/libusb/pyusb
$ ftdi_urls.py
Available interfaces:
  ftdi://ftdi:2232:3:2f/1   (Lattice FTUSB Interface Cable)
  ftdi://ftdi:2232:3:2f/2   (Lattice FTUSB Interface Cable)

@mcuee
Copy link
Member

mcuee commented Jul 7, 2021

Same report here. eblot/pyftdi#257

@jonasmalacofilho
Copy link
Member

I believe libusb_get_port_numbers() for a device should be unique.

That makes sense (within a given bus), and it seems to also hold for libusb_get_device_address() (again, within a given bus).
So it seems there's no need to remove Device.__eq__(), and this patch is sufficient. Thanks @nickrbogdanov!

Will give it a couple of days before releasing 1.2.1, just in case someone reports another problem with __eq__/__hash__.

And thanks again @mcuee.

@jonasmalacofilho jonasmalacofilho merged commit 7f3638b into pyusb:master Jul 7, 2021
@eblot
Copy link

eblot commented Jul 8, 2021

The pyftdi code and comments after the call that raises that exception makes me worry that we may instead need to remove Device.__eq__() altogether:

UsbTools in PyFtdi needs a major overhaul as it is too complex and dates back when earlier versions of PyUSB/libusb enumeration were very slow and were lacking useful features. I need to tackle with this rework "soon", as other features such as index-based selection is awful and buggy. Meanwhile I've disabled compatibility with PyUSB 1.2.0+ (and will restore support whenever 1.2.1 gets released).

@jonasmalacofilho
Copy link
Member

@eblot

Thanks for the additional context. Oh, and I just released 1.2.1.

@mcuee
Copy link
Member

mcuee commented Aug 23, 2021

pyftdi v0.53.3 has just been tagged and available on PyPI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ftdi_urls.py crashes with pyusb v1.2.0

4 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.