Skip to content

Commit

Permalink
Improve Streamlit CLI and clean up __main__.py to use Click properly. (
Browse files Browse the repository at this point in the history
…#516)

* Improve Streamlit CLI and clean up __main__.py to use Click properly.

* Replace 'streamlit help' with 'streamlit docs' in online docs.

* Lint __main__.py

* Hide deprecated CLI options from `streamlit help`

* Remove lingering `args` param from `streamlit hello` CLI option.

* Update docs to better describe our CLI tool
  • Loading branch information
tvst committed Mar 27, 2019
1 parent f38dab7 commit 9e1113f
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 87 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ for i in range(100):

For more help and a complete API documentation, please run:
```bash
streamlit help
streamlit docs
```
35 changes: 24 additions & 11 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,28 @@

# Command-line tool

When you install Streamlit, the Streamlit CLI tool gets installed as
well. The main purpose of this tool is to help you diagnose and fix issues.
When you install Streamlit, the Streamlit command-line CLI tool gets installed
as well. The main purpose of this tool is to help you diagnose and fix issues.

Below are the commands accepted by Streamlit CLI:
You can find docs for our CLI tool as usual:

## version
```bash
$ streamlit --help
```

Below are a few of the most useful commands accepted by Streamlit CLI:

## \-\-version

```bash
$ streamlit version
$ streamlit --version
```
Shows the version of Streamlit in your current Python environment.

## help
## docs

```bash
$ streamlit help
$ streamlit docs
```
Opens Streamlit's documentation (i.e. this website) in a web browser.

Expand All @@ -31,18 +37,25 @@ $ streamlit hello
Opens Streamlit's Hello World report in a web browser. This is useful for
testing Streamlit.

## kill_proxy
## proxy kill

```bash
$ streamlit kill_proxy
$ streamlit proxy kill
```
Kills the Streamlit proxy server, if any. This is useful when making config
changes, for example, since the config file is read when the server
initializes.

## show_config
## config show

```bash
$ streamlit show_config
$ streamlit config show
```
Shows all config options for Streamlit, as well as their current values.

## cache clear

```bash
$ streamlit cache clear
```
Clears the [Streamlit cache](/api/optimization).
2 changes: 1 addition & 1 deletion docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ continue reading this page or [contact us](mailto:help@streamlit.io).

## Remote operation: report URL doesn't load

You ran `streamlit help` or `python my_script.py` and it printed out the URL
You ran `streamlit hello` or `python my_script.py` and it printed out the URL
where you should find your report --- but it doesn't seem to work when you open
that link in a browser!

Expand Down
2 changes: 1 addition & 1 deletion docs/tutorial/tutorial1_first_steps.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ st.balloons()
Now you should have a basic idea of how to use Streamlit in your own scripts:
just `import streamlit as st` and experiment away from there! A good way to
start is by copy/pasting code snippets straight from the Streamlit
documentation. Just run `streamlit help` on a terminal to get to the docs, or
documentation. Just run `streamlit docs` on a terminal to get to the docs, or
click the *Documentation* item in Streamlit's top-right menu.

**Next up,** we'll run through a more concrete example of how you could use
Expand Down
185 changes: 112 additions & 73 deletions lib/streamlit/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,121 +11,160 @@
import click


def print_usage(args):
LOG_LEVELS = ['error', 'warning', 'info', 'debug']


@click.group()
@click.option('--log_level', show_default=True, type=click.Choice(LOG_LEVELS))
@click.version_option(prog_name='Streamlit')
@click.pass_context
def main(ctx, log_level='info'):
"""Main app entrypoint."""

if log_level:
import streamlit.logger
streamlit.logger.set_log_level(log_level.upper())


@main.command('help')
@click.pass_context
def help(ctx):
"""Print this help message."""
print("\nWhere [MODE] is one of:")
for command, handler in COMMAND_HANDLERS.items():
print(
' %(command)13s - %(doc)s' % {
'command': command,
'doc': handler.__doc__
})
# Pretend user typed 'streamlit --help' instead of 'streamlit help'.
import sys
assert len(sys.argv) == 2 # This is always true, but let's assert anyway.
sys.argv[1] = '--help'
main()


def clear_cache(args):
"""Clear the Streamlit cache."""
import streamlit.caching
streamlit.caching.clear_cache(True)
@main.command('version')
@click.pass_context
def main_version(ctx):
"""Print Streamlit's version number."""
# Pretend user typed 'streamlit --version' instead of 'streamlit version'
import sys
assert len(sys.argv) == 2 # This is always true, but let's assert anyway.
sys.argv[1] = '--version'
main()


def help(args):
@main.command('docs')
def main_docs():
"""Show help in browser."""
print('Showing help page in browser...')
from streamlit import util
util.open_browser('https://streamlit.io/secret/docs')


def hello(args):
@main.command('hello')
def main_hello():
"""Runs the Hello World script."""
print('Showing Hello World script in browser...')
import streamlit.hello
streamlit.hello.run()


def run(args):
@main.command('run')
@click.argument('file', type=click.Path(exists=True))
@click.argument('args', nargs=-1)
def main_run(file, args):
"""Run a Python script, piping stderr to Streamlit."""
import streamlit.process_runner as process_runner
import sys
cmd = [sys.executable, file] + list(args)
process_runner.run_handling_errors_in_this_process(cmd)

assert len(args) > 0, 'You must specify a file to run'

source_file_path = args[0]
cmd = [sys.executable] + list(args)
process_runner.run_handling_errors_in_this_process(cmd)
# DEPRECATED

@main.command('clear_cache', deprecated=True, hidden=True)
@click.pass_context
def main_clear_cache(ctx):
"""Deprecated."""
click.echo(click.style('Use "cache clear" instead.', fg='red'))
ctx.invoke(cache_clear)

def kill_proxy(*args):
"""Kill the Streamlit proxy."""
import psutil
import getpass

found_proxy = False
@main.command('kill_proxy', deprecated=True, hidden=True)
@click.pass_context
def main_kill_proxy(ctx):
"""Deprecated."""
click.echo(click.style('Use "proxy kill" instead.', fg='red'))
ctx.invoke(proxy_kill)

for p in psutil.process_iter(attrs=['name', 'username']):
# Attention: p.name() sometimes is 'python', sometimes 'Python', and
# sometimes '/crazycondastuff/python'.
try:
if (('python' in p.name() or 'Python' in p.name())
and 'streamlit.proxy' in p.cmdline()
and getpass.getuser() == p.info['username']):
print('Killing proxy with PID %d' % p.pid)
p.kill()
found_proxy = True
# Ignore zombie process and proceses that have terminated
# already. ie you can't call process.name() on a process that
# has terminated.
except (psutil.ZombieProcess, psutil.NoSuchProcess) as e:
pass

if not found_proxy:
print('No Streamlit proxies found.')
@main.command('show_config', deprecated=True, hidden=True)
@click.pass_context
def main_show_config(ctx):
"""Deprecated."""
click.echo(click.style('Use "config show" instead.', fg='red'))
ctx.invoke(config_show)


# SUBCOMMAND: cache

@main.group('cache')
def cache():
"""Manage the Streamlit cache."""
pass


@cache.command('clear')
def cache_clear():
"""Clear the Streamlit cache."""
import streamlit.caching
streamlit.caching.clear_cache(True)


def version(*args):
"""Print the version number."""
import streamlit
print('Streamlit v' + streamlit.__version__)
# SUBCOMMAND: config

@main.group('config')
def config():
"""Manage Streamlit's config settings."""
pass

def show_config(*args):

@config.command('show')
def config_show():
"""Show all of Streamlit's config settings."""
from streamlit import config
config.show_config()


COMMAND_HANDLERS = dict(
clear_cache = clear_cache,
hello = hello,
help = help,
kill_proxy = kill_proxy,
run = run,
show_config = show_config,
usage = print_usage,
version = version,
)

# SUBCOMMAND: proxy

COMMANDS = list(COMMAND_HANDLERS.keys())
@main.group('proxy')
def proxy():
"""Manage the Streamlit proxy."""
pass


LOG_LEVELS = ['error', 'warning', 'info', 'debug']

@proxy.command('kill')
def proxy_kill():
"""Kill the Streamlit proxy."""
import psutil
import getpass

@click.command()
@click.pass_context
@click.argument('mode', default='usage', type=click.Choice(COMMANDS))
@click.argument('args', type=str, nargs=-1)
@click.option('--log_level', show_default=True, type=click.Choice(LOG_LEVELS))
def main(ctx, mode, args, log_level): # pragma: no cover # noqa: D401
"""Main app entrypoint."""
if log_level:
import streamlit.logger
streamlit.logger.set_log_level(log_level)
found_proxy = False

if mode == 'usage':
click.echo(ctx.get_help())
for p in psutil.process_iter(attrs=['name', 'username']):
# Attention: p.name() sometimes is 'python', sometimes 'Python', and
# sometimes '/crazycondastuff/python'.
try:
if (('python' in p.name() or 'Python' in p.name())
and 'streamlit.proxy' in p.cmdline()
and getpass.getuser() == p.info['username']):
print('Killing proxy with PID %d' % p.pid)
p.kill()
found_proxy = True
# Ignore zombie process and proceses that have terminated
# already. ie you can't call process.name() on a process that
# has terminated.
except (psutil.ZombieProcess, psutil.NoSuchProcess):
pass

COMMAND_HANDLERS[mode](args)
if not found_proxy:
print('No Streamlit proxies found.')


if __name__ == '__main__':
Expand Down

0 comments on commit 9e1113f

Please sign in to comment.