HAP-python 4.9.0__tar.gz → 4.9.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. {HAP-python-4.9.0 → hap_python-4.9.2}/HAP_python.egg-info/PKG-INFO +10 -5
  2. {HAP-python-4.9.0 → hap_python-4.9.2}/HAP_python.egg-info/SOURCES.txt +17 -1
  3. {HAP-python-4.9.0 → hap_python-4.9.2}/PKG-INFO +10 -5
  4. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/accessory.py +8 -3
  5. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/accessory_driver.py +5 -2
  6. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/const.py +1 -1
  7. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/hap_crypto.py +3 -6
  8. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/hap_protocol.py +17 -11
  9. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/hap_server.py +20 -12
  10. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/iid_manager.py +16 -9
  11. {HAP-python-4.9.0 → hap_python-4.9.2}/pyproject.toml +1 -0
  12. hap_python-4.9.2/tests/test_accessory.py +588 -0
  13. hap_python-4.9.2/tests/test_accessory_driver.py +1235 -0
  14. hap_python-4.9.2/tests/test_camera.py +143 -0
  15. hap_python-4.9.2/tests/test_characteristic.py +574 -0
  16. hap_python-4.9.2/tests/test_encoder.py +121 -0
  17. hap_python-4.9.2/tests/test_hap_crypto.py +33 -0
  18. hap_python-4.9.2/tests/test_hap_handler.py +1042 -0
  19. hap_python-4.9.2/tests/test_hap_protocol.py +802 -0
  20. hap_python-4.9.2/tests/test_hap_server.py +198 -0
  21. hap_python-4.9.2/tests/test_hsrp.py +65 -0
  22. hap_python-4.9.2/tests/test_iid_manager.py +58 -0
  23. hap_python-4.9.2/tests/test_loader.py +75 -0
  24. hap_python-4.9.2/tests/test_service.py +241 -0
  25. hap_python-4.9.2/tests/test_state.py +119 -0
  26. hap_python-4.9.2/tests/test_tlv.py +29 -0
  27. hap_python-4.9.2/tests/test_util.py +48 -0
  28. {HAP-python-4.9.0 → hap_python-4.9.2}/HAP_python.egg-info/dependency_links.txt +0 -0
  29. {HAP-python-4.9.0 → hap_python-4.9.2}/HAP_python.egg-info/requires.txt +0 -0
  30. {HAP-python-4.9.0 → hap_python-4.9.2}/HAP_python.egg-info/top_level.txt +0 -0
  31. {HAP-python-4.9.0 → hap_python-4.9.2}/LICENSE +0 -0
  32. {HAP-python-4.9.0 → hap_python-4.9.2}/MANIFEST.in +0 -0
  33. {HAP-python-4.9.0 → hap_python-4.9.2}/README.md +0 -0
  34. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/__init__.py +0 -0
  35. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/camera.py +0 -0
  36. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/characteristic.py +0 -0
  37. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/encoder.py +0 -0
  38. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/hap_event.py +0 -0
  39. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/hap_handler.py +0 -0
  40. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/hsrp.py +0 -0
  41. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/loader.py +0 -0
  42. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/params.py +0 -0
  43. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/resources/characteristics.json +0 -0
  44. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/resources/services.json +0 -0
  45. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/resources/snapshot.jpg +0 -0
  46. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/service.py +0 -0
  47. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/state.py +0 -0
  48. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/tlv.py +0 -0
  49. {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/util.py +0 -0
  50. {HAP-python-4.9.0 → hap_python-4.9.2}/setup.cfg +0 -0
  51. {HAP-python-4.9.0 → hap_python-4.9.2}/setup.py +0 -0
@@ -1,13 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: HAP-python
3
- Version: 4.9.0
3
+ Version: 4.9.2
4
4
  Summary: HomeKit Accessory Protocol implementation in python
5
5
  Home-page: https://github.com/ikalchev/HAP-python
6
6
  License: Apache License 2.0
7
7
  Project-URL: Bug Reports, https://github.com/ikalchev/HAP-python/issues
8
8
  Project-URL: Documentation, http://hap-python.readthedocs.io/en/latest/
9
9
  Project-URL: Source, https://github.com/ikalchev/HAP-python/tree/master
10
- Platform: UNKNOWN
11
10
  Classifier: Development Status :: 5 - Production/Stable
12
11
  Classifier: Intended Audience :: Developers
13
12
  Classifier: Intended Audience :: End Users/Desktop
@@ -24,8 +23,16 @@ Classifier: Topic :: Home Automation
24
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
24
  Requires-Python: >=3.7
26
25
  Description-Content-Type: text/markdown
27
- Provides-Extra: QRCode
28
26
  License-File: LICENSE
27
+ Requires-Dist: async_timeout
28
+ Requires-Dist: cryptography
29
+ Requires-Dist: chacha20poly1305-reuseable
30
+ Requires-Dist: orjson>=3.7.2
31
+ Requires-Dist: zeroconf>=0.36.2
32
+ Requires-Dist: h11
33
+ Provides-Extra: qrcode
34
+ Requires-Dist: base36; extra == "qrcode"
35
+ Requires-Dist: pyqrcode; extra == "qrcode"
29
36
 
30
37
  [![PyPI version](https://badge.fury.io/py/HAP-python.svg)](https://badge.fury.io/py/HAP-python) [![Build Status](https://github.com/ikalchev/HAP-python/workflows/CI/badge.svg)](https://github.com/ikalchev/HAP-python) [![codecov](https://codecov.io/gh/ikalchev/HAP-python/branch/master/graph/badge.svg)](https://codecov.io/gh/ikalchev/HAP-python) [![Python Versions](https://img.shields.io/pypi/pyversions/HAP-python.svg)](https://pypi.python.org/pypi/HAP-python/) [![Documentation Status](https://readthedocs.org/projects/hap-python/badge/?version=latest)](http://hap-python.readthedocs.io/en/latest/?badge=latest) [![Downloads](https://pepy.tech/badge/hap-python)](https://pepy.tech/project/hap-python)
31
38
  # HAP-python
@@ -302,5 +309,3 @@ please report and I will try to fix them.
302
309
  Suggestions are always welcome.
303
310
 
304
311
  Have fun!
305
-
306
-
@@ -31,4 +31,20 @@ pyhap/tlv.py
31
31
  pyhap/util.py
32
32
  pyhap/resources/characteristics.json
33
33
  pyhap/resources/services.json
34
- pyhap/resources/snapshot.jpg
34
+ pyhap/resources/snapshot.jpg
35
+ tests/test_accessory.py
36
+ tests/test_accessory_driver.py
37
+ tests/test_camera.py
38
+ tests/test_characteristic.py
39
+ tests/test_encoder.py
40
+ tests/test_hap_crypto.py
41
+ tests/test_hap_handler.py
42
+ tests/test_hap_protocol.py
43
+ tests/test_hap_server.py
44
+ tests/test_hsrp.py
45
+ tests/test_iid_manager.py
46
+ tests/test_loader.py
47
+ tests/test_service.py
48
+ tests/test_state.py
49
+ tests/test_tlv.py
50
+ tests/test_util.py
@@ -1,13 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: HAP-python
3
- Version: 4.9.0
3
+ Version: 4.9.2
4
4
  Summary: HomeKit Accessory Protocol implementation in python
5
5
  Home-page: https://github.com/ikalchev/HAP-python
6
6
  License: Apache License 2.0
7
7
  Project-URL: Bug Reports, https://github.com/ikalchev/HAP-python/issues
8
8
  Project-URL: Documentation, http://hap-python.readthedocs.io/en/latest/
9
9
  Project-URL: Source, https://github.com/ikalchev/HAP-python/tree/master
10
- Platform: UNKNOWN
11
10
  Classifier: Development Status :: 5 - Production/Stable
12
11
  Classifier: Intended Audience :: Developers
13
12
  Classifier: Intended Audience :: End Users/Desktop
@@ -24,8 +23,16 @@ Classifier: Topic :: Home Automation
24
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
24
  Requires-Python: >=3.7
26
25
  Description-Content-Type: text/markdown
27
- Provides-Extra: QRCode
28
26
  License-File: LICENSE
27
+ Requires-Dist: async_timeout
28
+ Requires-Dist: cryptography
29
+ Requires-Dist: chacha20poly1305-reuseable
30
+ Requires-Dist: orjson>=3.7.2
31
+ Requires-Dist: zeroconf>=0.36.2
32
+ Requires-Dist: h11
33
+ Provides-Extra: qrcode
34
+ Requires-Dist: base36; extra == "qrcode"
35
+ Requires-Dist: pyqrcode; extra == "qrcode"
29
36
 
30
37
  [![PyPI version](https://badge.fury.io/py/HAP-python.svg)](https://badge.fury.io/py/HAP-python) [![Build Status](https://github.com/ikalchev/HAP-python/workflows/CI/badge.svg)](https://github.com/ikalchev/HAP-python) [![codecov](https://codecov.io/gh/ikalchev/HAP-python/branch/master/graph/badge.svg)](https://codecov.io/gh/ikalchev/HAP-python) [![Python Versions](https://img.shields.io/pypi/pyversions/HAP-python.svg)](https://pypi.python.org/pypi/HAP-python/) [![Documentation Status](https://readthedocs.org/projects/hap-python/badge/?version=latest)](http://hap-python.readthedocs.io/en/latest/?badge=latest) [![Downloads](https://pepy.tech/badge/hap-python)](https://pepy.tech/project/hap-python)
31
38
  # HAP-python
@@ -302,5 +309,3 @@ please report and I will try to fix them.
302
309
  Suggestions are always welcome.
303
310
 
304
311
  Have fun!
305
-
306
-
@@ -207,7 +207,7 @@ class Accessory:
207
207
  int(self.driver.state.pincode.replace(b"-", b""), 10) & 0x7FFFFFFF
208
208
  ) # pincode
209
209
 
210
- encoded_payload = base36.dumps(payload).upper()
210
+ encoded_payload = base36.dumps(payload).upper() # pylint: disable=possibly-used-before-assignment
211
211
  encoded_payload = encoded_payload.rjust(9, "0")
212
212
 
213
213
  return "X-HM://" + encoded_payload + self.driver.state.setup_id
@@ -241,7 +241,9 @@ class Accessory:
241
241
  """
242
242
  return {
243
243
  HAP_REPR_AID: self.aid,
244
- HAP_REPR_SERVICES: [s.to_HAP(include_value=include_value) for s in self.services],
244
+ HAP_REPR_SERVICES: [
245
+ s.to_HAP(include_value=include_value) for s in self.services
246
+ ],
245
247
  }
246
248
 
247
249
  def setup_message(self):
@@ -391,7 +393,10 @@ class Bridge(Accessory):
391
393
 
392
394
  .. seealso:: Accessory.to_HAP
393
395
  """
394
- return [acc.to_HAP(include_value=include_value) for acc in (super(), *self.accessories.values())]
396
+ return [
397
+ acc.to_HAP(include_value=include_value)
398
+ for acc in (super(), *self.accessories.values())
399
+ ]
395
400
 
396
401
  def get_characteristic(self, aid: int, iid: int) -> Optional["Characteristic"]:
397
402
  """.. seealso:: Accessory.to_HAP"""
@@ -323,7 +323,7 @@ class AccessoryDriver:
323
323
  and os.name != "nt"
324
324
  ):
325
325
  logger.debug("Setting child watcher")
326
- watcher = asyncio.SafeChildWatcher()
326
+ watcher = asyncio.SafeChildWatcher() # pylint: disable=deprecated-class
327
327
  watcher.attach_loop(self.loop)
328
328
  asyncio.set_child_watcher(watcher)
329
329
  else:
@@ -642,16 +642,19 @@ class AccessoryDriver:
642
642
  tmp_filename = None
643
643
  try:
644
644
  temp_dir = os.path.dirname(self.persist_file)
645
+ logger.debug("Creating temp persist file in '%s'", temp_dir)
645
646
  with tempfile.NamedTemporaryFile(
646
647
  mode="w", dir=temp_dir, delete=False
647
648
  ) as file_handle:
648
649
  tmp_filename = file_handle.name
650
+ logger.debug("Created temp persist file '%s' named '%s'", file_handle, tmp_filename)
649
651
  self.encoder.persist(file_handle, self.state)
650
652
  if (
651
653
  os.name == "nt"
652
654
  ): # Or `[WinError 5] Access Denied` will be raised on Windows
653
655
  os.chmod(tmp_filename, 0o644)
654
- os.chmod(self.persist_file, 0o644)
656
+ if os.path.exists(self.persist_file):
657
+ os.chmod(self.persist_file, 0o644)
655
658
  os.replace(tmp_filename, self.persist_file)
656
659
  except Exception: # pylint: disable=broad-except
657
660
  logger.exception("Failed to persist accessory state")
@@ -1,7 +1,7 @@
1
1
  """This module contains constants used by other modules."""
2
2
  MAJOR_VERSION = 4
3
3
  MINOR_VERSION = 9
4
- PATCH_VERSION = 0
4
+ PATCH_VERSION = 2
5
5
  __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
6
6
  __version__ = f"{__short_version__}.{PATCH_VERSION}"
7
7
  REQUIRED_PYTHON_VER = (3, 7)
@@ -3,7 +3,7 @@ from functools import partial
3
3
  import logging
4
4
  import struct
5
5
  from struct import Struct
6
- from typing import List
6
+ from typing import Iterable, List
7
7
 
8
8
  from chacha20poly1305_reuseable import ChaCha20Poly1305Reusable as ChaCha20Poly1305
9
9
  from cryptography.hazmat.backends import default_backend
@@ -112,7 +112,7 @@ class HAPCrypto:
112
112
 
113
113
  return result
114
114
 
115
- def encrypt(self, data: bytes) -> bytes:
115
+ def encrypt(self, data: bytes) -> Iterable[bytes]:
116
116
  """Encrypt and send the return bytes."""
117
117
  result: List[bytes] = []
118
118
  offset = 0
@@ -127,7 +127,4 @@ class HAPCrypto:
127
127
  offset += length
128
128
  self._out_count += 1
129
129
 
130
- # Join the result once instead of concatenating each time
131
- # as this is much faster than generating an new immutable
132
- # byte string each time.
133
- return b"".join(result)
130
+ return result
@@ -46,7 +46,7 @@ class HAPServerProtocol(asyncio.Protocol):
46
46
  connections: Dict[str, "HAPServerProtocol"],
47
47
  accessory_driver: "AccessoryDriver",
48
48
  ) -> None:
49
- self.loop: asyncio.AbstractEventLoop = loop
49
+ self.loop = loop
50
50
  self.conn = h11.Connection(h11.SERVER)
51
51
  self.connections = connections
52
52
  self.accessory_driver = accessory_driver
@@ -55,7 +55,7 @@ class HAPServerProtocol(asyncio.Protocol):
55
55
  self.transport: Optional[asyncio.Transport] = None
56
56
 
57
57
  self.request: Optional[h11.Request] = None
58
- self.request_body: Optional[bytes] = None
58
+ self.request_body: List[bytes] = []
59
59
  self.response: Optional[HAPResponse] = None
60
60
 
61
61
  self.last_activity: Optional[float] = None
@@ -104,7 +104,7 @@ class HAPServerProtocol(asyncio.Protocol):
104
104
  self.handler.client_uuid,
105
105
  data,
106
106
  )
107
- self.transport.write(result)
107
+ self.transport.writelines(result)
108
108
  else:
109
109
  logger.debug(
110
110
  "%s (%s): Send unencrypted: %s",
@@ -246,27 +246,33 @@ class HAPServerProtocol(asyncio.Protocol):
246
246
  logger.debug(
247
247
  "%s (%s): h11 Event: %s", self.peername, self.handler.client_uuid, event
248
248
  )
249
- if event in (h11.NEED_DATA, h11.ConnectionClosed):
249
+ if event is h11.NEED_DATA:
250
250
  return False
251
251
 
252
252
  if event is h11.PAUSED:
253
253
  self.conn.start_next_cycle()
254
254
  return True
255
255
 
256
- if isinstance(event, h11.Request):
256
+ event_type = type(event)
257
+ if event_type is h11.ConnectionClosed:
258
+ return False
259
+
260
+ if event_type is h11.Request:
257
261
  self.request = event
258
- self.request_body = b""
262
+ self.request_body = []
259
263
  return True
260
264
 
261
- if isinstance(event, h11.Data):
262
- self.request_body += event.data
265
+ if event_type is h11.Data:
266
+ if TYPE_CHECKING:
267
+ assert isinstance(event, h11.Data) # nosec
268
+ self.request_body.append(event.data)
263
269
  return True
264
270
 
265
- if isinstance(event, h11.EndOfMessage):
266
- response = self.handler.dispatch(self.request, bytes(self.request_body))
271
+ if event_type is h11.EndOfMessage:
272
+ response = self.handler.dispatch(self.request, b"".join(self.request_body))
267
273
  self._process_response(response)
268
274
  self.request = None
269
- self.request_body = None
275
+ self.request_body = []
270
276
  return True
271
277
 
272
278
  return self._handle_invalid_conn_state(f"Unexpected event: {event}")
@@ -3,15 +3,20 @@
3
3
  The HAPServer is the point of contact to and from the world.
4
4
  """
5
5
 
6
+ import asyncio
6
7
  import logging
7
8
  import time
9
+ from typing import TYPE_CHECKING, Dict, Optional, Tuple
8
10
 
9
11
  from .hap_protocol import HAPServerProtocol
10
12
  from .util import callback
11
13
 
14
+ if TYPE_CHECKING:
15
+ from .accessory_driver import AccessoryDriver
16
+
12
17
  logger = logging.getLogger(__name__)
13
18
 
14
- IDLE_CONNECTION_CHECK_INTERVAL_SECONDS = 120
19
+ IDLE_CONNECTION_CHECK_INTERVAL_SECONDS = 300
15
20
 
16
21
 
17
22
  class HAPServer:
@@ -28,17 +33,18 @@ class HAPServer:
28
33
  implements exclusive access to the send methods.
29
34
  """
30
35
 
31
- def __init__(self, addr_port, accessory_handler):
36
+ def __init__(
37
+ self, addr_port: Tuple[str, int], accessory_handler: "AccessoryDriver"
38
+ ) -> None:
32
39
  """Create a HAP Server."""
33
40
  self._addr_port = addr_port
34
- self.connections = {} # (address, port): socket
41
+ self.connections: Dict[Tuple[str, int], HAPServerProtocol] = {}
35
42
  self.accessory_handler = accessory_handler
36
- self.server = None
37
- self._serve_task = None
38
- self._connection_cleanup = None
39
- self.loop = None
43
+ self.server: Optional[asyncio.Server] = None
44
+ self._connection_cleanup: Optional[asyncio.TimerHandle] = None
45
+ self.loop: Optional[asyncio.AbstractEventLoop] = None
40
46
 
41
- async def async_start(self, loop):
47
+ async def async_start(self, loop: asyncio.AbstractEventLoop) -> None:
42
48
  """Start the http-hap server."""
43
49
  self.loop = loop
44
50
  self.server = await loop.create_server(
@@ -49,7 +55,7 @@ class HAPServer:
49
55
  self.async_cleanup_connections()
50
56
 
51
57
  @callback
52
- def async_cleanup_connections(self):
58
+ def async_cleanup_connections(self) -> None:
53
59
  """Cleanup stale connections."""
54
60
  now = time.time()
55
61
  for hap_proto in list(self.connections.values()):
@@ -59,7 +65,7 @@ class HAPServer:
59
65
  )
60
66
 
61
67
  @callback
62
- def async_stop(self):
68
+ def async_stop(self) -> None:
63
69
  """Stop the server.
64
70
 
65
71
  This method must be run in the event loop.
@@ -70,10 +76,12 @@ class HAPServer:
70
76
  self.server.close()
71
77
  self.connections.clear()
72
78
 
73
- def push_event(self, data, client_addr, immediate=False):
79
+ def push_event(
80
+ self, data: bytes, client_addr: Tuple[str, int], immediate: bool = False
81
+ ) -> bool:
74
82
  """Queue an event to the current connection with the provided data.
75
83
 
76
- :param data: The charateristic changes
84
+ :param data: The characteristic changes
77
85
  :type data: dict
78
86
 
79
87
  :param client_addr: A client (address, port) tuple to which to send the data.
@@ -1,5 +1,12 @@
1
1
  """Module for the IIDManager class."""
2
2
  import logging
3
+ from typing import TYPE_CHECKING, Dict, Optional, Union
4
+
5
+ if TYPE_CHECKING:
6
+ from .characteristic import Characteristic
7
+ from .service import Service
8
+
9
+ ServiceOrCharType = Union[Service, Characteristic]
3
10
 
4
11
  logger = logging.getLogger(__name__)
5
12
 
@@ -7,13 +14,13 @@ logger = logging.getLogger(__name__)
7
14
  class IIDManager:
8
15
  """Maintains a mapping between Service/Characteristic objects and IIDs."""
9
16
 
10
- def __init__(self):
17
+ def __init__(self) -> None:
11
18
  """Initialize an empty instance."""
12
19
  self.counter = 0
13
- self.iids = {}
14
- self.objs = {}
20
+ self.iids: Dict["ServiceOrCharType", int] = {}
21
+ self.objs: Dict[int, "ServiceOrCharType"] = {}
15
22
 
16
- def assign(self, obj):
23
+ def assign(self, obj: "ServiceOrCharType") -> None:
17
24
  """Assign an IID to given object. Print warning if already assigned.
18
25
 
19
26
  :param obj: The object that will be assigned an IID.
@@ -32,7 +39,7 @@ class IIDManager:
32
39
  self.iids[obj] = iid
33
40
  self.objs[iid] = obj
34
41
 
35
- def get_iid_for_obj(self, obj):
42
+ def get_iid_for_obj(self, obj: "ServiceOrCharType") -> int:
36
43
  """Get the IID for the given object.
37
44
 
38
45
  Override this method to provide custom IID assignment.
@@ -40,15 +47,15 @@ class IIDManager:
40
47
  self.counter += 1
41
48
  return self.counter
42
49
 
43
- def get_obj(self, iid):
50
+ def get_obj(self, iid: int) -> "ServiceOrCharType":
44
51
  """Get the object that is assigned the given IID."""
45
52
  return self.objs.get(iid)
46
53
 
47
- def get_iid(self, obj):
54
+ def get_iid(self, obj: "ServiceOrCharType") -> int:
48
55
  """Get the IID assigned to the given object."""
49
56
  return self.iids.get(obj)
50
57
 
51
- def remove_obj(self, obj):
58
+ def remove_obj(self, obj: "ServiceOrCharType") -> Optional[int]:
52
59
  """Remove an object from the IID list."""
53
60
  iid = self.iids.pop(obj, None)
54
61
  if iid is None:
@@ -57,7 +64,7 @@ class IIDManager:
57
64
  del self.objs[iid]
58
65
  return iid
59
66
 
60
- def remove_iid(self, iid):
67
+ def remove_iid(self, iid: int) -> Optional["ServiceOrCharType"]:
61
68
  """Remove an object with an IID from the IID list."""
62
69
  obj = self.objs.pop(iid, None)
63
70
  if obj is None:
@@ -77,6 +77,7 @@ disable = [
77
77
  "too-many-return-statements",
78
78
  "too-many-statements",
79
79
  "too-many-boolean-expressions",
80
+ "too-many-positional-arguments",
80
81
  "unused-argument",
81
82
  "wrong-import-order",
82
83
  "unused-argument",