---
title: 'PCREQ: Automated Python Upgrade Repair'
url: https://www.emergentmind.com/topics/pcreq
type: topic
---

# PCREQ: Automated Python Upgrade Repair

PCREQ is an automated analysis and repair system for Python third-party library upgrades that infers a new `requirements.txt` for a given project and target library upgrade while jointly enforcing version compatibility and code compatibility. In the formulation introduced by “PCREQ: Automated Inference of Compatible Requirements for Python Third-party Library Upgrades” [2508.02023], the central objective is to produce an environment in which `pip install -r requirements.txt` succeeds and the project does not crash because of removed or changed APIs, including both direct project–library interactions and indirect library–library interactions.

## 1. Problem definition and compatibility model

PCREQ addresses upgrade failures in Python projects that depend on third-party libraries (TPLs). The paper separates these failures into two categories. **Version Compatibility Issues (VCIs)** arise at the dependency-graph level, typically as unsatisfiable version constraints among packages. **Code Compatibility Issues (CCIs)** arise at the code/API level and may persist even when installation succeeds. CCIs are further divided into four types: **module**, **API name**, **API parameter**, and **API body** [2508.02023].

The distinction between VCIs and CCIs is operational rather than merely taxonomic. A VCI example is an upgrade that makes dependency constraints inconsistent, as in the reported case where `torchvision == 0.7.0` requires `torch == 1.6.0`, causing a conflict when `torch` is upgraded to `1.9.0`. A CCI example is a runtime failure caused by API removal despite a successful installation, such as `tf.placeholder` or `tensorflow.Session` disappearing in TensorFlow 2.0. The paper also emphasizes **TPL–TPL** incompatibilities, where the project does not directly call the breaking API but reaches it through another library, as in `scikit-learn 0.18.1` importing `from scipy.misc import comb`, which breaks when `scipy` is upgraded to `1.3.0` and `scipy.misc.comb` is removed [2508.02023].

An empirical study over 34 projects, 20 TPLs, and 2,095 upgrade experiments motivates this formulation. In those experiments, 140 upgrades had pip installation errors, but 102 of those still ran successfully; conversely, among 1,955 upgrades that installed successfully, 368 crashed at runtime. This demonstrates that satisfying version constraints is neither necessary nor sufficient for runtime correctness, and it motivates a system that combines dependency solving with code-level compatibility analysis [2508.02023].

## 2. System architecture and end-to-end workflow

PCREQ integrates six modules and applies them in an iterative repair loop. The modules are **knowledge acquisition**, **version compatibility assessment**, **invoked APIs and modules extraction**, **code compatibility assessment**, **version change**, and **missing TPL completion** [2508.02023].

| Module | Function |
|---|---|
| Knowledge acquisition | Collect candidate versions, metadata, source code, modules, and APIs |
| Version compatibility assessment | Encode dependency constraints as SMT and solve them with Z3 |
| Invoked APIs and modules extraction | Extract direct and indirect API/module usage |
| Code compatibility assessment | Detect module, API name, API parameter, and some body issues |
| Version change | Adjust target or dependent TPL versions and re-run analysis |
| Missing TPL completion | Add missing dependencies to the final `requirements.txt` |

The workflow begins from the project path, the original `requirements.txt`, the Python version, a target TPL, and a target version. PCREQ first parses the original environment, sets the target library to the desired version, generates an SMT expression for dependency consistency, and solves it to obtain a version-compatible assignment. It then extracts code entities used by the project and relevant libraries, checks code compatibility between the starting environment and the candidate environment, and, if issues remain, iteratively adjusts versions and repeats the analysis. After compatibility checks succeed, it completes missing TPLs and outputs both a compatible `requirements.txt` and a repair report [2508.02023].

This architecture is explicitly designed to combine global environment inference with local code reasoning. A plausible implication is that PCREQ treats upgrade repair as a constrained search over the joint space of dependency assignments and code-level breakages rather than as a pure resolver problem or a pure API-migration problem.

## 3. Knowledge acquisition and SMT-based version compatibility analysis

The knowledge acquisition stage has two parts. For **version-related knowledge**, PCREQ parses each `library==version` entry in the initial `requirements.txt`, queries PyPI for all versions compatible with the specified Python version, and stores version candidates and dependency metadata, especially `requires_dist`, in local JSON files. For **code-related knowledge**, it downloads source distributions for candidate versions and extracts modules and APIs from source code and stub files such as `*.pyi` [2508.02023].

Module extraction walks the package directory and keeps `*.py` files as modules and directories containing `__init__.py` as packages while filtering irrelevant directories such as `__pycache__`, tests, and docs. API extraction parses each Python file into an AST and traverses `FunctionDef`, `ClassDef`, and `Assign` nodes to construct fully qualified names, including nested definitions. The system also analyzes `__init__.py` files to recover re-exported names and simplify fully qualified API paths to user-facing forms. The paper reports that this step reuses techniques from PCART for name simplification [2508.02023].

For VCI handling, PCREQ encodes dependency resolution as an SMT problem. Given `Candidates[TPL]` and `Dependencies[TPL][version]`, it builds a formula in which each TPL must be assigned one candidate version whose dependencies are themselves satisfiable. The solver is not asked for an arbitrary model. Instead, PCREQ defines an optimization objective that prefers preserving the original version from the starting `requirements.txt`, and otherwise prefers newer versions:
$$
O = \max \sum_{n \in N} \left[ 1(n = V_x) \cdot (|V| + 1) + \text{index}(n) \right].
$$
Here, `V_x` is the starting version and `index(n)` orders versions from oldest to newest. Z3 `Optimize` is then used to produce `verCompatDeps`, a version-compatible assignment with this preference structure [2508.02023].

This solver design distinguishes PCREQ from tools that only enumerate dependency solutions. It makes the original environment a first-class optimization target while still supporting target-library upgrades.

## 4. Code compatibility analysis for direct and indirect breakages

Once a version-compatible assignment is available, PCREQ analyzes which APIs and modules are actually used from libraries whose versions changed. It compares `startDeps` and `verCompatDeps`, constructs upgrade triples of the form `<l, V_1, V_2>`, and then identifies usage along **TPL call chains** such as `project → numpy`, `project → scipy → numpy`, or `project → scikit-learn → scipy → numpy` [2508.02023].

For **Project–TPL** compatibility, PCREQ parses project files into ASTs and supports several invocation forms: direct invocation, class object invocation, return value invocation, argument invocation, and inheritance invocation. It tracks imports and assignments, resolves aliases, reconstructs call paths, and maps simplified names back to fully qualified names using the knowledge base. If multiple candidates exist, it uses fuzzy matching based on Levenshtein distance [2508.02023].

For **TPL–TPL** compatibility, PCREQ first extracts the entry APIs through which the project reaches an intermediate library, then builds an on-demand call graph with `code2flow` to identify downstream APIs in another library that are used indirectly. It supplements this with recursive import traversal over related `.py` files so that imported modules and APIs not captured by the call graph are still considered. The result is a set of used APIs `S_a` and used modules `S_m` for each upgraded library [2508.02023].

Compatibility checking is then divided into four analyses. **Module compatibility** computes the set of modules present in `V_1` but missing in `V_2` and checks whether the used-module set intersects that difference. **API name compatibility** does the same for removed APIs. **API parameter compatibility** reuses PCART: it compares parameter lists between versions, distinguishes positional and keyword parameters, maps parameters across versions, analyzes how call sites pass arguments, and evaluates compatibility with a function $f(P,E,M)$, where `P` is parameter type, `E` is change type, and `M` is passing mode. **API body compatibility** is acknowledged as difficult: the paper states that early attempts produced high false-positive rates, so the final system does not systematically detect behavior-breaking changes beyond limited cases [2508.02023].

Several case studies illustrate this design. In `deep-belief-network`, PCREQ traces the chain `project → scikit-learn → scipy`, extracts `scipy.misc.comb`, detects that the API is absent after upgrading SciPy, and searches for a compatible version. In `PyTorch-ENet`, it traces `project → torchvision → pillow` and identifies the removal of `PIL.PILLOW_VERSION`. In TensorFlow 1.x to 2.x upgrades, it detects the removal of `tensorflow.placeholder` and `tensorflow.Session` from direct project usage [2508.02023].

## 5. Version adjustment, missing TPL completion, and repair reporting

When CCIs are detected, PCREQ changes versions according to the role of the incompatible library. For **Project–TPL** issues involving the target library, it performs **downgrade-only** search over older candidate versions until both SMT-based version compatibility and code compatibility succeed. If the incompatible library is not the target TPL, PCREQ uses **bidirectional search**: it first tries upgrades, then downgrades [2508.02023].

For **TPL–TPL** issues, the policy depends on which library is the user-selected target. If the target TPL is one side of the incompatible pair, PCREQ keeps that library fixed and adjusts the other side with bidirectional search. If neither side is the target, PCREQ applies a cascading strategy: it first adjusts one incompatible TPL, and if that fails, adjusts the other, re-running SMT solving and CCI analysis after each attempt [2508.02023].

After a successful assignment is found, PCREQ performs **missing TPL completion**. It scans the chosen dependency set and, for any dependency referenced by version constraints but absent from `requirements.txt`, adds that TPL using the **oldest** version satisfying the constraints. The stated rationale is conservative backward compatibility, since code compatibility has not been analyzed for such a newly introduced library [2508.02023].

The system also emits a **repair report**. According to the paper, this report includes all version changes relative to the starting environment, the CCIs identified for each change, the TPLs and call chains involved, and the reasoning steps in the main loop, including SMT results, iterations required to resolve CCIs, and whether missing TPLs were added [2508.02023]. This makes the output diagnostically useful rather than merely executable.

## 6. REQBench, empirical results, and limitations

PCREQ is evaluated on **REQBench**, a benchmark with 34 real-world Python projects, 20 TPLs, and 2,095 upgrade test cases. Each case corresponds to a project, a target TPL, and a start-version to target-version upgrade. The benchmark contains 1,689 **Pip Solved** cases and 406 **Pip Unsolved** cases. Among the 406 pip-unsolved cases, the paper reports 211 Project–TPL cases and 195 TPL–TPL cases; by type, the distribution is 55 module cases, 247 API name cases, 21 API parameter cases, and 83 API body cases [2508.02023].

On full REQBench, PCREQ achieves a **94.03%** inference success rate, with 1,677 successes out of 1,689 Pip Solved cases and 293 successes out of 406 Pip Unsolved cases. The paper also reports fine-grained success over the 406 CCI cases: 54 of 55 module cases, 224 of 247 API name cases, 4 of 21 API parameter cases, and 11 of 83 API body cases. By level, it solves 169 of 211 Project–TPL cases and 124 of 195 TPL–TPL cases [2508.02023].

Against prior and alternative approaches, PCREQ substantially outperforms the reported baselines. On REQBench-PyEGo, PCREQ reaches 98.26% success versus 37.02% for PyEGo. On REQBench-ReadPyE, it reaches 95.18% versus 37.16% for ReadPyE. On full REQBench, it outperforms GPT-4o, DeepSeek V3, and DeepSeek R1 by 18.76, 20.00, and 18.61 percentage points, respectively [2508.02023].

The paper reports practical efficiency as well. PCREQ processes each case from REQBench in **60.79 seconds on average**, and 90% of cases finish within **193 seconds**. On REQBench-PyEGo, the average is approximately **69.93 seconds**; on REQBench-ReadPyE, approximately **65.85 seconds** [2508.02023].

The reported limitations are concentrated in forms of compatibility that are difficult to recover statically. PCREQ is weak on **API body** changes, where semantics change without signature changes, and on many **API parameter** changes that require deeper reasoning about constraints or types. It can also miss issues caused by **implicit dependencies** not declared in `requires_dist`, **API redirection or re-export tricks**, incomplete or inconsistent **type annotations**, and **dynamic imports, reflection, or runtime code generation** [2508.02023]. This suggests that PCREQ is strongest when incompatibilities are structural, name-based, or statically recoverable, and weaker when they depend on latent semantics or dynamic behavior.

Within the broader landscape of Python environment inference and upgrade repair, PCREQ is presented as the first approach to jointly handle VCIs together with both Project–TPL and TPL–TPL CCIs while using live PyPI metadata and source code rather than a prebuilt static knowledge graph [2508.02023]. Its central significance lies in converting library-upgrade repair from an ad hoc debugging exercise into a reproducible pipeline that combines SMT-based dependency reasoning, static API analysis, and iterative environment synthesis.

Source: https://www.emergentmind.com/topics/pcreq