Repository: theskumar/python-dotenv

Issue:
--format= option for CLI list command
It would be nice to have a way to dump all variables as JSON, which can be used by many other tools. I'd propose addition of a --json or -j option on the "list" command.

I'd be happy to submit a pull request if it sounds interesting.

Discussion:

Patch:
diff --git a/README.md b/README.md
index eb6bb53..a9d19bf 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 3411e34..b845b95 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()


Test patch:
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 223476f..ca5ba2a 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -2,19 +2,38 @@ import os
 
 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):