# The "quick fix" that works: runtime patching in Python

## **Motivation**

We wanted to generate demo videos with the python library Supervision but they wouldn't play in Chrome. There was a one-line fix buried inside the library -- but the public API gave us no way to reach it.

You have probably been in the same situation: a library does 99.9% of what you want, but there is one thing you cannot work around using its public APIs. Switching to a different library and rewriting your code is too much work. What do you do?

## **Runtime Patching Comes to the Rescue**

Since Python is a dynamically typed language, its possible change the behavior of a function/class/module at runtime.

This is known as runtime patching or monkey patching.

## **Supervision**

[Supervision](https://supervision.roboflow.com/) by Roboflow is a helper library for computer vision tasks.

One feature I particularly like is the ability to create demo videos with just a few lines of code:

```python
import numpy as np
import supervision as sv
from rfdetr import RFDETRNano

model = RFDETRNano()
model.optimize_for_inference()
box_annotator = sv.BoxAnnotator()

def callback(frame: np.ndarray, _: int) -> np.ndarray:
    detections = model.predict(frame)
    return box_annotator.annotate(frame.copy(), detections=detections)

sv.process_video(
    source_path="input.mp4",
    target_path="result.mp4",
    callback=callback
)
```

There is one small issue though: the generated video cannot be played in Chrome.

`chrome://media-internals` showed that the video used an unsupported codec.

Digging into the source code, we can see that the [OpenCV video writer is created here](https://github.com/roboflow/supervision/blob/0b4cc5ff5bc6dcfaee125fba32ddbd9bfd32b011/src/supervision/utils/video.py#L102) and It uses FourCC code "mp4v" by default, but what we really want is "avc1" -- I will explain why in a bit.

Here is the problem, the output video is written by a `VideoSink` instance. While `VideoSink` accepts a `codec` argument, there's no way to pass it through the `sv.process_video()` API.

What if we patch the `VideoSink` class so that it defaults to "avc1" instead? Well, below is one way to do just that:

```python
def patch_sv() -> None:
    orig_init = sv.VideoSink.__init__

    def patched_init(self, *args, **kwargs):
        kwargs.setdefault("codec", "avc1")
        return orig_init(self, *args, **kwargs)

    sv.VideoSink.__init__ = patched_init  # type: ignore
```

Note the use of `setdefault` -- this changes the default codec without preventing callers from choosing a different one explicitly.

As long as we call `patch_sv()` before `sv.process_video()`, the generated videos will be playable in Chrome. No more re-encoding with `ffmpeg`!

<details data-node-type="hn-details-summary">
<summary>Why "avc1"?</summary>
<p>You might be wondering why "avc1" specifically solves the problem.</p><p>By specifying "avc1", we are requesting OpenCV to encode using H.264 (Advanced Video Coding). Think of H.264 as the JPEG of video codecs: not the latest, but the most widely supported.</p><p>This ensures that the video can be played in web browsers like Chrome.</p><p>The previous choice, "mp4v", encodes using the older MPEG-4 Part 2 codec, which web browsers never adopted.</p><p></p>
</details>

## **Putting It All Together**

Here is the complete working example:

```python
import numpy as np
import supervision as sv
from rfdetr import RFDETRNano

def patch_sv() -> None:
    orig_init = sv.VideoSink.__init__

    def patched_init(self, *args, **kwargs):
        kwargs.setdefault("codec", "avc1")
        return orig_init(self, *args, **kwargs)

    sv.VideoSink.__init__ = patched_init  # type: ignore

model = RFDETRNano()
model.optimize_for_inference()
box_annotator = sv.BoxAnnotator()

def callback(frame: np.ndarray, _: int) -> np.ndarray:
    detections = model.predict(frame)
    return box_annotator.annotate(frame.copy(), detections=detections)

patch_sv()
sv.process_video(
    source_path="input.mp4",
    target_path="result.mp4",
    callback=callback
)
```

## **Recap**

Runtime patching is a legitimate tool in the pragmatic Python developer's toolbox. It lets us reach past public APIs to fix issues quickly, without forking a library or waiting for an upstream release. The same technique is also widely used in software testing, where it underpins the mocking of library dependencies.

That said, patches are inherently fragile -- a library update can move or rename the internals you depend on. A few ways to manage that risk:

*   **Pin the library version** so that updates are intentional and you can re-test your patch.
    
*   **Contribute the fix upstream.** A pull request or issue is the best long-term solution -- it removes the need for the patch entirely.
    
*   **Keep patches small and isolated.** A single, well-documented patch function is easy to find and remove later.
    

My recommendation is to reserve runtime patching for prototyping, research code, or code that is not on the critical path of a production application. But when the situation calls for it, don't be afraid to use it.
