| Title: | Runtime Namespace Patching Utilities for R Packages |
|---|---|
| Description: | Provides utilities for runtime hotpatching of locked R package namespaces. The package enables dynamic injection of function patches into sealed package environments without rebuilding or redeploying the package. This is particularly useful for legacy containerized workflows where package versions are frozen in place. The core functionality includes inject_patch() to inject patches into package namespaces, undo_patch() to restore original functions, apply_hotfix_file() to apply patches from external R scripts, and test_patched_dir() to run test suites against patched packages. The package implements namespace surgery techniques that allow internal callers to automatically see patched functions. |
| Authors: | David Munoz Tord [aut, cre] |
| Maintainer: | David Munoz Tord <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.0 |
| Built: | 2026-05-27 06:30:49 UTC |
| Source: | https://github.com/munoztd0/hotpatchr |
Apply a hotfix file and inject the patch definitions
apply_hotfix_file(file, pkg = NULL)apply_hotfix_file(file, pkg = NULL)
file |
Path to an R script that defines 'patch_list' and optionally 'pkg'. |
pkg |
Optional package name if not provided in the script. |
Invisibly TRUE on success.
# Create a temporary hotfix file hotfix_content <- " pkg <- 'hotpatchR' patch_list <- list( dummy_child_func = function(x) { paste('HOTFIXED! Input:', x) } ) " hotfix_file <- tempfile(fileext = ".R") writeLines(hotfix_content, hotfix_file) # Apply the hotfix from file apply_hotfix_file(file = hotfix_file, pkg = "hotpatchR") # Verify the patch works result <- dummy_parent_func("test") print(result) # Clean up unlink(hotfix_file)# Create a temporary hotfix file hotfix_content <- " pkg <- 'hotpatchR' patch_list <- list( dummy_child_func = function(x) { paste('HOTFIXED! Input:', x) } ) " hotfix_file <- tempfile(fileext = ".R") writeLines(hotfix_content, hotfix_file) # Apply the hotfix from file apply_hotfix_file(file = hotfix_file, pkg = "hotpatchR") # Verify the patch works result <- dummy_parent_func("test") print(result) # Clean up unlink(hotfix_file)
A dummy child function that we will "hotfix" in tests
dummy_child_func(x)dummy_child_func(x)
x |
Input value |
A character string with child output
# This function is called by dummy_parent_func # It serves as an example of a function that can be hotpatched result <- dummy_child_func("example") print(result)# This function is called by dummy_parent_func # It serves as an example of a function that can be hotpatched result <- dummy_child_func("example") print(result)
A dummy parent function to test namespace injection
dummy_parent_func(x)dummy_parent_func(x)
x |
Input value to pass to child function |
A character string with parent output
# Call the parent function which internally calls dummy_child_func result <- dummy_parent_func("sample") print(result)# Call the parent function which internally calls dummy_child_func result <- dummy_parent_func("sample") print(result)
Inject a runtime patch into a locked package namespace
inject_patch(pkg, patch_list, lock = TRUE)inject_patch(pkg, patch_list, lock = TRUE)
pkg |
A package name as a string. |
patch_list |
A named list of functions to overwrite in the package namespace. |
lock |
Whether to re-lock bindings after patching. |
Invisibly TRUE on success.
# Show baseline behavior with broken function baseline <- dummy_parent_func("test") print(baseline) # Inject a patched version of the child function inject_patch( pkg = "hotpatchR", patch_list = list(dummy_child_func = function(x) { paste("I am the FIXED child! Input:", x) }) ) # Call the parent function again - it now uses the patched child patched_result <- dummy_parent_func("test") print(patched_result)# Show baseline behavior with broken function baseline <- dummy_parent_func("test") print(baseline) # Inject a patched version of the child function inject_patch( pkg = "hotpatchR", patch_list = list(dummy_child_func = function(x) { paste("I am the FIXED child! Input:", x) }) ) # Call the parent function again - it now uses the patched child patched_result <- dummy_parent_func("test") print(patched_result)
Run testthat tests against a patched package namespace
test_patched_dir(pkg, test_path = "tests/testthat", reporter = "summary")test_patched_dir(pkg, test_path = "tests/testthat", reporter = "summary")
pkg |
Package name that has been patched in memory. |
test_path |
Path to tests to run. |
reporter |
testthat reporter name or object. |
Result object from testthat::test_dir.
## Not run: # Inject a patch to the package inject_patch( pkg = "hotpatchR", patch_list = list(dummy_child_func = function(x) { paste("PATCHED! Input:", x) }) ) # Run tests against the patched package test_patched_dir(pkg = "hotpatchR") ## End(Not run)## Not run: # Inject a patch to the package inject_patch( pkg = "hotpatchR", patch_list = list(dummy_child_func = function(x) { paste("PATCHED! Input:", x) }) ) # Run tests against the patched package test_patched_dir(pkg = "hotpatchR") ## End(Not run)
Undo a previously injected patch
undo_patch(pkg, names = NULL)undo_patch(pkg, names = NULL)
pkg |
Package name or environment. |
names |
Character vector of patched object names to restore. If NULL, restore all stored backups for pkg. |
Invisibly TRUE on success.
# First inject a patch inject_patch( pkg = "hotpatchR", patch_list = list(dummy_child_func = function(x) { paste("I am PATCHED! Input:", x) }) ) # Call with patched function patched <- dummy_parent_func("test") print(patched) # Restore the original function undo_patch(pkg = "hotpatchR", names = "dummy_child_func") # Now it's back to the original restored <- dummy_parent_func("test") print(restored)# First inject a patch inject_patch( pkg = "hotpatchR", patch_list = list(dummy_child_func = function(x) { paste("I am PATCHED! Input:", x) }) ) # Call with patched function patched <- dummy_parent_func("test") print(patched) # Restore the original function undo_patch(pkg = "hotpatchR", names = "dummy_child_func") # Now it's back to the original restored <- dummy_parent_func("test") print(restored)