diff --git a/respy/pre_processing/model_processing.py b/respy/pre_processing/model_processing.py
index 1d630123..b62c3003 100644
--- a/respy/pre_processing/model_processing.py
+++ b/respy/pre_processing/model_processing.py
@@ -1,5 +1,6 @@
 """Process model specification files or objects."""
 import json
+from pathlib import Path
 
 import numpy as np
 import pandas as pd
@@ -11,26 +12,19 @@ from respy.python.shared.shared_auxiliary import get_optim_paras
 
 
 def process_model_spec(params_spec, options_spec):
-    if not isinstance(params_spec, pd.DataFrame):
-        assert isinstance(
-            params_spec, str
-        ), "params_spec has to be a DataFrame or file path."
-        params_spec = _read_params_spec(params_spec)
-    if not isinstance(options_spec, dict):
-        assert isinstance(
-            options_spec, str
-        ), "options_spec has to be a dictionary or file path."
-        options_spec = _read_options_spec(options_spec)
+    params_spec = _read_params_spec(params_spec)
+    options_spec = _read_options_spec(options_spec)
     attr = _create_attribute_dictionary(params_spec, options_spec)
+
     return attr
 
 
 def write_out_model_spec(attr, save_path):
     params_spec = _params_spec_from_attributes(attr)
     options_spec = _options_spec_from_attributes(attr)
-    # todo: does this need index=False?
-    params_spec.to_csv(save_path + ".csv")
-    with open(save_path + ".json", "w") as j:
+
+    params_spec.to_csv(Path(save_path).with_suffix(".csv"))
+    with open(Path(save_path).with_suffix(".json"), "w") as j:
         json.dump(options_spec, j)
 
 
@@ -161,14 +155,12 @@ def _create_attribute_dictionary(params_spec, options_spec):
         "SCIPY-POWELL",
         "SCIPY-LBFGSB",
     ]
-    # to-do: add type checks and/or conversions for optimizer options
     attr["optimizer_options"] = {}
     for opt in optimizers:
         attr["optimizer_options"][opt] = options_spec[opt]
 
     attr["is_myopic"] = params_spec.loc[("delta", "delta"), "para"] == 0.0
 
-    # to-do: make asserts that all string values are lowercase
     return attr
 
 
@@ -178,28 +170,42 @@ def _get_num_types(params_spec):
         num_types = len_type_shares / 2 + 1
     else:
         num_types = 1
+
     return num_types
 
 
-def _read_params_spec(file_path):
-    assert file_path.endswith(".csv"), "file_path has to be a .csv file"
-    params_spec = pd.read_csv(file_path)
+def _read_params_spec(input_):
+    if not isinstance(input_, (Path, pd.DataFrame)):
+        raise TypeError("params_spec must be Path or pd.DataFrame.")
+
+    params_spec = pd.read_csv(input_) if isinstance(input_, Path) else input_
+
     params_spec["para"] = params_spec["para"].astype(float)
-    params_spec.set_index(["category", "name"], inplace=True)
+
+    if not params_spec.index.names == ["category", "name"]:
+        params_spec.set_index(["category", "name"], inplace=True)
+
     return params_spec
 
 
-def _read_options_spec(file_path):
-    if file_path.endswith(".json"):
-        with open(file_path, "r") as j:
-            options_spec = json.load(j)
-    elif file_path.endswith(".yaml"):
-        with open(file_path, "r") as y:
-            options_spec = yaml.load(y)
+def _read_options_spec(input_):
+    if not isinstance(input_, (Path, dict)):
+        raise TypeError("options_spec must be Path or dictionary.")
+
+    if isinstance(input_, Path):
+        with open(input_, "r") as file:
+            if input_.suffix == ".json":
+                options_spec = json.load(file)
+            elif input_.suffix == ".yaml":
+                options_spec = yaml.load(file)
+            else:
+                raise NotImplementedError(f"Format {input_.suffix} is not supported.")
+    else:
+        options_spec = input_
 
     default = default_model_dict()
-    default.update(options_spec)
-    options_spec = default
+    options_spec = {**default, **options_spec}
+
     return options_spec
 
 
diff --git a/respy/pre_processing/specification_helpers.py b/respy/pre_processing/specification_helpers.py
index 50b19ff3..b3030105 100644
--- a/respy/pre_processing/specification_helpers.py
+++ b/respy/pre_processing/specification_helpers.py
@@ -1,9 +1,8 @@
-from os.path import dirname
-from os.path import join
-
 import numpy as np
 import pandas as pd
 
+from respy.python.shared.shared_constants import ROOT_DIR
+
 
 def csv_template(num_types, save_path=None, initialize_coeffs=True):
     template = _base_template()
@@ -22,7 +21,7 @@ def csv_template(num_types, save_path=None, initialize_coeffs=True):
 
 
 def _base_template():
-    path = join(dirname(__file__), "base_spec.csv")
+    path = ROOT_DIR / "pre_processing" / "base_spec.csv"
     base_template = pd.read_csv(path)
     base_template.set_index(["category", "name"], inplace=True)
     return base_template

Make model_processing compatible with pathlib 
Currently respy objects can be generated with a standard file path but not with a pathlib object. Changing this requires several adjustments:

- don't check for file types using endswith
- adjust type checking
- don't modify paths using string operations
diff --git a/respy/tests/test_integration.py b/respy/tests/test_integration.py
index 953d5503..db324631 100644
--- a/respy/tests/test_integration.py
+++ b/respy/tests/test_integration.py
@@ -330,21 +330,21 @@ class TestClass(object):
         # This ensures that the experience effect is taken care of properly.
         open(".restud.respy.scratch", "w").close()
 
-        fname, result = random.choice(
+        kw_spec, result = random.choice(
             [
-                ("kw_data_one.ini", 10.45950941513551),
-                ("kw_data_two.ini", 45.04552402391903),
-                ("kw_data_three.ini", 74.28253652773714),
-                ("kw_data_one_types.ini", 9.098738585839529),
-                ("kw_data_one_initial.ini", 7.965979149372883),
+                ("kw_data_one", 10.45950941513551),
+                ("kw_data_two", 45.04552402391903),
+                ("kw_data_three", 74.28253652773714),
+                ("kw_data_one_types", 9.098738585839529),
+                ("kw_data_one_initial", 7.965979149372883),
             ]
         )
 
-        base_path = TEST_RESOURCES_DIR / fname
+        base_path = TEST_RESOURCES_DIR / kw_spec
 
         # Evaluate criterion function at true values.
         respy_obj = RespyCls(
-            str(base_path.with_suffix(".csv")), str(base_path.with_suffix(".json"))
+            base_path.with_suffix(".csv"), base_path.with_suffix(".json")
         )
 
         respy_obj.unlock()
diff --git a/respy/tests/test_unit.py b/respy/tests/test_unit.py
index e9b59ec9..2e77a3e2 100644
--- a/respy/tests/test_unit.py
+++ b/respy/tests/test_unit.py
@@ -1,4 +1,5 @@
 from functools import partial
+from pathlib import Path
 
 import numpy as np
 import pytest
@@ -55,10 +56,10 @@ class TestClass(object):
             params_spec, options_spec = generate_random_model()
             # Process request and write out again.
             respy_obj = RespyCls(params_spec, options_spec)
-            respy_obj.write_out("alt.respy")
+            respy_obj.write_out("alt_respy")
 
-            new_params_spec = _read_params_spec("alt.respy.csv")
-            new_options_spec = _read_options_spec("alt.respy.json")
+            new_params_spec = _read_params_spec(Path("alt_respy.csv"))
+            new_options_spec = _read_options_spec(Path("alt_respy.json"))
 
             assert options_spec == new_options_spec
 

environment.yml
pip install -e .[dev]
parse_log_pytest
environment.yml
pytest pytest-cov pytest-xdist pytest
apt-get update apt-get install -y gcc gfortran libblas-dev liblapack-dev
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
attrs==22.2.0
Babel==2.11.0
certifi==2021.5.30
charset-normalizer==2.0.12
codecov==2.1.13
colorama==0.4.5
coverage==6.2
distlib==0.3.9
doc8==0.11.2
docutils==0.18.1
execnet==1.9.0
filelock==3.4.1
idna==3.10
imagesize==1.4.1
importlib-metadata==4.8.3
importlib-resources==5.4.0
iniconfig==1.1.1
Jinja2==3.0.3
livereload==2.6.3
llvmlite==0.28.0
MarkupSafe==2.0.1
numba==0.43.1
numpy==1.19.2
packaging==21.3
pandas==0.24.2
patsy==0.5.1
pbr==6.1.1
platformdirs==2.4.0
pluggy==1.0.0
py==1.11.0
pyaml==23.5.8
Pygments==2.14.0
pyparsing==3.1.4
pytest==7.0.1
pytest-cov==4.0.0
pytest-xdist==3.0.2
python-dateutil==2.8.2
pytz==2021.3
PyYAML==6.0.1
requests==2.27.1
-e git+https://github.com/OpenSourceEconomics/respy.git@c3a6f820ffa6e6085cc2e400a68a53450daade3b#egg=respy
restructuredtext-lint==1.4.0
ruamel.yaml==0.17.40
ruamel.yaml.clib==0.2.8
scipy==1.2.1
six==1.16.0
snowballstemmer==2.2.0
sphinx==5.3.0
sphinx-autobuild==2021.3.14
sphinx-rtd-theme==2.0.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
statsmodels==0.9.0
stevedore==3.5.2
toml==0.10.2
tomli==1.2.3
tornado==6.1
tox==3.28.0
tox-conda==0.10.2
typing-extensions==4.1.1
urllib3==1.26.20
virtualenv==20.17.1
zipp==3.6.0

name: respy
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - blas=1.0=openblas
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgfortran-ng=7.5.0=ha8ba4b0_17
  - libgfortran4=7.5.0=ha8ba4b0_17
  - libgomp=11.2.0=h1234567_1
  - libopenblas=0.3.18=hf726d26_0
  - libstdcxx-ng=11.2.0=h1234567_1
  - llvmlite=0.28.0=py36hd408876_0
  - ncurses=6.4=h6a678d5_0
  - numba=0.43.1=py36h962f231_0
  - numpy=1.19.2=py36h6163131_0
  - numpy-base=1.19.2=py36h75fe3a5_0
  - openssl=1.1.1w=h7f8727e_0
  - pandas=0.24.2=py36he6710b0_0
  - patsy=0.5.1=py36_0
  - pip=19.0.3=py36_0
  - python=3.6.13=h12debd9_1
  - python-dateutil=2.8.2=pyhd3eb1b0_0
  - pytz=2021.3=pyhd3eb1b0_0
  - readline=8.2=h5eee18b_0
  - scipy=1.2.1=py36he2b7bc3_0
  - setuptools=58.0.4=py36h06a4308_0
  - six=1.16.0=pyhd3eb1b0_1
  - sqlite=3.45.3=h5eee18b_0
  - statsmodels=0.9.0=py36h035aef0_0
  - tbb=2022.0.0=hdb19cb5_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - attrs==22.2.0
      - babel==2.11.0
      - charset-normalizer==2.0.12
      - codecov==2.1.13
      - colorama==0.4.5
      - coverage==6.2
      - distlib==0.3.9
      - doc8==0.11.2
      - docutils==0.18.1
      - execnet==1.9.0
      - filelock==3.4.1
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==4.8.3
      - importlib-resources==5.4.0
      - iniconfig==1.1.1
      - jinja2==3.0.3
      - livereload==2.6.3
      - markupsafe==2.0.1
      - packaging==21.3
      - pbr==6.1.1
      - platformdirs==2.4.0
      - pluggy==1.0.0
      - py==1.11.0
      - pyaml==23.5.8
      - pygments==2.14.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - pytest-xdist==3.0.2
      - pyyaml==6.0.1
      - requests==2.27.1
      - restructuredtext-lint==1.4.0
      - ruamel-yaml==0.17.40
      - ruamel-yaml-clib==0.2.8
      - snowballstemmer==2.2.0
      - sphinx==5.3.0
      - sphinx-autobuild==2021.3.14
      - sphinx-rtd-theme==2.0.0
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jquery==4.1
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - stevedore==3.5.2
      - toml==0.10.2
      - tomli==1.2.3
      - tornado==6.1
      - tox==3.28.0
      - tox-conda==0.10.2
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - virtualenv==20.17.1
      - zipp==3.6.0
prefix: /opt/conda/envs/respy

respy/tests/test_unit.py::TestClass::test_2
respy/tests/test_integration.py::TestClass::test_9
respy/tests/test_unit.py::test_create_draws_and_prob_wages respy/tests/test_unit.py::TestClass::test_1 respy/tests/test_integration.py::TestClass::test_7 respy/tests/test_unit.py::test_state_space_restrictions_by_traversing_forward[15-5-edu_starts1-20] respy/tests/test_unit.py::test_state_space_restrictions_by_traversing_forward[15-1-edu_starts0-20] respy/tests/test_unit.py::TestClass::test_3 respy/tests/test_integration.py::TestClass::test_4 respy/tests/test_integration.py::TestClass::test_6 respy/tests/test_integration.py::TestClass::test_8 respy/tests/test_unit.py::TestClass::test_4 respy/tests/test_integration.py::TestClass::test_1 respy/tests/test_integration.py::TestClass::test_3 respy/tests/test_integration.py::TestClass::test_2 respy/tests/test_integration.py::TestClass::test_5 respy/tests/test_integration.py::TestClass::test_10
respy/pre_processing/model_processing.py respy/pre_processing/specification_helpers.py
respy/pre_processing/model_processing.py respy/pre_processing/specification_helpers.py