Skip to content

Refactor Slice 4: Decompose Case god object #5000

Description

@jasonb5

Part of #4950. Slice 4 of 5. Estimated 6 weeks. Depends on Slices 1–3B.

Goal

Decompose the Case class (case.py, 2600 lines, 63 methods) into focused
subsystems while consolidating the scattered Case-related free functions in
utils.py, build.py, status.py, locked_files.py, and case_run.py.

End state: a smaller Case with clear delegation to independently testable
subsystems.

Why Case needs refactoring

Case is a god object that directly handles XML get/set across env files,
status tracking (CaseStatus), lock management (LockedFiles), build
orchestration, job submission, namelist generation, and clone/setup/test
workflows.

On top of that, many Case operations live outside the class as free
functions reaching into private attributes like case._gitinterface. Case
responsibility is smeared across ~10 files.

Two-phase approach

Phase 1 — Consolidate into Case

Bring scattered free functions back into Case (or into subsystem classes Case
will own). This temporarily makes Case bigger but gathers responsibility
in one place so the seams are visible.

  • Status (from status.py): append_case_status(),
    run_and_log_case_status(). Every caller currently extracts caseroot
    and case._gitinterface.
  • Locking (from locked_files.py): check_lockedfiles(),
    check_lockedfile(), diff_lockedfile(), check_diff().
  • Case queries (from utils.py): is_comp_standalone(),
    find_system_test(), get_lids(), new_lid().
  • Case run helpers (from case/case_run.py): _pre_run_check(),
    _run_model_impl(), _post_run_check(), _resubmit_check().
  • TestScheduler (from test_scheduler.py): _get_time_est(),
    _order_tests_by_runtime(), _translate_test_names_for_new_pecount().
    Module-level _TIME_CACHE should be instance state.

Phase 2 — Extract subsystems

Decompose Case into focused modules it delegates to:

  • CIME/core/status/ — status tracking (CaseStatus file, append_status,
    run_and_log_status).
  • CIME/core/locking/ — lock management (LockedFiles, check_locked,
    diff_locked).
  • CIME/core/xml/ — XML value storage (env get/set/flush, the
    _env_entryid_files and _env_generic_files arrays).

Case keeps its public API but each method delegates:

class Case:
    def __init__(self, ...):
        self._status = StatusTracker(self._caseroot, self._gitinterface)
        self._locks = LockManager(self._caseroot)
        self._xml = XmlStore(self._env_files)

    def append_status(self, msg, ...):
        self._status.append(msg, ...)

    def check_lockedfiles(self):
        self._locks.check(self._xml)

Other procedural modules to decompose

  • case/case_st_archive.py (1395 lines, ~20 free functions taking case)
    → consolidate as Case methods or extract into CIME/core/archive/.
    Resolves Refactor config_archive.xml/case_st_archive.py #4792.
  • case/check_input_data.py (693 lines, ~12 free functions taking case)
    → consolidate.
  • baselines/performance.py (612 lines, ~12 free functions taking case)
    → baseline comparison subsystem.
  • hist_utils.py (836 lines, ~7 free functions taking case)
    → history file handling subsystem.
  • TestScheduler (test_scheduler.py, 1340 lines, 28 methods)
    → decompose into test creation / phase mgmt / submission / results.

Move remaining utils.py functions

  • wait_for_unlocked, normalize_case_id, parse_test_name,
    get_full_test_name, compute_total_timecore/.
  • convert_to_seconds, convert_to_babylonian_time, get_time_in_seconds,
    format_timeCIME/core/time.py.
  • convert_to_type, convert_to_unknown_type, convert_to_string,
    stringify_boolCIME/core/convert.py.

By end of this slice, utils.py is a thin re-export file with no
implementation code.

Definition of done

  • Case public API unchanged — external callers work without modification.
  • No free function reaches into case._gitinterface or other private
    Case state.
  • No module over ~500 lines.
  • core/status/, core/locking/, core/xml/ independently testable.
  • utils.py is thin re-exports only (was 2700 lines).
  • 80%+ coverage on new subsystems.
  • Resolves Refactor config_archive.xml/case_st_archive.py #4792.
  • All existing tests pass; E3SM/CESM/NorESM workflows unchanged.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions