Repository: glotzerlab/signac
Pull request #820 (2022-09-21 16:21:15)

Fix 1.8 deprecations
## Description
Fix deprecation warnings for the 1.8.0 release.

## Motivation and Context
Needed for #819.

## Checklist:
- [x] I am familiar with the [Contributing Guidelines](https://github.com/glotzerlab/signac/blob/master/CONTRIBUTING.md).
- [x] I agree with the terms of the [Contributor Agreement](https://github.com/glotzerlab/signac/blob/master/ContributorAgreement.md).
- [x] My name is on the [list of contributors](https://github.com/glotzerlab/signac/blob/master/contributors.yaml).
- [x] The changes introduced by this pull request are covered by existing or newly introduced tests.
- [x] The [package documentation](https://github.com/glotzerlab/signac/tree/master/doc) and [framework documentation](https://docs.signac.io/) in [signac-docs](https://github.com/glotzerlab/signac-docs) are up to date with these changes.
- [x] I have updated the [changelog](https://github.com/glotzerlab/signac/blob/master/changelog.txt) and added any related issue and pull request numbers for future reference.



Patch:
diff --git a/benchmark.py b/benchmark.py
index 682337f8..a6751ac6 100755
--- a/benchmark.py
+++ b/benchmark.py
@@ -196,7 +196,7 @@ def noop(*args, **kwargs):
 
 
 def benchmark_project(project, keys=None):
-    root = project.root_directory()
+    root = project.path
     setup = f"import signac; project = signac.get_project(root='{root}'); "
     setup += "from itertools import islice, repeat; import random; "
     setup += "from benchmark import noop; "
diff --git a/changelog.txt b/changelog.txt
index 6736b372..483ee1d7 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -26,6 +26,8 @@ Changed
  - Project names have a default in anticipation of removing names entirely. Project names will be removed in signac 2.0 (#644).
  - ``Project.workspace`` is now a property, not a method (#685).
  - Continuous integration uses GitHub Actions instead of CircleCI (#776, #788).
+ - Raise errors in testing when ``DeprecatedWarning``s or ``FutureWarning``s are raised (#713).
+ - Change GitHub PR to check for uncompleted tasks (i.e. unchecked checkboxes) (#686).
 
 Deprecated
 ++++++++++
diff --git a/setup.cfg b/setup.cfg
index a615fba9..26170b3c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -49,6 +49,8 @@ filterwarnings =
 	ignore:The index argument is deprecated:FutureWarning
 	ignore:copytree is deprecated:FutureWarning
 	ignore:dumps is deprecated:FutureWarning
+	ignore:Version is deprecated:FutureWarning
+	ignore:parse_version is deprecated:FutureWarning
 
 [bumpversion:file:setup.py]
 
diff --git a/signac/contrib/indexing.py b/signac/contrib/indexing.py
index b6f787d8..3c35ec6c 100644
--- a/signac/contrib/indexing.py
+++ b/signac/contrib/indexing.py
@@ -8,6 +8,7 @@ import logging
 import math
 import os
 import re
+import types
 import warnings
 from collections import defaultdict
 from time import sleep
@@ -45,8 +46,8 @@ def md5(file):
     return m.hexdigest()
 
 
-def _is_blank_module(module):
-    with open(module.__file__) as file:
+def _is_blank_module(name):
+    with open(name) as file:
         return not bool(file.read().strip())
 
 
@@ -101,6 +102,12 @@ class BaseCrawler:
         m.update(blob.encode("utf-8"))
         return m.hexdigest()
 
+    @deprecated(
+        deprecated_in="1.3",
+        removed_in="2.0",
+        current_version=__version__,
+        details="The indexing module is deprecated.",
+    )
     def crawl(self, depth=0):
         """Crawl through the `root` directory.
 
@@ -548,9 +555,11 @@ class MainCrawler(BaseCrawler):
 
     def _docs_from_module(self, dirpath, fn):
         name = os.path.join(dirpath, fn)
-        module = importlib.machinery.SourceFileLoader(name, name).load_module()
+        loader = importlib.machinery.SourceFileLoader("access_module", name)
+        module = types.ModuleType(loader.name)
+        loader.exec_module(module)
 
-        logger.info(f"Crawling from module '{module.__file__}'.")
+        logger.info(f"Crawling from module '{name}'.")
 
         has_tags = self.tags is not None and len(set(self.tags))
 
@@ -571,7 +580,7 @@ class MainCrawler(BaseCrawler):
                     logger.info("Skipping, tag mismatch.")
                     return False
 
-        if not has_tags and _is_blank_module(module):
+        if not has_tags and _is_blank_module(name):
             from .project import get_project
 
             for doc in get_project(root=dirpath).index():
diff --git a/signac/contrib/job.py b/signac/contrib/job.py
index 6d815a73..93136724 100644
--- a/signac/contrib/job.py
+++ b/signac/contrib/job.py
@@ -391,6 +391,29 @@ class Job:
         `Modifying the State Point
         <https://docs.signac.io/en/latest/jobs.html#modifying-the-state-point>`_.
 
+        .. danger::
+
+            Use this function with caution! Resetting a job's state point
+            may sometimes be necessary, but can possibly lead to incoherent
+            data spaces.
+
+        Parameters
+        ----------
+        new_statepoint : dict
+            The job's new state point.
+
+        """
+        self._reset_statepoint(new_statepoint)
+
+    def _reset_statepoint(self, new_statepoint):
+        """Overwrite the state point of this job while preserving job data.
+
+        This method will change the job id if the state point has been altered.
+
+        For more information, see
+        `Modifying the State Point
+        <https://docs.signac.io/en/latest/jobs.html#modifying-the-state-point>`_.
+
         .. danger::
 
             Use this function with caution! Resetting a job's state point
@@ -462,7 +485,7 @@ class Job:
                         "mapping with another value."
                     )
         statepoint.update(update)
-        self.reset_statepoint(statepoint)
+        self._reset_statepoint(statepoint)
 
     @property
     def statepoint(self):
@@ -521,7 +544,7 @@ class Job:
         new_statepoint : dict
             The new state point to be assigned.
         """
-        self.reset_statepoint(new_statepoint)
+        self._reset_statepoint(new_statepoint)
 
     @property
     def sp(self):
diff --git a/signac/contrib/project.py b/signac/contrib/project.py
index 239eddb3..fb2039b9 100644
--- a/signac/contrib/project.py
+++ b/signac/contrib/project.py
@@ -63,11 +63,11 @@ ACCESS_MODULE_MAIN = """#!/usr/bin/env python
 import signac
 
 def get_indexes(root):
-    yield signac.get_project(root).index()
+    yield signac.get_project(root)._index()
 
 if __name__ == '__main__':
     with signac.Collection.open('index.txt') as index:
-        signac.export(signac.index(), index, update=True)
+        signac.export(signac._index(), index, update=True)
 """
 
 # The warning used for doc filter deprecation everywhere. Don't use
@@ -327,7 +327,7 @@ class Project:
 
     def __repr__(self):
         return "{type}.get_project({root})".format(
-            type=self.__class__.__name__, root=repr(self.root_directory())
+            type=self.__class__.__name__, root=repr(self.path)
         )
 
     def _repr_html_(self):
@@ -342,7 +342,7 @@ class Project:
         return (
             "<p>"
             + f"<strong>Project:</strong> {self.id}<br>"
-            + f"<strong>Root:</strong> {self.root_directory()}<br>"
+            + f"<strong>Root:</strong> {self.path}<br>"
             + f"<strong>Workspace:</strong> {self.workspace}<br>"
             + f"<strong>Size:</strong> {len(self)}"
             + "</p>"
@@ -389,9 +389,7 @@ class Project:
         """
         if self._wd is None:
             wd = os.path.expandvars(self.config.get("workspace_dir", "workspace"))
-            self._wd = (
-                wd if os.path.isabs(wd) else os.path.join(self.root_directory(), wd)
-            )
+            self._wd = wd if os.path.isabs(wd) else os.path.join(self.path, wd)
         return _CallableString(self._wd)
 
     @deprecated(
@@ -498,7 +496,7 @@ class Project:
             The joined path of project root directory and filename.
 
         """
-        return os.path.join(self.root_directory(), filename)
+        return os.path.join(self.path, filename)
 
     def isfile(self, filename):
         """Check if a filename exists in the project's root directory.
@@ -528,7 +526,7 @@ class Project:
         """
         with self._lock:
             if self._document is None:
-                fn_doc = os.path.join(self.root_directory(), self.FN_DOCUMENT)
+                fn_doc = os.path.join(self.path, self.FN_DOCUMENT)
                 self._document = BufferedJSONAttrDict(
                     filename=fn_doc, write_concern=True
                 )
@@ -608,7 +606,7 @@ class Project:
         """
         with self._lock:
             if self._stores is None:
-                self._stores = H5StoreManager(self.root_directory())
+                self._stores = H5StoreManager(self.path)
         return self._stores
 
     @property
@@ -910,7 +908,7 @@ class Project:
         from .schema import _build_job_statepoint_index
 
         if index is None:
-            index = self.index(include_job_document=False)
+            index = self._index(include_job_document=False)
         else:
             warnings.warn(INDEX_DEPRECATION_WARNING, FutureWarning)
         if subset is not None:
@@ -1028,9 +1026,9 @@ class Project:
             if doc_filter:
                 warnings.warn(DOC_FILTER_WARNING, FutureWarning)
                 filter.update(parse_filter(_add_prefix("doc.", doc_filter)))
-                index = self.index(include_job_document=True)
+                index = self._index(include_job_document=True)
             elif "doc" in _root_keys(filter):
-                index = self.index(include_job_document=True)
+                index = self._index(include_job_document=True)
             else:
                 index = self._sp_index()
         else:
@@ -1306,7 +1304,9 @@ class Project:
         if fn is None:
             fn = self.fn(self.FN_STATEPOINTS)
         try:
-            tmp = self.read_statepoints(fn=fn)
+            with warnings.catch_warnings():
+                warnings.simplefilter("ignore")
+                tmp = self.read_statepoints(fn=fn)
         except OSError as error:
             if error.errno != errno.ENOENT:
                 raise
@@ -1560,7 +1560,7 @@ class Project:
             If the move failed due to an unknown system related error.
 
         """
-        job.reset_statepoint(new_statepoint=new_statepoint)
+        job._reset_statepoint(new_statepoint=new_statepoint)
 
     @deprecated(
         deprecated_in="1.3",
@@ -1917,7 +1917,9 @@ class Project:
             # In signac 2.0, Project.read_statepoints will be removed.
             # Remove this code path (only use "self._read_cache()" above) and
             # update the method signature and docs to remove "fn_statepoints."
-            self._sp_cache.update(self.read_statepoints(fn=fn_statepoints))
+            with warnings.catch_warnings():
+                warnings.simplefilter("ignore")
+                self._sp_cache.update(self.read_statepoints(fn=fn_statepoints))
         except OSError as error:
             if error.errno != errno.ENOENT or fn_statepoints is not None:
                 raise
@@ -2157,6 +2159,47 @@ class Project:
         dict
             Index document.
 
+        """
+        yield from self._index(formats, depth, skip_errors, include_job_document)
+
+    def _index(
+        self, formats=None, depth=0, skip_errors=False, include_job_document=True
+    ):
+        r"""Generate an index of the project's workspace.
+
+        This generator function indexes every file in the project's
+        workspace until the specified `depth`.
+        The job document if it exists, is always indexed, other
+        files need to be specified with the formats argument.
+
+        See :ref:`signac project -i <signac-cli-project>` for the command line equivalent.
+
+        .. code-block:: python
+
+            for doc in project._index({r'.*\.txt', 'TextFile'}):
+                print(doc)
+
+        Parameters
+        ----------
+        formats : str, dict
+            The format definitions as a pattern string (e.g. ``r'.*\.txt'``)
+            or a mapping from pattern strings to formats (e.g.
+            ``'TextFile'``). If None, only the job document is indexed
+            (Default value = None).
+        depth : int
+            Specifies the crawling depth. A value of 0 means no limit
+            (Default value = 0).
+        skip_errors : bool
+            Skip all errors which occur during indexing. This is useful when
+            trying to repair a broken workspace (Default value = False).
+        include_job_document : bool
+            Include the contents of job documents (Default value = True).
+
+        Yields
+        ------
+        dict
+            Index document.
+
         """
         if formats is None:
             root = self.workspace
@@ -2190,7 +2233,7 @@ class Project:
 
             for pattern, fmt in formats.items():
                 Crawler.define(pattern, fmt)
-            crawler = Crawler(self.root_directory())
+            crawler = Crawler(self.path)
             docs = crawler.crawl(depth=depth)
         if skip_errors:
             docs = _skip_errors(docs, logger.critical)
@@ -2233,7 +2276,7 @@ class Project:
             main = master
 
         if filename is None:
-            filename = os.path.join(self.root_directory(), MainCrawler.FN_ACCESS_MODULE)
+            filename = os.path.join(self.path, MainCrawler.FN_ACCESS_MODULE)
         with open(filename, "x") as file:
             if main:
                 file.write(ACCESS_MODULE_MAIN)
diff --git a/signac/core/utility.py b/signac/core/utility.py
index 35bfb44a..5c2e0c5d 100644
--- a/signac/core/utility.py
+++ b/signac/core/utility.py
@@ -6,6 +6,7 @@
 import os.path
 import re
 import subprocess
+import warnings
 
 from ..common.deprecation import deprecated
 from ..version import __version__
@@ -112,13 +113,15 @@ def parse_version(version_str):
     )
     r = p.match(version_str)
     v = r.groupdict()
-    version = Version(
-        **{
-            "major": int(v.get("major") or 0),
-            "minor": int(v.get("minor") or 0),
-            "change": int(v.get("change") or 0),
-            "postrelease": str(v.get("postrelease") or ""),
-            "prerelease": str(v.get("prerelease") or "final"),
-        }
-    )
+    with warnings.catch_warnings():
+        warnings.simplefilter("ignore")
+        version = Version(
+            **{
+                "major": int(v.get("major") or 0),
+                "minor": int(v.get("minor") or 0),
+                "change": int(v.get("change") or 0),
+                "postrelease": str(v.get("postrelease") or ""),
+                "prerelease": str(v.get("prerelease") or "final"),
+            }
+        )
     return version
diff --git a/signac/sync.py b/signac/sync.py
index 0c6e0d7d..27ff674f 100644
--- a/signac/sync.py
+++ b/signac/sync.py
@@ -537,7 +537,7 @@ def sync_projects(
         )
     else:
         logger.info(f"Synchronizing project '{source}' to '{destination}'.")
-    logger.more(f"'{source.root_directory()}' -> '{destination.root_directory()}'")
+    logger.more(f"'{source.path}' -> '{destination.path}'")
     if dry_run:
         logger.info("Performing dry run!")
     if exclude is not None:


Test patch:
diff --git a/tests/test_indexing.py b/tests/test_indexing.py
index 3a993ed3..0088f7cc 100644
--- a/tests/test_indexing.py
+++ b/tests/test_indexing.py
@@ -286,7 +286,8 @@ class TestIndexingBase:
 
     def test_main_crawler(self):
         self.setup_project()
-        crawler = indexing.MainCrawler(root=self._tmp_dir.name)
+        with pytest.warns(FutureWarning):
+            crawler = indexing.MainCrawler(root=self._tmp_dir.name)
         crawler.tags = {"test1"}
         no_find = True
         with pytest.warns(FutureWarning):
@@ -327,7 +328,8 @@ class TestIndexingBase:
             with pytest.raises(errors.FetchError):
                 signac.fetch(dict())
         self.setup_project()
-        crawler = indexing.MainCrawler(root=self._tmp_dir.name)
+        with pytest.warns(FutureWarning):
+            crawler = indexing.MainCrawler(root=self._tmp_dir.name)
         crawler.tags = {"test1"}
         with pytest.warns(FutureWarning):
             docs = list(crawler.crawl())
@@ -344,7 +346,8 @@ class TestIndexingBase:
 
     def test_export_one(self):
         self.setup_project()
-        crawler = indexing.MainCrawler(root=self._tmp_dir.name)
+        with pytest.warns(FutureWarning):
+            crawler = indexing.MainCrawler(root=self._tmp_dir.name)
         crawler.tags = {"test1"}
         index = self.get_index_collection()
         with pytest.warns(FutureWarning):
@@ -356,7 +359,8 @@ class TestIndexingBase:
 
     def test_export(self):
         self.setup_project()
-        crawler = indexing.MainCrawler(root=self._tmp_dir.name)
+        with pytest.warns(FutureWarning):
+            crawler = indexing.MainCrawler(root=self._tmp_dir.name)
         crawler.tags = {"test1"}
         index = self.get_index_collection()
         with pytest.warns(FutureWarning):
diff --git a/tests/test_job.py b/tests/test_job.py
index d57261cc..00bff317 100644
--- a/tests/test_job.py
+++ b/tests/test_job.py
@@ -600,7 +600,7 @@ class TestJobOpenAndClosing(TestJobBase):
         assert cwd == rp(os.getcwd())
         with job:
             assert rp(job.path) == rp(os.getcwd())
-            os.chdir(self.project.root_directory())
+            os.chdir(self.project.path)
         assert cwd == rp(os.getcwd())
         with job:
             assert rp(job.path) == rp(os.getcwd())
@@ -610,10 +610,10 @@ class TestJobOpenAndClosing(TestJobBase):
         assert cwd == rp(os.getcwd())
         with job:
             assert rp(job.path) == rp(os.getcwd())
-            os.chdir(self.project.root_directory())
+            os.chdir(self.project.path)
             with job:
                 assert rp(job.path) == rp(os.getcwd())
-            assert rp(os.getcwd()) == rp(self.project.root_directory())
+            assert rp(os.getcwd()) == rp(self.project.path)
         assert cwd == rp(os.getcwd())
         with job:
             job.close()
@@ -1001,7 +1001,7 @@ class TestJobDocument(TestJobBase):
         src_job.data[key] = d
         assert key in src_job.data
         assert len(src_job.data) == 1
-        src_job.reset_statepoint(dst)
+        src_job.sp = dst
         src_job = self.open_job(src)
         dst_job = self.open_job(dst)
         assert key in dst_job.document
@@ -1011,9 +1011,9 @@ class TestJobDocument(TestJobBase):
         assert len(dst_job.data) == 1
         assert key not in src_job.data
         with pytest.raises(RuntimeError):
-            src_job.reset_statepoint(dst)
+            src_job.sp = dst
         with pytest.raises(DestinationExistsError):
-            src_job.reset_statepoint(dst)
+            src_job.sp = dst
 
     @pytest.mark.skipif(not H5PY, reason="test requires the h5py package")
     def test_reset_statepoint_job_lazy_access(self):
@@ -1035,7 +1035,7 @@ class TestJobDocument(TestJobBase):
         # Check that the state point will be instantiated lazily during the
         # call to reset_statepoint
         assert src_job_by_id._statepoint_requires_init
-        src_job_by_id.reset_statepoint(dst)
+        src_job_by_id.sp = dst
         src_job = self.open_job(src)
         dst_job = self.open_job(dst)
         assert key in dst_job.document
@@ -1045,10 +1045,11 @@ class TestJobDocument(TestJobBase):
         assert len(dst_job.data) == 1
         assert key not in src_job.data
         with pytest.raises(RuntimeError):
-            src_job.reset_statepoint(dst)
+            src_job.sp = dst
         with pytest.raises(DestinationExistsError):
-            src_job.reset_statepoint(dst)
+            src_job.sp = dst
 
+    @pytest.mark.filterwarnings("ignore:reset_statepoint")
     @pytest.mark.skipif(not H5PY, reason="test requires the h5py package")
     def test_reset_statepoint_project(self):
         key = "move_job"
@@ -1510,7 +1511,7 @@ class TestJobOpenData(TestJobBase):
             src_job.data[key] = d
             assert key in src_job.data
             assert len(src_job.data) == 1
-        src_job.reset_statepoint(dst)
+        src_job.sp = dst
         src_job = self.open_job(src)
         dst_job = self.open_job(dst)
         with self.open_data(dst_job):
@@ -1519,9 +1520,9 @@ class TestJobOpenData(TestJobBase):
         with self.open_data(src_job):
             assert key not in src_job.data
         with pytest.raises(RuntimeError):
-            src_job.reset_statepoint(dst)
+            src_job.sp = dst
         with pytest.raises(DestinationExistsError):
-            src_job.reset_statepoint(dst)
+            src_job.sp = dst
 
     def test_reset_statepoint_project(self):
         key = "move_job"
diff --git a/tests/test_project.py b/tests/test_project.py
index fdce665b..4c7852f2 100644
--- a/tests/test_project.py
+++ b/tests/test_project.py
@@ -105,8 +105,8 @@ class TestProject(TestProjectBase):
     def test_str(self):
         str(self.project) == self.project.id
 
-    def test_root_directory(self):
-        assert self._tmp_pr == self.project.root_directory()
+    def test_path(self):
+        assert self._tmp_pr == self.project.path
 
     def test_workspace_directory(self):
         assert self._tmp_wd == self.project.workspace
@@ -133,7 +133,7 @@ class TestProject(TestProjectBase):
 
     def test_fn(self):
         assert self.project.fn("test/abc") == os.path.join(
-            self.project.root_directory(), "test/abc"
+            self.project.path, "test/abc"
         )
 
     def test_isfile(self):
@@ -148,7 +148,7 @@ class TestProject(TestProjectBase):
         self.project.document["a"] = 42
         assert len(self.project.document) == 1
         assert self.project.document
-        prj2 = type(self.project).get_project(root=self.project.root_directory())
+        prj2 = type(self.project).get_project(root=self.project.path)
         assert prj2.document
         assert len(prj2.document) == 1
         self.project.document.clear()
@@ -169,7 +169,7 @@ class TestProject(TestProjectBase):
         self.project.doc["a"] = 42
         assert len(self.project.doc) == 1
         assert self.project.doc
-        prj2 = type(self.project).get_project(root=self.project.root_directory())
+        prj2 = type(self.project).get_project(root=self.project.path)
         assert prj2.doc
         assert len(prj2.doc) == 1
         self.project.doc.clear()
@@ -193,7 +193,7 @@ class TestProject(TestProjectBase):
             self.project.data["a"] = 42
             assert len(self.project.data) == 1
             assert self.project.data
-        prj2 = type(self.project).get_project(root=self.project.root_directory())
+        prj2 = type(self.project).get_project(root=self.project.path)
         with prj2.data:
             assert prj2.data
             assert len(prj2.data) == 1
@@ -216,6 +216,9 @@ class TestProject(TestProjectBase):
         self.project.data = {"a": {"b": 45}}
         assert self.project.data == {"a": {"b": 45}}
 
+    @pytest.mark.filterwarnings("ignore:dump_statepoint")
+    @pytest.mark.filterwarnings("ignore:write_statepoint")
+    @pytest.mark.filterwarnings("ignore:read_statepoint")
     def test_write_read_statepoint(self):
         statepoints = [{"a": i} for i in range(5)]
         self.project.dump_statepoints(statepoints)
@@ -247,7 +250,7 @@ class TestProject(TestProjectBase):
         rel_path = norm_path(os.path.join("path", "to", "workspace"))
         self.project.config["workspace_dir"] = rel_path
         assert self.project.workspace == norm_path(
-            os.path.join(self.project.root_directory(), self.project.workspace)
+            os.path.join(self.project.path, self.project.workspace)
         )
 
     def test_no_workspace_warn_on_find(self, caplog):
@@ -313,7 +316,7 @@ class TestProject(TestProjectBase):
             assert 0 == len(list(self.project.find_job_ids({"sp.a": 5, "doc.b": 5})))
             for job_id in self.project.find_job_ids():
                 assert self.project.open_job(id=job_id).get_id() == job_id
-            index = list(self.project.index())
+            index = list(self.project._index())
             for job_id in self.project.find_job_ids(index=index):
                 assert self.project.open_job(id=job_id).get_id() == job_id
 
@@ -464,6 +467,7 @@ class TestProject(TestProjectBase):
         assert_result_len({"$and": [{"sp.a": 0}, {"doc.d": 1}]}, 0)
         assert_result_len({"$or": [{"sp.a": 0}, {"doc.d": 1}]}, 2)
 
+    @pytest.mark.filterwarnings("ignore:num_jobs")
     def test_num_jobs(self):
         statepoints = [{"a": i} for i in range(5)]
         for sp in statepoints:
@@ -606,6 +610,7 @@ class TestProject(TestProjectBase):
         finally:
             logging.disable(logging.NOTSET)
 
+    @pytest.mark.filterwarnings("ignore:write_statepoint")
     def test_repair_corrupted_workspace(self):
         statepoints = [{"a": i} for i in range(5)]
         for sp in statepoints:
@@ -654,6 +659,7 @@ class TestProject(TestProjectBase):
         finally:
             logging.disable(logging.NOTSET)
 
+    @pytest.mark.filterwarnings("ignore:index")
     def test_index(self):
         docs = list(self.project.index(include_job_document=True))
         assert len(docs) == 0
@@ -681,6 +687,7 @@ class TestProject(TestProjectBase):
 
     # Index schema is changed
     @pytest.mark.xfail()
+    @pytest.mark.filterwarnings("ignore:index")
     def test_signac_project_crawler(self):
         statepoints = [{"a": i} for i in range(5)]
         for sp in statepoints:
@@ -691,7 +698,7 @@ class TestProject(TestProjectBase):
             index[doc["_id"]] = doc
         assert len(index) == len(job_ids)
         assert set(index.keys()) == set(job_ids)
-        crawler = signac.contrib.SignacProjectCrawler(self.project.root_directory())
+        crawler = signac.contrib.SignacProjectCrawler(self.project.path)
         index2 = {}
         for doc in crawler.crawl():
             index2[doc["_id"]] = doc
@@ -722,7 +729,7 @@ class TestProject(TestProjectBase):
             with pytest.warns(FutureWarning):
                 Crawler.define(p, fmt)
         index2 = {}
-        for doc in Crawler(root=self.project.root_directory()).crawl():
+        for doc in Crawler(root=self.project.path).crawl():
             index2[doc["_id"]] = doc
         assert index == index2
         assert Crawler.called
@@ -731,7 +738,7 @@ class TestProject(TestProjectBase):
         class CustomProject(signac.Project):
             pass
 
-        project = CustomProject.get_project(root=self.project.root_directory())
+        project = CustomProject.get_project(root=self.project.path)
         assert isinstance(project, signac.Project)
         assert isinstance(project, CustomProject)
 
@@ -743,7 +750,7 @@ class TestProject(TestProjectBase):
         class CustomProject(signac.Project):
             Job = CustomJob
 
-        project = CustomProject.get_project(root=self.project.root_directory())
+        project = CustomProject.get_project(root=self.project.path)
         assert isinstance(project, signac.Project)
         assert isinstance(project, CustomProject)
         job = project.open_job(dict(a=0))
@@ -878,6 +885,7 @@ class TestProject(TestProjectBase):
         )
         assert s == s_sub
 
+    @pytest.mark.filterwarnings("ignore:__call__")
     def test_schema_eval(self):
         for i in range(10):
             for j in range(10):
@@ -1100,7 +1108,7 @@ class TestProject(TestProjectBase):
     def test_temp_project(self):
         with self.project.temporary_project() as tmp_project:
             assert len(tmp_project) == 0
-            tmp_root_dir = tmp_project.root_directory()
+            tmp_root_dir = tmp_project.path
             assert os.path.isdir(tmp_root_dir)
             for i in range(10):  # init some jobs
                 tmp_project.open_job(dict(a=i)).init()
@@ -1753,10 +1761,10 @@ class TestLinkedViewProject(TestProjectBase):
             )
         )
         src = set(map(lambda j: os.path.realpath(j.path), self.project.find_jobs()))
-        assert len(all_links) == self.project.num_jobs()
+        assert len(all_links) == len(self.project)
         self.project.create_linked_view(prefix=view_prefix)
         all_links = list(_find_all_links(view_prefix))
-        assert len(all_links) == self.project.num_jobs()
+        assert len(all_links) == len(self.project)
         dst = set(
             map(
                 lambda l: os.path.realpath(os.path.join(view_prefix, l, "job")),
@@ -1789,7 +1797,7 @@ class TestLinkedViewProject(TestProjectBase):
         # some jobs removed
         clean({"b": 0})
         all_links = list(_find_all_links(view_prefix))
-        assert len(all_links) == self.project.num_jobs()
+        assert len(all_links) == len(self.project)
         dst = set(
             map(
                 lambda l: os.path.realpath(os.path.join(view_prefix, l, "job")),
@@ -1801,7 +1809,7 @@ class TestLinkedViewProject(TestProjectBase):
         # all jobs removed
         clean()
         all_links = list(_find_all_links(view_prefix))
-        assert len(all_links) == self.project.num_jobs()
+        assert len(all_links) == len(self.project)
         dst = set(
             map(
                 lambda l: os.path.realpath(os.path.join(view_prefix, l, "job")),
@@ -2290,19 +2298,19 @@ class TestProjectInit:
         project = signac.init_project(name="testproject", root=root)
         assert project.id == "testproject"
         assert project.workspace == os.path.join(root, "workspace")
-        assert project.root_directory() == root
+        assert project.path == root
         project = signac.Project.init_project(name="testproject", root=root)
         assert project.id == "testproject"
         assert project.workspace == os.path.join(root, "workspace")
-        assert project.root_directory() == root
+        assert project.path == root
         project = signac.get_project(root=root)
         assert project.id == "testproject"
         assert project.workspace == os.path.join(root, "workspace")
-        assert project.root_directory() == root
+        assert project.path == root
         project = signac.Project.get_project(root=root)
         assert project.id == "testproject"
         assert project.workspace == os.path.join(root, "workspace")
-        assert project.root_directory() == root
+        assert project.path == root
 
     def test_get_project_all_printable_characters(self):
         root = self._tmp_dir.name
@@ -2350,17 +2358,17 @@ class TestProjectInit:
         project = signac.init_project(name="testproject", root=root)
         assert project.id == "testproject"
         assert project.workspace == os.path.join(root, "workspace")
-        assert project.root_directory() == root
+        assert project.path == root
         # Second initialization should not make any difference.
         project = signac.init_project(name="testproject", root=root)
         project = signac.get_project(root=root)
         assert project.id == "testproject"
         assert project.workspace == os.path.join(root, "workspace")
-        assert project.root_directory() == root
+        assert project.path == root
         project = signac.Project.get_project(root=root)
         assert project.id == "testproject"
         assert project.workspace == os.path.join(root, "workspace")
-        assert project.root_directory() == root
+        assert project.path == root
         # Deviating initialization parameters should result in errors.
         with pytest.raises(RuntimeError):
             signac.init_project(name="testproject2", root=root)
@@ -2374,7 +2382,7 @@ class TestProjectInit:
             if root is None:
                 root = os.getcwd()
             assert os.path.realpath(
-                signac.get_project(root=root).root_directory()
+                signac.get_project(root=root).path
             ) == os.path.realpath(root)
 
         root = self._tmp_dir.name
@@ -2504,13 +2512,11 @@ class TestProjectSchema(TestProjectBase):
         config["schema_version"] = impossibly_high_schema_version
         config.write()
         with pytest.raises(IncompatibleSchemaVersion):
-            signac.init_project(
-                name=str(self.project), root=self.project.root_directory()
-            )
+            signac.init_project(name=str(self.project), root=self.project.path)
 
         # Ensure that migration fails on an unsupported version.
         with pytest.raises(RuntimeError):
-            apply_migrations(self.project.root_directory())
+            apply_migrations(self.project.path)
 
         # Ensure that migration fails on an invalid version.
         invalid_schema_version = "0.5"
@@ -2518,7 +2524,7 @@ class TestProjectSchema(TestProjectBase):
         config["schema_version"] = invalid_schema_version
         config.write()
         with pytest.raises(RuntimeError):
-            apply_migrations(self.project.root_directory())
+            apply_migrations(self.project.path)
 
     @pytest.mark.parametrize("implicit_version", [True, False])
     def test_project_schema_version_migration(self, implicit_version):
@@ -2534,10 +2540,10 @@ class TestProjectSchema(TestProjectBase):
         config.write()
         err = io.StringIO()
         with redirect_stderr(err):
-            apply_migrations(self.project.root_directory())
+            apply_migrations(self.project.path)
         config = _get_config(self.project.fn("signac.rc"))
         assert config["schema_version"] == "1"
-        project = signac.get_project(root=self.project.root_directory())
+        project = signac.get_project(root=self.project.path)
         assert project.config["schema_version"] == "1"
         assert "OK" in err.getvalue()
         assert "0 to 1" in err.getvalue()
@@ -2552,7 +2558,7 @@ class TestProjectSchema(TestProjectBase):
         # 2. Either update or remove this unit test.
         from signac.contrib.migration import _collect_migrations
 
-        migrations = list(_collect_migrations(self.project.root_directory()))
+        migrations = list(_collect_migrations(self.project.path))
         assert len(migrations) == 0
 
 
diff --git a/tests/test_temporary_project.py b/tests/test_temporary_project.py
index abab37b9..b60587d7 100644
--- a/tests/test_temporary_project.py
+++ b/tests/test_temporary_project.py
@@ -9,17 +9,17 @@ import signac
 class TestTemporaryProject:
     def test_init_context_manager_constructor(self):
         with signac.TemporaryProject() as tmp_project:
-            assert os.path.isdir(tmp_project.root_directory())
+            assert os.path.isdir(tmp_project.path)
             for i in range(10):
                 tmp_project.open_job(dict(a=i)).init()
             assert len(tmp_project) == 10
-        assert not os.path.isdir(tmp_project.root_directory())
+        assert not os.path.isdir(tmp_project.path)
 
     def test_init_project_method(self):
         with signac.TemporaryProject() as project:
             with project.temporary_project() as tmp_project:
-                assert os.path.isdir(tmp_project.root_directory())
+                assert os.path.isdir(tmp_project.path)
                 for i in range(10):
                     tmp_project.open_job(dict(a=i)).init()
                 assert len(tmp_project) == 10
-            assert not os.path.isdir(tmp_project.root_directory())
+            assert not os.path.isdir(tmp_project.path)