Repository: NSLS-II/nslsii

Issue:
provide a reasonable bluesky log file path for Windows
The bluesky log path is /var/log/bluesky/bluesky.log. This is not reasonable for Windows.

Discussion:

Patch:
diff --git a/nslsii/__init__.py b/nslsii/__init__.py
index c3a3654..de86137 100644
--- a/nslsii/__init__.py
+++ b/nslsii/__init__.py
@@ -273,7 +273,8 @@ def configure_bluesky_logging(ipython, appdirs_appname="bluesky"):
 
     The log file path is taken from environment variable BLUESKY_LOG_FILE, if
     that variable has been set. If not the default log file location is determined
-    by the appdirs package.
+    by the appdirs package. The default log directory will be created if it does
+    not exist.
 
     Parameters
     ----------
@@ -300,14 +301,18 @@ def configure_bluesky_logging(ipython, appdirs_appname="bluesky"):
             file=sys.stderr,
         )
     else:
-        bluesky_log_file_path = Path(
+        bluesky_log_dir = Path(
             appdirs.user_log_dir(appname=appdirs_appname)
-        ) / Path("bluesky.log")
+        )
+        if not bluesky_log_dir.exists():
+            bluesky_log_dir.mkdir(parents=True, exist_ok=True)
+        bluesky_log_file_path = bluesky_log_dir / Path("bluesky.log")
         print(
             f"environment variable BLUESKY_LOG_FILE is not set,"
             f" using default log file path '{bluesky_log_file_path}'",
             file=sys.stderr,
         )
+
     log_file_handler = TimedRotatingFileHandler(
         filename=str(bluesky_log_file_path), when="W0", backupCount=10
     )
@@ -376,9 +381,12 @@ def configure_ipython_logging(
             file=sys.stderr,
         )
     else:
-        bluesky_ipython_log_file_path = Path(
+        bluesky_ipython_log_dir = Path(
             appdirs.user_log_dir(appname=appdirs_appname)
-        ) / Path("bluesky_ipython.log")
+        )
+        if not bluesky_ipython_log_dir.exists():
+            bluesky_ipython_log_dir.mkdir(parents=True, exist_ok=True)
+        bluesky_ipython_log_file_path = bluesky_ipython_log_dir / Path("bluesky_ipython.log")
         print(
             "environment variable BLUESKY_IPYTHON_LOG_FILE is not set,"
             f" using default file path '{bluesky_ipython_log_file_path}'",


Test patch:
diff --git a/nslsii/tests/test_logutils.py b/nslsii/tests/test_logutils.py
index 5cb3eb9..edb5222 100644
--- a/nslsii/tests/test_logutils.py
+++ b/nslsii/tests/test_logutils.py
@@ -1,5 +1,6 @@
 import os
 from pathlib import Path
+import shutil
 import stat
 from unittest.mock import MagicMock
 
@@ -57,24 +58,47 @@ def test_configure_bluesky_logging_with_unwriteable_dir(tmpdir):
         configure_bluesky_logging(ipython=ip,)
 
 
-def test_default_bluesky_logging():
+def test_configure_bluesky_logging_creates_default_dir():
     """
     Remove environment variable BLUESKY_LOG_FILE and test that
-    the default log file path is used. This test creates a
+    the default log file path is created. This test creates a
     directory rather than using pytest's tmp_path so the test
     must clean up at the end.
     """
     test_appname = "bluesky-test"
-    user_log_dir = Path(appdirs.user_log_dir(appname=test_appname))
+    log_dir = Path(appdirs.user_log_dir(appname=test_appname))
+    # remove log_dir if it exists to test that it will be created
+    if log_dir.exists():
+        shutil.rmtree(path=log_dir)
+    log_file_path = log_dir / Path("bluesky.log")
 
-    # log directory must exist
-    # nslsii.configure_bluesky_logging() will not create it
-    user_log_dir.mkdir(parents=True)
+    ip = IPython.core.interactiveshell.InteractiveShell()
+    os.environ.pop("BLUESKY_LOG_FILE", default=None)
 
-    log_file_path = Path(appdirs.user_log_dir(appname=test_appname)) / Path(
-        "bluesky.log"
+    bluesky_log_file_path = configure_bluesky_logging(
+        ipython=ip, appdirs_appname=test_appname
     )
+    assert bluesky_log_file_path == log_file_path
+    assert log_file_path.exists()
+
+    # clean up the file and directory this test creates
+    bluesky_log_file_path.unlink()
+    bluesky_log_file_path.parent.rmdir()
+
+
+def test_configure_bluesky_logging_existing_default_dir():
+    """
+    Remove environment variable BLUESKY_LOG_FILE and test that
+    the default log file path is used. This test creates a
+    directory rather than using pytest's tmp_path so the test
+    must clean up at the end.
+    """
+    test_appname = "bluesky-test"
+    log_dir = Path(appdirs.user_log_dir(appname=test_appname))
+    # create the default log directory
+    log_dir.mkdir(parents=True, exist_ok=True)
 
+    log_file_path = log_dir / Path("bluesky.log")
     ip = IPython.core.interactiveshell.InteractiveShell()
     os.environ.pop("BLUESKY_LOG_FILE", default=None)
 
@@ -97,14 +121,45 @@ def test_ipython_log_exception():
     ip.logger.log_write.assert_called_with("Exception\n", kind="output")
 
 
-def test_default_ipython_exc_logging():
+def test_ipython_exc_logging_creates_default_dir():
+    """
+    Remove environment variable BLUESKY_IPYTHON_LOG_FILE and
+    test that the default log file path is created. This test creates
+    a directory rather than using pytest's tmp_path so the test
+    must clean up at the end.
+    """
     test_appname = "bluesky-test"
     log_dir = Path(appdirs.user_log_dir(appname=test_appname))
+    # remove log_dir if it exists to test that it will be created
+    if log_dir.exists():
+        shutil.rmtree(path=log_dir)
     log_file_path = log_dir / Path("bluesky_ipython.log")
 
-    # the log directory must exist already
-    # nslsii.configure_ipython_exc_logging() will not create it
-    log_dir.mkdir(parents=True)
+    ip = IPython.core.interactiveshell.InteractiveShell()
+    os.environ.pop("BLUESKY_IPYTHON_LOG_FILE", default=None)
+    bluesky_ipython_log_file_path = configure_ipython_logging(
+        exception_logger=log_exception, ipython=ip, appdirs_appname=test_appname
+    )
+
+    assert bluesky_ipython_log_file_path == log_file_path
+    assert log_file_path.exists()
+
+    bluesky_ipython_log_file_path.unlink()
+    bluesky_ipython_log_file_path.parent.rmdir()
+
+
+def test_ipython_exc_logging_existing_default_dir():
+    """
+    Remove environment variable BLUESKY_IPYTHON_LOG_FILE and
+    test that the default log file path is used. This test creates
+    a directory rather than using pytest's tmp_path so the test
+    must clean up at the end.
+    """
+    test_appname = "bluesky-test"
+    log_dir = Path(appdirs.user_log_dir(appname=test_appname))
+    # create the default log directory
+    log_dir.mkdir(parents=True, exist_ok=True)
+    log_file_path = log_dir / Path("bluesky_ipython.log")
 
     ip = IPython.core.interactiveshell.InteractiveShell()
     os.environ.pop("BLUESKY_IPYTHON_LOG_FILE", default=None)