Skip to content
This repository was archived by the owner on Mar 3, 2023. It is now read-only.

Commit 98ada0f

Browse files
authored
Use click instead of argparse in tracker (#3599)
1 parent 612de8d commit 98ada0f

File tree

3 files changed

+101
-162
lines changed

3 files changed

+101
-162
lines changed

heron/tools/tracker/src/python/BUILD

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ pex_library(
77
exclude = ["main.py"],
88
),
99
reqs = [
10-
"protobuf==3.8.0",
11-
"tornado==4.0.2",
10+
"click==7.1.2",
1211
"javaobj-py3==0.4.1",
1312
"networkx==2.4",
13+
"protobuf==3.8.0",
14+
"tornado==4.0.2",
1415
],
1516
deps = [
1617
"//heron/common/src/python:common-py",

heron/tools/tracker/src/python/main.py

+98-159
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
# under the License.
2020

2121
''' main.py '''
22-
import argparse
22+
import logging
2323
import os
2424
import signal
2525
import sys
@@ -29,14 +29,16 @@
2929
from tornado.options import define
3030
from tornado.httpclient import AsyncHTTPClient
3131

32-
import heron.tools.common.src.python.utils.config as common_config
33-
import heron.common.src.python.utils.log as log
32+
from heron.tools.common.src.python.utils import config as common_config
33+
from heron.common.src.python.utils import log
3434
from heron.tools.tracker.src.python import constants
3535
from heron.tools.tracker.src.python import handlers
3636
from heron.tools.tracker.src.python import utils
3737
from heron.tools.tracker.src.python.config import Config, STATEMGRS_KEY
3838
from heron.tools.tracker.src.python.tracker import Tracker
3939

40+
import click
41+
4042
Log = log.Log
4143

4244
class Application(tornado.web.Application):
@@ -93,173 +95,110 @@ def __init__(self, config):
9395
def stop(self):
9496
self.tracker.stop_sync()
9597

96-
# pylint: disable=protected-access
97-
class _HelpAction(argparse._HelpAction):
98-
""" HelpAction """
99-
def __call__(self, parser, namespace, values, option_string=None):
100-
parser.print_help()
101-
102-
# retrieve subparsers from parser
103-
subparsers_actions = [
104-
action for action in parser._actions
105-
if isinstance(action, argparse._SubParsersAction)]
106-
107-
# there will probably only be one subparser_action,
108-
# but better save than sorry
109-
for subparsers_action in subparsers_actions:
110-
# get all subparsers and print help
111-
for choice, subparser in list(subparsers_action.choices.items()):
112-
print("Subparser '{}'".format(choice))
113-
print(subparser.format_help())
114-
115-
parser.exit()
116-
117-
# pylint: disable=bad-super-call
118-
class SubcommandHelpFormatter(argparse.RawDescriptionHelpFormatter):
119-
""" Subcommand help formatter """
120-
def _format_action(self, action):
121-
parts = super(argparse.RawDescriptionHelpFormatter, self)._format_action(action)
122-
if action.nargs == argparse.PARSER:
123-
parts = "\n".join(parts.split("\n")[1:])
124-
return parts
125-
126-
127-
def add_titles(parser):
128-
""" add titles """
129-
parser._positionals.title = "Required arguments"
130-
parser._optionals.title = "Optional arguments"
131-
return parser
132-
133-
134-
def add_arguments(parser):
135-
""" add arguments """
136-
default_config_file = os.path.join(
137-
utils.get_heron_tracker_conf_dir(), constants.DEFAULT_CONFIG_FILE)
138-
139-
parser.add_argument(
140-
'--config-file',
141-
metavar='(a string; path to config file; default: "' + default_config_file + '")',
142-
default=default_config_file)
143-
144-
parser.add_argument(
145-
'--type',
146-
metavar='(an string; type of state manager (zookeeper or file, etc.); example: ' \
147-
+ str(constants.DEFAULT_STATE_MANAGER_TYPE) + ')',
148-
choices=["file", "zookeeper"])
149-
150-
parser.add_argument(
151-
'--name',
152-
metavar='(an string; name to be used for the state manager; example: ' \
153-
+ str(constants.DEFAULT_STATE_MANAGER_NAME) + ')')
154-
155-
parser.add_argument(
156-
'--rootpath',
157-
metavar='(an string; where all the states are stored; example: ' \
158-
+ str(constants.DEFAULT_STATE_MANAGER_ROOTPATH) + ')')
159-
160-
parser.add_argument(
161-
'--tunnelhost',
162-
metavar='(an string; if ssh tunneling needs to be established to connect to it; example: ' \
163-
+ str(constants.DEFAULT_STATE_MANAGER_TUNNELHOST) + ')')
164-
165-
parser.add_argument(
166-
'--hostport',
167-
metavar='(an string; only used to connect to zk, must be of the form \'host:port\';'\
168-
' example: ' + str(constants.DEFAULT_STATE_MANAGER_HOSTPORT) + ')')
169-
170-
parser.add_argument(
171-
'--port',
172-
metavar='(an integer; port to listen; default: ' + str(constants.DEFAULT_PORT) + ')',
173-
type=int,
174-
default=constants.DEFAULT_PORT)
17598

176-
parser.add_argument(
177-
'--verbose',
178-
action='store_true')
179-
180-
return parser
181-
182-
def create_parsers():
183-
""" create argument parser """
184-
parser = argparse.ArgumentParser(
185-
epilog='For detailed documentation, go to http://github.com/apache/incubator-heron',
186-
usage="%(prog)s [options] [help]",
187-
add_help=False)
188-
189-
parser = add_titles(parser)
190-
parser = add_arguments(parser)
191-
192-
ya_parser = argparse.ArgumentParser(
193-
parents=[parser],
194-
formatter_class=SubcommandHelpFormatter,
195-
add_help=False)
196-
197-
subparsers = ya_parser.add_subparsers(
198-
title="Available commands")
199-
200-
help_parser = subparsers.add_parser(
201-
'help',
202-
help='Prints help',
203-
add_help=False)
204-
205-
help_parser.set_defaults(help=True)
206-
207-
subparsers.add_parser(
208-
'version',
209-
help='Prints version',
210-
add_help=True)
211-
212-
return parser, ya_parser
213-
214-
def define_options(port, config_file):
99+
def define_options(port: int, config_file: str) -> None:
215100
""" define Tornado global variables """
216101
define("port", default=port)
217102
define("config_file", default=config_file)
218103

219-
def create_tracker_config(namespace):
104+
105+
def create_tracker_config(config_file: str, stmgr_override: dict) -> dict:
220106
# try to parse the config file if we find one
221-
config_file = namespace["config_file"]
222107
config = utils.parse_config_file(config_file)
223108
if config is None:
224-
Log.debug("Config file does not exists: %s" % config_file)
109+
Log.debug(f"Config file does not exists: {config_file}")
225110
config = {STATEMGRS_KEY:[{}]}
226111

227-
# update the config if we have any flags
228-
config_flags = ["type", "name", "rootpath", "tunnelhost", "hostport"]
229-
config_to_update = config[STATEMGRS_KEY][0]
230-
for flag in config_flags:
231-
value = namespace.get(flag, None)
232-
if value is not None:
233-
config_to_update[flag] = value
234-
112+
# update non-null options
113+
config[STATEMGRS_KEY][0].update(
114+
(k, v)
115+
for k, v in stmgr_override.items()
116+
if v is not None
117+
)
235118
return config
236119

237-
def main():
238-
""" main """
239-
# create the parser and parse the arguments
240-
(parser, _) = create_parsers()
241-
(args, remaining) = parser.parse_known_args()
242120

243-
if remaining == ['help']:
244-
parser.print_help()
245-
parser.exit()
246-
247-
elif remaining == ['version']:
121+
def show_version(_, __, value):
122+
if value:
248123
common_config.print_build_info()
249-
parser.exit()
250-
251-
elif remaining != []:
252-
Log.error('Invalid subcommand')
253-
sys.exit(1)
254-
255-
namespace = vars(args)
256-
257-
log.set_logging_level(namespace)
124+
sys.exit(0)
125+
126+
127+
@click.command()
128+
@click.option(
129+
"--version",
130+
is_flag=True,
131+
is_eager=True,
132+
expose_value=False,
133+
callback=show_version,
134+
)
135+
@click.option('--verbose', is_flag=True)
136+
@click.option(
137+
'--config-file',
138+
help="path to a tracker config file",
139+
default=os.path.join(utils.get_heron_tracker_conf_dir(), constants.DEFAULT_CONFIG_FILE),
140+
show_default=True,
141+
)
142+
@click.option(
143+
'--port',
144+
type=int,
145+
default=constants.DEFAULT_PORT,
146+
show_default=True,
147+
help="local port to serve on",
148+
)
149+
@click.option(
150+
'--type',
151+
"stmgr_type",
152+
help=f"statemanager type e.g. {constants.DEFAULT_STATE_MANAGER_TYPE}",
153+
type=click.Choice(choices=["file", "zookeeper"]),
154+
)
155+
@click.option(
156+
'--name',
157+
help=f"statemanager name e.g. {constants.DEFAULT_STATE_MANAGER_NAME}",
158+
)
159+
@click.option(
160+
'--rootpath',
161+
help=f"statemanager rootpath e.g. {constants.DEFAULT_STATE_MANAGER_ROOTPATH}",
162+
)
163+
@click.option(
164+
'--tunnelhost',
165+
help=f"statemanager tunnelhost e.g. {constants.DEFAULT_STATE_MANAGER_TUNNELHOST}",
166+
)
167+
@click.option(
168+
'--hostport',
169+
help=f"statemanager hostport e.g. {constants.DEFAULT_STATE_MANAGER_HOSTPORT}",
170+
)
171+
def cli(
172+
config_file: str,
173+
stmgr_type: str,
174+
name: str,
175+
rootpath: str,
176+
tunnelhost: str,
177+
hostport: str,
178+
port: int,
179+
verbose: bool,
180+
) -> None:
181+
"""
182+
A HTTP service for serving data about clusters.
183+
184+
The statemanager's config from the given config file can be overrided using
185+
options on this executable.
186+
187+
"""
188+
189+
log.configure(logging.DEBUG if verbose else logging.INFO)
258190

259191
# set Tornado global option
260-
define_options(namespace['port'], namespace['config_file'])
192+
define_options(port, config_file)
261193

262-
config = Config(create_tracker_config(namespace))
194+
stmgr_override = {
195+
"type": stmgr_type,
196+
"name": name,
197+
"rootpath": rootpath,
198+
"tunnelhost": tunnelhost,
199+
"hostport": hostport,
200+
}
201+
config = Config(create_tracker_config(config_file, stmgr_override))
263202

264203
# create Tornado application
265204
application = Application(config)
@@ -278,15 +217,15 @@ def signal_handler(signum, frame):
278217
signal.signal(signal.SIGINT, signal_handler)
279218
signal.signal(signal.SIGTERM, signal_handler)
280219

281-
Log.info("Running on port: %d", namespace['port'])
282-
if namespace["config_file"]:
283-
Log.info("Using config file: %s", namespace['config_file'])
284-
Log.info("Using state manager:\n" + str(config))
220+
Log.info("Running on port: %d", port)
221+
if config_file:
222+
Log.info("Using config file: %s", config_file)
223+
Log.info(f"Using state manager:\n{config}")
285224

286225
http_server = tornado.httpserver.HTTPServer(application)
287-
http_server.listen(namespace['port'])
226+
http_server.listen(port)
288227

289228
tornado.ioloop.IOLoop.instance().start()
290229

291230
if __name__ == "__main__":
292-
main()
231+
cli() # pylint: disable=no-value-for-parameter

heron/tools/tracker/src/python/utils.py

-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ def parse_config_file(config_file):
162162
if not os.path.lexists(expanded_config_file_path):
163163
return None
164164

165-
configs = {}
166165
# Read the configuration file
167166
with open(expanded_config_file_path, 'r') as f:
168167
configs = yaml.load(f)

0 commit comments

Comments
 (0)