Repository: cogent3/cogent3 (python)

Issue:
load_tree doesn't cope with pathlib.Path
```python
import pathlib
from cogent3 import load_tree

path = pathlib.Path("~/repos/Cogent3/tests/data")
tree = load_tree(path / "murphy.tree")
```
generates
```
AttributeError                            Traceback (most recent call last)
/var/folders/rh/t24zj19s7qxd4_4p0n5rqjy00000gn/T/ipykernel_9969/1104250210.py in <module>
      3 
      4 path = pathlib.Path("~/repos/Cogent3/tests/data")
----> 5 tree = load_tree(path / "murphy.tree")

~/opt/miniconda3/envs/c39dev/lib/python3.9/site-packages/cogent3/__init__.py in load_tree(filename, format, underscore_unmunge)
    594     with open_(filename) as tfile:
    595         treestring = tfile.read()
--> 596         if format is None and filename.endswith(".xml"):
    597             format = "xml"
    598 

AttributeError: 'PosixPath' object has no attribute 'endswith'
```

Pull request description:
BUG: load_tree now handles pathlib.Path's as input, fixes #991
[CHANGED] format setting now overrides file name suffix in load_tree and
    Tree.write

[CHANGED] improved docstrings

Patch:
diff --git a/src/cogent3/__init__.py b/src/cogent3/__init__.py
index 1389924d3..f2551909d 100644
--- a/src/cogent3/__init__.py
+++ b/src/cogent3/__init__.py
@@ -573,7 +573,8 @@ def load_tree(filename, format=None, underscore_unmunge=False):
     filename : str
         a file path containing a newick or xml formatted tree.
     format : str
-        either newick, xml or cogent3 json, default is newick
+        either xml or json, all other values default to newick. Overrides
+        file name suffix.
     underscore_unmunge : bool
         replace underscores with spaces in all names read, i.e. "sp_name"
         becomes "sp name".
@@ -581,19 +582,19 @@ def load_tree(filename, format=None, underscore_unmunge=False):
     Notes
     -----
     Underscore unmunging is turned off by default, although it is part
-    of the Newick format.
+    of the Newick format. Only the cogent3 json and xml tree formats are
+    supported.
 
     Returns
     -------
     PhyloNode
     """
     file_format, _ = get_format_suffixes(filename)
-    if file_format == "json":
+    format = format or file_format
+    if format == "json":
         return load_from_json(filename, (TreeNode, PhyloNode))
 
     with open_(filename) as tfile:
         treestring = tfile.read()
-        if format is None and filename.endswith(".xml"):
-            format = "xml"
 
     return make_tree(treestring, format=format, underscore_unmunge=underscore_unmunge)
diff --git a/src/cogent3/core/tree.py b/src/cogent3/core/tree.py
index 8e629439c..33418b5d7 100644
--- a/src/cogent3/core/tree.py
+++ b/src/cogent3/core/tree.py
@@ -1447,10 +1447,16 @@ class TreeNode(object):
         format
             default is newick, xml and json are alternate. Argument overrides
             the filename suffix. All attributes are saved in the xml format.
+            Value overrides the file name suffix.
+
+        Notes
+        -----
+        Only the cogent3 json and xml tree formats are supported.
 
         """
         file_format, _ = get_format_suffixes(filename)
-        if file_format == "json":
+        format = format or file_format
+        if format == "json":
             with atomic_write(filename, mode="wt") as f:
                 f.write(self.to_json())
             return


Test patch:
diff --git a/tests/test_core/test_tree.py b/tests/test_core/test_tree.py
index e8d599d33..daffbd51a 100644
--- a/tests/test_core/test_tree.py
+++ b/tests/test_core/test_tree.py
@@ -3,6 +3,7 @@
 """
 import json
 import os
+import pathlib
 
 from copy import copy, deepcopy
 from tempfile import TemporaryDirectory
@@ -2199,6 +2200,28 @@ class TestTree(TestCase):
                 self.tree.get_newick(with_node_names=True),
             )
             self.assertEqual(got.get_node_names(), self.tree.get_node_names())
+            # now try using non json suffix
+            json_path = os.path.join(dirname, "tree.txt")
+            self.tree.write(json_path, format="json")
+            got = load_tree(json_path, format="json")
+            self.assertIsInstance(got, PhyloNode)
+
+    def test_load_tree(self):
+        """tests loading a newick formatted Tree"""
+        with TemporaryDirectory(dir=".") as dirname:
+            tree_path = os.path.join(dirname, "tree.tree")
+            self.tree.write(tree_path)
+            got = load_tree(tree_path)
+            self.assertIsInstance(got, PhyloNode)
+            self.assertEqual(
+                got.get_newick(),
+                self.tree.get_newick(),
+            )
+            self.assertEqual(got.get_node_names(), self.tree.get_node_names())
+            # now try specifying path as pathlib.Path
+            tree_path = pathlib.Path(tree_path)
+            got = load_tree(tree_path)
+            self.assertIsInstance(got, PhyloNode)
 
     def test_ascii(self):
         self.tree.ascii_art()