Skip to content

Commit 1081515

Browse files
wesmclaude
andcommitted
refactor: Consolidate inline imports across entire codebase
Moved all inline imports to top of files per project code style: Files updated by Task agents: - app_controller.py: 10 inline imports removed - demo_data_generator.py: 1 inline import removed - screens/credential_screens.py: 3 inline imports removed - screens/duplicates_screen.py: 2 inline imports removed - keybindings.py: 1 inline import removed Also updated CLAUDE.md to document the "no inline imports" rule. Benefits: - Faster execution (imports at module load, not per call) - Better code readability - Dependencies visible at a glance - Follows Python best practices All 653 tests pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 7d2bbbe commit 1081515

File tree

6 files changed

+22
-43
lines changed

6 files changed

+22
-43
lines changed

CLAUDE.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,14 @@ open htmlcov/index.html
183183

184184
## Code Style
185185

186-
- Use type hints for all function signatures
187-
- Document complex logic with comments
188-
- Keep functions focused and single-purpose
189-
- Use meaningful variable names
186+
- **Use type hints** for all function signatures
187+
- **No inline imports**: All imports must be at the top of the file, not inside functions/methods
188+
- Inline imports are slower (import happens on every call)
189+
- Harder to see dependencies at a glance
190+
- Exception: Circular import issues (rare)
191+
- **Document complex logic** with comments explaining "why", not "what"
192+
- **Keep functions focused** - Single responsibility, easy to test
193+
- **Use meaningful variable names** - Prefer clarity over brevity
190194

191195
## Making Changes to monarchmoney.py
192196

moneyflow/app_controller.py

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@
1818
"""
1919

2020
from typing import Optional, List
21+
from datetime import datetime, date as date_type
22+
import polars as pl
23+
2124
from .view_interface import IViewPresenter
22-
from .state import AppState, ViewMode, SortMode, SortDirection, TransactionEdit
25+
from .state import AppState, ViewMode, SortMode, SortDirection, TransactionEdit, TimeFrame
2326
from .data_manager import DataManager
2427
from .formatters import ViewPresenter
2528
from .commit_orchestrator import CommitOrchestrator
29+
from .time_navigator import TimeNavigator
2630
from .logging_config import get_logger
2731

2832
logger = get_logger(__name__)
@@ -150,7 +154,6 @@ def refresh_view(self, force_rebuild: bool = True) -> None:
150154
if filtered_df is not None and not filtered_df.is_empty():
151155
# Exclude hidden from totals
152156
non_hidden_df = filtered_df.filter(filtered_df["hideFromReports"] == False)
153-
import polars as pl
154157

155158
income_df = non_hidden_df.filter(pl.col("group") == "Income")
156159
total_income = float(income_df["amount"].sum()) if not income_df.is_empty() else 0.0
@@ -332,22 +335,16 @@ def reverse_sort(self) -> str:
332335
# Time navigation operations
333336
def set_timeframe_this_year(self):
334337
"""Set view to current year."""
335-
from .state import TimeFrame
336-
337338
self.state.set_timeframe(TimeFrame.THIS_YEAR)
338339
self.refresh_view()
339340

340341
def set_timeframe_all_time(self):
341342
"""Set view to all time."""
342-
from .state import TimeFrame
343-
344343
self.state.set_timeframe(TimeFrame.ALL_TIME)
345344
self.refresh_view()
346345

347346
def set_timeframe_this_month(self):
348347
"""Set view to current month."""
349-
from .state import TimeFrame
350-
351348
self.state.set_timeframe(TimeFrame.THIS_MONTH)
352349
self.refresh_view()
353350

@@ -361,10 +358,6 @@ def select_month(self, month: int) -> str:
361358
Returns:
362359
Description of the selected time range
363360
"""
364-
from datetime import date as date_type
365-
from .time_navigator import TimeNavigator
366-
from .state import TimeFrame
367-
368361
today = date_type.today()
369362
date_range = TimeNavigator.get_month_range(today.year, month)
370363

@@ -383,9 +376,6 @@ def navigate_prev_period(self) -> tuple[bool, Optional[str]]:
383376
- should_fallback_to_year: True if in all-time view (no prev period)
384377
- description: Time range description if navigated
385378
"""
386-
from .time_navigator import TimeNavigator
387-
from .state import TimeFrame
388-
389379
if self.state.start_date is None:
390380
# In all-time view, signal to fallback to current year
391381
return (True, None)
@@ -406,9 +396,6 @@ def navigate_next_period(self) -> tuple[bool, Optional[str]]:
406396
- should_fallback_to_year: True if in all-time view (no next period)
407397
- description: Time range description if navigated
408398
"""
409-
from .time_navigator import TimeNavigator
410-
from .state import TimeFrame
411-
412399
if self.state.start_date is None:
413400
# In all-time view, signal to fallback to current year
414401
return (True, None)
@@ -615,8 +602,6 @@ def queue_category_edits(self, transactions_df, new_category_id: str) -> int:
615602
Returns:
616603
int: Number of edits queued
617604
"""
618-
from datetime import datetime
619-
620605
count = 0
621606
for txn in transactions_df.iter_rows(named=True):
622607
self.data_manager.pending_edits.append(
@@ -645,8 +630,6 @@ def queue_merchant_edits(self, transactions_df, old_merchant: str, new_merchant:
645630
Returns:
646631
int: Number of edits queued
647632
"""
648-
from datetime import datetime
649-
650633
count = 0
651634
for txn in transactions_df.iter_rows(named=True):
652635
self.data_manager.pending_edits.append(
@@ -674,8 +657,6 @@ def queue_hide_toggle_edits(self, transactions_df) -> int:
674657
Returns:
675658
int: Number of edits queued
676659
"""
677-
from datetime import datetime
678-
679660
count = 0
680661
for txn in transactions_df.iter_rows(named=True):
681662
current_hidden = txn.get("hideFromReports", False)

moneyflow/demo_data_generator.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from typing import List, Dict, Any
1111
import hashlib
1212

13+
from moneyflow.data_manager import CATEGORY_GROUPS
14+
1315

1416
class DemoDataGenerator:
1517
"""Generate realistic synthetic financial data."""
@@ -167,9 +169,6 @@ def _create_categories(self) -> List[Dict]:
167169
"Uncategorized": "grp_uncategorized",
168170
}
169171

170-
# Import CATEGORY_GROUPS to get all categories
171-
from moneyflow.data_manager import CATEGORY_GROUPS
172-
173172
# Add all categories from CATEGORY_GROUPS that aren't already in base list
174173
cat_id_counter = 100 # Start at 100 to avoid conflicts with hardcoded IDs
175174
for group_name, category_list in CATEGORY_GROUPS.items():

moneyflow/keybindings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
from dataclasses import dataclass
1010
from typing import List
1111

12+
from textual.binding import Binding
13+
1214

1315
@dataclass
1416
class KeyBinding:
@@ -85,8 +87,6 @@ def get_help_text() -> str:
8587

8688
def get_textual_bindings():
8789
"""Get bindings in Textual's Binding format."""
88-
from textual.binding import Binding
89-
9090
bindings = []
9191
for kb in KEYBINDINGS:
9292
# Only include single-key bindings for Textual

moneyflow/screens/credential_screens.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from textual.containers import Container, Vertical
77
from textual.widgets import Button, Input, Label, Static, Checkbox
88

9+
from ..credentials import CredentialManager
10+
911

1012
class BackendSelectionScreen(Screen):
1113
"""Backend selection screen for first-time setup."""
@@ -239,8 +241,6 @@ async def save_credentials(self) -> None:
239241

240242
# Save credentials
241243
try:
242-
from ..credentials import CredentialManager
243-
244244
error_label.update("💾 Saving credentials...")
245245
cred_manager = CredentialManager()
246246
cred_manager.save_credentials(
@@ -376,8 +376,6 @@ async def unlock_credentials(self) -> None:
376376
return
377377

378378
try:
379-
from ..credentials import CredentialManager
380-
381379
error_label.update("🔓 Unlocking...")
382380
cred_manager = CredentialManager()
383381
creds = cred_manager.load_credentials(encryption_password=encryption_password)
@@ -397,8 +395,6 @@ async def unlock_credentials(self) -> None:
397395
async def reset_credentials(self) -> None:
398396
"""Delete credentials and show setup screen."""
399397
try:
400-
from ..credentials import CredentialManager
401-
402398
cred_manager = CredentialManager()
403399
cred_manager.delete_credentials()
404400

moneyflow/screens/duplicates_screen.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
from textual.binding import Binding
1111
import polars as pl
1212

13+
from .transaction_detail_screen import TransactionDetailScreen
14+
from .edit_screens import DeleteConfirmationScreen
15+
1316

1417
class DuplicatesScreen(Screen):
1518
"""Screen to review and handle duplicate transactions."""
@@ -223,8 +226,6 @@ def action_show_details(self) -> None:
223226
if not txn_data:
224227
return
225228

226-
from .transaction_detail_screen import TransactionDetailScreen
227-
228229
self.app.push_screen(TransactionDetailScreen(txn_data))
229230

230231
async def action_delete_transaction(self) -> None:
@@ -239,8 +240,6 @@ async def action_delete_transaction(self) -> None:
239240
to_delete = [txn_id]
240241

241242
# Show confirmation
242-
from .edit_screens import DeleteConfirmationScreen
243-
244243
confirmed = await self.push_screen(
245244
DeleteConfirmationScreen(transaction_count=len(to_delete)), wait_for_dismiss=True
246245
)

0 commit comments

Comments
 (0)