Currently, none of the classes derived from Interface use `super`. If
it's added, for example by adding this to `SocketInterface`:
```
def __init__(self):
super().__init__()
```
, a deprecation warning will appear in Python 3.6 and 3.7:
```
2021-04-30 13:14:25,000 24736 WARNING ? py.warnings: /home/pi/odoo/addons/hw_drivers/iot_handlers/interfaces/SocketInterface.py:11: DeprecationWarning: class not set defining 'SocketInterface' as <class 'SocketInterface.py.SocketInterface'>. Was classcell propagated to type.new?
class SocketInterface(Interface):
```
In Python 3.8 this is no longer a warning and becomes a `RuntimeError`.
The reason this happens is that `InterfaceMetaClass` caches the results
of the `__new__` calls it does in `interfaces`. CPython's data model
specifies that when calling `__new__`, the cell for `__class__` needs to
be passed along to the parent class: https://docs.python.org/3/reference/datamodel.html#creating-the-class-object .
See https://docs.python.org/3/c-api/cell.html for a definition of what
a cell is.
Because of the caching, this doesn't happen when `__new__` is called
multiple times for the same class. In the case of the `SocketInterface`
example, `__new__` is called twice: first when it's imported directly by
`load_iot_handlers` (https://github.com/odoo/odoo/blob/841c016913a87133bf7257c62ae5f6bf7d99e06d/addons/hw_drivers/main.py#L81)
and second when `IngenicoDriver` imports it. The `__class__` cell is
different each time in that case. With the caching present, this means
the second cell isn't propagated correctly, resulting in the warning /
error.
After `load_iot_handlers` has finished, the `Manager`'s `run` method
iterates over `interfaces`, instantiating and starting the loaded
classes. Because of this, simply removing the caching appears to be
sufficient to avoid the issue while keeping the end result the same.
This issue was encountered while forward-porting a V13 PR, which adds a
`super` use to `SocketInterface`. The issue didn't occur in V13, because
the metaclass doesn't have a cache there.
I found a second problem resulting from the caching. I had noticed that
the `socket_devices` dict the `IngenicoDriver` had access to did not
have the same contents as the one the `SocketInterface` was operating
on. This means that whenever you called the `disconnect` function on the
`IngenicoDriver`, which deletes its entry from `socket_devices`, it will
always fail. The bluetooth driver seems to be impacted by the same.
It turns out the reason for that is how downloaded modules get loaded in
`load_iot_handlers`. That logic (using `exec_module`) doesn't actually
import the module, it just executes the code (registering the interfaces
and drivers as a side-effect). This means the module doesn't get cached
in sys.modules. That means that when another module (`IngenicoDriver`)
imports that same module (`SocketInterface`), Python will execute the
entire contents again, in this case effectively replacing socket_devices
with a new dict, which `IngenicoDriver` gets a reference to, while the
`SocketInterface` thread that runs is still using the old reference to
the original dict. Meaning the two are out of sync. See
https://docs.python.org/3/reference/import.html#the-module-cache for
details on how the import caching works.
The reason the classes get out of sync is because the metaclass doesn't
update interfaces when the second actual import happens, so the
`SocketInterface` class that gets instantiated holds a reference to the
old `socket_devices` dict. Removing the caching removes that problem as
well. If there's no caching, both the `SocketInterface` class and
`IngenicoDriver` will hold a reference to the same instance of
`socket_devices`.
X-original-commit: edffdd10f3ceb3826bf8897f93fa8290d7fce1ec
This commit was originally part of a larger commit related to an
Ingenico double connection deadlock bugfix in V13, but in V14 most of
the related code has moved to enterprise. When a driver thread is
running, calling `disconnect` on it will delete it from iot_devices. If
the driver thread gets added to iot_devices in the Interface thread
after starting it, this means there is a tiny window of time where
`disconnect` could be called and the thread wouldn't be in iot_devices
yet, resulting in an exception. This change puts the driver thread in
iot_devices before starting it, avoiding that scenario and the need to
check in `disconnect` for that happening.
X-original-commit: 6c24d30c3910e67ed751327d1554e2e8cf5e958b
- Split driver.py to multiple files
- Remove hw_proxy module as most of its routes were not used and
move remaingin routes to a controller in hw_drivers
- Add missing coding and license lines at the beginning of files
- Reorder imports and remove unused ones
- Rename device_id to device_identifier for clarity
- Rename screen to display for consistency
- Other minors improvements
Taskid: 1973956
X-original-commit: 841c016913a87133bf7257c62ae5f6bf7d99e06d