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

Commit 857d8ca

Browse files
committed
Add override option to CLI
1 parent 8a3a3e0 commit 857d8ca

File tree

2 files changed

+62
-27
lines changed

2 files changed

+62
-27
lines changed

README.md

+24
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,30 @@ E.g., if you want to use two hosts(localhost and anotherhost) and run `ppo_atari
6767
horovodrun -np 2 -H localhost:1,anotherhost:1 pipenv run python examples/ppo_atari.py train
6868
```
6969

70+
## Override configuration from CLI
71+
Currently, Rainy provides a easy-to-use CLI via [click](https://palletsprojects.com/p/click/).
72+
You can view its usages by, say,
73+
```bash
74+
pipenv run python examples/a2c_cart_pole.py --help
75+
```
76+
77+
This CLI has a simple data-driven interface.
78+
I.e., you fill a config object, then all commands(train, eval, retarain, and etc.) work.
79+
So you can start experiments easily without copying and pasting, say, argument parser codes.
80+
81+
However, it has a limitation that you cannot add new options and arguments to each subcommands.
82+
83+
So Rainy CLI provides an option named `override`, which executes the given string as a Python code
84+
with the config object set as `config`.
85+
86+
Example usage:
87+
```bash
88+
pipenv run python examples/a2c_cart_pole.py --override='config.grad_clip=0.5; config.nsteps=10' train
89+
```
90+
91+
If this feature still doesn't satisfy your requirement,
92+
then suddenly you need to write your own CLI :cold_sweat:.
93+
7094
## Implementation Status
7195

7296
|**Algorithm** |**Multi Worker(Sync)**|**Recurrent** |**Discrete Action** |**Continuous Action**|**MPI** |

rainy/utils/cli.py

+38-27
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,26 @@
99

1010

1111
@click.group()
12-
@click.option('--gpu', required=False, type=int)
13-
@click.option('--seed', type=int, default=None)
12+
@click.option('--gpu', required=False, type=int, help='How many gpus you allow Rainy to use')
13+
@click.option('--seed', type=int, default=None, help='Random seed set before training')
14+
@click.option('--override', type=str, default='', help='Override string(See README for detail)')
1415
@click.pass_context
15-
def rainy_cli(ctx: dict, gpu: Tuple[int], seed: Optional[int]) -> None:
16+
def rainy_cli(ctx: dict, gpu: Tuple[int], seed: Optional[int], override: str) -> None:
1617
ctx.obj['gpu'] = gpu
1718
ctx.obj['config'].seed = seed
19+
if len(override) > 0:
20+
import builtins
21+
try:
22+
exec(override, builtins.__dict__, {'config': ctx.obj['config']})
23+
except Exception as e:
24+
print('!!! Your override string \'{}\' contains an error !!!'.format(override))
25+
raise e
1826

1927

20-
@rainy_cli.command()
28+
@rainy_cli.command(help='Train agents')
2129
@click.pass_context
22-
@click.option('--comment', type=str, default=None)
23-
@click.option('--prefix', type=str, default='')
30+
@click.option('--comment', type=str, default=None, help='Comment wrote to fingerprint.txt')
31+
@click.option('--prefix', type=str, default='', help='Prefix of the log directory')
2432
def train(ctx: dict, comment: Optional[str], prefix: str) -> None:
2533
c = ctx.obj['config']
2634
scr = ctx.obj['script_path']
@@ -33,11 +41,12 @@ def train(ctx: dict, comment: Optional[str], prefix: str) -> None:
3341
print("random play: {}, trained: {}".format(ag.random_episode(), ag.eval_episode()))
3442

3543

36-
@rainy_cli.command()
37-
@click.option('--save', is_flag=True)
38-
@click.option('--render', is_flag=True)
39-
@click.option('--replay', is_flag=True)
40-
@click.option('--action-file', type=str, default='random-actions.json')
44+
@rainy_cli.command(help='Run the random agent and show its result')
45+
@click.option('--save', is_flag=True, help='Save actions')
46+
@click.option('--render', is_flag=True, help='Render the agent')
47+
@click.option('--replay', is_flag=True, help='Show replay(works with special environments)')
48+
@click.option('--action-file', type=str,
49+
default='best-actions.json', help='Name of the action file')
4150
@click.pass_context
4251
def random(ctx: dict, save: bool, render: bool, replay: bool, action_file: str) -> None:
4352
c = ctx.obj['config']
@@ -46,11 +55,12 @@ def random(ctx: dict, save: bool, render: bool, replay: bool, action_file: str)
4655
run.random_agent(ag, render=render, replay=replay, action_file=action_file)
4756

4857

49-
@rainy_cli.command()
58+
@rainy_cli.command(help='Load a save file and restart training')
5059
@click.pass_context
5160
@click.argument('logdir')
52-
@click.option('--model', type=str, default=run.SAVE_FILE_DEFAULT)
53-
@click.option('--additional-steps', type=int, default=100)
61+
@click.option('--model', type=str, default=run.SAVE_FILE_DEFAULT, help='Name of the save file')
62+
@click.option('--additional-steps', type=int,
63+
default=100, help='The number of additional training steps')
5464
def retrain(ctx: dict, logdir: str, model: str, additional_steps: int) -> None:
5565
c = ctx.obj['config']
5666
log = c.logger.retrive(logdir)
@@ -60,12 +70,13 @@ def retrain(ctx: dict, logdir: str, model: str, additional_steps: int) -> None:
6070
print("random play: {}, trained: {}".format(ag.random_episode(), ag.eval_episode()))
6171

6272

63-
@rainy_cli.command()
64-
@click.argument('logdir', required=True, type=str)
65-
@click.option('--model', type=str, default=run.SAVE_FILE_DEFAULT)
66-
@click.option('--render', is_flag=True)
67-
@click.option('--replay', is_flag=True)
68-
@click.option('--action-file', type=str, default='best-actions.json')
73+
@rainy_cli.command(help='Load a save file and evaluate the agent')
74+
@click.argument('logdir')
75+
@click.option('--model', type=str, default=run.SAVE_FILE_DEFAULT, help='Name of the save file')
76+
@click.option('--render', is_flag=True, help='Render the agent')
77+
@click.option('--replay', is_flag=True, help='Show replay(works with special environments)')
78+
@click.option('--action-file', type=str,
79+
default='best-actions.json', help='Name of the action file')
6980
@click.pass_context
7081
def eval(ctx: dict, logdir: str, model: str, render: bool, replay: bool, action_file: str) -> None:
7182
c = ctx.obj['config']
@@ -80,19 +91,19 @@ def eval(ctx: dict, logdir: str, model: str, render: bool, replay: bool, action_
8091
)
8192

8293

83-
@rainy_cli.command()
84-
@click.option('--log-dir', type=str)
85-
@click.option('--vi-mode', is_flag=True)
94+
@rainy_cli.command(help='Open an ipython shell with rainy imported')
95+
@click.option('--logdir', type=str, help='Name of the directly where the log file')
96+
@click.option('--vi-mode', is_flag=True, help='Open ipython shell with vi-mode enabled')
8697
@click.pass_context
87-
def ipython(ctx: dict, log_dir: Optional[str], vi_mode: bool) -> None:
98+
def ipython(ctx: dict, logdir: Optional[str], vi_mode: bool) -> None:
8899
config, make_agent = ctx.obj['config'], ctx.obj['make_agent'] # noqa
89-
if log_dir is not None:
90-
log = ExperimentLog(log_dir) # noqa
100+
if logdir is not None:
101+
log = ExperimentLog(logdir) # noqa
91102
else:
92103
open_log = ExperimentLog # noqa
93104
try:
94105
from ptpython.ipython import embed
95-
del ctx, log_dir
106+
del ctx, logdir
96107
import rainy # noqa
97108
embed(vi_mode=vi_mode)
98109
except ImportError:

0 commit comments

Comments
 (0)