import argparse
import builtins
-from collections import defaultdict
import functools
-from glob import glob
import json
import keyword
-from pathlib import Path
import pprint
import re
import textwrap
-from typing import Sequence, Mapping, TypeVar, Any, Union
import typing
+from collections import defaultdict
+from glob import glob
+from pathlib import Path
+from typing import Any, Mapping, Sequence, TypeVar, Union
from . import codegen
_marker = object()
-JUJU_VERSION = re.compile('[0-9]+\.[0-9-]+[\.\-][0-9a-z]+(\.[0-9]+)?')
+JUJU_VERSION = re.compile(r'[0-9]+\.[0-9-]+[\.\-][0-9a-z]+(\.[0-9]+)?')
# Workaround for https://bugs.launchpad.net/juju/+bug/1683906
NAUGHTY_CLASSES = ['ClientFacade', 'Client', 'FullStatus', 'ModelStatusInfo',
- 'ModelInfo']
+ 'ModelInfo', 'ApplicationDeploy']
# Map basic types to Python's typing with a callable
of the correct client<version>.py file.
"""
- try:
- facade = getattr(CLIENTS[str(version)], name)
- except KeyError:
- raise ImportError("No facades found for version {}".format(version))
- except AttributeError:
- raise ImportError(
- "No facade with name '{}' in version {}".format(name, version))
- return facade
+ for _version in range(int(version), 0, -1):
+ try:
+ facade = getattr(CLIENTS[str(_version)], name)
+ return facade
+ except (KeyError, AttributeError):
+ continue
+ else:
+ raise ImportError("No supported version for facade: "
+ "{}".format(name))
'''
@param connection: initialized Connection object.
"""
- version = connection.facades[cls.__name__[:-6]]
+ facade_name = cls.__name__
+ if not facade_name.endswith('Facade'):
+ raise TypeError('Unexpected class name: {}'.format(facade_name))
+ facade_name = facade_name[:-len('Facade')]
+ version = connection.facades.get(facade_name)
+ if version is None:
+ raise Exception('No facade {} in facades {}'.format(facade_name,
+ connection.facades))
c = lookup_facade(cls.__name__, version)
c = c()
def strcast(kind, keep_builtins=False):
- if issubclass(kind, typing.GenericMeta):
- return str(kind)[1:]
- if str(kind).startswith('~'):
- return str(kind)[1:]
if (kind in basic_types or
type(kind) in basic_types) and keep_builtins is False:
return kind.__name__
+ if str(kind).startswith('~'):
+ return str(kind)[1:]
+ if issubclass(kind, typing.GenericMeta):
+ return str(kind)[1:]
return kind
source.append("{}self.{} = {}".format(INDENT * 2,
arg_name,
arg_name))
+ elif type(arg_type) is typing.TypeVar:
+ source.append("{}self.{} = {}.from_json({}) "
+ "if {} else None".format(INDENT * 2,
+ arg_name,
+ arg_type_name,
+ arg_name,
+ arg_name))
elif issubclass(arg_type, typing.Sequence):
value_type = (
arg_type_name.__parameters__[0]
source.append("{}self.{} = {}".format(INDENT * 2,
arg_name,
arg_name))
- elif type(arg_type) is typing.TypeVar:
- source.append("{}self.{} = {}.from_json({}) "
- "if {} else None".format(INDENT * 2,
- arg_name,
- arg_type_name,
- arg_name,
- arg_name))
else:
source.append("{}self.{} = {}".format(INDENT * 2,
arg_name,
return decorator
-def makeFunc(cls, name, params, result, async=True):
+def makeFunc(cls, name, params, result, _async=True):
INDENT = " "
args = Args(params)
assignments = []
source = """
@ReturnMapping({rettype})
-{async}def {name}(self{argsep}{args}):
+{_async}def {name}(self{argsep}{args}):
'''
{docstring}
Returns -> {res}
version={cls.version},
params=_params)
{assignments}
- reply = {await}self.rpc(msg)
+ reply = {_await}self.rpc(msg)
return reply
"""
- fsource = source.format(async="async " if async else "",
+ fsource = source.format(_async="async " if _async else "",
name=name,
argsep=", " if args else "",
args=args,
docstring=textwrap.indent(args.get_doc(), INDENT),
cls=cls,
assignments=assignments,
- await="await " if async else "")
+ _await="await " if _async else "")
ns = _getns()
exec(fsource, ns)
func = ns[name]