Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
if TYPE_CHECKING:
from binarylane.client import Client

import binarylane.console.commands.api.get_v2_servers as api_get_v2_servers
from binarylane.console.parser import Mapping, PrimitiveAttribute
from binarylane.console.runners.command import CommandRunner

Expand Down Expand Up @@ -44,13 +45,18 @@ def create_mapping(self) -> Mapping:

json_body = mapping.add_json_body(ServerIdsRequest)

def lookup_server_id(ref: str) -> Union[None, int]:
return api_get_v2_servers.Command(self._context).lookup(ref)

json_body.add(
PrimitiveAttribute(
"server_ids",
List[int],
required=True,
option_name="server-ids",
description="""A list of server IDs.""",
option_name=("servers", "server-ids"),
metavar="servers",
description="""A list of server ID or names.""",
lookup=lookup_server_id,
)
)

Expand Down
10 changes: 8 additions & 2 deletions src/binarylane/console/commands/api/post_v2_load_balancers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
if TYPE_CHECKING:
from binarylane.client import Client

import binarylane.console.commands.api.get_v2_servers as api_get_v2_servers
from binarylane.console.parser import ListAttribute, Mapping, ObjectAttribute, PrimitiveAttribute
from binarylane.console.runners.actionlink import ActionLinkRunner

Expand Down Expand Up @@ -112,13 +113,18 @@ def create_mapping(self) -> Mapping:
)
)

def lookup_server_id(ref: str) -> Union[None, int]:
return api_get_v2_servers.Command(self._context).lookup(ref)

json_body.add(
PrimitiveAttribute(
"server_ids",
Union[Unset, None, List[int]],
required=False,
option_name="server-ids",
description="""A list of server IDs to assign to this load balancer.""",
option_name=("servers", "server-ids"),
metavar="servers",
description="""A list of server ID or names to assign to this load balancer.""",
lookup=lookup_server_id,
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
if TYPE_CHECKING:
from binarylane.client import Client

import binarylane.console.commands.api.get_v2_servers as api_get_v2_servers
from binarylane.console.parser import Mapping, PrimitiveAttribute
from binarylane.console.runners.command import CommandRunner

Expand Down Expand Up @@ -44,13 +45,18 @@ def create_mapping(self) -> Mapping:

json_body = mapping.add_json_body(ServerIdsRequest)

def lookup_server_id(ref: str) -> Union[None, int]:
return api_get_v2_servers.Command(self._context).lookup(ref)

json_body.add(
PrimitiveAttribute(
"server_ids",
List[int],
required=True,
option_name="server-ids",
description="""A list of server IDs.""",
option_name=("servers", "server-ids"),
metavar="servers",
description="""A list of server ID or names.""",
lookup=lookup_server_id,
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
if TYPE_CHECKING:
from binarylane.client import Client

import binarylane.console.commands.api.get_v2_servers as api_get_v2_servers
from binarylane.console.parser import ListAttribute, Mapping, ObjectAttribute, PrimitiveAttribute
from binarylane.console.runners.command import CommandRunner

Expand Down Expand Up @@ -125,13 +126,18 @@ def create_mapping(self) -> Mapping:
)
)

def lookup_server_id(ref: str) -> Union[None, int]:
return api_get_v2_servers.Command(self._context).lookup(ref)

json_body.add(
PrimitiveAttribute(
"server_ids",
Union[Unset, None, List[int]],
required=False,
option_name="server-ids",
description="""A list of server IDs to assign to this load balancer.""",
option_name=("servers", "server-ids"),
metavar="servers",
description="""A list of server ID or names to assign to this load balancer.""",
lookup=lookup_server_id,
)
)

Expand Down
18 changes: 12 additions & 6 deletions src/binarylane/console/parser/attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import logging
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, ClassVar, List, Optional
from typing import TYPE_CHECKING, ClassVar, List, Optional, Sequence, Union

logger = logging.getLogger(__name__)

Expand All @@ -23,7 +23,7 @@ class Attribute(ABC):
init: bool
# if required is True, this attribute is mandatory in the command-line parsing sense
required: bool
option_name: Optional[str]
option_names: List[str]
description: Optional[str]

def __init__(
Expand All @@ -32,14 +32,14 @@ def __init__(
attribute_type: type,
*,
required: bool,
option_name: Optional[str],
option_name: Union[str, Sequence[str], None],
description: Optional[str],
) -> None:
self.attribute_name = attribute_name
self.attribute_type = attribute_type
self.init = required
self.required = required
self.option_name = option_name
self.option_names = [option_name] if isinstance(option_name, str) else list(option_name) if option_name else []
self.description = description

@property
Expand All @@ -51,8 +51,14 @@ def usage(self) -> Optional[str]:
return None

@property
def name_or_flag(self) -> str:
return f"--{self.option_name}" if self.option_name else self.attribute_name
def option_name(self) -> Optional[str]:
return self.option_names[0] if self.option_names else None

@property
def name_or_flag(self) -> Sequence[str]:
if not self.option_names:
return [self.attribute_name]
return [f"--{opt}" for opt in self.option_names]

@property
def attributes(self) -> List[Attribute]:
Expand Down
9 changes: 5 additions & 4 deletions src/binarylane/console/parser/object_attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,10 @@ def configure(self, parser: Parser) -> None:
existing_arguments = parser.argument_names

# If any argument names for this class conflict with existing names, prefix all the argument names
if any(arg for arg in self.attributes if arg.name_or_flag in existing_arguments):
if any(arg for arg in self.attributes if any(opt for opt in arg.name_or_flag if opt in existing_arguments)):
self._unsupported("Prefixing option names", False)
for arg in self.attributes:
if arg.option_name:
arg.option_name = f"{self.attribute_name.replace('_', '-')}-{arg.option_name}"
arg.option_names = [f"{self.attribute_name.replace('_', '-')}-{opt}" for opt in arg.option_names]

group = self.group_name
if group:
Expand All @@ -97,7 +96,9 @@ def construct(self, parser: Parser, parsed: argparse.Namespace) -> object:
# If there are required attributes for the class constructor
if init_kwargs:
# See if any were not provided a value
missing = [attr.name_or_flag for attr in self.init_attributes if init_kwargs[attr.attribute_name] is UNSET]
missing = [
attr.name_or_flag[0] for attr in self.init_attributes if init_kwargs[attr.attribute_name] is UNSET
]

# If one or more required attributes did not receive a value:
if missing:
Expand Down
9 changes: 7 additions & 2 deletions src/binarylane/console/parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ def remove_group(self, group_name: str) -> None:
del self._groups[group_name]
self._action_groups = [g for g in self._action_groups if g.title != group_name]

def add_to_group(self, group_name: Union[str, bool], name_or_flag: str, type_: type, **kwargs: Any) -> None:
def add_to_group(self, group_name: Union[str, bool], names: Sequence[str], type_: type, **kwargs: Any) -> None:
name_or_flag, *aliases = names
self._argument_names.append(name_or_flag)

if isinstance(group_name, bool):
Expand All @@ -150,4 +151,8 @@ def add_to_group(self, group_name: Union[str, bool], name_or_flag: str, type_: t
del kwargs["required"]

logger.debug("add_argument %s (%s) - %s", name_or_flag, type_, repr(kwargs))
group.add_argument(name_or_flag, type=type_, **kwargs)

action = group.add_argument(*names, type=type_, **kwargs)
# Do not show option aliases in help output
if aliases:
action.option_strings = [name_or_flag]
6 changes: 3 additions & 3 deletions src/binarylane/console/parser/primitive_attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import logging
from datetime import datetime
from enum import Enum
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, Union
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Type, Union
from binarylane.pycompat import actions, typing

from binarylane.types import UNSET, Unset
Expand Down Expand Up @@ -47,7 +47,7 @@ def __init__(
attribute_name: str,
attribute_type_hint: object,
*,
option_name: Optional[str],
option_name: Union[Sequence[str], str, None],
required: bool,
description: Optional[str] = None,
metavar: Optional[str] = None,
Expand All @@ -72,7 +72,7 @@ def __init__(
self._dest = attribute_name
self._action = action
self._lookup = lookup
self._metavar = (metavar or option_name or attribute_name).replace("-", "_").upper()
self._metavar = (metavar or self.option_name or attribute_name).replace("-", "_").upper()

@property
def usage(self) -> Optional[str]:
Expand Down
93 changes: 93 additions & 0 deletions templates/endpoint_module/attributes.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{% macro add_lookup(property) %}
{% set lookup_endpoint = lookup_parameters.get(property.name) %}
{% set lookup_entity = lookup_endpoint.data["x-cli-entity"] if lookup_endpoint %}
{% set lookup_type = property.get_instance_type_string() %}
{% if lookup_type == 'list' %}
{% set lookup_type = property.inner_property.get_instance_type_string() %}
{% endif %}
{% if lookup_endpoint -%}
def {{ lookup_function(property, lookup_entity.id) }}(ref: str) -> Union[None, {{ lookup_type }}]:
return api_{{ python_identifier(lookup_endpoint.name) }}.Command(self._context).lookup(ref)

{% endif %}
{% endmacro %}

{% macro add_primitive(property, argument) %}
add(PrimitiveAttribute(
"{{ property.python_name }}",
{{ property.get_type_string() }},
required = {{ property.required if not argument else True }},
{% set lookup_endpoint = lookup_parameters.get(property.name) %}
{% set lookup_entity = lookup_endpoint.data["x-cli-entity"] if lookup_endpoint %}
{% if not lookup_endpoint %}
{{ option_name(property if not argument else None) }}
{{ description_argument(property) }}
{% else %}
{% set option_name = property.name.replace('_','-') %}
{% set entity_id = lookup_entity['id'] %}
{% if argument %}
option_name = None,
{% elif option_name.endswith("-" + entity_id) or option_name.endswith("-" + entity_id + "s") %}
option_name = ("{{ remove_suffix(option_name, "-" + entity_id) }}", "{{ option_name }}"),
{% else %}
option_name = "{{ option_name }}",
{% endif %}
{% set entity_ref = lookup_entity['ref'] %}
metavar="{{ remove_suffix(property.python_name, "_" + entity_id) }}",
{{ description_argument(property)
.replace(" " + entity_id.upper(), " " + entity_id.upper() + " or " + entity_ref)
.replace(" " + entity_id, " " + entity_id + " or " + entity_ref) }}
lookup = {{ lookup_function(property, entity_id )}}
{% endif %}
))
{%- endmacro %}

{% macro add_list(property) %}
add(ListAttribute(
"{{ property.python_name }}",
{{ property.inner_property.get_type_string(no_optional=True) }},
required={{ property.required }},
{{ option_name(property) }}
{{ description_argument(property) }}
))
{% endmacro %}

{% macro add_object(property, description_property) %}
add(ObjectAttribute(
"{{ property.python_name }}",
{{ property.get_type_string(no_optional=True) }},
{{ option_name(property) }}
required = {{ property.required }},
{{ description_argument(description_property) }}
))
{% endmacro %}

{% macro description_argument(prop) %}
{% if prop.description %}
description="""{{ prop.description.replace(" ","") }}""",
{% endif %}
{% endmacro %}

{% macro option_name(prop) %}
option_name = {{ '"' + prop.name.replace('_','-') + '"' if prop else None }},
{% endmacro %}

{% macro lookup_function(property, entity_id) %}
{% filter trim %}
{% set suffix = "_" + entity_id %}
{# Normalize property.python_name of "server"/"server_id"/"server_ids" to "server_id" #}
lookup_{{ property.python_name.removesuffix(suffix+"s").removesuffix(suffix) }}{{ suffix }}
{% endfilter %}
{% endmacro %}

{% macro remove_suffix(value, suffix) %}
{% filter trim %}
{% if value.endswith(suffix + "s") %}
{{ value.removesuffix(suffix + "s") }}s
{% elif value.endswith(suffix) %}
{{ value.removesuffix(suffix) }}
{% else %}
{{ value }}
{% endif %}
{% endfilter %}
{% endmacro %}
Loading
Loading