Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ArtVsMark/Stepik-Python-Grader
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.4.0
Choose a base ref
...
head repository: ArtVsMark/Stepik-Python-Grader
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v1.5.0
Choose a head ref
  • 6 commits
  • 20 files changed
  • 2 contributors

Commits on Jul 5, 2026

  1. refactor(memory): prlimit after spawn + remove unwired timeout helper (

    …#67, #69)
    
    * fix(core): apply memory cap via resource.prlimit after spawn, not preexec_fn (#67)
    
    preexec_fn форкает в многопоточном родителе (грейдер держит psutil-поток
    мониторинга памяти) — документированно небезопасно. Ставим RLIMIT_AS через
    resource.prlimit на pid дочернего процесса ПОСЛЕ spawn, без fork в родителе.
    
    - _make_memory_limiter() (preexec_fn-фабрика) → _apply_memory_limit(pid, mb)
      в core/grader_core.py и core/microbench_runner.py (дубль, тот же DAG-довод).
    - run_single_test: prlimit(proc.pid) после Popen (preexec_fn убран).
    - run_microbench: subprocess.run → Popen + communicate(timeout=60), чтобы
      получить pid для prlimit; таймаут/OSError-ветки сохранены.
    - Linux-only: prlimit отсутствует на macOS (AttributeError → no-op), нет
      resource на Windows — cap всё равно best-effort (#43 S-01), thread-safety
      важнее. Окно «ребёнок без лимита» ~мс до exec пользовательского кода.
    
    Тесты: _make_memory_limiter-тесты (×4 в двух файлах) переписаны под
    _apply_memory_limit (prlimit-моки, AttributeError-ветка macOS); mock
    subprocess.run → Popen в timeout/OSError тестах microbench. 624 зелёных,
    ruff/mypy чисто, E2E режимы 3/4 работают.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01VcThPLNgaqaBbEDouT5THj
    
    * chore(core): remove unwired run_microbench_with_timeout() (#69)
    
    Вердикт по «музейному экспонату»: Remove. Функция жила два релизных цикла
    без единого production-вызова; её собственный докстринг признавал, что
    защиты она не даёт (ThreadPoolExecutor-обёртка поверх уже
    subprocess.run(timeout=60)-защищённого вызова), а при реальном таймауте
    оставляет orphan-поток. Мёртвый код, вводящий в заблуждение — история в git.
    
    Убраны заодно: import concurrent.futures и Callable (больше нигде не нужны
    в microbench_runner.py), два теста в test_microbench_runner_module.py и
    import time (использовался только ими). Заметки в CLAUDE.md/CHANGELOG.
    
    622 теста зелёные, ruff/mypy чисто.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01VcThPLNgaqaBbEDouT5THj
    
    ---------
    
    Co-authored-by: Claude <noreply@anthropic.com>
    ArtVsMark and claude authored Jul 5, 2026
    Configuration menu
    Copy the full SHA
    50baaa6 View commit details
    Browse the repository at this point in the history
  2. feat(cache): opt-in result caching to skip unchanged solutions (#56)

    Add a new leaf module core/cache.py (stdlib hashlib + core/storage.py
    JSON I/O only) that caches --mode 1/2 correctness verdicts keyed by
    sha256(solution content) + sha256(all test-dir files). A solution is
    skipped and its previous verdict reused until either hash changes.
    
    - --cache / --no-cache (argparse.BooleanOptionalAction, default from
      GraderConfig.use_cache / [tool.stepik-grader] use_cache)
    - --clear-cache deletes .grader_cache/ and reports removed entry count
    - cache stored in .grader_cache/results.json (CWD), added to .gitignore
    - corrupt/version-mismatched cache file degrades to empty, never crashes
    - mode 1 prints a cache-hit note; mode 2 prints an N-of-M summary
    - 15 tests (hashing stability/sensitivity, roundtrip, invalidation,
      corrupt-file handling, CLI integration)
    
    Docs: README --cache section, CHANGELOG, CLAUDE.md (structure/DAG/backlog).
    
    
    Claude-Session: https://claude.ai/code/session_01VcThPLNgaqaBbEDouT5THj
    
    Co-authored-by: Claude <noreply@anthropic.com>
    ArtVsMark and claude authored Jul 5, 2026
    Configuration menu
    Copy the full SHA
    04140e8 View commit details
    Browse the repository at this point in the history

Commits on Jul 6, 2026

  1. feat(pytest): run grader as a pytest plugin via --grader-mode (#57)

    Add pytest_plugin.py registered as a pytest11 entry point. With
    `pytest --grader-mode StepikTasks/`, pytest collects each solution file
    (task*.py) as a pytest.File and yields one pytest.Item per test case from
    its tests/ directory, running through the same run_single_test engine as
    CLI mode 1. A wrong answer is a normal FAILED with an "expected/actual"
    diff; a runtime error surfaces the exception text.
    
    - off by default (no-op unless --grader-mode or `grader_mode = true` ini),
      so it never interferes with a project's own test suite
    - core imports are lazy (inside hooks): pytest11 plugins load before
      coverage.py starts, so a module-level core import would mark the whole
      package's def-lines uncovered
    - pytest_plugin.py itself is omitted from coverage (its def/class headers
      execute at plugin-load before coverage; bodies are covered behaviorally)
    - 16 tests via the pytester fixture (enabled in conftest.py)
    
    Docs: README pytest-plugin section, CHANGELOG, CLAUDE.md (structure/DAG/backlog).
    
    
    Claude-Session: https://claude.ai/code/session_01VcThPLNgaqaBbEDouT5THj
    
    Co-authored-by: Claude <noreply@anthropic.com>
    ArtVsMark and claude authored Jul 6, 2026
    Configuration menu
    Copy the full SHA
    cdae390 View commit details
    Browse the repository at this point in the history
  2. feat(watch): incremental --mode 2 re-run via the #56 cache (#71)

    Under --watch, --mode 2 now auto-enables the result cache (issue #56), so a
    file-change event only re-runs the changed solution — the other rows are
    served from cache instead of re-running the whole folder. On a folder with a
    dozen tasks the feedback loop no longer degrades. Reuses #56's disk cache
    (the existing load-on-create + save-at-end plumbing persists state across
    watch iterations) rather than a separate in-memory solution, as the issue
    proposed.
    
    - --cache default changed from CONFIG.use_cache to None so an explicit
      --cache/--no-cache is distinguishable from "unset" and always wins
    - new _resolve_use_cache helper: explicit flag → watch-incremental default
      → [tool.stepik-grader] use_cache
    - --mode 1 (single file) does NOT auto-enable cache under --watch
    - --no-cache opts out (restores the old full-folder rerun)
    - 7 tests (resolution matrix + main() wiring for mode 1/2 watch)
    
    Docs: README --watch section, CHANGELOG, CLAUDE.md backlog #71.
    
    
    Claude-Session: https://claude.ai/code/session_01VcThPLNgaqaBbEDouT5THj
    
    Co-authored-by: Claude <noreply@anthropic.com>
    ArtVsMark and claude authored Jul 6, 2026
    Configuration menu
    Copy the full SHA
    a4f6358 View commit details
    Browse the repository at this point in the history
  3. fix(ide): launch tasks via the editor interpreter + version consisten…

    …cy audit
    
    The generated VS Code tasks and the README PyCharm recipe invoked the bare
    `stepik-grader` console script, which is only on PATH when the venv is
    activated — the most likely reason the beginner IDE integration "wouldn't
    launch". Both now run through the interpreter selected in the IDE, so no
    manual venv activation is needed.
    
    - VS Code (--init-vscode): command → ${command:python.interpreterPath},
      type → process (robust to interpreter paths with spaces), args prefixed
      with -m stepik_grader.grader
    - README PyCharm External Tool: Program → $PyInterpreterDirectory$/python,
      Arguments → -m stepik_grader.grader ...
    - test_ide.py updated for the new command/args/type
    
    Docs: also audited version consistency across README/CLAUDE.md (fixed a stale
    `# 1.2.0` --version example → 1.4.0; refreshed the metrics table test count
    622 → 660) and added a pre-merge rule to CLAUDE.md's checklist: always verify
    the version is consistent across the whole project before merging.
    
    
    Claude-Session: https://claude.ai/code/session_01VcThPLNgaqaBbEDouT5THj
    
    Co-authored-by: Claude <noreply@anthropic.com>
    ArtVsMark and claude authored Jul 6, 2026
    Configuration menu
    Copy the full SHA
    d504b61 View commit details
    Browse the repository at this point in the history
  4. chore(release): prepare v1.5.0

    - pyproject version 1.4.0 → 1.5.0
    - CHANGELOG: consolidate [Unreleased] into [1.5.0] (2026-07-06) with a concise
      summary + collapsible full details; batch = #56 cache, #57 pytest plugin,
      #71 incremental watch, IDE launch fix, and post-v1.4.0 cleanup (#67/#69)
    - README: version badge, Status, --version example → 1.5.0; new v1.5.0 column
      in the release-comparison table (660 / 95%)
    - CLAUDE.md: metrics table → v1.5.0 (stable), 660 tests
    - version consistent across pyproject/README/CLAUDE.md (pre-merge rule)
    
    
    Claude-Session: https://claude.ai/code/session_01VcThPLNgaqaBbEDouT5THj
    
    Co-authored-by: Claude <noreply@anthropic.com>
    ArtVsMark and claude authored Jul 6, 2026
    Configuration menu
    Copy the full SHA
    5941b5f View commit details
    Browse the repository at this point in the history
Loading