Skip to content

Commit 60cf6c3

Browse files
Check a valid service name has been provided (#171)
- Remove some redundant parameters. - Construct the known_services help string separately (we'll use it again). - Exit early if no service has been specified, instead of opening a frontend with no service and quietly twiddling our thumbs. No attempt is made here to migrate to argparse.ArgumentParser instead of the deprecated optparse.OptionParser, though the 'required' argument of 'ArgumentParser.add_argument' would obviate the need for separately checking whether the '--service' option has been invoked. * Tidy up the service name resolution logic At present, a user can grow accustomed to lazily providing lower-case service names when invoking 'workflows.service' from the command line. This works fine until you encounter the following situation: - Known services include "DLSISPyB" and "DLSISPyBPIA"; - user specifies '-s dlsispyb', expecting this to be interpreted as 'DLSISPyB', or else expecting an error; - not finding a perfect match, the current logic checks whether any known service names, rendered in lower case, start with 'dlsispyb', finds two such matches; - rather than selecting either of these two matches, the program quietly continues to open a frontend and doesn't start any service; - user sees so warning or error, program twiddles its thumbs until terminated. New logic: 1. Check for perfect match; 2. Failing that, check for case-insensitive match; 3. Failing that, check for case-sensitive partial matches. If ambiguous, exit with error. 4. Failing that, repeat 3 but now case-insensitive. 5. If still no matches, exit with error. * Neaten up the if nots
1 parent b20f7da commit 60cf6c3

File tree

1 file changed

+38
-22
lines changed

1 file changed

+38
-22
lines changed

src/workflows/contrib/start_service.py

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
import sys
4+
35
from optparse import SUPPRESS_HELP, OptionParser
46

57
import workflows
@@ -62,6 +64,7 @@ def run(
6264

6365
# Enumerate all known services
6466
known_services = sorted(workflows.services.get_known_services())
67+
known_services_help = "Known services: " + ", ".join(known_services)
6568

6669
if version:
6770
version = f"{version} (workflows {workflows.version()})"
@@ -76,15 +79,11 @@ def run(
7679
parser.add_option(
7780
"-s",
7881
"--service",
79-
dest="service",
8082
metavar="SVC",
81-
default=None,
82-
help="Name of the service to start. Known services: "
83-
+ ", ".join(known_services),
83+
help=f"Name of the service to start. {known_services_help}",
8484
)
8585
parser.add_option(
8686
"--liveness",
87-
dest="liveness",
8887
action="store_true",
8988
default=False,
9089
help=(
@@ -95,14 +94,12 @@ def run(
9594
)
9695
parser.add_option(
9796
"--liveness-port",
98-
dest="liveness_port",
9997
default=8000,
10098
type="int",
10199
help="Expose liveness check endpoint on this port.",
102100
)
103101
parser.add_option(
104102
"--liveness-timeout",
105-
dest="liveness_timeout",
106103
default=30,
107104
type="float",
108105
help="Timeout for the liveness check (in seconds).",
@@ -111,17 +108,15 @@ def run(
111108
parser.add_option(
112109
"-m",
113110
"--metrics",
114-
dest="metrics",
115111
action="store_true",
116112
default=False,
117113
help=(
118-
"Record metrics for this service and expose them on the port defined by"
119-
"the --metrics-port option."
114+
"Record metrics for this service and expose them on the port "
115+
"defined by the --metrics-port option."
120116
),
121117
)
122118
parser.add_option(
123119
"--metrics-port",
124-
dest="metrics_port",
125120
default=8080,
126121
type="int",
127122
help="Expose metrics via a prometheus endpoint on this port.",
@@ -137,6 +132,10 @@ def run(
137132
# Call on_parsing hook
138133
(options, args) = self.on_parsing(options, args) or (options, args)
139134

135+
# Exit with error if no service has been specified.
136+
if not options.service:
137+
parser.error(f"Please specify a service name. {known_services_help}")
138+
140139
# Create Transport factory
141140
transport_factory = workflows.transport.lookup(options.transport)
142141

@@ -155,17 +154,34 @@ def on_transport_preparation_hook():
155154

156155
transport_factory = on_transport_preparation_hook
157156

158-
# When service name is specified, check if service exists or can be derived
159-
if options.service and options.service not in known_services:
160-
matching = [s for s in known_services if s.startswith(options.service)]
161-
if not matching:
162-
matching = [
163-
s
164-
for s in known_services
165-
if s.lower().startswith(options.service.lower())
166-
]
167-
if matching and len(matching) == 1:
168-
options.service = matching[0]
157+
# When service name is specified, check if service exists or can be derived.
158+
if options.service not in known_services:
159+
# First check whether the provided service name is a case-insensitive match.
160+
service_lower = options.service.lower()
161+
match = {s.lower(): s for s in known_services}.get(service_lower, None)
162+
match = (
163+
[match]
164+
if match
165+
# Next, check whether the provided service name is a partial
166+
# case-sensitive match.
167+
else [s for s in known_services if s.startswith(options.service)]
168+
# Next check whether the provided service name is a partial
169+
# case-insensitive match.
170+
or [s for s in known_services if s.lower().startswith(service_lower)]
171+
)
172+
173+
# Catch ambiguous partial matches and exit with an error.
174+
if len(match) > 1:
175+
sys.exit(
176+
f"Specified service name {options.service} is ambiguous, partially "
177+
f"matching each of these known services: " + ", ".join(match)
178+
)
179+
# Otherwise, set the derived service name, if there's a unique match.
180+
elif match:
181+
(options.service,) = match
182+
# Otherwise, exit with an error.
183+
else:
184+
sys.exit(f"Please specify a valid service name. {known_services_help}")
169185

170186
kwargs.update(
171187
{

0 commit comments

Comments
 (0)