11# Copyright 2023 Canonical Ltd.
22# Licensed under the Apache V2, see LICENCE file for details.
3+ from __future__ import annotations
34
5+ import asyncio
46import base64
57import os
68import textwrap
79import zipfile
810from collections import defaultdict
9- from functools import partial
1011from pathlib import Path
12+ from typing import Any
1113
1214import yaml
1315from pyasn1 .codec .der .encoder import encode
2022
2123async def execute_process (* cmd , log = None ):
2224 """Wrapper around asyncio.create_subprocess_exec."""
23- p = await jasyncio .create_subprocess_exec (
25+ p = await asyncio .create_subprocess_exec (
2426 * cmd ,
25- stdin = jasyncio .subprocess .PIPE ,
26- stdout = jasyncio .subprocess .PIPE ,
27- stderr = jasyncio .subprocess .PIPE ,
27+ stdin = asyncio .subprocess .PIPE ,
28+ stdout = asyncio .subprocess .PIPE ,
29+ stderr = asyncio .subprocess .PIPE ,
2830 )
2931 stdout , stderr = await p .communicate ()
3032 if log :
@@ -84,7 +86,7 @@ async def read_ssh_key():
8486 """Attempt to read the local juju admin's public ssh key, so that it can be
8587 passed on to a model.
8688 """
87- loop = jasyncio .get_running_loop ()
89+ loop = asyncio .get_running_loop ()
8890 return await loop .run_in_executor (None , _read_ssh_key )
8991
9092
@@ -93,20 +95,31 @@ class IdQueue:
9395 ID.
9496 """
9597
96- def __init__ (self , maxsize = 0 ):
97- self ._queues = defaultdict (partial (jasyncio .Queue , maxsize ))
98-
99- async def get (self , id_ ):
98+ _queues : dict [int , asyncio .Queue [dict [str , Any ] | Exception ]]
99+
100+ def __init__ (self ):
101+ self ._queues = defaultdict (asyncio .Queue )
102+ # FIXME cleanup needed.
103+ # in some cases an Exception is put into the queue.
104+ # if the main coro exits, this exception will be logged as "never awaited"
105+ # we gotta do something about that to keep the output clean.
106+ #
107+ # Additionally, it's conceivable that a response is put in the queue
108+ # and then an exception is put via put_all()
109+ # the reader only ever fetches one item, and exception is "never awaited"
110+ # rewrite put_all to replace the pending response instead.
111+
112+ async def get (self , id_ : int ) -> dict [str , Any ]:
100113 value = await self ._queues [id_ ].get ()
101114 del self ._queues [id_ ]
102115 if isinstance (value , Exception ):
103116 raise value
104117 return value
105118
106- async def put (self , id_ , value ):
119+ async def put (self , id_ : int , value : dict [ str , Any ] ):
107120 await self ._queues [id_ ].put (value )
108121
109- async def put_all (self , value ):
122+ async def put_all (self , value : Exception ):
110123 for queue in self ._queues .values ():
111124 await queue .put (value )
112125
@@ -120,9 +133,9 @@ async def block_until(*conditions, timeout=None, wait_period=0.5):
120133
121134 async def _block ():
122135 while not all (c () for c in conditions ):
123- await jasyncio .sleep (wait_period )
136+ await asyncio .sleep (wait_period )
124137
125- await jasyncio .shield (jasyncio .wait_for (_block (), timeout ))
138+ await asyncio .shield (asyncio .wait_for (_block (), timeout ))
126139
127140
128141async def block_until_with_coroutine (
@@ -136,12 +149,12 @@ async def block_until_with_coroutine(
136149
137150 async def _block ():
138151 while not await condition_coroutine ():
139- await jasyncio .sleep (wait_period )
152+ await asyncio .sleep (wait_period )
140153
141- await jasyncio .shield (jasyncio .wait_for (_block (), timeout = timeout ))
154+ await asyncio .shield (asyncio .wait_for (_block (), timeout = timeout ))
142155
143156
144- async def wait_for_bundle (model , bundle , ** kwargs ):
157+ async def wait_for_bundle (model , bundle : str | Path , ** kwargs ) -> None :
145158 """Helper to wait for just the apps in a specific bundle.
146159
147160 Equivalent to loading the bundle, pulling out the app names, and calling::
@@ -156,8 +169,8 @@ async def wait_for_bundle(model, bundle, **kwargs):
156169 bundle = bundle_path / "bundle.yaml"
157170 except OSError :
158171 pass
159- bundle = yaml .safe_load (textwrap .dedent (bundle ).strip ())
160- apps = list (bundle .get ("applications" , bundle .get ("services" )).keys ())
172+ content : dict [ str , Any ] = yaml .safe_load (textwrap .dedent (bundle ).strip ())
173+ apps = list (content .get ("applications" , content .get ("services" )).keys ())
161174 await model .wait_for_idle (apps , ** kwargs )
162175
163176
0 commit comments