Skip to content

Migrate potential interface from atomistics to pyiron_lammps#261

Merged
jan-janssen merged 22 commits intomainfrom
potential
Nov 19, 2025
Merged

Migrate potential interface from atomistics to pyiron_lammps#261
jan-janssen merged 22 commits intomainfrom
potential

Conversation

@jan-janssen
Copy link
Copy Markdown
Member

@jan-janssen jan-janssen commented Nov 8, 2025

Summary by CodeRabbit

  • New Features

    • Public API to list, query, and retrieve LAMMPS interatomic potentials by name or for a given atomic structure.
    • Returns resolved, normalized potential file paths and handles resource discovery and sensible defaults.
  • Tests

    • Added comprehensive tests covering potential lookups, validation, path normalization, resource-path discovery, and default-selection behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 8, 2025

Warning

Rate limit exceeded

@jan-janssen has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 39 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 6327e73 and 335cb48.

📒 Files selected for processing (1)
  • tests/test_potential.py (1 hunks)

Walkthrough

Adds a new potential management module for LAMMPS (pyiron_lammps.potential), exposes two functions via the package init, and includes unit tests exercising dataframe loading, path resolution, potential lookup, and abstract/concrete potential classes.

Changes

Cohort / File(s) Summary
Public API exports
pyiron_lammps/__init__.py
Imported and re-exported get_potential_by_name and get_potential_dataframe; updated __all__.
Potential management framework
pyiron_lammps/potential.py
New module adding PotentialAbstract, LammpsPotentialFile, PotentialAvailable; utilities find_potential_file_base, convert_path_to_abs_posix, update_potential_paths, get_resource_path_from_conda; public functions get_potential_dataframe, get_potential_by_name, view_potentials, and validate_potential_dataframe; CSV loading/conversion helpers and path-resolution logic.
Tests
tests/test_potential.py
New test suite covering validation, path conversion, resource discovery, dataframe updates, potential lookup by name/file, view_potentials, and behaviors of PotentialAbstract, LammpsPotentialFile, and PotentialAvailable.

Sequence Diagram

sequenceDiagram
    participant User
    participant API as get_potential_dataframe / get_potential_by_name
    participant Conda as get_resource_path_from_conda
    participant Loader as _get_potential_df / find_potential_file_base
    participant Updater as update_potential_paths
    participant DF as Potentials DataFrame
    participant Finder as PotentialAbstract / LammpsPotentialFile

    User->>API: call(structure or name, resource_path?)
    API->>Conda: if no resource_path -> query env
    Conda-->>API: return resource_path
    API->>Loader: load CSV(s) (lazy if needed)
    Loader-->>DF: return raw dataframe
    API->>Updater: expand Filename -> absolute POSIX paths
    Updater-->>DF: return enriched dataframe
    API->>Finder: filter/find potentials (by elements or name)
    Finder-->>API: return filtered dataframe / series
    API-->>User: return potential(s)
    
    Note over Loader,Updater: path resolution and CSV converters (Species, Config, Filename)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review focus:
    • Dataframe loading/conversion and the CSV converters (Species/Config/Filename).
    • Filesystem and environment logic in find_potential_file_base and get_resource_path_from_conda.
    • Inheritance and delegation between PotentialAbstract and LammpsPotentialFile.
    • Tests that validate edge cases (empty/multi-row validation, path normalization).

Poem

🐰
I hopped through CSVs and paths so bright,
Resolved each filename into the light,
Classes tuned, the potentials aligned,
A rabbit's code — clean, tidy, and kind! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.91% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective of the changeset: migrating the potential interface from the atomistics module to pyiron_lammps, which is evidenced by the new potential.py module, public API exports in init.py, and comprehensive tests.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Nov 8, 2025

Codecov Report

❌ Patch coverage is 97.85714% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.76%. Comparing base (115a677) to head (335cb48).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
pyiron_lammps/potential.py 97.84% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #261      +/-   ##
==========================================
+ Coverage   95.17%   95.76%   +0.59%     
==========================================
  Files           4        5       +1     
  Lines         497      637     +140     
==========================================
+ Hits          473      610     +137     
- Misses         24       27       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (5)
pyiron_lammps/potential.py (5)

49-60: Consider explicit Optional type annotation.

Line 53 uses list[str] = None which implicitly creates an Optional type. PEP 484 recommends explicit Optional[list[str]] for clarity.

Apply this diff if desired:

     def __init__(
         self,
         potential_df: pandas.DataFrame,
         default_df: pandas.DataFrame = None,
-        selected_atoms: list[str] = None,
+        selected_atoms: Optional[list[str]] = None,
     ):

104-111: Consider spread operator for list construction.

Line 106 concatenates lists using +. The spread operator provides a more explicit alternative.

Apply this diff if desired:

-        selected_atoms = self._selected_atoms + [item]
+        selected_atoms = [*self._selected_atoms, item]

223-231: Consider spread operator for list construction.

Similar to the parent class, line 225 could use the spread operator for list construction.

Apply this diff if desired:

-        selected_atoms = self._selected_atoms + [item]
+        selected_atoms = [*self._selected_atoms, item]

287-304: Remove redundant backslash replacement.

Line 303 calls .replace("\\", "/") after .as_posix(), but as_posix() already converts all path separators to forward slashes, making the replacement redundant.

Apply this diff:

     return (
         Path(path.strip())
         .expanduser()
         .resolve()
         .absolute()
         .as_posix()
-        .replace("\\", "/")
     )

358-380: Consider shorter error messages or custom exception classes.

The validation logic is comprehensive and correct. However, the error messages on lines 367-370, 372-375, and 377-380 are quite verbose. While they provide helpful context, consider either:

  1. Creating custom exception classes with descriptive names, or
  2. Moving detailed messages to exception docstrings

This aligns with best practices for exception handling (TRY003).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3d27297 and 5f29115.

📒 Files selected for processing (2)
  • pyiron_lammps/__init__.py (1 hunks)
  • pyiron_lammps/potential.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
pyiron_lammps/__init__.py (1)
pyiron_lammps/potential.py (2)
  • get_potential_by_name (349-355)
  • get_potential_dataframe (340-346)
🪛 Ruff (0.14.3)
pyiron_lammps/potential.py

53-53: PEP 484 prohibits implicit Optional

Convert to Optional[T]

(RUF013)


78-78: Avoid specifying long messages outside the exception class

(TRY003)


89-89: Avoid specifying long messages outside the exception class

(TRY003)


106-106: Consider [*self._selected_atoms, item] instead of concatenation

Replace with [*self._selected_atoms, item]

(RUF005)


213-213: Avoid specifying long messages outside the exception class

(TRY003)


225-225: Consider [*self._selected_atoms, item] instead of concatenation

Replace with [*self._selected_atoms, item]

(RUF005)


263-267: Avoid specifying long messages outside the exception class

(TRY003)


367-370: Avoid specifying long messages outside the exception class

(TRY003)


372-375: Avoid specifying long messages outside the exception class

(TRY003)


377-380: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: unittest_matrix (windows-latest, 3.14)

Comment thread pyiron_lammps/__init__.py
Comment on lines +8 to +34
potential_installation = """
Potential installation guide:

1. Check whether iprpy-data is installed. If not, install it using:

`conda install -c conda-forge iprpy-data`

2. Check whether the resource path is set via:

```python
import os
print(os.environ["CONDA_PREFIX"])
```

3. If the resource path is set, you can call the potential using:

```python
from atomistics.calculators import get_potential_by_name


get_potential_by_name(
potential_name=my_potential,
resource_path=os.path.join(os.environ["CONDA_PREFIX"], "share", "iprpy"),
)
```

"""
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Update module reference in installation guide.

The installation guide references atomistics.calculators on line 25, but this PR migrates the interface to pyiron_lammps. The import statement should be updated to reflect the correct module name.

Apply this diff:

-from atomistics.calculators import get_potential_by_name
+from pyiron_lammps import get_potential_by_name
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
potential_installation = """
Potential installation guide:
1. Check whether iprpy-data is installed. If not, install it using:
`conda install -c conda-forge iprpy-data`
2. Check whether the resource path is set via:
```python
import os
print(os.environ["CONDA_PREFIX"])
```
3. If the resource path is set, you can call the potential using:
```python
from atomistics.calculators import get_potential_by_name
get_potential_by_name(
potential_name=my_potential,
resource_path=os.path.join(os.environ["CONDA_PREFIX"], "share", "iprpy"),
)
```
"""
potential_installation = """
Potential installation guide:
1. Check whether iprpy-data is installed. If not, install it using:
`conda install -c conda-forge iprpy-data`
2. Check whether the resource path is set via:
🤖 Prompt for AI Agents
In pyiron_lammps/potential.py around lines 8 to 34, the installation guide
string references the old module name "atomistics.calculators" on the import
example; update that import to the new module "pyiron_lammps" and adjust the
subsequent usage example to call get_potential_by_name from pyiron_lammps (i.e.,
replace the import line and any fully-qualified calls in the string), keeping
the rest of the textual instructions unchanged.

Comment on lines +186 to +192
def default(self):
if self._default_df is not None:
atoms_str = "_".join(sorted(self._selected_atoms))
return self._default_df[
(self._default_df["Name"] == self._default_df.loc[atoms_str].values[0])
]
return None
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add error handling for missing default potential.

Line 190 accesses self._default_df.loc[atoms_str] without checking if atoms_str exists in the index. This will raise a KeyError if the element combination has no default defined.

Apply this diff to add proper error handling:

     def default(self):
         if self._default_df is not None:
             atoms_str = "_".join(sorted(self._selected_atoms))
+            if atoms_str not in self._default_df.index:
+                return None
             return self._default_df[
                 (self._default_df["Name"] == self._default_df.loc[atoms_str].values[0])
             ]
         return None
🤖 Prompt for AI Agents
In pyiron_lammps/potential.py around lines 186 to 192, the code directly indexes
self._default_df.loc[atoms_str] which will raise a KeyError if atoms_str is not
present; update the method to first check whether self._default_df is not None
and atoms_str exists in self._default_df.index (or catch KeyError) and if
missing return None (or raise a clear ValueError with context), otherwise
proceed to select rows matching the stored default Name; ensure no unguarded
.loc access remains so the method never throws an uncaught KeyError.

Comment on lines +194 to +221
def find_default(
self, element: Union[set[str], list[str], str]
) -> pandas.DataFrame:
"""
Find the potentials

Args:
element (set, str): element or set of elements for which you want the possible LAMMPS potentials
path (bool): choose whether to return the full path to the potential or just the potential name

Returns:
list: of possible potentials for the element or the combination of elements

"""
if isinstance(element, list):
element = set(element)
elif isinstance(element, str):
element = {element}
elif not isinstance(element, set):
raise TypeError("Only, str, list and set supported!")
element_lst = list(element)
if self._default_df is not None:
merged_lst = list(set(self._selected_atoms + element_lst))
atoms_str = "_".join(sorted(merged_lst))
return self._default_df[
(self._default_df["Name"] == self._default_df.loc[atoms_str].values[0])
]
return None
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add error handling for missing default potential.

Line 219 has the same issue as the default() method - accessing self._default_df.loc[atoms_str] without checking if the key exists in the index.

Apply this diff:

     def find_default(
         self, element: Union[set[str], list[str], str]
     ) -> pandas.DataFrame:
         """
         Find the potentials
 
         Args:
             element (set, str): element or set of elements for which you want the possible LAMMPS potentials
             path (bool): choose whether to return the full path to the potential or just the potential name
 
         Returns:
             list: of possible potentials for the element or the combination of elements
 
         """
         if isinstance(element, list):
             element = set(element)
         elif isinstance(element, str):
             element = {element}
         elif not isinstance(element, set):
             raise TypeError("Only, str, list and set supported!")
         element_lst = list(element)
         if self._default_df is not None:
             merged_lst = list(set(self._selected_atoms + element_lst))
             atoms_str = "_".join(sorted(merged_lst))
+            if atoms_str not in self._default_df.index:
+                return None
             return self._default_df[
                 (self._default_df["Name"] == self._default_df.loc[atoms_str].values[0])
             ]
         return None
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def find_default(
self, element: Union[set[str], list[str], str]
) -> pandas.DataFrame:
"""
Find the potentials
Args:
element (set, str): element or set of elements for which you want the possible LAMMPS potentials
path (bool): choose whether to return the full path to the potential or just the potential name
Returns:
list: of possible potentials for the element or the combination of elements
"""
if isinstance(element, list):
element = set(element)
elif isinstance(element, str):
element = {element}
elif not isinstance(element, set):
raise TypeError("Only, str, list and set supported!")
element_lst = list(element)
if self._default_df is not None:
merged_lst = list(set(self._selected_atoms + element_lst))
atoms_str = "_".join(sorted(merged_lst))
return self._default_df[
(self._default_df["Name"] == self._default_df.loc[atoms_str].values[0])
]
return None
def find_default(
self, element: Union[set[str], list[str], str]
) -> pandas.DataFrame:
"""
Find the potentials
Args:
element (set, str): element or set of elements for which you want the possible LAMMPS potentials
path (bool): choose whether to return the full path to the potential or just the potential name
Returns:
list: of possible potentials for the element or the combination of elements
"""
if isinstance(element, list):
element = set(element)
elif isinstance(element, str):
element = {element}
elif not isinstance(element, set):
raise TypeError("Only, str, list and set supported!")
element_lst = list(element)
if self._default_df is not None:
merged_lst = list(set(self._selected_atoms + element_lst))
atoms_str = "_".join(sorted(merged_lst))
if atoms_str not in self._default_df.index:
return None
return self._default_df[
(self._default_df["Name"] == self._default_df.loc[atoms_str].values[0])
]
return None
🧰 Tools
🪛 Ruff (0.14.3)

213-213: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In pyiron_lammps/potential.py around lines 194 to 221, the code accesses
self._default_df.loc[atoms_str] without verifying that atoms_str is a valid
index key; update the logic to check whether atoms_str exists in
self._default_df.index (or wrap the access in a try/except KeyError) before
attempting to read .loc; if the key is missing, return None (or raise a clear
KeyError with a descriptive message) instead of letting an unhandled exception
occur, ensuring behavior matches the existing default() method.

Comment on lines +349 to +355
def get_potential_by_name(potential_name: str, resource_path=None):
if resource_path is None:
resource_path = get_resource_path_from_conda()
df = LammpsPotentialFile(resource_path=resource_path).list()
return update_potential_paths(
df_pot=df[df.Name == potential_name], resource_path=resource_path
).iloc[0]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add validation before accessing filtered result.

Line 354 filters by potential_name and immediately accesses .iloc[0] without checking if any results were found. If no matching potential exists, this will raise an IndexError instead of a meaningful error message.

Apply this diff:

 def get_potential_by_name(potential_name: str, resource_path=None):
     if resource_path is None:
         resource_path = get_resource_path_from_conda()
     df = LammpsPotentialFile(resource_path=resource_path).list()
-    return update_potential_paths(
-        df_pot=df[df.Name == potential_name], resource_path=resource_path
-    ).iloc[0]
+    filtered_df = df[df.Name == potential_name]
+    if len(filtered_df) == 0:
+        raise ValueError(f"Potential '{potential_name}' not found in database.")
+    return update_potential_paths(
+        df_pot=filtered_df, resource_path=resource_path
+    ).iloc[0]
🤖 Prompt for AI Agents
pyiron_lammps/potential.py around lines 349 to 355: the function filters df by
potential_name and directly returns .iloc[0], which will raise IndexError if no
match is found; modify the function to check if the filtered dataframe is empty
before accessing iloc[0], and if empty raise a clear ValueError (or custom
exception) stating the requested potential_name was not found and include either
a short list of available potential names or the resource_path used for lookup
so callers can diagnose the issue; if not empty, proceed to return the first row
as before.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f29115 and d6490b6.

⛔ Files ignored due to path filters (1)
  • tests/static/potentials_lammps.csv is excluded by !**/*.csv
📒 Files selected for processing (2)
  • pyiron_lammps/__init__.py (2 hunks)
  • tests/test_potential.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
tests/test_potential.py (1)
pyiron_lammps/potential.py (2)
  • validate_potential_dataframe (358-380)
  • get_potential_dataframe (340-346)
pyiron_lammps/__init__.py (1)
pyiron_lammps/potential.py (2)
  • get_potential_by_name (349-355)
  • get_potential_dataframe (340-346)
🪛 GitHub Actions: Pipeline
tests/test_potential.py

[error] 28-28: test_get_potential_dataframe failed due to a ValueError raised in potential.py (missing potential files).

🔇 Additional comments (2)
pyiron_lammps/__init__.py (1)

3-3: LGTM! Public API correctly expanded.

The new potential management functions are properly imported and exported via __all__, making them part of the public API alongside existing exports.

Also applies to: 19-20

tests/test_potential.py (1)

13-25: LGTM! Comprehensive validation test coverage.

The test method correctly validates all error paths (empty DataFrame, multiple rows, wrong type) and the success path (single-row DataFrame returning Series).

Comment thread tests/test_potential.py Outdated
Comment on lines +30 to +32
resource_path=os.path.abspath(
os.path.join("..", os.path.dirname(__file__), "static", "lammps")
),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the resource path construction causing test failure.

The path construction includes an extra ".." that navigates up from the repository root, causing the test to look for resources at ../tests/static/lammps instead of tests/static/lammps. This is why the pipeline reports missing potential files.

Apply this diff to fix the path:

-            resource_path=os.path.abspath(
-                os.path.join("..", os.path.dirname(__file__), "static", "lammps")
-            ),
+            resource_path=os.path.abspath(
+                os.path.join(os.path.dirname(__file__), "static", "lammps")
+            ),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
resource_path=os.path.abspath(
os.path.join("..", os.path.dirname(__file__), "static", "lammps")
),
resource_path=os.path.abspath(
os.path.join(os.path.dirname(__file__), "static", "lammps")
),
🤖 Prompt for AI Agents
In tests/test_potential.py around lines 30 to 32, the resource_path incorrectly
includes an extra ".." which causes it to resolve to ../tests/static/lammps;
remove the leading ".." from the join so the path is built relative to the test
file itself—for example, use
os.path.abspath(os.path.join(os.path.dirname(__file__), "static", "lammps"))—so
the tests look for tests/static/lammps instead of ../tests/static/lammps.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
tests/test_potential.py (1)

27-28: Fix the resource path construction causing test failures.

The path construction includes an extra ".." that navigates up from the repository root. This is causing multiple test failures in the pipeline.

Apply this diff to fix the path:

-        cls.resource_path = os.path.abspath(
-            os.path.join("..", os.path.dirname(__file__), "static", "potential")
-        )
+        cls.resource_path = os.path.abspath(
+            os.path.join(os.path.dirname(__file__), "static", "potential")
+        )
🧹 Nitpick comments (1)
tests/test_potential.py (1)

69-77: Use tempfile for cross-platform compatibility.

The hardcoded /tmp/conda path will fail on Windows and is flagged by static analysis. Use tempfile.TemporaryDirectory() for a portable solution.

Apply this diff:

-    @mock.patch.dict(os.environ, {"CONDA_PREFIX": "/tmp/conda"}, clear=True)
-    def test_get_resource_path_from_conda(self):
-        with self.assertRaises(ValueError):
-            get_resource_path_from_conda()
-
-        os.makedirs("/tmp/conda/share/iprpy")
-        self.assertEqual(get_resource_path_from_conda(), "/tmp/conda/share/iprpy")
-        shutil.rmtree("/tmp/conda")
+    def test_get_resource_path_from_conda(self):
+        with tempfile.TemporaryDirectory() as tmpdir:
+            with mock.patch.dict(os.environ, {"CONDA_PREFIX": tmpdir}, clear=True):
+                with self.assertRaises(ValueError):
+                    get_resource_path_from_conda()
+
+                conda_path = os.path.join(tmpdir, "share", "iprpy")
+                os.makedirs(conda_path)
+                self.assertEqual(get_resource_path_from_conda(), conda_path)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a00cf14 and 8e00b1d.

📒 Files selected for processing (1)
  • tests/test_potential.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/test_potential.py (1)
pyiron_lammps/potential.py (16)
  • validate_potential_dataframe (358-380)
  • get_potential_dataframe (340-346)
  • get_potential_by_name (349-355)
  • convert_path_to_abs_posix (287-304)
  • get_resource_path_from_conda (328-337)
  • find_potential_file_base (254-267)
  • view_potentials (270-284)
  • update_potential_paths (307-325)
  • PotentialAbstract (37-153)
  • LammpsPotentialFile (156-231)
  • PotentialAvailable (234-251)
  • find (62-84)
  • find_by_name (86-90)
  • _get_potential_df (117-153)
  • find_default (194-221)
  • default (186-192)
🪛 GitHub Actions: Pipeline
tests/test_potential.py

[error] 217-217: Assertion failed: len(potential.list()) == 1 but got 0; list() returns no items when exactly one item is expected.


[error] 140-140: Assertion failed: Updated potential paths do not include the resource_path in df_updated.Config.values[0][0].


[error] 233-233: Directory listing mismatch: expected ['pot_pot1', 'pot_pot_2', 'pot_pot_3'] but got ['pot_1', 'pot_2', 'pot_3'].

🪛 Ruff (0.14.4)
tests/test_potential.py

69-69: Probable insecure usage of temporary file or directory: "/tmp/conda"

(S108)


74-74: Probable insecure usage of temporary file or directory: "/tmp/conda/share/iprpy"

(S108)


75-75: Probable insecure usage of temporary file or directory: "/tmp/conda/share/iprpy"

(S108)


76-76: Probable insecure usage of temporary file or directory: "/tmp/conda"

(S108)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: unittest_matrix (ubuntu-latest, 3.12)
  • GitHub Check: unittest_matrix (windows-latest, 3.14)
  • GitHub Check: unittest_matrix (ubuntu-latest, 3.11)
  • GitHub Check: unittest_matrix (ubuntu-latest, 3.14)
  • GitHub Check: unittest_matrix (ubuntu-latest, 3.13)
  • GitHub Check: pip_check
  • GitHub Check: unittest_old
  • GitHub Check: minimal
  • GitHub Check: notebooks
  • GitHub Check: coverage
🔇 Additional comments (21)
tests/test_potential.py (21)

1-21: LGTM!

The imports are appropriate and comprehensive for testing the potential module.


35-48: LGTM!

The test comprehensively covers all validation branches: empty dataframe, multi-row dataframe, invalid type, and valid single-row input.


49-51: LGTM!

Simple and effective test for dataframe retrieval. This will pass once the path construction issue in setUpClass is resolved.


52-58: LGTM!

The test correctly verifies retrieval of a specific potential by name.


59-67: LGTM!

The test effectively validates path conversion including home directory expansion and POSIX formatting.


78-81: LGTM!

The test correctly verifies that ValueError is raised when no conda environment variables are present.


83-129: LGTM!

Excellent test coverage for find_potential_file_base, including nested paths, direct paths, and error cases. Proper use of tempfile.TemporaryDirectory() for cleanup.


130-136: LGTM!

The test appropriately validates the view_potentials function for a bulk Al structure.


137-141: Test logic is correct but fails due to path issue.

The pipeline failure on line 140 is caused by the incorrect resource path construction in setUpClass. Once that issue is fixed, this test should pass.


144-151: LGTM!

Clean test setup with appropriate test data for exercising PotentialAbstract.


153-171: LGTM!

Thorough test coverage for the find method, including string, list, set inputs and type error handling.


172-178: LGTM!

The test correctly validates both successful lookup and error handling for missing potentials.


180-181: LGTM!

Simple and effective test for the list() method.


183-186: LGTM!

The test effectively validates chained element selection through __getitem__.


188-189: LGTM!

Correct validation of the __str__ method.


191-195: LGTM!

The test appropriately validates error handling when potential files cannot be located.


205-207: LGTM!

The test correctly validates the inheritance hierarchy.


209-212: LGTM!

The test appropriately verifies behavior when no default dataframe is configured.


214-218: Test logic is correct but fails due to path issue.

The pipeline failure on line 217 (expecting 1 item but getting 0) is caused by the incorrect resource path in setUp. Once the path construction is fixed, this test should pass.


221-223: LGTM!

The setup correctly initializes PotentialAvailable with test data.


237-238: LGTM!

The test correctly validates that __repr__ returns the string representation of the directory listing. This will work correctly once test_dir is fixed.

Comment thread tests/test_potential.py
Comment on lines +200 to +201
self.resource_path = os.path.abspath(
os.path.join("..", os.path.dirname(__file__), "static", "potential")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the resource path construction.

The same path construction error from TestPotential.setUpClass is repeated here. The extra ".." will cause tests to fail.

Apply this diff:

-        self.resource_path = os.path.abspath(
-            os.path.join("..", os.path.dirname(__file__), "static", "potential")
-        )
+        self.resource_path = os.path.abspath(
+            os.path.join(os.path.dirname(__file__), "static", "potential")
+        )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
self.resource_path = os.path.abspath(
os.path.join("..", os.path.dirname(__file__), "static", "potential")
self.resource_path = os.path.abspath(
os.path.join(os.path.dirname(__file__), "static", "potential")
)
🤖 Prompt for AI Agents
In tests/test_potential.py around lines 200-201, the resource path construction
incorrectly includes an extra ".." segment which builds the wrong path; remove
the leading ".." and build the absolute path from the test file directory
instead (i.e., call os.path.abspath on os.path.join(os.path.dirname(__file__),
"static", "potential") ), making sure parentheses are balanced so the resulting
self.resource_path points to the correct static/potential directory.

Comment thread tests/test_potential.py Outdated
Comment on lines +225 to +230
def test_getattr(self):
self.assertEqual(self.available.pot_1, "pot1")
self.assertEqual(self.available.pot_2, "pot-2")
self.assertEqual(self.available.pot_3, "pot.3")
with self.assertRaises(AttributeError):
_ = self.available.not_a_potential
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix attribute access to match implementation.

The test expects pot_1, pot_2, pot_3, but PotentialAvailable prepends "pot_" to each potential name. For input ["pot1", "pot-2", "pot.3"], the keys should be pot_pot1, pot_pot_2, pot_pot_3.

Apply this diff:

     def test_getattr(self):
-        self.assertEqual(self.available.pot_1, "pot1")
-        self.assertEqual(self.available.pot_2, "pot-2")
-        self.assertEqual(self.available.pot_3, "pot.3")
+        self.assertEqual(self.available.pot_pot1, "pot1")
+        self.assertEqual(self.available.pot_pot_2, "pot-2")
+        self.assertEqual(self.available.pot_pot_3, "pot.3")
         with self.assertRaises(AttributeError):
             _ = self.available.not_a_potential
🤖 Prompt for AI Agents
In tests/test_potential.py around lines 225 to 230, the assertions assume
attribute names pot_1, pot_2, pot_3 but PotentialAvailable prepends "pot_" to
each potential name, so for input ["pot1","pot-2","pot.3"] the actual attributes
are pot_pot1, pot_pot_2, pot_pot_3; update the three assertEqual lines to check
self.available.pot_pot1 == "pot1", self.available.pot_pot_2 == "pot-2",
self.available.pot_pot_3 == "pot.3" and keep the AttributeError check for an
attribute that truly doesn't exist (e.g., self.available.not_a_potential).

Comment thread tests/test_potential.py Outdated
Comment on lines +232 to +235
def test_dir(self):
self.assertEqual(
sorted(dir(self.available)), sorted(["pot_1", "pot_2", "pot_3"])
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix directory listing expectation.

The pipeline failure confirms that the expected keys should be ['pot_pot1', 'pot_pot_2', 'pot_pot_3'] to match the implementation, not ['pot_1', 'pot_2', 'pot_3'].

Apply this diff:

     def test_dir(self):
         self.assertEqual(
-            sorted(dir(self.available)), sorted(["pot_1", "pot_2", "pot_3"])
+            sorted(dir(self.available)), sorted(["pot_pot1", "pot_pot_2", "pot_pot_3"])
         )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_dir(self):
self.assertEqual(
sorted(dir(self.available)), sorted(["pot_1", "pot_2", "pot_3"])
)
def test_dir(self):
self.assertEqual(
sorted(dir(self.available)), sorted(["pot_pot1", "pot_pot_2", "pot_pot_3"])
)
🧰 Tools
🪛 GitHub Actions: Pipeline

[error] 233-233: Directory listing mismatch: expected ['pot_pot1', 'pot_pot_2', 'pot_pot_3'] but got ['pot_1', 'pot_2', 'pot_3'].

🤖 Prompt for AI Agents
In tests/test_potential.py around lines 232 to 235, the test asserts the
directory listing uses keys ["pot_1","pot_2","pot_3"] but the implementation
produces ["pot_pot1","pot_pot_2","pot_pot_3"]; update the expected list in the
assertion to sorted(["pot_pot1","pot_pot_2","pot_pot_3"]) so the test matches
the actual keys returned by dir(self.available).

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
tests/test_potential.py (2)

27-29: Fix the resource path construction causing test failure.

The extra ".." in the path construction causes tests to look for resources at ../tests/static/potential instead of tests/static/potential, as previously flagged.

Apply this diff:

-        cls.resource_path = os.path.abspath(
-            os.path.join("..", os.path.dirname(__file__), "static", "potential")
-        )
+        cls.resource_path = os.path.abspath(
+            os.path.join(os.path.dirname(__file__), "static", "potential")
+        )

195-197: Fix the resource path construction.

The same path construction error from line 27-29 is repeated here, as previously flagged.

Apply this diff:

-        self.resource_path = os.path.abspath(
-            os.path.join("..", os.path.dirname(__file__), "static", "potential")
-        )
+        self.resource_path = os.path.abspath(
+            os.path.join(os.path.dirname(__file__), "static", "potential")
+        )
🧹 Nitpick comments (1)
tests/test_potential.py (1)

193-208: Consider adding test for getitem override.

LammpsPotentialFile overrides __getitem__ to return a LammpsPotentialFile instance rather than PotentialAbstract. This behavior should be tested to ensure the subclass chain is preserved.

Add this test method to the class:

def test_getitem(self):
    potential = self.potential["Al"]
    self.assertIsInstance(potential, LammpsPotentialFile)
    self.assertIsInstance(potential, PotentialAbstract)
    self.assertEqual(potential._resource_path, self.resource_path)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e00b1d and 6f433cd.

📒 Files selected for processing (1)
  • tests/test_potential.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/test_potential.py (1)
pyiron_lammps/potential.py (16)
  • validate_potential_dataframe (358-380)
  • get_potential_dataframe (340-346)
  • get_potential_by_name (349-355)
  • convert_path_to_abs_posix (287-304)
  • get_resource_path_from_conda (328-337)
  • find_potential_file_base (254-267)
  • view_potentials (270-284)
  • update_potential_paths (307-325)
  • PotentialAbstract (37-153)
  • LammpsPotentialFile (156-231)
  • PotentialAvailable (234-251)
  • find (62-84)
  • find_by_name (86-90)
  • _get_potential_df (117-153)
  • find_default (194-221)
  • default (186-192)
🪛 Ruff (0.14.4)
tests/test_potential.py

69-69: Probable insecure usage of temporary file or directory: "/tmp/conda"

(S108)


74-74: Probable insecure usage of temporary file or directory: "/tmp/conda/share/iprpy"

(S108)


75-75: Probable insecure usage of temporary file or directory: "/tmp/conda/share/iprpy"

(S108)


76-76: Probable insecure usage of temporary file or directory: "/tmp/conda"

(S108)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: unittest_matrix (windows-latest, 3.14)
  • GitHub Check: unittest_matrix (macos-latest, 3.14)
🔇 Additional comments (2)
tests/test_potential.py (2)

83-129: Good use of tempfile.TemporaryDirectory for test isolation.

The test properly uses tempfile.TemporaryDirectory as a context manager, ensuring automatic cleanup and avoiding platform-specific paths.


138-191: Comprehensive test coverage for PotentialAbstract.

The test suite thoroughly validates input handling (str, list, set, invalid types), error cases (missing potentials, missing files), and core functionality (find, find_by_name, list operations).

Comment thread tests/test_potential.py
Comment on lines +69 to +77
@mock.patch.dict(os.environ, {"CONDA_PREFIX": "/tmp/conda"}, clear=True)
def test_get_resource_path_from_conda(self):
with self.assertRaises(ValueError):
get_resource_path_from_conda()

os.makedirs("/tmp/conda/share/iprpy")
self.assertEqual(get_resource_path_from_conda(), "/tmp/conda/share/iprpy")
shutil.rmtree("/tmp/conda")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace hardcoded /tmp path with tempfile for cross-platform compatibility.

The hardcoded /tmp/conda path will fail on Windows and may conflict with existing directories on Unix systems. Additionally, if the assertion at line 75 fails, the cleanup at line 76 won't execute, leaving test artifacts.

Apply this diff:

-    @mock.patch.dict(os.environ, {"CONDA_PREFIX": "/tmp/conda"}, clear=True)
     def test_get_resource_path_from_conda(self):
-        with self.assertRaises(ValueError):
-            get_resource_path_from_conda()
-
-        os.makedirs("/tmp/conda/share/iprpy")
-        self.assertEqual(get_resource_path_from_conda(), "/tmp/conda/share/iprpy")
-        shutil.rmtree("/tmp/conda")
+        with tempfile.TemporaryDirectory() as tmpdir:
+            # Test ValueError when directory doesn't exist
+            with mock.patch.dict(os.environ, {"CONDA_PREFIX": tmpdir}, clear=True):
+                with self.assertRaises(ValueError):
+                    get_resource_path_from_conda()
+
+            # Test success when directory exists
+            iprpy_path = os.path.join(tmpdir, "share", "iprpy")
+            os.makedirs(iprpy_path)
+            with mock.patch.dict(os.environ, {"CONDA_PREFIX": tmpdir}, clear=True):
+                self.assertEqual(get_resource_path_from_conda(), iprpy_path)
🧰 Tools
🪛 Ruff (0.14.4)

69-69: Probable insecure usage of temporary file or directory: "/tmp/conda"

(S108)


74-74: Probable insecure usage of temporary file or directory: "/tmp/conda/share/iprpy"

(S108)


75-75: Probable insecure usage of temporary file or directory: "/tmp/conda/share/iprpy"

(S108)


76-76: Probable insecure usage of temporary file or directory: "/tmp/conda"

(S108)

🤖 Prompt for AI Agents
In tests/test_potential.py around lines 69 to 77, replace the hardcoded
"/tmp/conda" usage with a temporary directory (via tempfile.mkdtemp() or
tempfile.TemporaryDirectory()) and point the CONDA_PREFIX mock to that temp
path; create the expected subpath (tempdir/share/iprpy) for the assertion, and
ensure cleanup always runs by using a try/finally or Python's TemporaryDirectory
context manager so the temp directory is removed even if the assertion fails.

Comment thread tests/test_potential.py
Comment on lines +210 to +216
class TestPotentialAvailable(unittest.TestCase):
def setUp(self):
self.potentials = ["pot1", "pot-2", "pot.3"]
self.available = PotentialAvailable(list_of_potentials=self.potentials)

def test_repr(self):
self.assertEqual(repr(self.available), str(dir(self.available)))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add missing tests for PotentialAvailable.getattr and dir.

The test class only covers __repr__ but not __getattr__ and __dir__ methods. Based on past review comments and the implementation, these methods need test coverage.

Add these test methods to validate the dynamic attribute behavior:

def test_getattr(self):
    self.assertEqual(self.available.pot_pot1, "pot1")
    self.assertEqual(self.available.pot_pot_2, "pot-2")
    self.assertEqual(self.available.pot_pot_3, "pot.3")
    with self.assertRaises(AttributeError):
        _ = self.available.not_a_potential

def test_dir(self):
    self.assertEqual(
        sorted(dir(self.available)), sorted(["pot_pot1", "pot_pot_2", "pot_pot_3"])
    )

Note: The implementation prepends "pot_" and normalizes - and . to _, so "pot1""pot_pot1", "pot-2""pot_pot_2", "pot.3""pot_pot_3".

🤖 Prompt for AI Agents
In tests/test_potential.py around lines 210 to 216, the TestPotentialAvailable
class only tests __repr__; add two new test methods: test_getattr to verify
dynamic attribute access maps potentials to attributes (e.g. available.pot_pot1
== "pot1", available.pot_p_2 == "pot-2", available.pot_p_3 == "pot.3") and
raises AttributeError for unknown attributes, and test_dir to assert
dir(available) returns the normalized attribute names (sorted equals
["pot_pot1","pot_p_2","pot_p_3"]). Implement these methods in the
TestPotentialAvailable class using self.assertEqual and self.assertRaises as
shown in the review comment.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
tests/test_potential.py (4)

26-28: Fix the resource path construction causing test failure.

The path construction includes an extra ".." that navigates up from the repository root, causing the test to look for resources at ../tests/static/potential instead of tests/static/potential.

Apply this diff to fix the path:

-        cls.resource_path = os.path.abspath(
-            os.path.join("..", os.path.dirname(__file__), "static", "potential")
-        )
+        cls.resource_path = os.path.abspath(
+            os.path.join(os.path.dirname(__file__), "static", "potential")
+        )

70-83: Replace hardcoded /tmp path with tempfile for cross-platform compatibility.

The hardcoded /tmp/conda path will fail on Windows and may conflict with existing directories on Unix systems. Additionally, if the assertion at lines 76-78 fails, the cleanup at line 83 won't execute, leaving test artifacts.

Apply this diff:

-    @mock.patch.dict(os.environ, {"CONDA_PREFIX": "/tmp/conda"}, clear=True)
     def test_get_resource_path_from_conda(self):
-        with self.assertRaises(ValueError):
-            get_resource_path_from_conda()
-
-        os.makedirs("/tmp/conda/share/iprpy")
-        self.assertEqual(
-            get_resource_path_from_conda().replace("\\", "/"), "/tmp/conda/share/iprpy"
-        )
-        df = get_potential_dataframe(
-            structure=bulk("Al"),
-        )
-        self.assertEqual(len(df), 0)
-        shutil.rmtree("/tmp/conda")
+        with tempfile.TemporaryDirectory() as tmpdir:
+            # Test ValueError when directory doesn't exist
+            with mock.patch.dict(os.environ, {"CONDA_PREFIX": tmpdir}, clear=True):
+                with self.assertRaises(ValueError):
+                    get_resource_path_from_conda()
+
+            # Test success when directory exists
+            iprpy_path = os.path.join(tmpdir, "share", "iprpy")
+            os.makedirs(iprpy_path)
+            with mock.patch.dict(os.environ, {"CONDA_PREFIX": tmpdir}, clear=True):
+                self.assertEqual(get_resource_path_from_conda(), iprpy_path)
+
+            # Test get_potential_dataframe with empty resource
+            with mock.patch.dict(os.environ, {"CONDA_PREFIX": tmpdir}, clear=True):
+                df = get_potential_dataframe(structure=bulk("Al"))
+                self.assertEqual(len(df), 0)

202-204: Fix the resource path construction.

The same path construction error from TestPotential.setUpClass is repeated here. The extra ".." will cause tests to fail.

Apply this diff:

-        self.resource_path = os.path.abspath(
-            os.path.join("..", os.path.dirname(__file__), "static", "potential")
-        )
+        self.resource_path = os.path.abspath(
+            os.path.join(os.path.dirname(__file__), "static", "potential")
+        )

217-223: Add missing tests for PotentialAvailable.getattr and dir.

The test class only covers __repr__ but not __getattr__ and __dir__ methods. Based on past review comments and the implementation, these methods need test coverage.

Add these test methods to validate the dynamic attribute behavior:

def test_getattr(self):
    self.assertEqual(self.available.pot_pot1, "pot1")
    self.assertEqual(self.available.pot_pot_2, "pot-2")
    self.assertEqual(self.available.pot_pot_3, "pot.3")
    with self.assertRaises(AttributeError):
        _ = self.available.not_a_potential

def test_dir(self):
    self.assertEqual(
        sorted(dir(self.available)), sorted(["pot_pot1", "pot_pot_2", "pot_pot_3"])
    )

Note: The implementation prepends "pot_" and normalizes - and . to _, so "pot1""pot_pot1", "pot-2""pot_pot_2", "pot.3""pot_pot_3".

🧹 Nitpick comments (1)
tests/test_compatibility_file.py (1)

52-61: Consider prefixing unused variables with underscore.

The shell_output and job_crashed return values are not used in this test. Following Python convention, prefix unused variables with _ to signal intent.

Apply this diff:

-        shell_output, parsed_output, job_crashed = lammps_file_interface_function(
+        _shell_output, parsed_output, _job_crashed = lammps_file_interface_function(

Note: Apply the same pattern to all test methods in this class (lines 64, 76, 89, 117, 145, 174).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c2880a and 1e94a9a.

📒 Files selected for processing (2)
  • tests/test_compatibility_file.py (1 hunks)
  • tests/test_potential.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
tests/test_compatibility_file.py (1)
pyiron_lammps/potential.py (1)
  • get_potential_by_name (349-355)
tests/test_potential.py (1)
pyiron_lammps/potential.py (15)
  • validate_potential_dataframe (358-380)
  • get_potential_dataframe (340-346)
  • get_potential_by_name (349-355)
  • convert_path_to_abs_posix (287-304)
  • get_resource_path_from_conda (328-337)
  • find_potential_file_base (254-267)
  • view_potentials (270-284)
  • PotentialAbstract (37-153)
  • LammpsPotentialFile (156-231)
  • PotentialAvailable (234-251)
  • find (62-84)
  • find_by_name (86-90)
  • _get_potential_df (117-153)
  • find_default (194-221)
  • default (186-192)
🪛 Ruff (0.14.5)
tests/test_compatibility_file.py

53-53: Unpacked variable shell_output is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


53-53: Unpacked variable job_crashed is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


64-64: Unpacked variable shell_output is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


64-64: Unpacked variable job_crashed is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


76-76: Unpacked variable shell_output is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


76-76: Unpacked variable job_crashed is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


89-89: Unpacked variable shell_output is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


89-89: Unpacked variable job_crashed is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


117-117: Unpacked variable shell_output is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


117-117: Unpacked variable job_crashed is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


145-145: Unpacked variable shell_output is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


145-145: Unpacked variable job_crashed is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


174-174: Unpacked variable shell_output is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)


174-174: Unpacked variable job_crashed is never used

Prefix it with an underscore or any other dummy variable pattern

(RUF059)

tests/test_potential.py

70-70: Probable insecure usage of temporary file or directory: "/tmp/conda"

(S108)


75-75: Probable insecure usage of temporary file or directory: "/tmp/conda/share/iprpy"

(S108)


77-77: Probable insecure usage of temporary file or directory: "/tmp/conda/share/iprpy"

(S108)


83-83: Probable insecure usage of temporary file or directory: "/tmp/conda"

(S108)

🔇 Additional comments (14)
tests/test_potential.py (7)

34-48: LGTM!

Comprehensive test coverage for validate_potential_dataframe error handling and type conversions.


53-58: LGTM!

Correctly validates potential lookup by name.


60-68: LGTM!

Properly tests path normalization for both home directory and current directory cases.


85-88: LGTM!

Correctly validates error handling when conda environment variables are absent.


90-135: LGTM!

Excellent test coverage for find_potential_file_base with proper use of tempfile.TemporaryDirectory for cross-platform compatibility and automatic cleanup.


145-197: LGTM!

Comprehensive test suite for PotentialAbstract covering element filtering, name lookup, list operations, item access, and error handling.


207-214: LGTM!

Tests correctly validate LammpsPotentialFile initialization and default lookup behavior.

tests/test_compatibility_file.py (7)

48-50: LGTM!

Proper cleanup of test artifacts.


63-72: LGTM!

Correctly tests minimize mode for positions.


74-85: LGTM!

Correctly tests minimize mode with volume relaxation.


87-108: LGTM!

Comprehensive validation of MD trajectory output shapes and keys for NVT ensemble.


110-136: LGTM!

Comprehensive validation of MD trajectory output for NPT ensemble.


138-164: LGTM!

Comprehensive validation of MD trajectory output for Langevin NVT ensemble.


166-193: LGTM!

Comprehensive validation of MD trajectory output for Langevin NPT ensemble.

Comment thread tests/test_compatibility_file.py Outdated
Comment on lines +16 to +18
resource_path=os.path.abspath(
os.path.join("..", os.path.dirname(__file__), "static", "lammps")
),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the resource path construction.

The path construction includes an extra ".." that will cause the test to look for resources at ../tests/static/lammps instead of tests/static/lammps.

Apply this diff:

         self.potential = get_potential_by_name(
             potential_name="2009--Mendelev-M-I--Al-Mg--LAMMPS--ipr1",
             resource_path=os.path.abspath(
-                os.path.join("..", os.path.dirname(__file__), "static", "lammps")
+                os.path.join(os.path.dirname(__file__), "static", "lammps")
             ),
         )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
resource_path=os.path.abspath(
os.path.join("..", os.path.dirname(__file__), "static", "lammps")
),
resource_path=os.path.abspath(
os.path.join(os.path.dirname(__file__), "static", "lammps")
),
🤖 Prompt for AI Agents
In tests/test_compatibility_file.py around lines 16 to 18, the resource_path is
built with an extra ".." which makes it resolve to ../tests/static/lammps
instead of tests/static/lammps; remove the leading ".." from the os.path.join
call so the path is os.path.join(os.path.dirname(__file__), "static", "lammps")
wrapped with os.path.abspath, ensuring the test looks up tests/static/lammps
correctly.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
tests/test_potential.py (4)

26-28: Fix the resource path construction causing test failures.

The extra ".." in the path construction navigates up from the repository root, causing the test to look for ../tests/static/potential instead of tests/static/potential.

Apply this diff:

         cls.resource_path = os.path.abspath(
-            os.path.join("..", os.path.dirname(__file__), "static", "potential")
+            os.path.join(os.path.dirname(__file__), "static", "potential")
         )

70-84: Replace hardcoded /tmp path with tempfile for cross-platform compatibility.

The hardcoded /tmp/conda path will fail on Windows and may conflict with existing directories. Additionally, if assertions fail, the cleanup at line 83 won't execute.

Apply this diff:

-    @mock.patch.dict(os.environ, {"CONDA_PREFIX": "/tmp/conda"}, clear=True)
     def test_get_resource_path_from_conda(self):
-        with self.assertRaises(ValueError):
-            get_resource_path_from_conda()
-
-        os.makedirs("/tmp/conda/share/iprpy")
-        self.assertEqual(
-            get_resource_path_from_conda().replace("\\", "/"), "/tmp/conda/share/iprpy"
-        )
-        df = get_potential_dataframe(
-            structure=bulk("Al"),
-        )
-        self.assertEqual(len(df), 0)
-        shutil.rmtree("/tmp/conda")
+        with tempfile.TemporaryDirectory() as tmpdir:
+            # Test ValueError when directory doesn't exist
+            with mock.patch.dict(os.environ, {"CONDA_PREFIX": tmpdir}, clear=True):
+                with self.assertRaises(ValueError):
+                    get_resource_path_from_conda()
+
+            # Test success when directory exists
+            iprpy_path = os.path.join(tmpdir, "share", "iprpy")
+            os.makedirs(iprpy_path)
+            with mock.patch.dict(os.environ, {"CONDA_PREFIX": tmpdir}, clear=True):
+                self.assertEqual(
+                    get_resource_path_from_conda().replace("\\", "/"),
+                    iprpy_path.replace("\\", "/")
+                )
+                df = get_potential_dataframe(structure=bulk("Al"))
+                self.assertEqual(len(df), 0)

202-204: Fix the resource path construction.

Same issue as in TestPotential.setUpClass: the extra ".." causes the path to resolve incorrectly.

Apply this diff:

         self.resource_path = os.path.abspath(
-            os.path.join("..", os.path.dirname(__file__), "static", "potential")
+            os.path.join(os.path.dirname(__file__), "static", "potential")
         )

217-226: Add missing test coverage for __getattr__ and __dir__.

The test only covers __repr__ and one successful __getattr__ call. Missing coverage for the AttributeError case and the __dir__ method.

Add these test methods:

    def test_get_attr_error(self):
        with self.assertRaises(AttributeError):
            _ = self.available.not_a_potential

    def test_dir(self):
        self.assertEqual(
            sorted(dir(self.available)), sorted(["pot_pot1", "pot_pot2", "pot_pot3"])
        )
🧹 Nitpick comments (1)
tests/test_potential.py (1)

218-220: Pass a list instead of a dict for clarity.

The parameter name is list_of_potentials but a dict is passed. While this works (iterating over a dict gives keys), the dict values are never used, making the intent unclear.

Apply this diff:

     def setUp(self):
-        self.potentials = {"pot1": 1, "pot2": 2, "pot3": 3}
-        self.available = PotentialAvailable(list_of_potentials=self.potentials)
+        self.available = PotentialAvailable(list_of_potentials=["pot1", "pot2", "pot3"])
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e94a9a and 985520d.

📒 Files selected for processing (1)
  • tests/test_potential.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/test_potential.py (1)
pyiron_lammps/potential.py (15)
  • validate_potential_dataframe (358-380)
  • get_potential_dataframe (340-346)
  • get_potential_by_name (349-355)
  • convert_path_to_abs_posix (287-304)
  • get_resource_path_from_conda (328-337)
  • find_potential_file_base (254-267)
  • view_potentials (270-284)
  • PotentialAbstract (37-153)
  • LammpsPotentialFile (156-231)
  • PotentialAvailable (234-251)
  • find (62-84)
  • find_by_name (86-90)
  • _get_potential_df (117-153)
  • find_default (194-221)
  • default (186-192)
🪛 Ruff (0.14.5)
tests/test_potential.py

70-70: Probable insecure usage of temporary file or directory: "/tmp/conda"

(S108)


75-75: Probable insecure usage of temporary file or directory: "/tmp/conda/share/iprpy"

(S108)


77-77: Probable insecure usage of temporary file or directory: "/tmp/conda/share/iprpy"

(S108)


83-83: Probable insecure usage of temporary file or directory: "/tmp/conda"

(S108)

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
tests/test_potential.py (3)

198-200: Fix the resource path construction causing test failure.

Same path construction error as lines 26–28: the extra ".." causes the path to resolve incorrectly.

Apply this diff:

         self.resource_path = os.path.abspath(
-            os.path.join("..", os.path.dirname(__file__), "static", "potential")
+            os.path.join(os.path.dirname(__file__), "static", "potential")
         )

213-222: Complete the TestPotentialAvailable test methods.

The test setup uses a simple dict input that doesn't fully exercise the normalization logic (replacing - and . with _). Additionally, test_getattr appears incomplete and test_dir is missing entirely. Update the test class to fully validate attribute access and directory listing.

Apply this diff to complete the test methods:

 class TestPotentialAvailable(unittest.TestCase):
     def setUp(self):
-        self.potentials = {"pot1": 1, "pot2": 2, "pot3": 3}
-        self.available = PotentialAvailable(list_of_potentials=self.potentials)
+        self.available = PotentialAvailable(
+            list_of_potentials=["pot1", "pot-2", "pot.3"]
+        )
 
     def test_repr(self):
         self.assertEqual(repr(self.available), str(dir(self.available)))
 
-    def test_get_attr(self):
-        self.assertEqual(str(self.available.pot_pot1), "pot1")
+    def test_getattr(self):
+        self.assertEqual(self.available.pot_pot1, "pot1")
+        self.assertEqual(self.available.pot_pot_2, "pot-2")
+        self.assertEqual(self.available.pot_pot_3, "pot.3")
+        with self.assertRaises(AttributeError):
+            _ = self.available.not_a_potential
+
+    def test_dir(self):
+        self.assertEqual(
+            sorted(dir(self.available)),
+            sorted(["pot_pot1", "pot_pot_2", "pot_pot_3"]),
+        )

70-79: Replace hardcoded /tmp path with tempfile for cross-platform compatibility and guaranteed cleanup.

The hardcoded /tmp/conda path is Unix-specific and fails on Windows. Additionally, if the assertion fails, the cleanup code doesn't execute, leaving test artifacts behind. Use tempfile.TemporaryDirectory() for safe, automatic cleanup.

Apply this diff:

-    @mock.patch.dict(os.environ, {"CONDA_PREFIX": "/tmp/conda"}, clear=True)
     def test_get_resource_path_from_conda(self):
-        with self.assertRaises(ValueError):
-            get_resource_path_from_conda()
-
-        os.makedirs("/tmp/conda/share/iprpy")
-        self.assertEqual(
-            get_resource_path_from_conda().replace("\\", "/"), "/tmp/conda/share/iprpy"
-        )
-        shutil.rmtree("/tmp/conda")
+        with tempfile.TemporaryDirectory() as tmpdir:
+            # Test ValueError when directory doesn't exist
+            with mock.patch.dict(os.environ, {"CONDA_PREFIX": tmpdir}, clear=True):
+                with self.assertRaises(ValueError):
+                    get_resource_path_from_conda()
+
+            # Test success when directory exists
+            iprpy_path = os.path.join(tmpdir, "share", "iprpy")
+            os.makedirs(iprpy_path)
+            with mock.patch.dict(os.environ, {"CONDA_PREFIX": tmpdir}, clear=True):
+                self.assertEqual(
+                    get_resource_path_from_conda().replace("\\", "/"), 
+                    iprpy_path.replace("\\", "/")
+                )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 985520d and d55975c.

📒 Files selected for processing (1)
  • tests/test_potential.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/test_potential.py (1)
pyiron_lammps/potential.py (15)
  • validate_potential_dataframe (358-380)
  • get_potential_dataframe (340-346)
  • get_potential_by_name (349-355)
  • convert_path_to_abs_posix (287-304)
  • get_resource_path_from_conda (328-337)
  • find_potential_file_base (254-267)
  • view_potentials (270-284)
  • PotentialAbstract (37-153)
  • LammpsPotentialFile (156-231)
  • PotentialAvailable (234-251)
  • find (62-84)
  • find_by_name (86-90)
  • _get_potential_df (117-153)
  • find_default (194-221)
  • default (186-192)
🪛 Ruff (0.14.5)
tests/test_potential.py

70-70: Probable insecure usage of temporary file or directory: "/tmp/conda"

(S108)


75-75: Probable insecure usage of temporary file or directory: "/tmp/conda/share/iprpy"

(S108)


77-77: Probable insecure usage of temporary file or directory: "/tmp/conda/share/iprpy"

(S108)


79-79: Probable insecure usage of temporary file or directory: "/tmp/conda"

(S108)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: unittest_matrix (windows-latest, 3.14)

Comment thread tests/test_potential.py
Comment on lines +26 to +28
cls.resource_path = os.path.abspath(
os.path.join("..", os.path.dirname(__file__), "static", "potential")
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the resource path construction causing test failures.

The extra ".." prefix in the path join causes the resource path to resolve to ../tests/static/potential instead of tests/static/potential, which breaks resource discovery.

Apply this diff:

         cls.resource_path = os.path.abspath(
-            os.path.join("..", os.path.dirname(__file__), "static", "potential")
+            os.path.join(os.path.dirname(__file__), "static", "potential")
         )
🤖 Prompt for AI Agents
In tests/test_potential.py around lines 26 to 28, the resource path is built
with an extra ".." which causes the path to point to ../tests/static/potential
instead of tests/static/potential; fix by removing the leading ".." from the
os.path.join call so the path is constructed as
os.path.join(os.path.dirname(__file__), "static", "potential") and then wrapped
with os.path.abspath (i.e., compute the absolute path based on the test file
directory without the extra parent directory step).

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
tests/test_potential.py (3)

26-28: Fix the resource path construction causing test failure.

The extra ".." in the path construction causes it to resolve to ../tests/static/potential instead of tests/static/potential, breaking resource discovery.

Apply this diff:

         cls.resource_path = os.path.abspath(
-            os.path.join("..", os.path.dirname(__file__), "static", "potential")
+            os.path.join(os.path.dirname(__file__), "static", "potential")
         )

70-80: Replace hardcoded /tmp path with tempfile for cross-platform compatibility.

The hardcoded /tmp/conda path will fail on Windows and may conflict with existing directories. If the assertion at line 76 fails, cleanup at line 79 won't execute.

Apply this diff:

-    @mock.patch.dict(os.environ, {"CONDA_PREFIX": "/tmp/conda"}, clear=True)
     def test_get_resource_path_from_conda(self):
-        with self.assertRaises(ValueError):
-            get_resource_path_from_conda()
-
-        os.makedirs("/tmp/conda/share/iprpy")
-        self.assertEqual(
-            get_resource_path_from_conda().replace("\\", "/"), "/tmp/conda/share/iprpy"
-        )
-        shutil.rmtree("/tmp/conda")
+        with tempfile.TemporaryDirectory() as tmpdir:
+            # Test ValueError when directory doesn't exist
+            with mock.patch.dict(os.environ, {"CONDA_PREFIX": tmpdir}, clear=True):
+                with self.assertRaises(ValueError):
+                    get_resource_path_from_conda()
+
+            # Test success when directory exists
+            iprpy_path = os.path.join(tmpdir, "share", "iprpy")
+            os.makedirs(iprpy_path)
+            with mock.patch.dict(os.environ, {"CONDA_PREFIX": tmpdir}, clear=True):
+                self.assertEqual(
+                    get_resource_path_from_conda().replace("\\", "/"),
+                    iprpy_path.replace("\\", "/")
+                )

198-200: Fix the resource path construction.

Same path construction error as in TestPotential.setUpClass. The extra ".." will cause tests to fail.

Apply this diff:

         self.resource_path = os.path.abspath(
-            os.path.join("..", os.path.dirname(__file__), "static", "potential")
+            os.path.join(os.path.dirname(__file__), "static", "potential")
         )
🧹 Nitpick comments (3)
tests/test_potential.py (3)

210-221: Consider adding test for LammpsPotentialFile.getitem override.

LammpsPotentialFile overrides __getitem__ to return a LammpsPotentialFile instance instead of PotentialAbstract. This behavioral difference should be tested to ensure type consistency.

Add this test method:

def test_getitem(self):
    potential = self.potential["Al"]
    self.assertIsInstance(potential, LammpsPotentialFile)
    self.assertEqual(len(potential.list()), 1)

226-227: Consider using a list instead of a dict for semantic clarity.

While iterating over a dict yields keys (making the test functional), list_of_potentials is semantically a list of potential names, not a dict.

Apply this diff:

-        self.potentials = {"pot1": 1, "pot2": 2, "pot3": 3}
-        self.available = PotentialAvailable(list_of_potentials=self.potentials)
+        self.available = PotentialAvailable(list_of_potentials=["pot1", "pot2", "pot3"])

229-233: Expand test coverage for PotentialAvailable.

Good progress adding test_get_attr, but the test coverage for __getattr__ and __dir__ remains incomplete. Consider adding more assertions and a dedicated test_dir method.

Expand the test coverage:

     def test_get_attr(self):
         self.assertEqual(str(self.available.pot_pot1), "pot1")
+        self.assertEqual(self.available.pot_pot2, "pot2")
+        self.assertEqual(self.available.pot_pot3, "pot3")
+        with self.assertRaises(AttributeError):
+            _ = self.available.not_a_potential
+
+    def test_dir(self):
+        self.assertEqual(
+            sorted(dir(self.available)), 
+            sorted(["pot_pot1", "pot_pot2", "pot_pot3"])
+        )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d55975c and 6327e73.

📒 Files selected for processing (1)
  • tests/test_potential.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/test_potential.py (1)
pyiron_lammps/potential.py (15)
  • validate_potential_dataframe (358-380)
  • get_potential_dataframe (340-346)
  • get_potential_by_name (349-355)
  • convert_path_to_abs_posix (287-304)
  • get_resource_path_from_conda (328-337)
  • find_potential_file_base (254-267)
  • view_potentials (270-284)
  • PotentialAbstract (37-153)
  • LammpsPotentialFile (156-231)
  • PotentialAvailable (234-251)
  • find (62-84)
  • find_by_name (86-90)
  • _get_potential_df (117-153)
  • find_default (194-221)
  • default (186-192)
🪛 Ruff (0.14.5)
tests/test_potential.py

70-70: Probable insecure usage of temporary file or directory: "/tmp/conda"

(S108)


75-75: Probable insecure usage of temporary file or directory: "/tmp/conda/share/iprpy"

(S108)


77-77: Probable insecure usage of temporary file or directory: "/tmp/conda/share/iprpy"

(S108)


79-79: Probable insecure usage of temporary file or directory: "/tmp/conda"

(S108)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: unittest_matrix (windows-latest, 3.14)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant