diff --git a/README.md b/README.md index eb6bb538..a9d19bfa 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,11 @@ $ dotenv set EMAIL foo@example.org $ dotenv list USER=foo EMAIL=foo@example.org +$ dotenv list --format=json +{ + "USER": "foo", + "EMAIL": "foo@example.org" +} $ dotenv run -- python foo.py ``` diff --git a/src/dotenv/cli.py b/src/dotenv/cli.py index 3411e346..b845b95e 100644 --- a/src/dotenv/cli.py +++ b/src/dotenv/cli.py @@ -1,4 +1,6 @@ +import json import os +import shlex import sys from subprocess import Popen from typing import Any, Dict, List @@ -36,7 +38,11 @@ def cli(ctx: click.Context, file: Any, quote: Any, export: Any) -> None: @cli.command() @click.pass_context -def list(ctx: click.Context) -> None: +@click.option('--format', default='simple', + type=click.Choice(['simple', 'json', 'shell', 'export']), + help="The format in which to display the list. Default format is simple, " + "which displays name=value without quotes.") +def list(ctx: click.Context, format: bool) -> None: '''Display all the stored key/value.''' file = ctx.obj['FILE'] if not os.path.isfile(file): @@ -45,8 +51,16 @@ def list(ctx: click.Context) -> None: ctx=ctx ) dotenv_as_dict = dotenv_values(file) - for k, v in dotenv_as_dict.items(): - click.echo('%s=%s' % (k, v)) + if format == 'json': + click.echo(json.dumps(dotenv_as_dict, indent=2, sort_keys=True)) + else: + prefix = 'export ' if format == 'export' else '' + for k in sorted(dotenv_as_dict): + v = dotenv_as_dict[k] + if v is not None: + if format in ('export', 'shell'): + v = shlex.quote(v) + click.echo('%s%s=%s' % (prefix, k, v)) @cli.command() diff --git a/tests/test_cli.py b/tests/test_cli.py index 223476fe..ca5ba2a1 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2,19 +2,38 @@ import pytest import sh - +from typing import Optional import dotenv from dotenv.cli import cli as dotenv_cli from dotenv.version import __version__ -def test_list(cli, dotenv_file): +@pytest.mark.parametrize( + "format,content,expected", + ( + (None, "x='a b c'", '''x=a b c\n'''), + ("simple", "x='a b c'", '''x=a b c\n'''), + ("simple", """x='"a b c"'""", '''x="a b c"\n'''), + ("simple", '''x="'a b c'"''', '''x='a b c'\n'''), + ("json", "x='a b c'", '''{\n "x": "a b c"\n}\n'''), + ("shell", "x='a b c'", "x='a b c'\n"), + ("shell", """x='"a b c"'""", '''x='"a b c"'\n'''), + ("shell", '''x="'a b c'"''', '''x=''"'"'a b c'"'"''\n'''), + ("shell", "x='a\nb\nc'", "x='a\nb\nc'\n"), + ("export", "x='a b c'", '''export x='a b c'\n'''), + ) +) +def test_list(cli, dotenv_file, format: Optional[str], content: str, expected: str): with open(dotenv_file, "w") as f: - f.write("a=b") + f.write(content + '\n') + + args = ['--file', dotenv_file, 'list'] + if format is not None: + args.extend(['--format', format]) - result = cli.invoke(dotenv_cli, ['--file', dotenv_file, 'list']) + result = cli.invoke(dotenv_cli, args) - assert (result.exit_code, result.output) == (0, result.output) + assert (result.exit_code, result.output) == (0, expected) def test_list_non_existent_file(cli):