Skip to content

fix: fix .show() crash when restraints weren't added add_soft_bounds() - #227

Merged
sbillinge merged 1 commit into
diffpy:mainfrom
cadenmyers13:fix-show-restraints
Oct 15, 2026
Merged

sbillinge merged 1 commit into
diffpy:mainfrom
cadenmyers13:fix-show-restraints

Conversation

@cadenmyers13

Copy link
Copy Markdown
Contributor

What problem does this PR address?

Closes #24

show() crashed with an AttributeError whenever a recipe held a restraint that wasn't made by add_soft_bounds. That includes:

  • ObjCryst molecule restraints (bond lengths, bond angles, dihedral angles)
  • BVS restraints from restrainBVS()
  • A Restraint added directly with register_soft_bounds()

Why: show() printed every restraint as eqstr: lower_bound = ..., upper_bound = ..., sig = ..., scaled = .... Only restraints made by add_soft_bounds have those attributes. The others store different information. For example, an ObjCryst restraint only wraps a pyobjcryst object, so show() failed when it tried to read them.

Fix: Each restraint class now describes itself through __str__, and show() prints what each restraint returns:

Restraint Line shown in show()
add_soft_bounds("p", 0, 2) (unchanged) p: lower_bound = 0.000000, upper_bound = 2.000000, sig = 1.000000, scaled = False
Restraint via register_soft_bounds eq_p: lower_bound = 0.000000, upper_bound = 2.000000, sig = 1.000000, scaled = False
BVS restraint BVS of nacl: sig = 0.500000, scaled = True
ObjCryst bond length C0-C5: length = 3.300000, sigma = 0.100000, delta = 0.200000, scaled = False
ObjCryst bond / dihedral angle C0-C5-C7: angle = 1.000000, sigma = 0.100000, delta = 0.200000, scaled = False

Before:

>>> structure.restrainBVS()
>>> structure.show()
AttributeError: 'BVSRestraint' object has no attribute 'eqstr'

After: the Restraints section lists BVS of nacl: sig = 1.000000, scaled = False.

Changes:

  • Restraint sets eqstr to the equation's name by default, so a restraint built by hand has something to show. add_soft_bounds still overrides it as before.
  • Restraint, BVSRestraint and ObjCrystMoleculeRestraint each get a __str__. The ObjCryst one also covers restraints wrapped from an existing molecule with wrapRestraints().

Tests: I added show() tests for each kind of restraint in test_recipeorganizer.py, test_diffpyparset.py and test_objcrystparset.py.

What should the reviewer(s) do?

Please review and merge. One point you may want to check: ObjCrystMoleculeRestraint.__str__ decides between a bond length and an angle by checking hasattr(res, "GetLength0"). It has to work this way because restraints wrapped from an existing molecule (wrapRestraints()) are plain ObjCrystMoleculeRestraint objects, not the bond-length or angle subclasses.

@cadenmyers13

Copy link
Copy Markdown
Contributor Author

@sbillinge ready for review, The description is very claude-y 😂 but i felt it did a good job describing the changes

@codecov

codecov Bot commented Oct 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 46.29630% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.70%. Comparing base (a9eae82) to head (69a21f8).
⚠️ Report is 21 commits behind head on main.

Files with missing lines Patch % Lines
tests/test_objcrystparset.py 9.09% 20 Missing ⚠️
tests/test_diffpyparset.py 30.76% 9 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main     #227       +/-   ##
===========================================
+ Coverage   71.63%   84.70%   +13.07%     
===========================================
  Files          25       43       +18     
  Lines        3437     4805     +1368     
===========================================
+ Hits         2462     4070     +1608     
+ Misses        975      735      -240     
Files with missing lines Coverage Δ
tests/test_recipeorganizer.py 100.00% <100.00%> (+0.30%) ⬆️
tests/test_diffpyparset.py 89.58% <30.76%> (-9.22%) ⬇️
tests/test_objcrystparset.py 10.19% <9.09%> (-0.08%) ⬇️

... and 19 files with indirect coverage changes

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

@cadenmyers13

Copy link
Copy Markdown
Contributor Author

I think codecov isnt happy because def __str__ doesnt have tests. Do you think it really needs tests?

@sbillinge
sbillinge merged commit bf21ba0 into diffpy:main Oct 15, 2026
6 of 7 checks passed
@sbillinge

Copy link
Copy Markdown
Contributor

yes, claudy but easy to follow. It will be work to test those string methods and if we change them the test will break for no particularly good reason, so I think this is ok as it is.

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.

RecipeOrganizer.show() fails when objcryst restraints are defined

2 participants