Repository: csdms/bmi-topography

Issue:
Underscores in bmi-topography command line options
I'm wondering about the `_`s in the *bmi-topography* command line options (e.g. `--no_fetch`, `--api_key`). It seems more standard to use `-`s in such cases (e.g. `--no-fetch`, `--api-key`).

Comments:

Patch:
diff --git a/bmi_topography/cli.py b/bmi_topography/cli.py
index 20c2121..4bbf24a 100644
--- a/bmi_topography/cli.py
+++ b/bmi_topography/cli.py
@@ -8,7 +8,7 @@ from .topography import Topography
 @click.version_option()
 @click.option("-q", "--quiet", is_flag=True, help="Enables quiet mode.")
 @click.option(
-    "--dem_type",
+    "--dem-type",
     type=click.Choice(Topography.VALID_DEM_TYPES, case_sensitive=True),
     default=Topography.DEFAULT["dem_type"],
     help="The global raster dataset.",
@@ -43,19 +43,19 @@ from .topography import Topography
     show_default=True,
 )
 @click.option(
-    "--output_format",
+    "--output-format",
     type=click.Choice(Topography.VALID_OUTPUT_FORMATS.keys(), case_sensitive=True),
     default=Topography.DEFAULT["output_format"],
     help="Output file format.",
     show_default=True,
 )
 @click.option(
-    "--api_key",
+    "--api-key",
     type=str,
     help="OpenTopography API key.",
     show_default=True,
 )
-@click.option("--no_fetch", is_flag=True, help="Do not fetch data from server.")
+@click.option("--no-fetch", is_flag=True, help="Do not fetch data from server.")
 def main(quiet, dem_type, south, north, west, east, output_format, api_key, no_fetch):
     """Fetch and cache NASA SRTM and JAXA ALOS land elevation data
 
@@ -73,9 +73,12 @@ def main(quiet, dem_type, south, north, west, east, output_format, api_key, no_f
     topo = Topography(dem_type, south, north, west, east, output_format)
     if not no_fetch:
         if not quiet:
-            click.secho("Fetching data...", fg="yellow")
-        topo.fetch()
+            click.secho("Fetching data...", fg="yellow", err=True)
+        path_to_dem = topo.fetch()
         if not quiet:
             click.secho(
-                "File downloaded to {}".format(getattr(topo, "cache_dir")), fg="green"
+                "File downloaded to {}".format(getattr(topo, "cache_dir")),
+                fg="green",
+                err=True,
             )
+        print(path_to_dem)
diff --git a/bmi_topography/topography.py b/bmi_topography/topography.py
index 6198662..91070dd 100644
--- a/bmi_topography/topography.py
+++ b/bmi_topography/topography.py
@@ -1,6 +1,7 @@
 """Base class to access elevation data"""
 import os
 import urllib
+import warnings
 from pathlib import Path
 
 import requests
@@ -9,7 +10,7 @@ import xarray as xr
 from .bbox import BoundingBox
 
 
-def find_api_key():
+def find_user_api_key():
     """Search for an API key."""
     if "OPENTOPOGRAPHY_API_KEY" in os.environ:
         api_key = os.environ["OPENTOPOGRAPHY_API_KEY"]
@@ -21,6 +22,15 @@ def find_api_key():
     return api_key
 
 
+def use_demo_key():
+    warnings.warn(
+        "You are using a demo key to fetch data from OpenTopography, functionality "
+        "will be limited. See https://bmi-topography.readthedocs.io/en/latest/#api-key "
+        "for more information."
+    )
+    return "demoapikeyot2022"
+
+
 def read_first_of(files):
     """Read the contents of the first file encountered."""
     contents = ""
@@ -67,7 +77,7 @@ class Topography:
         api_key=None,
     ):
         if api_key is None:
-            self._api_key = find_api_key()
+            self._api_key = find_user_api_key() or use_demo_key()
         else:
             self._api_key = api_key
 
diff --git a/examples/.opentopography.txt b/examples/.opentopography.txt
deleted file mode 100644
index e367779..0000000
--- a/examples/.opentopography.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-demoapikeyot2022
diff --git a/examples/bmi-topography_ex.sh b/examples/bmi-topography_ex.sh
index 4234a7c..389ca57 100644
--- a/examples/bmi-topography_ex.sh
+++ b/examples/bmi-topography_ex.sh
@@ -12,9 +12,9 @@ bmi-topography --version
 bmi-topography --help
 
 bmi-topography \
-    --dem_type=$DEM_TYPE \
+    --dem-type=$DEM_TYPE \
     --south=$SOUTH \
     --north=$NORTH \
     --west=$WEST \
     --east=$EAST \
-    --output_format=$OUTPUT_FORMAT
+    --output-format=$OUTPUT_FORMAT


Test patch:
diff --git a/tests/test_api_key.py b/tests/test_api_key.py
index 1751b37..9107a76 100644
--- a/tests/test_api_key.py
+++ b/tests/test_api_key.py
@@ -1,7 +1,9 @@
 import os
 from unittest import mock
 
-from bmi_topography.topography import find_api_key, read_first_of
+import pytest
+
+from bmi_topography.topography import find_user_api_key, read_first_of, use_demo_key
 
 
 def copy_environ(exclude=None):
@@ -13,24 +15,24 @@ def copy_environ(exclude=None):
     return {key: value for key, value in os.environ.items() if key not in exclude}
 
 
-def test_find_api_key_not_found():
+def test_find_user_api_key_not_found():
     """The API key is not given anywhere"""
     env = copy_environ(exclude="OPENTOPOGRAPHY_API_KEY")
     with mock.patch.dict(os.environ, env, clear=True):
-        assert find_api_key() == ""
+        assert find_user_api_key() == ""
 
 
 @mock.patch.dict(os.environ, {"OPENTOPOGRAPHY_API_KEY": "foo"})
-def test_find_api_key_env(tmpdir):
+def test_find_user_api_key_env(tmpdir):
     """The API key is an environment variable"""
     with tmpdir.as_cwd():
         with open(".opentopography.txt", "w") as fp:
             fp.write("bar")
-    assert find_api_key() == "foo"
+    assert find_user_api_key() == "foo"
 
 
 @mock.patch.dict(os.environ, {"OPENTOPOGRAPHY_API_KEY": "foo"})
-def test_find_api_key_from_file(tmpdir):
+def test_find_user_api_key_from_file(tmpdir):
     """The API key is in a file"""
     env = copy_environ(exclude="OPENTOPOGRAPHY_API_KEY")
     with tmpdir.as_cwd():
@@ -38,7 +40,7 @@ def test_find_api_key_from_file(tmpdir):
             fp.write("bar")
 
         with mock.patch.dict(os.environ, env, clear=True):
-            assert find_api_key() == "bar"
+            assert find_user_api_key() == "bar"
 
 
 def test_read_first_missing(tmpdir):
@@ -56,3 +58,14 @@ def test_read_first_file(tmpdir):
 
         assert read_first_of(["foo.txt", "bar.txt"]) == "foo"
         assert read_first_of(["bar.txt", "foo.txt"]) == "bar"
+
+
+def test_use_demo_key_is_a_string():
+    demo_key = use_demo_key()
+    assert isinstance(demo_key, str)
+    assert len(demo_key) > 0
+
+
+def test_use_demo_key_issues_warning():
+    with pytest.warns(UserWarning):
+        use_demo_key()
diff --git a/tests/test_cli.py b/tests/test_cli.py
index cd5ec63..696cd67 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -1,4 +1,6 @@
 """Test bmi-topography command-line interface"""
+import pathlib
+
 from click.testing import CliRunner
 
 from bmi_topography.cli import main
@@ -20,31 +22,40 @@ def test_version():
 
 def test_defaults():
     runner = CliRunner()
-    result = runner.invoke(main)
+    result = runner.invoke(main, ["--quiet"])
+    assert pathlib.Path(result.output.strip()).is_file()
     assert result.exit_code == 0
 
 
+def test_quiet():
+    runner = CliRunner()
+    quiet_lines = runner.invoke(main, ["--quiet"]).output.splitlines()
+    verbose_lines = runner.invoke(main).output.splitlines()
+
+    assert len(verbose_lines) > len(quiet_lines)
+
+
 def test_demtype_valid():
     runner = CliRunner()
-    result = runner.invoke(main, ["--dem_type=SRTMGL1", "--no_fetch"])
+    result = runner.invoke(main, ["--dem-type=SRTMGL1", "--no-fetch"])
     assert result.exit_code == 0
 
 
 def test_demtype_invalid():
     runner = CliRunner()
-    result = runner.invoke(main, ["--dem_type=foobar"])
+    result = runner.invoke(main, ["--dem-type=foobar"])
     assert result.exit_code != 0
 
 
 def test_demtype_is_case_sensitive():
     runner = CliRunner()
-    result = runner.invoke(main, ["--dem_type=srtmgl1"])
+    result = runner.invoke(main, ["--dem-type=srtmgl1"])
     assert result.exit_code != 0
 
 
 def test_south_inrange():
     runner = CliRunner()
-    result = runner.invoke(main, ["--south=-90.0", "--no_fetch"])
+    result = runner.invoke(main, ["--south=-90.0", "--no-fetch"])
     assert result.exit_code == 0
 
 
@@ -56,7 +67,7 @@ def test_south_outrange():
 
 def test_north_inrange():
     runner = CliRunner()
-    result = runner.invoke(main, ["--north=90.0", "--no_fetch"])
+    result = runner.invoke(main, ["--north=90.0", "--no-fetch"])
     assert result.exit_code == 0
 
 
@@ -68,7 +79,7 @@ def test_north_outrange():
 
 def test_west_inrange():
     runner = CliRunner()
-    result = runner.invoke(main, ["--west=-180.0", "--no_fetch"])
+    result = runner.invoke(main, ["--west=-180.0", "--no-fetch"])
     assert result.exit_code == 0
 
 
@@ -80,7 +91,7 @@ def test_west_outrange():
 
 def test_east_inrange():
     runner = CliRunner()
-    result = runner.invoke(main, ["--east=180.0", "--no_fetch"])
+    result = runner.invoke(main, ["--east=180.0", "--no-fetch"])
     assert result.exit_code == 0
 
 
@@ -92,17 +103,17 @@ def test_east_outrange():
 
 def test_output_format_valid():
     runner = CliRunner()
-    result = runner.invoke(main, ["--output_format=GTiff", "--no_fetch"])
+    result = runner.invoke(main, ["--output-format=GTiff", "--no-fetch"])
     assert result.exit_code == 0
 
 
 def test_output_format_invalid():
     runner = CliRunner()
-    result = runner.invoke(main, ["--output_format=foobar"])
+    result = runner.invoke(main, ["--output-format=foobar"])
     assert result.exit_code != 0
 
 
 def test_output_format_is_case_sensitive():
     runner = CliRunner()
-    result = runner.invoke(main, ["--output_format=gtiff"])
+    result = runner.invoke(main, ["--output-format=gtiff"])
     assert result.exit_code != 0