Skip to content

Commit

Permalink
Merge pull request #1245 from dimaqq/feat-new-wait-for-idle--dumb-class
Browse files Browse the repository at this point in the history
#1245

Same as #1219 but using dumb classes instead of async generator.
  • Loading branch information
jujubot authored Dec 20, 2024
2 parents 9b4e411 + 716adb1 commit e0199c8
Show file tree
Hide file tree
Showing 18 changed files with 1,511 additions and 99 deletions.
9 changes: 7 additions & 2 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ jobs:
- "3.4/stable"
- "3.5/stable"
- "3.6/stable"
new_wait_for_idle:
- "True"
- "False"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
Expand Down Expand Up @@ -116,7 +119,9 @@ jobs:
# # set model defaults
# 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
# juju model-defaults
- run: uvx -p ${{ matrix.python }} tox -e integration
- run: uvx -p ${{ matrix.python }} tox -s -e integration
env:
JUJU_NEW_WAIT_FOR_IDLE: ${{ matrix.new_wait_for_idle }}

integration-quarantine:
name: Quarantined Integration Tests
Expand Down Expand Up @@ -144,4 +149,4 @@ jobs:
with:
provider: lxd
juju-channel: ${{ matrix.juju }}
- run: uvx -p ${{ matrix.python }} tox -e integration-quarantine
- run: uvx -p ${{ matrix.python }} tox -s -e integration-quarantine
12 changes: 12 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
Changelog
---------

3.6.1.0
^^^^^^^

Friday 20th Dec 2024

## What's Changed
* add 3.5.5 schema and update SCHEMAS.md by @james-garner-canonical in https://github.com/juju/python-libjuju/pull/1223
* feat: larger default websockets frame size by @dimaqq in https://github.com/juju/python-libjuju/pull/1239
* deprecate juju.jasyncio by @EdmilsonRodrigues in https://github.com/juju/python-libjuju/pull/1221
* remove juju.loop, deprecated before 3.0 by @dimaqq in https://github.com/juju/python-libjuju/pull/1242
* new wait for idle implementation, behind a feature flag ``JUJU_NEW_WAIT_FOR_IDLE`` in https://github.com/juju/python-libjuju/pull/1245

3.6.0.0
^^^^^^^

Expand Down
3 changes: 3 additions & 0 deletions docs/readme.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Bug reports: https://github.com/juju/python-libjuju/issues

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

Supported Python versions: 3.8 through 3.13
Supported Juju versions: 3.1 through 3.6


Design Notes
------------
Expand Down
72 changes: 45 additions & 27 deletions juju/client/facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from collections import defaultdict
from glob import glob
from pathlib import Path
from typing import Any, Mapping, Sequence
from typing import Any, Mapping, Sequence, TypeVar, overload

import packaging.version
import typing_inspect
Expand Down Expand Up @@ -183,7 +183,7 @@ def ref_type(self, obj):
return self.get_ref_type(obj["$ref"])


CLASSES = {}
CLASSES: dict[str, type[Type]] = {}
factories = codegen.Capture()


Expand Down Expand Up @@ -479,37 +479,52 @@ def ReturnMapping(cls): # noqa: N802
def decorator(f):
@functools.wraps(f)
async def wrapper(*args, **kwargs):
nonlocal cls
reply = await f(*args, **kwargs)
if cls is None:
return reply
if "error" in reply:
cls = CLASSES["Error"]
if typing_inspect.is_generic_type(cls) and issubclass(
typing_inspect.get_origin(cls), Sequence
):
parameters = typing_inspect.get_parameters(cls)
result = []
item_cls = parameters[0]
for item in reply:
result.append(item_cls.from_json(item))
"""
if 'error' in item:
cls = CLASSES['Error']
else:
cls = item_cls
result.append(cls.from_json(item))
"""
else:
result = cls.from_json(reply["response"])

return result
return _convert_response(reply, cls=cls)

return wrapper

return decorator


@overload
def _convert_response(response: dict[str, Any], *, cls: type[SomeType]) -> SomeType: ...


@overload
def _convert_response(response: dict[str, Any], *, cls: None) -> dict[str, Any]: ...


def _convert_response(response: dict[str, Any], *, cls: type[Type] | None) -> Any:
if cls is None:
return response
if "error" in response:
# TODO: I don't think this ever happens,
# errors are handled by Connection.rpc(),
# though, admittedly the shape is different.
cls = CLASSES["Error"]
if typing_inspect.is_generic_type(cls) and issubclass(
typing_inspect.get_origin(cls), Sequence
):
# TODO: I'm not sure this ever happens either.
parameters = typing_inspect.get_parameters(cls)
result = []
item_cls = parameters[0]
for item in response:
result.append(item_cls.from_json(item))
"""
if 'error' in item:
cls = CLASSES['Error']
else:
cls = item_cls
result.append(cls.from_json(item))
"""
else:
result = cls.from_json(response["response"])

return result


def make_func(cls, name, description, params, result, _async=True):
indent = " "
args = Args(cls.schema, params)
Expand Down Expand Up @@ -663,7 +678,7 @@ async def rpc(self, msg: dict[str, _RichJson]) -> _Json:
return result

@classmethod
def from_json(cls, data):
def from_json(cls, data: Type | str | dict[str, Any] | list[Any]) -> Type | None:
def _parse_nested_list_entry(expr, result_dict):
if isinstance(expr, str):
if ">" in expr or ">=" in expr:
Expand Down Expand Up @@ -742,6 +757,9 @@ def get(self, key, default=None):
return getattr(self, attr, default)


SomeType = TypeVar("SomeType", bound=Type)


class Schema(dict):
def __init__(self, schema):
self.name = schema["Name"]
Expand Down
Loading

0 comments on commit e0199c8

Please sign in to comment.