Skip to content

sqs improve interface#476

Open
dgehringer wants to merge 10 commits intomainfrom
sqs-improve-interface
Open

sqs improve interface#476
dgehringer wants to merge 10 commits intomainfrom
sqs-improve-interface

Conversation

@dgehringer
Copy link
Copy Markdown

@dgehringer dgehringer commented Apr 1, 2026

Add more features to the sqsgenerator interface (>0.4.9)

Summary by CodeRabbit

  • Refactor

    • Reworked SQS generation internals into a dedicated package with a streamlined public interface.
  • New Features

    • Introduced richer, iterable result objects exposing per-result structures, objectives, SRO access, and sublattice-aware modes.
  • Tests

    • Updated tests to validate the new result API, multi-sublattice scenarios, error cases, and iterator behavior.
  • Chores

    • Updated sqsgenerator dependency to 0.5.5.

@dgehringer dgehringer requested a review from jan-janssen as a code owner April 1, 2026 18:19
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 1, 2026

📝 Walkthrough

Walkthrough

The monolithic src/structuretoolkit/build/sqs.py was removed and replaced by a new src/structuretoolkit/build/sqs/ package exposing typed interfaces (_types.py), a threaded public API (_interface.py) and an initializer; tests were updated to use the new result-object API and CI pinned sqsgenerator was bumped to 0.5.5.

Changes

Cohort / File(s) Summary
Dependency Update
\.ci_support/environment.yml
Bumped pinned sqsgenerator from 0.5.40.5.5.
Removed Monolith
src/structuretoolkit/build/sqs.py
Deleted legacy single-file SQS utilities and previous sqs_structures implementation (all functions removed).
New Package Init
src/structuretoolkit/build/sqs/__init__.py
Added package initializer re-exporting public types and sqs_structures, and defining __all__.
New Types
src/structuretoolkit/build/sqs/_types.py
Added SQS type aliases, Composition/Site definitions, SroParameter class, and SqsResultInteract / SqsResultSplit Protocols for result shapes.
New Interface
src/structuretoolkit/build/sqs/_interface.py
Added public sqs_structures(...) with overloaded signatures, config parsing via sqsgenerator.parse_config, threaded optimizer invocation with stop callback, and SqsResultPack / _SqsResultProxy wrappers exposing best(), atoms(), sro(), sublattices(), iteration, and metadata.
Tests
tests/test_sqs.py
Updated tests to the composition-based API and new result-object semantics; added error checks and multi-sublattice cases; removed old mole-fractions/statistics-style assertions.

Sequence Diagram(s)

sequenceDiagram
    participant Caller as Client
    participant API as stk.build.sqs_structures
    participant Parser as sqsgenerator.parse_config
    participant Worker as WorkerThread
    participant Core as sqsgenerator.core.optimize
    participant ResultWrap as SqsResultPack

    Caller->>API: call sqs_structures(Atoms, composition, ...)
    API->>Parser: parse configuration
    alt parse ok
        API->>Worker: start optimizer thread (with stop callback)
        Worker->>Core: run optimize(config, callback)
        Core-->>Worker: optimization result
        Worker->>API: signal completion
        API->>ResultWrap: wrap optimizer result
        API-->>Caller: return SqsResultPack
    else parse failure
        Parser-->>API: raise ValueError
        API-->>Caller: raise ValueError
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 Once a long file, I hopped and I split,
Types in tidy rows, interfaces fit.
Threads hum and optimize, results wrapped with care,
Tests hop through compositions — carrots to share. 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'sqs improve interface' is vague and generic, lacking specific details about what improvements were made to the SQS interface. Use a more descriptive title that clarifies the specific improvements, such as 'Refactor SQS interface to use result objects' or 'Replace sqs_structures with result-wrapper API'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sqs-improve-interface

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.

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: 6

🧹 Nitpick comments (2)
src/structuretoolkit/build/sqs/__init__.py (1)

19-35: Optional: __all__ is not sorted.

Per static analysis (RUF022), __all__ entries should follow isort-style sorting for consistency.

♻️ Sorted `__all__`
 __all__ = [
     "Composition",
     "Element",
     "IterationMode",
     "Prec",
-    "ResultType",
     "Shell",
     "ShellRadii",
     "ShellWeights",
     "Site",
+    "SqsResult",
     "SroParameter",
-    "SqsResult",
     "SqsResultInteract",
     "SqsResultSplit",
     "SublatticeMode",
     "sqs_structures",
 ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/structuretoolkit/build/sqs/__init__.py` around lines 19 - 35, The __all__
list in this module is not alphabetically sorted; update the __all__ variable to
use isort-style (alphabetical) ordering so exports are consistent and satisfy
RUF022. Locate the __all__ declaration (the list containing "Composition",
"Element", ..., "sqs_structures") and reorder its string entries alphabetically
(preserving exact symbol names and casing), then run the project's
linters/formatters to confirm the change.
src/structuretoolkit/build/sqs/_interface.py (1)

284-288: Exception in worker thread is silently lost.

If sqs_optimize raises an exception, it's swallowed by the thread and optimization_result remains None, resulting in a generic RuntimeError. Consider capturing and re-raising the actual exception for better debugging.

♻️ Proposed enhancement to preserve exception info
+    optimization_exception: BaseException | None = None
+
     def _optimize():
+        nonlocal optimization_exception
         try:
             result_local = sqs_optimize(config, log_level=level, callback=_callback)
-            stop_event.set()
             nonlocal optimization_result
             optimization_result = result_local
+        except BaseException as e:
+            optimization_exception = e
+        finally:
+            stop_event.set()

Then at line 300:

     if optimization_result is None:
+        if optimization_exception is not None:
+            raise RuntimeError("Optimization failed") from optimization_exception
         raise RuntimeError("Optimization failed to produce a result.")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/structuretoolkit/build/sqs/_interface.py` around lines 284 - 288, The
worker thread function _optimize currently calls sqs_optimize and assigns
optimization_result but swallows exceptions; change _optimize to catch Exception
as e, store that exception (e) or its traceback into a shared variable (e.g.,
optimization_result or a new optimization_exception) and ensure stop_event.set()
is still called, then in the main thread where optimization_result is inspected
re-raise the captured exception (or raise RuntimeError from the captured
exception) so the actual error/traceback from sqs_optimize is preserved;
reference _optimize, sqs_optimize, optimization_result and stop_event when
implementing this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/structuretoolkit/build/sqs/__init__.py`:
- Around line 1-16: The import of ResultType in
src/structuretoolkit/build/sqs/__init__.py is invalid because ResultType is not
defined in _types.py; either remove ResultType from the import list in
__init__.py (and from any __all__ export there) or add a proper ResultType
definition to _types.py if it's required; update usages referencing ResultType
(search for ResultType in the package) to use the new type or appropriate
existing type to avoid broken references.

In `@src/structuretoolkit/build/sqs/_interface.py`:
- Around line 99-118: The overload for sqs_structures has a type mismatch:
objective is annotated as list[float] but given a default 0.0; update the
overload signature for sqs_structures so the objective parameter matches the
implementation (accepts list[float] | float with default 0.0) — i.e., change the
overload's objective type to list[float] | float = 0.0 (or alternatively provide
a list default like [0.0] if you prefer list-only), ensuring the overload and
the implementation (which declares objective: list[float] | float = 0.0) are
consistent.
- Around line 292-298: The loop waiting on stop_event can block forever if the
worker thread (t running sqs_optimize) crashes before setting stop_event; change
the wait to a timed wait so the main thread wakes periodically and re-checks
thread liveness. Update the loop around t.is_alive() and stop_event.wait() to
call stop_event.wait(timeout=...) (e.g., 0.5–1s) or replace with
t.join(timeout=...) so that if t.is_alive() becomes False the loop breaks; keep
the existing except/finally behavior (including t.join()) but ensure the timed
wait lets the main thread detect a dead worker and exit instead of hanging.

In `@src/structuretoolkit/build/sqs/_types.py`:
- Line 131: The Composition type fails because Element is currently defined as a
list object rather than a type alias; change the Element symbol into a proper
type alias (e.g., a Literal union or Typed alias) so Composition = dict[Element
| Literal["sites"], Union[int, Site]] is valid; update the Element declaration
(and imports if necessary) to use typing/typing_extensions TypeAlias or
Literal/Union types and ensure Site and Composition use those type names
consistently.
- Around line 9-125: Element is currently a runtime list literal, not a type, so
using it in annotations like Composition causes errors; replace it with a proper
type definition and keep the runtime list separate: create a runtime constant
(e.g., ELEMENT_SYMBOLS) holding the list of symbols and define Element as a type
alias or a Literal union (e.g., Element: TypeAlias = Literal["0","H","He",...])
or simply Element: TypeAlias = str if you don't need strict literals, then
update usages (e.g., Composition) to reference the new Element type alias
instead of the list object.
- Around line 1-195: Define the missing exported type alias ResultType in this
module by adding a TypeAlias that points to the existing union SqsResult (e.g.
ResultType: TypeAlias = SqsResult) so imports from __init__.py succeed; place
the declaration near the other aliases (after SqsResult) and ensure the name
ResultType is available at module level for export.

---

Nitpick comments:
In `@src/structuretoolkit/build/sqs/__init__.py`:
- Around line 19-35: The __all__ list in this module is not alphabetically
sorted; update the __all__ variable to use isort-style (alphabetical) ordering
so exports are consistent and satisfy RUF022. Locate the __all__ declaration
(the list containing "Composition", "Element", ..., "sqs_structures") and
reorder its string entries alphabetically (preserving exact symbol names and
casing), then run the project's linters/formatters to confirm the change.

In `@src/structuretoolkit/build/sqs/_interface.py`:
- Around line 284-288: The worker thread function _optimize currently calls
sqs_optimize and assigns optimization_result but swallows exceptions; change
_optimize to catch Exception as e, store that exception (e) or its traceback
into a shared variable (e.g., optimization_result or a new
optimization_exception) and ensure stop_event.set() is still called, then in the
main thread where optimization_result is inspected re-raise the captured
exception (or raise RuntimeError from the captured exception) so the actual
error/traceback from sqs_optimize is preserved; reference _optimize,
sqs_optimize, optimization_result and stop_event when implementing this change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 529c7ba7-59c5-4ed0-90a3-6e9e805cb137

📥 Commits

Reviewing files that changed from the base of the PR and between 2af45d9 and 09e48a1.

📒 Files selected for processing (6)
  • .ci_support/environment.yml
  • src/structuretoolkit/build/sqs.py
  • src/structuretoolkit/build/sqs/__init__.py
  • src/structuretoolkit/build/sqs/_interface.py
  • src/structuretoolkit/build/sqs/_types.py
  • tests/test_sqs.py
💤 Files with no reviewable changes (1)
  • src/structuretoolkit/build/sqs.py

Comment on lines +1 to +195
import numpy as np
from ase import Atoms
from typing import Literal, TypeAlias, Protocol, overload, Union

Shell: TypeAlias = int

SublatticeMode = Literal["split", "interact"]
IterationMode = Literal["random", "systematic"]
Element = [
"0",
"H",
"He",
"Li",
"Be",
"B",
"C",
"N",
"O",
"F",
"Ne",
"Na",
"Mg",
"Al",
"Si",
"P",
"S",
"Cl",
"Ar",
"K",
"Ca",
"Sc",
"Ti",
"V",
"Cr",
"Mn",
"Fe",
"Co",
"Ni",
"Cu",
"Zn",
"Ga",
"Ge",
"As",
"Se",
"Br",
"Kr",
"Rb",
"Sr",
"Y",
"Zr",
"Nb",
"Mo",
"Tc",
"Ru",
"Rh",
"Pd",
"Ag",
"Cd",
"In",
"Sn",
"Sb",
"Te",
"I",
"Xe",
"Cs",
"Ba",
"La",
"Ce",
"Pr",
"Nd",
"Pm",
"Sm",
"Eu",
"Gd",
"Tb",
"Dy",
"Ho",
"Er",
"Tm",
"Yb",
"Lu",
"Hf",
"Ta",
"W",
"Re",
"Os",
"Ir",
"Pt",
"Au",
"Hg",
"Tl",
"Pb",
"Bi",
"Po",
"At",
"Rn",
"Fr",
"Ra",
"Ac",
"Th",
"Pa",
"U",
"Np",
"Pu",
"Am",
"Cm",
"Bk",
"Cf",
"Es",
"Fm",
"Md",
"No",
"Lr",
"Rf",
"Db",
"Sg",
"Bh",
"Hs",
"Mt",
"Ds",
"Rg",
"Cn",
"Uut",
"Fl",
]

Site = Union[str, list[int]]

Prec = Literal["single", "double"]

Composition = dict[Element | Literal["sites"], Union[int, Site]]

ShellWeights = dict[Shell, float]

ShellRadii = list[float]

LogLevel = Literal["warn", "info", "debug", "error", "trace"]


class SroParameter:
@property
def i(self) -> int: ...

@property
def j(self) -> int: ...

@property
def shell(self) -> int: ...

def __float__(self) -> float: ...

@property
def value(self) -> float: ...


class SqsResultInteract(Protocol):
def shell_index(self, shell: int) -> int: ...

def species_index(self, species: int) -> int: ...

def rank(self) -> str: ...

@overload
def sro(
self,
) -> (
np.ndarray[tuple[int, int, int], np.dtype[np.float32]]
| np.ndarray[tuple[int, int, int], np.dtype[np.float64]]
): ...

@overload
def sro(self, shell: int) -> list[SroParameter]: ...

@overload
def sro(self, i: int, j: int) -> list[SroParameter]: ...

@overload
def sro(self, shell: int, i: int, j: int) -> SroParameter: ...

@property
def objective(self) -> float: ...

def atoms(self) -> Atoms: ...


class SqsResultSplit(Protocol):
@property
def objective(self) -> float: ...

def atoms(self) -> Atoms: ...

def sublattices(self) -> list[SqsResultInteract]: ...


SqsResult = SqsResultSplit | SqsResultInteract
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify ResultType is not defined in _types.py
echo "Searching for ResultType definition in _types.py:"
rg -n "ResultType" src/structuretoolkit/build/sqs/_types.py || echo "ResultType NOT found in _types.py"

echo ""
echo "Checking if ResultType is imported in __init__.py:"
rg -n "ResultType" src/structuretoolkit/build/sqs/__init__.py

Repository: pyiron/structuretoolkit

Length of output: 241


Critical: ResultType is not defined in this file but is imported in __init__.py.

The pipeline fails because __init__.py imports ResultType from this module (line 9) and exports it (line 24), but it's not defined here. Add the ResultType definition to this file—likely as a type alias for SqsResult or another result type.

🧰 Tools
🪛 GitHub Actions: Pipeline

[error] 1-1: ImportError during test collection: cannot import name 'ResultType' from 'structuretoolkit.build.sqs._types' (/home/runner/miniconda3/envs/test/lib/python3.14/site-packages/structuretoolkit/build/sqs/_types.py).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/structuretoolkit/build/sqs/_types.py` around lines 1 - 195, Define the
missing exported type alias ResultType in this module by adding a TypeAlias that
points to the existing union SqsResult (e.g. ResultType: TypeAlias = SqsResult)
so imports from __init__.py succeed; place the declaration near the other
aliases (after SqsResult) and ensure the name ResultType is available at module
level for export.

Comment on lines +9 to +125
Element = [
"0",
"H",
"He",
"Li",
"Be",
"B",
"C",
"N",
"O",
"F",
"Ne",
"Na",
"Mg",
"Al",
"Si",
"P",
"S",
"Cl",
"Ar",
"K",
"Ca",
"Sc",
"Ti",
"V",
"Cr",
"Mn",
"Fe",
"Co",
"Ni",
"Cu",
"Zn",
"Ga",
"Ge",
"As",
"Se",
"Br",
"Kr",
"Rb",
"Sr",
"Y",
"Zr",
"Nb",
"Mo",
"Tc",
"Ru",
"Rh",
"Pd",
"Ag",
"Cd",
"In",
"Sn",
"Sb",
"Te",
"I",
"Xe",
"Cs",
"Ba",
"La",
"Ce",
"Pr",
"Nd",
"Pm",
"Sm",
"Eu",
"Gd",
"Tb",
"Dy",
"Ho",
"Er",
"Tm",
"Yb",
"Lu",
"Hf",
"Ta",
"W",
"Re",
"Os",
"Ir",
"Pt",
"Au",
"Hg",
"Tl",
"Pb",
"Bi",
"Po",
"At",
"Rn",
"Fr",
"Ra",
"Ac",
"Th",
"Pa",
"U",
"Np",
"Pu",
"Am",
"Cm",
"Bk",
"Cf",
"Es",
"Fm",
"Md",
"No",
"Lr",
"Rf",
"Db",
"Sg",
"Bh",
"Hs",
"Mt",
"Ds",
"Rg",
"Cn",
"Uut",
"Fl",
]
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

Element is defined as a list, not a type alias.

Element is assigned a list literal, which makes it a runtime list object rather than a type. This will cause issues when used in type annotations like Composition = dict[Element | Literal["sites"], ...] at line 131 — a list cannot be used as a type key.

If the intent is to define a type for valid element symbols, use Literal:

🔧 Proposed fix using Literal type
-Element = [
-    "0",
-    "H",
-    "He",
-    ...
-    "Fl",
-]
+Element: TypeAlias = Literal[
+    "0", "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne",
+    "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca",
+    "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn",
+    "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr",
+    "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn",
+    "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd",
+    "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb",
+    "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg",
+    "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th",
+    "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm",
+    "Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds",
+    "Rg", "Cn", "Uut", "Fl",
+]

Alternatively, if you want to keep the list for runtime validation and have a separate type, define both:

ELEMENT_SYMBOLS = ["0", "H", "He", ...]  # runtime list
Element: TypeAlias = str  # simple type alias
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/structuretoolkit/build/sqs/_types.py` around lines 9 - 125, Element is
currently a runtime list literal, not a type, so using it in annotations like
Composition causes errors; replace it with a proper type definition and keep the
runtime list separate: create a runtime constant (e.g., ELEMENT_SYMBOLS) holding
the list of symbols and define Element as a type alias or a Literal union (e.g.,
Element: TypeAlias = Literal["0","H","He",...]) or simply Element: TypeAlias =
str if you don't need strict literals, then update usages (e.g., Composition) to
reference the new Element type alias instead of the list object.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 1, 2026

Codecov Report

❌ Patch coverage is 82.22222% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.03%. Comparing base (94ec44a) to head (a4fb0e1).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/structuretoolkit/build/sqs/_interface.py 79.13% 24 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #476      +/-   ##
==========================================
+ Coverage   82.69%   83.03%   +0.33%     
==========================================
  Files          25       27       +2     
  Lines        1832     1904      +72     
==========================================
+ Hits         1515     1581      +66     
- Misses        317      323       +6     

☔ 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.

🧹 Nitpick comments (1)
src/structuretoolkit/build/sqs/__init__.py (1)

18-33: Consider sorting __all__ for consistency.

Ruff flags that __all__ is not sorted. Sorting the list alphabetically improves consistency and makes future additions easier to review.

📋 Proposed alphabetical sort
 __all__ = [
     "Composition",
     "Element",
     "IterationMode",
     "Prec",
     "Shell",
     "ShellRadii",
     "ShellWeights",
     "Site",
-    "SroParameter",
     "SqsResult",
     "SqsResultInteract",
     "SqsResultSplit",
+    "SroParameter",
     "SublatticeMode",
     "sqs_structures",
 ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/structuretoolkit/build/sqs/__init__.py` around lines 18 - 33, The __all__
list in the module is unsorted; sort the entries in the __all__ list
alphabetically (by string) while keeping the same items and punctuation so
semantics don't change—update the __all__ definition (which currently contains
"Composition", "Element", "IterationMode", "Prec", "Shell", "ShellRadii",
"ShellWeights", "Site", "SroParameter", "SqsResult", "SqsResultInteract",
"SqsResultSplit", "SublatticeMode", "sqs_structures") to an alphabetically
ordered sequence to satisfy the linter.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/structuretoolkit/build/sqs/__init__.py`:
- Around line 18-33: The __all__ list in the module is unsorted; sort the
entries in the __all__ list alphabetically (by string) while keeping the same
items and punctuation so semantics don't change—update the __all__ definition
(which currently contains "Composition", "Element", "IterationMode", "Prec",
"Shell", "ShellRadii", "ShellWeights", "Site", "SroParameter", "SqsResult",
"SqsResultInteract", "SqsResultSplit", "SublatticeMode", "sqs_structures") to an
alphabetically ordered sequence to satisfy the linter.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eb11e8f4-48e2-4d88-b418-239b2b8743cb

📥 Commits

Reviewing files that changed from the base of the PR and between 09e48a1 and 5d01b00.

📒 Files selected for processing (1)
  • src/structuretoolkit/build/sqs/__init__.py

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/structuretoolkit/build/sqs/_interface.py`:
- Around line 298-305: The current except TimeoutError after calling
t.join(timeout=5.0) is ineffective because Thread.join never raises
TimeoutError; instead check t.is_alive() after join and raise the RuntimeError
if the thread remains alive. Update the block around t.join(timeout=5.0) (the
thread variable `t`) to call t.join(timeout=5.0) then if t.is_alive(): raise the
same RuntimeError (leave message unchanged) rather than relying on an except
TimeoutError branch so stopping logic in sqs_structures becomes effective.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 23325877-d56f-4034-bb90-09a2e736845b

📥 Commits

Reviewing files that changed from the base of the PR and between 5d01b00 and a4fb0e1.

📒 Files selected for processing (3)
  • src/structuretoolkit/build/sqs/_interface.py
  • src/structuretoolkit/build/sqs/_types.py
  • tests/test_sqs.py
✅ Files skipped from review due to trivial changes (1)
  • src/structuretoolkit/build/sqs/_types.py

Comment on lines +298 to +305
try:
t.join(timeout=5.0)
except TimeoutError:
raise RuntimeError(
"Optimization thread did not finish within the timeout period after requesting it to stop. "
"The optimization may still be running in the background. Try to decrease chunk_size by passing it as a "
"keyword argument to sqs_structures to make the optimization more responsive to stop requests."
)
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

Thread.join() does not raise TimeoutError—the timeout check is ineffective.

Thread.join(timeout=5.0) returns None regardless of whether the thread terminated or the timeout elapsed. To detect a timeout, you must check t.is_alive() afterwards. The current code's except TimeoutError block will never execute.

🐛 Proposed fix
     finally:
-        try:
-            t.join(timeout=5.0)
-        except TimeoutError:
+        t.join(timeout=5.0)
+        if t.is_alive():
             raise RuntimeError(
                 "Optimization thread did not finish within the timeout period after requesting it to stop. "
                 "The optimization may still be running in the background. Try to decrease chunk_size by passing it as a "
                 "keyword argument to sqs_structures to make the optimization more responsive to stop requests."
             )
📝 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
try:
t.join(timeout=5.0)
except TimeoutError:
raise RuntimeError(
"Optimization thread did not finish within the timeout period after requesting it to stop. "
"The optimization may still be running in the background. Try to decrease chunk_size by passing it as a "
"keyword argument to sqs_structures to make the optimization more responsive to stop requests."
)
finally:
t.join(timeout=5.0)
if t.is_alive():
raise RuntimeError(
"Optimization thread did not finish within the timeout period after requesting it to stop. "
"The optimization may still be running in the background. Try to decrease chunk_size by passing it as a "
"keyword argument to sqs_structures to make the optimization more responsive to stop requests."
)
🧰 Tools
🪛 Ruff (0.15.7)

[warning] 301-305: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/structuretoolkit/build/sqs/_interface.py` around lines 298 - 305, The
current except TimeoutError after calling t.join(timeout=5.0) is ineffective
because Thread.join never raises TimeoutError; instead check t.is_alive() after
join and raise the RuntimeError if the thread remains alive. Update the block
around t.join(timeout=5.0) (the thread variable `t`) to call t.join(timeout=5.0)
then if t.is_alive(): raise the same RuntimeError (leave message unchanged)
rather than relying on an except TimeoutError branch so stopping logic in
sqs_structures becomes effective.

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