Skip to main content

Detect Python refactorings across Git revisions with functional-change reporting

Project description

PyRef2

PyRef2 is based on the core idea behind PyRef: automatically detecting refactorings in Python code. It extends that idea with a typed Python 3.13 codebase, direct Git revision analysis, structured JSON and Markdown reporting, hierarchical change summaries, and explicit functional-change detection for both refactoring-related and standalone behavior changes.

Use it when you need a fast, review-friendly report for questions like:

  • What was renamed, moved, extracted, inlined, or signature-changed between two revisions?
  • Did any of those findings also change method/class behavior?
  • Were there behavior changes that are not simple rename/move operations?

It is designed for CI checks, release reviews, and repository archaeology.

Install and run in 60 seconds

uv sync --extra dev
uv run pyref2 analyze-revisions --repo path/to/repo origin/main..HEAD --format markdown

What you get:

  • machine-readable JSON findings (default)
  • optional Markdown report grouped by change type and scope
  • per-finding functional-change status
  • condensed method-level code diffs when a functional change is detected

How to read the output

  • No Functional Change means PyRef2 did not find structural behavior signals for the compared entity.
  • Functional Change Detected means at least one behavior signal changed and the report includes reasons.
  • Markdown reports group findings into module-level changes, class-wise changes, mixed-scope method changes, and other refactorings.

Architecture overview

PyRef2 uses a small layered pipeline so each stage stays testable and replaceable:

  • Parsing layer (core/ast_analysis.py): converts Python source into typed entities (ModuleEntity, ClassEntity, MethodEntity).
  • Diff layer (core/diff_engine.py): matches entities between revisions and produces a structural ModuleDiff.
  • Detection layer (core/detectors/): runs focused heuristic detectors (rename/extract/inline/move/signature changes).
  • Service layer (service.py): orchestrates parse → diff → detect and returns sorted findings.
  • Interface layer (cli/commands.py): exposes analyze-files and emits schema-versioned JSON output.

How functional changes are detected

PyRef2 reports functional-change status using static structural checks for move-related and non-move findings.

Method-level checks

For method comparisons, PyRef2 marks Functional Change Detected when any of these differ between before/after revisions:

  • body_signature: normalized AST statement signatures for the method body
  • params: method parameter tuple
  • called_names: set of called symbol names detected in the method body

If none of those differ, status is No Functional Change.

These method-level checks are used by:

  • Move Method
  • Rename Method
  • Modify Method (same name, same scope, same module, but behavior changed)
  • Change Method Signature
  • Extract Method (assesses whether the source caller changed)
  • Inline Method (assesses whether the destination caller changed)

Class-level checks

For class moves, PyRef2 combines class-level and member-level signals:

  • class bases changed
  • class method-name set changed
  • any contained matched method is marked Functional Change Detected

If none of the above is true, class status is No Functional Change.

For class signature changes (for example, base-class changes), PyRef2 reports Functional Change Detected with reasons when the class signature differs.

Reporting behavior

  • Move-related and non-move behavior findings include a functional-change status in JSON and Markdown.
  • In Markdown, same-name method entries are suppressed unless status is Functional Change Detected.
  • Class entries can include child method changes used to justify class-level status.
  • When status is Functional Change Detected, Markdown includes a condensed unified code diff scoped to the relevant method.

Current limitations

This is a static heuristic, not dynamic execution equivalence. It does not guarantee runtime equivalence in all cases. In particular, behavior can still change through effects not captured by the current signals (for example, external state interactions or semantics-preserving AST rewrites that alter call-name sets).

Quickstart

uv sync --extra dev
uv run pyref2 analyze-files --before path/to/old.py --after path/to/new.py
uv run pyref2 analyze-tree --before-root path/to/revision-A --after-root path/to/revision-B
uv run pyref2 analyze-revisions --repo path/to/repo origin/main..HEAD
uv run pyref2 analyze-revisions --format markdown --repo path/to/repo origin/main..HEAD

Git revision analysis

Use analyze-revisions to compare repository states directly from Git without exporting trees by hand.

  • Pass a standard Git double-dot range: uv run pyref2 analyze-revisions --repo path/to/repo origin/main..HEAD
  • main..feature means: analyze the total effect between the tree at main and the tree at feature, which matches the common feature-branch review workflow.
  • Add --format markdown to get a developer-oriented report grouped by refactoring type.

Tree test fixtures

Whole-tree regression tests live under tests/source_trees/ and use this layout:

  • <test-name>/revision-A/
  • <test-name>/revision-B/

This keeps it easy to add a new before/after source tree pair whenever a bug needs a permanent fixture.

Documentation conventions

  • Use Google-style docstrings for public Python modules, classes, and functions.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyref2-0.1.0.tar.gz (14.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyref2-0.1.0-py3-none-any.whl (20.2 kB view details)

Uploaded Python 3

File details

Details for the file pyref2-0.1.0.tar.gz.

File metadata

  • Download URL: pyref2-0.1.0.tar.gz
  • Upload date:
  • Size: 14.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pyref2-0.1.0.tar.gz
Algorithm Hash digest
SHA256 048459daa8a7571d812fcb39574961a1c9e40b5aecd5578d2f387110006d0646
MD5 ee7fdb04b143f9b0238f8508cc430fa9
BLAKE2b-256 0ca6e4724c13d0c11e88c00077eafe338596fea2761d2505322ec89fb95970b5

See more details on using hashes here.

File details

Details for the file pyref2-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pyref2-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 20.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pyref2-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f0732309524c45fd7b50db5985a84934b9124fa7b1adf7fe0c044931b9a3e68b
MD5 1cc212e7d003e143eaa55e56adb68525
BLAKE2b-256 965d99e298a9dbac2e6bdd9a754330c03d238f5cc382ab98ea9cd263f5cd830a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page