Skip to content

Commit b9a27c4

Browse files
Apply ruff format to fix CI lint failures
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c4ae1c0 commit b9a27c4

7 files changed

Lines changed: 37 additions & 46 deletions

File tree

src/collegeplan/reporting.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,25 @@
88
from typing import Any
99

1010
# Fields that represent rates, ratios, or ages — NOT dollar amounts.
11-
_NO_ROUND_FIELDS = frozenset({
12-
"funded_ratio",
13-
"achieved_funding_ratio",
14-
"scholarship_pct",
15-
"annual_cost_growth",
16-
"general_inflation",
17-
"expected_return_nominal",
18-
"expected_return_real",
19-
"child_age",
20-
"target_funding_ratio",
21-
})
11+
_NO_ROUND_FIELDS = frozenset(
12+
{
13+
"funded_ratio",
14+
"achieved_funding_ratio",
15+
"scholarship_pct",
16+
"annual_cost_growth",
17+
"general_inflation",
18+
"expected_return_nominal",
19+
"expected_return_real",
20+
"child_age",
21+
"target_funding_ratio",
22+
}
23+
)
2224

2325

2426
def _clean(obj: Any, field_name: str = "") -> Any:
2527
"""Recursively convert a dataclass-derived structure for JSON output."""
2628
if dataclasses.is_dataclass(obj) and not isinstance(obj, type):
27-
return {
28-
f.name: _clean(getattr(obj, f.name), f.name)
29-
for f in dataclasses.fields(obj)
30-
}
29+
return {f.name: _clean(getattr(obj, f.name), f.name) for f in dataclasses.fields(obj)}
3130
if isinstance(obj, enum.Enum):
3231
return obj.value
3332
if isinstance(obj, dict):

src/collegeplan/sensitivity.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,21 +76,24 @@ def run_sensitivity(
7676
]
7777
elif key == "scholarship_pct":
7878
mod_children = [
79-
replace(c, scholarship_pct=value, scholarship_offset=0.0)
80-
for c in mod_children
79+
replace(c, scholarship_pct=value, scholarship_offset=0.0) for c in mod_children
8180
]
8281
elif key == "target_funding_ratio":
8382
mod_target = value
8483

8584
solution = solve_required_savings(
86-
mod_children, mod_assumptions, household_fund,
85+
mod_children,
86+
mod_assumptions,
87+
household_fund,
8788
target_funding_ratio=mod_target,
8889
)
8990

9091
household_result = None
9192
if include_projection:
9293
household_result = project_household_plan(
93-
mod_children, mod_assumptions, household_fund,
94+
mod_children,
95+
mod_assumptions,
96+
household_fund,
9497
)
9598

9699
cases.append(

src/collegeplan/solver.py

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ def _run_with_contribution(
2727
n = len(children)
2828
per_child = annual_contribution / n
2929
modified = [
30-
replace(c, annual_contribution=c.annual_contribution + per_child)
31-
for c in children
30+
replace(c, annual_contribution=c.annual_contribution + per_child) for c in children
3231
]
3332
hf = household_fund
3433
else: # shared_pool
@@ -76,9 +75,7 @@ def solve_required_savings(
7675
validate_plan(children, assumptions, household_fund)
7776

7877
# Check if already funded with zero additional contribution
79-
current_ratio = _run_with_contribution(
80-
children, assumptions, household_fund, 0.0, solve_mode
81-
)
78+
current_ratio = _run_with_contribution(children, assumptions, household_fund, 0.0, solve_mode)
8279
if current_ratio >= target_funding_ratio:
8380
n = len(children)
8481
return SavingsSolution(
@@ -99,46 +96,36 @@ def solve_required_savings(
9996
upper = total_cost / min_years
10097

10198
# Verify upper bound is sufficient
102-
upper_ratio = _run_with_contribution(
103-
children, assumptions, household_fund, upper, solve_mode
104-
)
99+
upper_ratio = _run_with_contribution(children, assumptions, household_fund, upper, solve_mode)
105100
while upper_ratio < target_funding_ratio:
106101
upper *= 2
107102
upper_ratio = _run_with_contribution(
108103
children, assumptions, household_fund, upper, solve_mode
109104
)
110105
if upper > total_cost * 10:
111-
raise SolverError(
112-
"Cannot find a feasible contribution within reasonable bounds"
113-
)
106+
raise SolverError("Cannot find a feasible contribution within reasonable bounds")
114107

115108
# Bisection
116109
lo, hi = 0.0, upper
117110
for _ in range(max_iterations):
118111
mid = (lo + hi) / 2
119-
ratio = _run_with_contribution(
120-
children, assumptions, household_fund, mid, solve_mode
121-
)
112+
ratio = _run_with_contribution(children, assumptions, household_fund, mid, solve_mode)
122113
if ratio < target_funding_ratio:
123114
lo = mid
124115
else:
125116
hi = mid
126117
if hi - lo < tolerance:
127118
break
128119
else:
129-
raise SolverError(
130-
f"Solver did not converge after {max_iterations} iterations"
131-
)
120+
raise SolverError(f"Solver did not converge after {max_iterations} iterations")
132121

133122
annual = (lo + hi) / 2
134123
monthly = annual / 12
135124
n = len(children)
136125
per_child = {c.name: annual / n for c in children}
137126

138127
# Verify achieved ratio
139-
achieved = _run_with_contribution(
140-
children, assumptions, household_fund, annual, solve_mode
141-
)
128+
achieved = _run_with_contribution(children, assumptions, household_fund, annual, solve_mode)
142129

143130
return SavingsSolution(
144131
required_annual_contribution=annual,

tests/test_allocation.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ def test_equal_split_capped_at_need():
2020

2121
def test_oldest_first():
2222
needs = {"A": 500, "B": 800}
23-
result = allocate_shared_withdrawal(
24-
AllocationPolicy.OLDEST_FIRST, 1000, needs, ["A", "B"]
25-
)
23+
result = allocate_shared_withdrawal(AllocationPolicy.OLDEST_FIRST, 1000, needs, ["A", "B"])
2624
assert result["A"] == 500
2725
assert result["B"] == 500 # remainder after A is fully funded
2826

tests/test_assumptions.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""Tests for assumptions normalization (Fisher equation)."""
22

3-
43
from collegeplan import Assumptions, deflate, normalize_assumptions
54
from collegeplan.assumptions import resolve_nominal_return, resolve_real_return
65

tests/test_golden.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ def test_golden_two_children_household():
6161
)
6262
assumptions = Assumptions(expected_return_nominal=0.07, general_inflation=0.03)
6363
hf = HouseholdFund(
64-
shared_balance=20_000, shared_annual_contribution=5_000,
64+
shared_balance=20_000,
65+
shared_annual_contribution=5_000,
6566
allocation_policy=AllocationPolicy.PROPORTIONAL_TO_NEED,
6667
)
6768
result = project_household_plan([c1, c2], assumptions, hf)

tests/test_reporting.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@ def test_to_dict_rounds_dollars():
2626
def test_to_dict_preserves_ratios():
2727
profile = CostProfile(label="T", current_total_cost=10_000, annual_cost_growth=0.05)
2828
child = Child(
29-
name="A", current_age=14, cost_profile=profile,
30-
start_age=18, attendance_years=4, current_529_balance=20_000,
29+
name="A",
30+
current_age=14,
31+
cost_profile=profile,
32+
start_age=18,
33+
attendance_years=4,
34+
current_529_balance=20_000,
3135
)
3236
assumptions = Assumptions(expected_return_nominal=0.07, general_inflation=0.03)
3337
result = project_child_plan(child, assumptions)

0 commit comments

Comments
 (0)