Repository: materialsproject/jobflow (language: python)

Issue:
Match `atomate2` `ruff` config
I don't think we should ignore `PT011: pytest-raises-too-broad` long-term. Better to add `match="expected error message"` to all `pytest.raises()`. But there were 45 violations so I didn't fix all of them.


Discussion / hints:
## [Codecov](https://app.codecov.io/gh/materialsproject/jobflow/pull/464?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=materialsproject) Report
> Merging [#464](https://app.codecov.io/gh/materialsproject/jobflow/pull/464?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=materialsproject) (a34e772) into [main](https://app.codecov.io/gh/materialsproject/jobflow/commit/abca261f8b39eb13b5339156c7a26e4aa7a13ded?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=materialsproject) (abca261) will **decrease** coverage by `0.01%`.
> The diff coverage is `100.00%`.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #464      +/-   ##
==========================================
- Coverage   99.86%   99.86%   -0.01%     
==========================================
  Files          20       20              
  Lines        1523     1516       -7     
  Branches      419      419              
==========================================
- Hits         1521     1514       -7     
  Misses          2        2              
```

| [Files](https://app.codecov.io/gh/materialsproject/jobflow/pull/464?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=materialsproject) | Coverage Δ | |
|---|---|---|
| [src/jobflow/core/flow.py](https://app.codecov.io/gh/materialsproject/jobflow/pull/464?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=materialsproject#diff-c3JjL2pvYmZsb3cvY29yZS9mbG93LnB5) | `100.00% <ø> (ø)` | |
| [src/jobflow/core/reference.py](https://app.codecov.io/gh/materialsproject/jobflow/pull/464?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=materialsproject#diff-c3JjL2pvYmZsb3cvY29yZS9yZWZlcmVuY2UucHk=) | `100.00% <100.00%> (ø)` | |
| [src/jobflow/core/state.py](https://app.codecov.io/gh/materialsproject/jobflow/pull/464?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=materialsproject#diff-c3JjL2pvYmZsb3cvY29yZS9zdGF0ZS5weQ==) | `100.00% <ø> (ø)` | |
| [src/jobflow/core/store.py](https://app.codecov.io/gh/materialsproject/jobflow/pull/464?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=materialsproject#diff-c3JjL2pvYmZsb3cvY29yZS9zdG9yZS5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/jobflow/managers/fireworks.py](https://app.codecov.io/gh/materialsproject/jobflow/pull/464?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=materialsproject#diff-c3JjL2pvYmZsb3cvbWFuYWdlcnMvZmlyZXdvcmtzLnB5) | `100.00% <100.00%> (ø)` | |
| [src/jobflow/managers/local.py](https://app.codecov.io/gh/materialsproject/jobflow/pull/464?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=materialsproject#diff-c3JjL2pvYmZsb3cvbWFuYWdlcnMvbG9jYWwucHk=) | `100.00% <100.00%> (ø)` | |
| [src/jobflow/utils/enum.py](https://app.codecov.io/gh/materialsproject/jobflow/pull/464?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=materialsproject#diff-c3JjL2pvYmZsb3cvdXRpbHMvZW51bS5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/jobflow/utils/find.py](https://app.codecov.io/gh/materialsproject/jobflow/pull/464?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=materialsproject#diff-c3JjL2pvYmZsb3cvdXRpbHMvZmluZC5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/jobflow/utils/graph.py](https://app.codecov.io/gh/materialsproject/jobflow/pull/464?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=materialsproject#diff-c3JjL2pvYmZsb3cvdXRpbHMvZ3JhcGgucHk=) | `100.00% <100.00%> (ø)` | |

</details>

Fix patch:
diff --git a/pyproject.toml b/pyproject.toml
index fe20de09..9510291a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -151,16 +151,12 @@ select = [
     "W",      # pycodestyle warning
     "YTT",    # flake8-2020
 ]
-# PLR0911: too-many-return-statements
-# PLR0912: too-many-branches
-# PLR0913: too-many-arguments
-# PLR0915: too-many-statements
 ignore = [
     "B028",
-    "PLR0911",
-    "PLR0912",
-    "PLR0913",
-    "PLR0915",
+    "PLR0911", # too-many-return-statements
+    "PLR0912", # too-many-branches
+    "PLR0913", # too-many-arguments
+    "PLR0915", # too-many-statements
     "PLW0603",
     "RUF013",
 ]
@@ -168,9 +164,9 @@ pydocstyle.convention = "numpy"
 isort.known-first-party = ["jobflow"]
 
 [tool.ruff.per-file-ignores]
+# F401: unused import
 "__init__.py" = ["F401"]
+# D: pydocstyle
 # PLR2004: magic-value-comparison
 # PT004: pytest-missing-fixture-name-underscore
-# PLR0915: too-many-statements
-# PT011: pytest-raises-too-broad TODO fix these, should not be ignored
-"**/tests/*" = ["B018", "D", "PLR0915", "PLR2004", "PT004", "PT011"]
+"**/tests/*" = ["D", "PLR2004", "PT004"]


Test patch:
diff --git a/tests/core/test_flow.py b/tests/core/test_flow.py
index 2bd5a0c9..7fd3af83 100644
--- a/tests/core/test_flow.py
+++ b/tests/core/test_flow.py
@@ -197,7 +197,7 @@ def test_flow_of_flows_init():
     # test all jobflow included needed to generate outputs
     add_job = get_test_job()
     sub_flow = Flow([add_job], output=add_job.output)
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError, match="jobs array does not contain all jobs"):
         Flow([], output=sub_flow.output)
 
     # test flow given rather than outputs
@@ -216,13 +216,13 @@ def test_flow_of_flows_init():
     add_job = get_test_job()
     sub_flow = Flow([add_job], output=add_job.output)
     Flow([sub_flow])
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError, match="already belongs to another flow"):
         Flow([sub_flow])
 
     # test that two of the same flow cannot be used in the same flow
     add_job = get_test_job()
     sub_flow = Flow([add_job], output=add_job.output)
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError, match="jobs array contains multiple jobs/flows"):
         Flow([sub_flow, sub_flow])
 
 
@@ -252,7 +252,7 @@ def test_flow_job_mixed():
     add_job = get_test_job()
     add_job2 = get_test_job()
     subflow = Flow([add_job2], output=add_job2.output)
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError, match="jobs array does not contain all jobs"):
         Flow([add_job], output=[add_job.output, subflow.output])
 
 
@@ -456,7 +456,7 @@ def test_iterflow():
     add_job2 = get_test_job()
     add_job1.function_args = (2, add_job2.output)
     flow = Flow([add_job1, add_job2], order=JobOrder.LINEAR)
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError, match="Job connectivity contains cycles"):
         list(flow.iterflow())
 
     # test with external reference
@@ -615,7 +615,10 @@ def test_get_flow():
     # test all jobs included for graph to work
     job1 = Job(add, function_args=(1, 2))
     job2 = Job(add, function_args=(job1.output.value, 2))
-    with pytest.raises(ValueError):
+    with pytest.raises(
+        ValueError,
+        match="The following jobs were not found in the jobs array and are needed to ",
+    ):
         get_flow(job2, allow_external_references=False)
 
     job1 = Job(add, function_args=(1, 2))
@@ -683,18 +686,18 @@ def test_add_jobs():
     assert len(flow1.jobs) == 2
     assert add_job2.hosts == [flow1.uuid]
 
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError, match="jobs array contains multiple jobs/flows"):
         flow1.add_jobs(add_job2)
 
     add_job3 = get_test_job()
     Flow(add_job3)
 
     # job belongs to another Flow
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError, match="already belongs to another flow"):
         flow1.add_jobs(add_job3)
 
     add_job4 = get_test_job()
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError, match="jobs array contains multiple jobs/flows"):
         flow1.add_jobs([add_job4, add_job4])
 
     # nested flows
@@ -709,7 +712,9 @@ def test_add_jobs():
     flow1 = Flow([get_test_flow()])
     flow2 = Flow([flow1])
     flow3 = Flow([flow2])
-    with pytest.raises(ValueError):
+    with pytest.raises(
+        ValueError, match="circular dependency: Flow .+ contains the current Flow"
+    ):
         flow1.add_jobs(flow3)
 
     # test passing single job to @jobs setter
@@ -730,9 +735,12 @@ def test_remove_jobs():
     assert len(flow1.jobs) == 1
     assert flow1.jobs[0].uuid is add_job2.uuid
 
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError, match="Only indices between 0 and the number of"):
         flow1.remove_jobs(-1)
-    with pytest.raises(ValueError):
+    with pytest.raises(
+        ValueError,
+        match="Only indices between 0 and the number of the jobs are accepted",
+    ):
         flow1.remove_jobs(10)
 
     # test removing two jobs
@@ -750,7 +758,9 @@ def test_remove_jobs():
     add_job2 = get_test_job()
     flow2 = Flow([add_job1, add_job2], output=add_job2.output)
 
-    with pytest.raises(ValueError):
+    with pytest.raises(
+        ValueError, match="Removed Jobs/Flows are referenced in the output of the Flow"
+    ):
         flow2.remove_jobs(1)
 
     # test removing a flow
@@ -771,7 +781,9 @@ def test_remove_jobs():
     flow_inner = Flow([add_job1, add_job2])
     flow = Flow([flow_inner, add_job3], output=flow_inner.jobs[0].output)
 
-    with pytest.raises(ValueError):
+    with pytest.raises(
+        ValueError, match="Removed Jobs/Flows are referenced in the output of the Flow"
+    ):
         flow.remove_jobs(0)
 
     # test removing a job in a flow containing another flow
@@ -797,7 +809,9 @@ def test_set_output():
     flow.output = add_job1.output
     assert flow.output.uuid == add_job1.uuid
 
-    with pytest.raises(ValueError):
+    with pytest.raises(
+        ValueError, match="jobs array does not contain all jobs needed for flow output"
+    ):
         flow.output = [add_job3.output]
 
 
diff --git a/tests/core/test_job.py b/tests/core/test_job.py
index f14c852f..a0320d40 100644
--- a/tests/core/test_job.py
+++ b/tests/core/test_job.py
@@ -59,7 +59,9 @@ def test_job_init():
     assert test_job._kwargs == {"data": "output", "graphs": "graph"}
 
     # check giving True for multiple stores fails
-    with pytest.raises(ValueError):
+    with pytest.raises(
+        ValueError, match="Cannot select True for multiple additional stores"
+    ):
         Job(function=add, function_args=(1,), data=True, graphs=True)
 
     # test changing job name (test needed due to setattr override
@@ -260,7 +262,9 @@ def return_arg(arg):
     ref = OutputReference("xyz")
     config = JobConfig(on_missing_references=OnMissing.ERROR)
     test_job = Job(return_arg, function_args=(ref,), config=config)
-    with pytest.raises(ValueError):
+    with pytest.raises(
+        ValueError, match="Could not resolve reference - xyz not in store"
+    ):
         test_job.run(memory_jobstore)
 
     config = JobConfig(on_missing_references=OnMissing.NONE)
@@ -535,7 +539,10 @@ def test_response():
     assert response_original == response_processed
 
     # test Response and another output
-    with pytest.raises(ValueError):
+    with pytest.raises(
+        ValueError,
+        match="Response cannot be returned in combination with other outputs",
+    ):
         Response.from_job_returns([response_original, 5])
 
     # test schema
@@ -909,15 +916,20 @@ def add_schema_replace(a, b):
     assert response.replace.jobs[-1].output_schema.__name__ == "AddSchema"
 
     test_job = add_schema_bad(5, 6)
-    with pytest.raises(ValueError):
+    with pytest.raises(
+        ValueError,
+        match="Expected output to be AddSchema or dict but got output type of int",
+    ):
         test_job.run(memory_jobstore)
 
     test_job = add_schema_wrong_key(5, 6)
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError, match="1 validation error for AddSchema\n"):
         test_job.run(memory_jobstore)
 
     test_job = add_schema_no_output(5, 6)
-    with pytest.raises(ValueError):
+    with pytest.raises(
+        ValueError, match="Expected output of type AddSchema but got no output"
+    ):
         test_job.run(memory_jobstore)
 
 
diff --git a/tests/core/test_reference.py b/tests/core/test_reference.py
index 525f1837..4126477b 100644
--- a/tests/core/test_reference.py
+++ b/tests/core/test_reference.py
@@ -186,25 +186,26 @@ class MySchema(BaseModel):
     assert new_ref.output_schema is None
 
     with pytest.raises(AttributeError):
-        ref.a.uuid
+        _ = ref.a.uuid
 
     with pytest.raises(AttributeError):
-        ref["a"].uuid
+        _ = ref["a"].uuid
 
     with pytest.raises(AttributeError):
-        ref[1].uuid
+        _ = ref[1].uuid
 
     # check valid nested schemas
     assert ref.nested.s.uuid == "123"
     with pytest.raises(AttributeError):
-        ref.nested.m.uuid
+        _ = ref.nested.m.uuid
+
     assert ref.nested.nested.n.uuid == "123"
     with pytest.raises(AttributeError):
-        ref.nested.nested.m.uuid
+        _ = ref.nested.nested.m.uuid
 
     assert ref.nested.nested_opt.n.uuid == "123"
     with pytest.raises(AttributeError):
-        ref.nested.nested_opt.m.uuid
+        _ = ref.nested.nested_opt.m.uuid
 
     # Union, List and Dict are currently not recognized by their inner type
     # but check that there is no problem with them