Repository: spacetelescope/jwst

Issue:
datamodels should accept a pathlib.Path object as init
Currently datamodels can be given a string of a path to a FITS or ASDF file to open via `datamodels.open()` or directly passing the string to a `datamodels` subclass.  One should also be able to pass a `pathlib.Path` object (introduced in `python 3.4`).  These objects are becoming more widely used in python, specifically in `pytest`, and we should support their use.

Discussion:
hbushouse: Can someone enlighten this python neophyte as to just what a `PurePath` object is and what's the use case in the jwst pipeline driving this change?
stscieisenhamer: The module is the [pathlib](https://docs.python.org/3/library/pathlib.html) module, which provides a more object-oriented approach to handling file and path names. For a full discussion, [Trey Hunner](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) has a bit to say on the subject.
jdavies-st: Not just you @stscieisenhamer .  It resolves an issue I filed a while back.  Updated your message up top.

There's also #3864

Patch:
diff --git a/.travis.yml b/.travis.yml
index e4a410778..8887f4680 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,6 +12,11 @@ env:
     - TEST_COMMAND="pytest --cov-report=xml --cov=./"
     - PIP_DEPENDENCIES=.[test]
 
+cache:
+  - pip
+  - directories:
+    - /tmp/crds_cache
+
 jobs:
   # Don't wait for allowed failures
   fast_finish: true
diff --git a/jwst/datamodels/model_base.py b/jwst/datamodels/model_base.py
index 028df5369..9b649940d 100644
--- a/jwst/datamodels/model_base.py
+++ b/jwst/datamodels/model_base.py
@@ -5,6 +5,7 @@ Data model class heirarchy
 import copy
 import datetime
 import os
+from pathlib import PurePath
 import sys
 import warnings
 
@@ -170,7 +171,9 @@ class DataModel(properties.ObjectNode, ndmodel.NDModel):
             asdffile = fits_support.from_fits(init, self._schema, self._ctx,
                                               **kwargs)
 
-        elif isinstance(init, (str, bytes)):
+        elif isinstance(init, (str, bytes, PurePath)):
+            if isinstance(init, PurePath):
+                init = str(init)
             if isinstance(init, bytes):
                 init = init.decode(sys.getfilesystemencoding())
             file_type = filetype.check(init)


Test patch:
diff --git a/jwst/datamodels/tests/test_models.py b/jwst/datamodels/tests/test_models.py
index e80190f84..6e9f283c7 100644
--- a/jwst/datamodels/tests/test_models.py
+++ b/jwst/datamodels/tests/test_models.py
@@ -1,5 +1,6 @@
 import os
 from os import path as op
+from pathlib import Path
 import shutil
 import tempfile
 import warnings
@@ -84,6 +85,15 @@ def make_models(tmpdir_factory):
     }
 
 
+def test_init_from_pathlib(make_models):
+    """Test initializing model from a PurePath object"""
+    path = Path(make_models['model'])
+    model = ImageModel(path)
+
+    # Test is basically, did we open the model?
+    assert isinstance(model, ImageModel)
+
+
 @pytest.mark.parametrize(
     'which_file, skip_fits_update, expected_exp_type',
     [
diff --git a/jwst/datamodels/tests/test_open.py b/jwst/datamodels/tests/test_open.py
index 98e9c2d2c..b8ff2e052 100644
--- a/jwst/datamodels/tests/test_open.py
+++ b/jwst/datamodels/tests/test_open.py
@@ -4,6 +4,7 @@ Test datamodel.open
 
 import os
 import os.path
+from pathlib import Path, PurePath
 import warnings
 
 import pytest
@@ -16,6 +17,17 @@ from jwst.datamodels import (DataModel, ModelContainer, ImageModel,
 from jwst import datamodels
 
 
+def test_open_from_pathlib():
+    """Test opening a PurePath object"""
+    path = Path(t_path('test.fits'))
+    assert isinstance(path, PurePath)
+
+    with warnings.catch_warnings():
+        warnings.filterwarnings("ignore", "model_type not found")
+        with datamodels.open(path) as model:
+            assert isinstance(model, DataModel)
+
+
 def test_open_fits():
     """Test opening a model from a FITS file"""