Skip to content

Commit 5b512de

Browse files
authored
[RSDK-11498] Make the client an async context manager (#1009)
As the Jira ticket describes, we used to have code like this: ```python client = None try: client = await ViamClient.create_from_dial_options(...) ... # use the client finally: if client is not None: await client.close() ``` and now we can have code like this: ```python async with ViamClient.create_from_dial_options(...) as client: ... # use the client ```
1 parent 4fc4a25 commit 5b512de

File tree

9 files changed

+168
-196
lines changed

9 files changed

+168
-196
lines changed

docs/examples/example.ipynb

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,8 @@
137137
" return await RobotClient.with_channel(channel, RobotClient.Options(refresh_interval=10, log_level=logging.FATAL))\n",
138138
"\n",
139139
"\n",
140-
"robot = await connect_with_channel()\n",
141-
"print(robot.resource_names)\n",
142-
"await robot.close()"
140+
"async with await connect_with_channel() as robot:\n",
141+
" print(robot.resource_names)"
143142
]
144143
},
145144
{
@@ -163,14 +162,11 @@
163162
"from viam.media.video import CameraMimeType\n",
164163
"from viam.media.utils.pil import viam_to_pil_image\n",
165164
"\n",
166-
"robot = await connect_with_channel()\n",
167-
"camera = Camera.from_robot(robot, \"camera0\")\n",
168-
"image = await camera.get_image(CameraMimeType.JPEG)\n",
169-
"pil = viam_to_pil_image(image)\n",
170-
"pil.save(\"foo.png\")\n",
171-
"\n",
172-
"# Don't forget to close the robot when you're done!\n",
173-
"await robot.close()"
165+
"async with await connect_with_channel() as robot:\n",
166+
" camera = Camera.from_robot(robot, \"camera0\")\n",
167+
" image = await camera.get_image(CameraMimeType.JPEG)\n",
168+
" pil = viam_to_pil_image(image)\n",
169+
" pil.save(\"foo.png\")"
174170
]
175171
},
176172
{
@@ -215,26 +211,9 @@
215211
"\n",
216212
"\n",
217213
"async def vision():\n",
218-
" robot = await connect_with_channel()\n",
219-
" vision = VisionClient.from_robot(robot)\n",
220-
" detections = await vision.get_detections_from_camera(\"camera_1\", \"detector_1\")"
221-
]
222-
},
223-
{
224-
"cell_type": "markdown",
225-
"metadata": {},
226-
"source": [
227-
"At the end, don't forget to close the connection"
228-
]
229-
},
230-
{
231-
"cell_type": "code",
232-
"execution_count": 5,
233-
"metadata": {},
234-
"outputs": [],
235-
"source": [
236-
"async def cleanup():\n",
237-
" await robot.close()"
214+
" async with await connect_with_channel() as robot:\n",
215+
" vision = VisionClient.from_robot(robot)\n",
216+
" detections = await vision.get_detections_from_camera(\"camera_1\", \"detector_1\")"
238217
]
239218
},
240219
{

src/viam/app/app_client.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -530,11 +530,9 @@ async def connect() -> ViamClient:
530530
async def main():
531531
532532
# Make a ViamClient
533-
viam_client = await connect()
534-
# Instantiate an AppClient called "cloud" to run cloud app API methods on
535-
cloud = viam_client.app_client
536-
537-
viam_client.close()
533+
async with await connect() as viam_client:
534+
# Instantiate an AppClient called "cloud" to run cloud app API methods on
535+
cloud = viam_client.app_client
538536
539537
if __name__ == '__main__':
540538
asyncio.run(main())

src/viam/app/billing_client.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,9 @@ async def connect() -> ViamClient:
4242
4343
async def main():
4444
# Make a ViamClient
45-
viam_client = await connect()
46-
# Instantiate a BillingClient to run billing client API methods on
47-
billing_client = viam_client.billing_client
48-
49-
viam_client.close()
45+
async with await connect() as viam_client:
46+
# Instantiate a BillingClient to run billing client API methods on
47+
billing_client = viam_client.billing_client
5048
5149
if __name__ == '__main__':
5250
asyncio.run(main())

src/viam/app/data_client.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,9 @@ async def connect() -> ViamClient:
136136
137137
async def main():
138138
# Make a ViamClient
139-
viam_client = await connect()
140-
# Instantiate a DataClient to run data client API methods on
141-
data_client = viam_client.data_client
142-
143-
viam_client.close()
139+
async with await connect() as viam_client:
140+
# Instantiate a DataClient to run data client API methods on
141+
data_client = viam_client.data_client
144142
145143
if __name__ == '__main__':
146144
asyncio.run(main())
@@ -1888,9 +1886,8 @@ async def file_upload_from_path(
18881886
path = Path(filepath)
18891887
file_name = path.stem
18901888
file_extension = path.suffix if path.suffix != "" else None
1891-
f = open(filepath, "rb")
1892-
data = f.read()
1893-
f.close()
1889+
with open(filepath, "rb") as f:
1890+
data = f.read()
18941891

18951892
metadata = UploadMetadata(
18961893
part_id=part_id,

src/viam/app/ml_training_client.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,9 @@ async def connect() -> ViamClient:
4646
async def main():
4747
4848
# Make a ViamClient
49-
viam_client = await connect()
50-
# Instantiate an MLTrainingClient to run ML training client API methods on
51-
ml_training_client = viam_client.ml_training_client
52-
53-
viam_client.close()
49+
async with await connect() as viam_client:
50+
# Instantiate an MLTrainingClient to run ML training client API methods on
51+
ml_training_client = viam_client.ml_training_client
5452
5553
if __name__ == '__main__':
5654
asyncio.run(main())

src/viam/app/provisioning_client.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,9 @@ async def connect() -> ViamClient:
4141
async def main():
4242
4343
# Make a ViamClient
44-
viam_client = await connect()
45-
# Instantiate a ProvisioningClient to run provisioning client API methods on
46-
provisioning_client = viam_client.provisioning_client
47-
48-
viam_client.close()
44+
async with await connect() as viam_client:
45+
# Instantiate a ProvisioningClient to run provisioning client API methods on
46+
provisioning_client = viam_client.provisioning_client
4947
5048
if __name__ == '__main__':
5149
asyncio.run(main())

src/viam/app/viam_client.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import os
2-
from typing import Mapping, Optional
2+
from typing import Any, Mapping, Optional
33

44
from grpclib.client import Channel
55
from typing_extensions import Self
@@ -206,6 +206,24 @@ async def main():
206206
"""
207207
return ProvisioningClient(self._channel, self._metadata)
208208

209+
async def __aenter__(self) -> "ViamClient":
210+
"""A ViamClient can act as an asynchronous context manager. It will do nothing special when
211+
the context is entered.
212+
"""
213+
return self
214+
215+
async def __aexit__(self, exc_type: Optional[type], exc_value: Optional[BaseException], traceback: Optional[Any]) -> None:
216+
"""A ViamClient can act as an asynchronous context manager. It will close itself when
217+
the context is exited.
218+
219+
::
220+
221+
async with ViamClient.create_from_dial_options(...) as client:
222+
await do_something_with(client)
223+
# client is closed here
224+
"""
225+
self.close()
226+
209227
def close(self) -> None:
210228
"""Close opened channels used for the various service stubs initialized."""
211229
if self._closed:

0 commit comments

Comments
 (0)