68eb4f482a
The zaza and zaza-openstack-tests project are currently installed within the same package. A side effect of that is that the zaza/__init__.py from zaza is overwritten by the one in zaza-openstack-tests. We ought to find a better way of doing this but we're currently blocked on using zaza + zaza-openstack-tests on systems with Python 3.10 installed, and updating this file unblocks us. Update zaza/__init__.py from openstack-charmers/zaza@c48c955ef7
110 lines
3.6 KiB
Python
110 lines
3.6 KiB
Python
# Copyright 2018 Canonical Ltd.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
# __NOTE__
|
|
#
|
|
# Whenever this file is changed, make sure to update the copy of it in
|
|
# ``zaza-openstack-tests``.
|
|
#
|
|
# The ``zaza`` and ``zaza-openstack-tests`` projects are related, and currently
|
|
# the latter is installed as a package inside the former. As a consequence
|
|
# ``zaza-openstack-tests`` needs to carry a copy of this file
|
|
# (``zaza/__init__.py``) as this file will be overwritten by the copy in
|
|
# ``zaza-openstack-tests`` on install.
|
|
#
|
|
# We of course want a better solution to this, but in the interest of time
|
|
# this note is left here until we get around to fixing it properly.
|
|
#
|
|
# __NOTE__
|
|
|
|
"""Functions to support converting async function to a sync equivalent."""
|
|
import asyncio
|
|
import logging
|
|
from pkgutil import extend_path
|
|
from sys import version_info
|
|
|
|
|
|
__path__ = extend_path(__path__, __name__)
|
|
|
|
|
|
def run(*steps):
|
|
"""Run the given steps in an asyncio loop.
|
|
|
|
If the tasks spawns other future (tasks) then these are also cleaned up
|
|
after each step is performed.
|
|
|
|
:returns: The result of the last asyncio.Task
|
|
:rtype: Any
|
|
"""
|
|
if not steps:
|
|
return
|
|
try:
|
|
loop = asyncio.get_running_loop()
|
|
except RuntimeError:
|
|
loop = asyncio.new_event_loop()
|
|
except AttributeError:
|
|
# Remove once support for Python 3.6 is dropped
|
|
loop = asyncio.get_event_loop()
|
|
|
|
for step in steps:
|
|
task = loop.create_task(step)
|
|
loop.run_until_complete(asyncio.wait([task]))
|
|
|
|
# Let's also cancel any remaining tasks:
|
|
while True:
|
|
# issue #445 - asyncio.Task.all_tasks() deprecated in 3.7
|
|
if version_info.major == 3 and version_info.minor >= 7:
|
|
try:
|
|
tasklist = asyncio.all_tasks()
|
|
except RuntimeError:
|
|
# no running event loop
|
|
break
|
|
else:
|
|
tasklist = asyncio.Task.all_tasks()
|
|
pending_tasks = [p for p in tasklist if not p.done()]
|
|
if pending_tasks:
|
|
logging.info(
|
|
"async -> sync. cleaning up pending tasks: len: {}"
|
|
.format(len(pending_tasks)))
|
|
for pending_task in pending_tasks:
|
|
pending_task.cancel()
|
|
try:
|
|
loop.run_until_complete(pending_task)
|
|
except asyncio.CancelledError:
|
|
pass
|
|
except Exception as e:
|
|
logging.error(
|
|
"A pending task caused an exception: {}"
|
|
.format(str(e)))
|
|
else:
|
|
break
|
|
|
|
return task.result()
|
|
|
|
|
|
def sync_wrapper(f):
|
|
"""Convert the given async function into a sync function.
|
|
|
|
This is only to be called from sync code and it runs all tasks (and cancels
|
|
all tasks at the end of each run) for the code that is being given.
|
|
|
|
:returns: The de-async'd function
|
|
:rtype: function
|
|
"""
|
|
def _wrapper(*args, **kwargs):
|
|
async def _run_it():
|
|
return await f(*args, **kwargs)
|
|
return run(_run_it())
|
|
return _wrapper
|