Skip to content

Commit e0199c8

Browse files
authored
Merge pull request #1245 from dimaqq/feat-new-wait-for-idle--dumb-class
#1245 Same as #1219 but using dumb classes instead of async generator.
2 parents 9b4e411 + 716adb1 commit e0199c8

18 files changed

+1511
-99
lines changed

.github/workflows/test.yaml

+7-2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ jobs:
7878
- "3.4/stable"
7979
- "3.5/stable"
8080
- "3.6/stable"
81+
new_wait_for_idle:
82+
- "True"
83+
- "False"
8184
steps:
8285
- uses: actions/checkout@v4
8386
- uses: actions/setup-python@v5
@@ -116,7 +119,9 @@ jobs:
116119
# # set model defaults
117120
# juju model-defaults apt-http-proxy=$PROXY apt-https-proxy=$PROXY juju-http-proxy=$PROXY juju-https-proxy=$PROXY snap-http-proxy=$PROXY snap-https-proxy=$PROXY
118121
# juju model-defaults
119-
- run: uvx -p ${{ matrix.python }} tox -e integration
122+
- run: uvx -p ${{ matrix.python }} tox -s -e integration
123+
env:
124+
JUJU_NEW_WAIT_FOR_IDLE: ${{ matrix.new_wait_for_idle }}
120125

121126
integration-quarantine:
122127
name: Quarantined Integration Tests
@@ -144,4 +149,4 @@ jobs:
144149
with:
145150
provider: lxd
146151
juju-channel: ${{ matrix.juju }}
147-
- run: uvx -p ${{ matrix.python }} tox -e integration-quarantine
152+
- run: uvx -p ${{ matrix.python }} tox -s -e integration-quarantine

docs/changelog.rst

+12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
Changelog
22
---------
33

4+
3.6.1.0
5+
^^^^^^^
6+
7+
Friday 20th Dec 2024
8+
9+
## What's Changed
10+
* add 3.5.5 schema and update SCHEMAS.md by @james-garner-canonical in https://github.com/juju/python-libjuju/pull/1223
11+
* feat: larger default websockets frame size by @dimaqq in https://github.com/juju/python-libjuju/pull/1239
12+
* deprecate juju.jasyncio by @EdmilsonRodrigues in https://github.com/juju/python-libjuju/pull/1221
13+
* remove juju.loop, deprecated before 3.0 by @dimaqq in https://github.com/juju/python-libjuju/pull/1242
14+
* new wait for idle implementation, behind a feature flag ``JUJU_NEW_WAIT_FOR_IDLE`` in https://github.com/juju/python-libjuju/pull/1245
15+
416
3.6.0.0
517
^^^^^^^
618

docs/readme.rst

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ Bug reports: https://github.com/juju/python-libjuju/issues
77

88
Documentation: https://pythonlibjuju.readthedocs.io/en/latest/
99

10+
Supported Python versions: 3.8 through 3.13
11+
Supported Juju versions: 3.1 through 3.6
12+
1013

1114
Design Notes
1215
------------

juju/client/facade.py

+45-27
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from collections import defaultdict
1515
from glob import glob
1616
from pathlib import Path
17-
from typing import Any, Mapping, Sequence
17+
from typing import Any, Mapping, Sequence, TypeVar, overload
1818

1919
import packaging.version
2020
import typing_inspect
@@ -183,7 +183,7 @@ def ref_type(self, obj):
183183
return self.get_ref_type(obj["$ref"])
184184

185185

186-
CLASSES = {}
186+
CLASSES: dict[str, type[Type]] = {}
187187
factories = codegen.Capture()
188188

189189

@@ -479,37 +479,52 @@ def ReturnMapping(cls): # noqa: N802
479479
def decorator(f):
480480
@functools.wraps(f)
481481
async def wrapper(*args, **kwargs):
482-
nonlocal cls
483482
reply = await f(*args, **kwargs)
484-
if cls is None:
485-
return reply
486-
if "error" in reply:
487-
cls = CLASSES["Error"]
488-
if typing_inspect.is_generic_type(cls) and issubclass(
489-
typing_inspect.get_origin(cls), Sequence
490-
):
491-
parameters = typing_inspect.get_parameters(cls)
492-
result = []
493-
item_cls = parameters[0]
494-
for item in reply:
495-
result.append(item_cls.from_json(item))
496-
"""
497-
if 'error' in item:
498-
cls = CLASSES['Error']
499-
else:
500-
cls = item_cls
501-
result.append(cls.from_json(item))
502-
"""
503-
else:
504-
result = cls.from_json(reply["response"])
505-
506-
return result
483+
return _convert_response(reply, cls=cls)
507484

508485
return wrapper
509486

510487
return decorator
511488

512489

490+
@overload
491+
def _convert_response(response: dict[str, Any], *, cls: type[SomeType]) -> SomeType: ...
492+
493+
494+
@overload
495+
def _convert_response(response: dict[str, Any], *, cls: None) -> dict[str, Any]: ...
496+
497+
498+
def _convert_response(response: dict[str, Any], *, cls: type[Type] | None) -> Any:
499+
if cls is None:
500+
return response
501+
if "error" in response:
502+
# TODO: I don't think this ever happens,
503+
# errors are handled by Connection.rpc(),
504+
# though, admittedly the shape is different.
505+
cls = CLASSES["Error"]
506+
if typing_inspect.is_generic_type(cls) and issubclass(
507+
typing_inspect.get_origin(cls), Sequence
508+
):
509+
# TODO: I'm not sure this ever happens either.
510+
parameters = typing_inspect.get_parameters(cls)
511+
result = []
512+
item_cls = parameters[0]
513+
for item in response:
514+
result.append(item_cls.from_json(item))
515+
"""
516+
if 'error' in item:
517+
cls = CLASSES['Error']
518+
else:
519+
cls = item_cls
520+
result.append(cls.from_json(item))
521+
"""
522+
else:
523+
result = cls.from_json(response["response"])
524+
525+
return result
526+
527+
513528
def make_func(cls, name, description, params, result, _async=True):
514529
indent = " "
515530
args = Args(cls.schema, params)
@@ -663,7 +678,7 @@ async def rpc(self, msg: dict[str, _RichJson]) -> _Json:
663678
return result
664679

665680
@classmethod
666-
def from_json(cls, data):
681+
def from_json(cls, data: Type | str | dict[str, Any] | list[Any]) -> Type | None:
667682
def _parse_nested_list_entry(expr, result_dict):
668683
if isinstance(expr, str):
669684
if ">" in expr or ">=" in expr:
@@ -742,6 +757,9 @@ def get(self, key, default=None):
742757
return getattr(self, attr, default)
743758

744759

760+
SomeType = TypeVar("SomeType", bound=Type)
761+
762+
745763
class Schema(dict):
746764
def __init__(self, schema):
747765
self.name = schema["Name"]

0 commit comments

Comments
 (0)