Skip to content

Commit 85fccc7

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "typing: Fixups for typed osc-lib"
2 parents 08b1bb7 + e8ae075 commit 85fccc7

File tree

19 files changed

+165
-62
lines changed

19 files changed

+165
-62
lines changed

openstackclient/common/clientmanager.py

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,25 @@
1515

1616
"""Manage access to the clients, including authenticating when needed."""
1717

18+
import argparse
19+
from collections.abc import Callable
1820
import importlib
1921
import logging
2022
import sys
2123
import typing as ty
2224

25+
from osc_lib.cli import client_config
2326
from osc_lib import clientmanager
2427
from osc_lib import shell
2528
import stevedore
2629

30+
if ty.TYPE_CHECKING:
31+
from keystoneauth1 import access as ksa_access
32+
from openstack.compute.v2 import _proxy as compute_proxy
33+
from openstack.image.v2 import _proxy as image_proxy
34+
from openstack.network.v2 import _proxy as network_proxy
35+
36+
from openstackclient.api import object_store_v1
2737

2838
LOG = logging.getLogger(__name__)
2939

@@ -40,6 +50,24 @@ class ClientManager(clientmanager.ClientManager):
4050
in osc-lib so we need to maintain a transition period.
4151
"""
4252

53+
if ty.TYPE_CHECKING:
54+
# we know this will be set by us and will not be nullable
55+
auth_ref: ksa_access.AccessInfo
56+
57+
# this is a hack to keep mypy happy: the actual attributes are set in
58+
# get_plugin_modules below
59+
# TODO(stephenfin): Change the types of identity and volume once we've
60+
# migrated everything to SDK. Hopefully by then we'll have figured out
61+
# how to statically distinguish between the v2 and v3 versions of both
62+
# services...
63+
# TODO(stephenfin): We also need to migrate object storage...
64+
compute: compute_proxy.Proxy
65+
identity: ty.Any
66+
image: image_proxy.Proxy
67+
network: network_proxy.Proxy
68+
object_store: object_store_v1.APIv1
69+
volume: ty.Any
70+
4371
def __init__(
4472
self,
4573
cli_options=None,
@@ -75,6 +103,12 @@ def setup_auth(self):
75103
self._auth_required
76104
and self._cli_options._openstack_config is not None
77105
):
106+
if not isinstance(
107+
self._cli_options._openstack_config, client_config.OSC_Config
108+
):
109+
# programmer error
110+
raise TypeError('unexpected type for _openstack_config')
111+
78112
self._cli_options._openstack_config._pw_callback = (
79113
shell.prompt_for_password
80114
)
@@ -101,6 +135,13 @@ def _fallback_load_auth_plugin(self, e):
101135
self._cli_options.config['auth_type'] = self._original_auth_type
102136
del self._cli_options.config['auth']['token']
103137
del self._cli_options.config['auth']['endpoint']
138+
139+
if not isinstance(
140+
self._cli_options._openstack_config, client_config.OSC_Config
141+
):
142+
# programmer error
143+
raise TypeError('unexpected type for _openstack_config')
144+
104145
self._cli_options._auth = (
105146
self._cli_options._openstack_config.load_auth_plugin(
106147
self._cli_options.config,
@@ -138,11 +179,25 @@ def is_volume_endpoint_enabled(self, volume_client=None):
138179

139180
# Plugin Support
140181

182+
ArgumentParserT = ty.TypeVar('ArgumentParserT', bound=argparse.ArgumentParser)
183+
184+
185+
@ty.runtime_checkable # Optional: allows usage with isinstance()
186+
class PluginModule(ty.Protocol):
187+
DEFAULT_API_VERSION: str
188+
API_VERSION_OPTION: str
189+
API_NAME: str
190+
API_VERSIONS: tuple[str]
191+
192+
make_client: Callable[..., ty.Any]
193+
build_option_parser: Callable[[ArgumentParserT], ArgumentParserT]
194+
check_api_version: Callable[[str], bool]
195+
141196

142197
def _on_load_failure_callback(
143198
manager: stevedore.ExtensionManager,
144199
ep: importlib.metadata.EntryPoint,
145-
err: Exception,
200+
err: BaseException,
146201
) -> None:
147202
sys.stderr.write(
148203
f"WARNING: Failed to import plugin {ep.group}:{ep.name}: {err}.\n"
@@ -152,6 +207,7 @@ def _on_load_failure_callback(
152207
def get_plugin_modules(group):
153208
"""Find plugin entry points"""
154209
mod_list = []
210+
mgr: stevedore.ExtensionManager[PluginModule]
155211
mgr = stevedore.ExtensionManager(
156212
group, on_load_failure_callback=_on_load_failure_callback
157213
)
@@ -164,8 +220,8 @@ def get_plugin_modules(group):
164220
module = importlib.import_module(module_name)
165221
except Exception as err:
166222
sys.stderr.write(
167-
f"WARNING: Failed to import plugin {ep.group}:{ep.name}: "
168-
f"{err}.\n"
223+
f"WARNING: Failed to import plugin "
224+
f"{ep.module_name}:{ep.name}: {err}.\n"
169225
)
170226
continue
171227

openstackclient/common/module.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ def take_action(self, parsed_args):
4848
columns = ('Command Group', 'Commands')
4949

5050
if parsed_args.group:
51-
groups = (group for group in groups if parsed_args.group in group)
51+
groups = sorted(
52+
group for group in groups if parsed_args.group in group
53+
)
5254

5355
commands = []
5456
for group in groups:

openstackclient/compute/v2/aggregate.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import logging
2020
import typing as ty
2121

22+
from cliff import columns
2223
from openstack import utils as sdk_utils
2324
from osc_lib.cli import format_columns
2425
from osc_lib.cli import parseractions
@@ -32,7 +33,7 @@
3233
LOG = logging.getLogger(__name__)
3334

3435

35-
_aggregate_formatters = {
36+
_aggregate_formatters: dict[str, type[columns.FormattableColumn[ty.Any]]] = {
3637
'Hosts': format_columns.ListColumn,
3738
'Metadata': format_columns.DictColumn,
3839
'hosts': format_columns.ListColumn,

openstackclient/compute/v2/console.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,15 @@ def take_action(self, parsed_args):
6464
output = compute_client.get_server_console_output(
6565
server.id, length=parsed_args.lines
6666
)
67-
data = None
67+
data: str | None = None
6868
if output:
6969
data = output.get('output', None)
7070

7171
if data and data[-1] != '\n':
7272
data += '\n'
73-
self.app.stdout.write(data)
73+
74+
if data:
75+
self.app.stdout.write(data)
7476

7577

7678
class ShowConsoleURL(command.ShowOne):

openstackclient/compute/v2/server_group.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
"""Compute v2 Server Group action implementations"""
1717

1818
import logging
19+
import typing as ty
1920

21+
from cliff import columns
2022
from openstack import utils as sdk_utils
2123
from osc_lib.cli import format_columns
2224
from osc_lib.cli import parseractions
@@ -30,7 +32,7 @@
3032
LOG = logging.getLogger(__name__)
3133

3234

33-
_formatters = {
35+
_formatters: dict[str, type[columns.FormattableColumn[ty.Any]]] = {
3436
'member_ids': format_columns.ListColumn,
3537
'policies': format_columns.ListColumn,
3638
'rules': format_columns.DictColumn,

openstackclient/identity/v2_0/role_assignment.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def take_action(self, parsed_args):
6868
parsed_args.user,
6969
)
7070
elif parsed_args.authuser:
71-
if auth_ref:
71+
if auth_ref and auth_ref.user_id:
7272
user = utils.find_resource(
7373
identity_client.users, auth_ref.user_id
7474
)

openstackclient/identity/v3/access_rule.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ def get_parser(self, prog_name):
4444
def take_action(self, parsed_args):
4545
identity_client = self.app.client_manager.sdk_connection.identity
4646
conn = self.app.client_manager.sdk_connection
47-
user_id = conn.config.get_auth().get_user_id(conn.identity)
47+
auth = conn.config.get_auth()
48+
if auth is None:
49+
# this will never happen
50+
raise exceptions.CommandError('invalid authentication info')
51+
user_id = auth.get_user_id(conn.identity)
4852

4953
errors = 0
5054
for ac in parsed_args.access_rule:
@@ -87,7 +91,11 @@ def take_action(self, parsed_args):
8791
).id
8892
else:
8993
conn = self.app.client_manager.sdk_connection
90-
user_id = conn.config.get_auth().get_user_id(conn.identity)
94+
auth = conn.config.get_auth()
95+
if auth is None:
96+
# this will never happen
97+
raise exceptions.CommandError('invalid authentication info')
98+
user_id = auth.get_user_id(conn.identity)
9199

92100
columns = ('ID', 'Service', 'Method', 'Path')
93101
data = identity_client.access_rules(user=user_id)
@@ -119,7 +127,11 @@ def get_parser(self, prog_name):
119127
def take_action(self, parsed_args):
120128
identity_client = self.app.client_manager.sdk_connection.identity
121129
conn = self.app.client_manager.sdk_connection
122-
user_id = conn.config.get_auth().get_user_id(conn.identity)
130+
auth = conn.config.get_auth()
131+
if auth is None:
132+
# this will never happen
133+
raise exceptions.CommandError('invalid authentication info')
134+
user_id = auth.get_user_id(conn.identity)
123135

124136
access_rule = identity_client.get_access_rule(
125137
user_id, parsed_args.access_rule

openstackclient/identity/v3/application_credential.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,12 @@ def get_parser(self, prog_name):
206206
def take_action(self, parsed_args):
207207
identity_client = self.app.client_manager.sdk_connection.identity
208208
conn = self.app.client_manager.sdk_connection
209-
user_id = conn.config.get_auth().get_user_id(conn.identity)
209+
auth = conn.config.get_auth()
210+
if auth is None:
211+
# this will never happen
212+
raise exceptions.CommandError('invalid authentication info')
213+
214+
user_id = auth.get_user_id(conn.identity)
210215

211216
role_ids = []
212217
for role in parsed_args.roles:
@@ -274,7 +279,12 @@ def get_parser(self, prog_name):
274279
def take_action(self, parsed_args):
275280
identity_client = self.app.client_manager.sdk_connection.identity
276281
conn = self.app.client_manager.sdk_connection
277-
user_id = conn.config.get_auth().get_user_id(conn.identity)
282+
auth = conn.config.get_auth()
283+
if auth is None:
284+
# this will never happen
285+
raise exceptions.CommandError('invalid authentication info')
286+
287+
user_id = auth.get_user_id(conn.identity)
278288

279289
errors = 0
280290
for ac in parsed_args.application_credential:
@@ -327,7 +337,11 @@ def take_action(self, parsed_args):
327337
)
328338
else:
329339
conn = self.app.client_manager.sdk_connection
330-
user_id = conn.config.get_auth().get_user_id(conn.identity)
340+
auth = conn.config.get_auth()
341+
if auth is None:
342+
# this will never happen
343+
raise exceptions.CommandError('invalid authentication info')
344+
user_id = auth.get_user_id(conn.identity)
331345

332346
application_credentials = identity_client.application_credentials(
333347
user=user_id
@@ -351,7 +365,11 @@ def get_parser(self, prog_name):
351365
def take_action(self, parsed_args):
352366
identity_client = self.app.client_manager.sdk_connection.identity
353367
conn = self.app.client_manager.sdk_connection
354-
user_id = conn.config.get_auth().get_user_id(conn.identity)
368+
auth = conn.config.get_auth()
369+
if auth is None:
370+
# this will never happen
371+
raise exceptions.CommandError('invalid authentication info')
372+
user_id = auth.get_user_id(conn.identity)
355373

356374
application_credential = identity_client.find_application_credential(
357375
user_id, parsed_args.application_credential, ignore_missing=False

openstackclient/identity/v3/identity_provider.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,9 @@ def take_action(self, parsed_args):
137137
)
138138

139139
idp._info.pop('links', None)
140-
remote_ids = format_columns.ListColumn(idp._info.pop('remote_ids', []))
141-
idp._info['remote_ids'] = remote_ids
140+
idp._info['remote_ids'] = format_columns.ListColumn(
141+
idp._info.pop('remote_ids', [])
142+
)
142143
return zip(*sorted(idp._info.items()))
143144

144145

openstackclient/identity/v3/user.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,12 @@ def get_parser(self, prog_name):
693693
def take_action(self, parsed_args):
694694
identity_client = self.app.client_manager.sdk_connection.identity
695695
conn = self.app.client_manager.sdk_connection
696-
user_id = conn.config.get_auth().get_user_id(conn.identity)
696+
auth = conn.config.get_auth()
697+
if auth is None:
698+
# this will never happen
699+
raise exceptions.CommandError('invalid authentication info')
700+
701+
user_id = auth.get_user_id(conn.identity)
697702

698703
# FIXME(gyee): there are two scenarios:
699704
#

0 commit comments

Comments
 (0)