Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

igraph/tools

Updating the sources

In the shell:

scripts/vendor.sh <path-to-cigraph-working-copy>

By default, cigraph is assumed to be in ../../../igraph.

There is a CI/CD workflow that runs this very frequently.

Running Stimulus

Currently, there are only two files generated by Stimulus. They are version-controlled and depend on various input files. The logic is governed by the Makefile-cigraph file. To update them, do the following steps:

make -f Makefile-cigraph src/rinterface.c R/aaa-auto.R

Type Definitions

Stimulus uses type definition files to generate R-to-C interface code:

  • tools/stimulus/types-RR.yaml: Defines R-level type conversions and argument validation for the R wrapper layer. All arguments in autogenerated *_impl() functions are validated here before being passed to C.
  • tools/stimulus/types-RC.yaml: Defines C-level type conversions between R's SEXP objects and igraph C types.

When adding support for new types (e.g., GRAPH_PTR_LIST, MATRIX_LIST):

  1. Add the type definition to types-RR.yaml with INCONV for R-level validation
  2. Add the type definition to types-RC.yaml with INCONV/OUTCONV for C conversion
  3. Regenerate the interface files with the make command above

Managing Stimulus with git subrepo

What is git subrepo?

The Stimulus code generator is vendored into this repository using git subrepo, a tool that allows you to include an external git repository as a subdirectory within your main repository. Unlike git submodules, subrepos are easier to work with because they don't require special checkout commands; the code is directly present in the repository. This approach gives us the flexibility to test local modifications to Stimulus while still being able to pull upstream updates cleanly.

Installing git subrepo

If you don't have git subrepo installed yet:

# On macOS with Homebrew
brew install git-subrepo

# Or install from source
git clone https://github.com/ingydotnet/git-subrepo /path/to/git-subrepo
echo 'source /path/to/git-subrepo/.rc' >> ~/.zshrc

Updating Stimulus from upstream

To pull the latest changes from the upstream Stimulus repository:

# Pull the latest from the main branch
git subrepo pull tools/py-stimulus

# Or pull a specific branch
git subrepo pull tools/py-stimulus -b branch-name

# Or pull a specific commit or tag
git subrepo pull tools/py-stimulus -b v0.21.5

This command will:

  1. Fetch changes from the upstream Stimulus repository
  2. Merge them into the tools/py-stimulus/ directory
  3. Create a merge commit with metadata in .gitrepo
  4. Update the commit hash in tools/py-stimulus/.gitrepo

After pulling, test the changes:

# Clean the virtualenv to ensure fresh installation
rm -rf .venv

# Regenerate the interface files
make -f Makefile-cigraph src/rinterface.c R/aaa-auto.R

Working with a modified version of Stimulus

If you need to test local changes to Stimulus:

  1. Make your changes directly in tools/py-stimulus/:

    cd tools/py-stimulus
    # Edit files as needed
    vim src/stimulus/some_file.py
    cd ../..
  2. Test your changes:

    # Remove the virtualenv to force reinstallation
    rm -rf .venv
    
    # Regenerate with your modified Stimulus
    make -f Makefile-cigraph src/rinterface.c R/aaa-auto.R
  3. Commit everything together:

    git add tools/py-stimulus/ src/rinterface.c R/aaa-auto.R
    git commit -m "feat: update Stimulus and regenerated interface files"

The modified Stimulus code and generated files will be committed together, making it easy to see what Stimulus changes caused which interface changes.

Pushing Stimulus changes upstream

If your Stimulus modifications should be contributed back to the upstream repository:

# Push your changes to the upstream Stimulus repository
git subrepo push tools/py-stimulus

# Or push to a specific branch (useful for PRs)
git subrepo push tools/py-stimulus -b feature-branch-name

This will create commits in the upstream Stimulus repository based on the commits you made in tools/py-stimulus/.

Checking subrepo status

To see information about the subrepo:

git subrepo status tools/py-stimulus

This shows the remote URL, branch, and commit hashes.

Argument-migration blocks

When a public function's signature is migrated by inserting ... and renaming arguments, pre-existing calls would otherwise break: the old positional (or partially-named) values land in ... and hit check_dots_empty(). To soften that, the migrated function carries a small generated block in its body that recovers the legacy call — by position or by (partial) name — and emits one soft-deprecation.

The block is generated in place, not written by hand:

  • tools/migrations.R is the single source of truth. Each entry declares the function's old and new signatures as literal R functions; renames and defaults are read straight off their formals (a bare-symbol default in old, e.g. c = c_renamed, means a rename). The schema is documented at the top of that file.
  • tools/generate-migrations.R reads the registry and rewrites the code between the # BEGIN GENERATED ARG_HANDLE: <fn> / # END GENERATED ARG_HANDLE markers inside each function. Output is idempotent (running twice produces no diff) and laid out exactly as air formats it, so the host files stay clean.

Regenerate after editing the registry:

Rscript tools/generate-migrations.R

This usually happens for you: a testthat helper (tests/testthat/helper-migrations.R) regenerates the blocks whenever the registry is newer, and CI re-runs the generator (in custom/after-install) and fails if the committed code has drifted. Do not edit between the markers by hand.

Call-site usage

A migrated function gets the marker pair at the top of its body; the generator fills it in:

as_biadjacency_matrix <- function(graph, types, ..., weights = NULL,
                                  attr = lifecycle::deprecated(),
                                  names = TRUE, sparse = FALSE) {
  # BEGIN GENERATED ARG_HANDLE: as_biadjacency_matrix
  # ... generated: recover dots, reassign weights/attr/..., deprecate_soft() ...
  # END GENERATED ARG_HANDLE

  # ... rest of the body, using the (possibly reassigned) locals
}

The recovery runs inline, so there is no handler function and no .user_env plumbing. The generated lifecycle::deprecate_soft() sits directly in the function body, so its default user_env (caller_env(2)) already resolves to the user's frame: real user calls warn, while genuinely internal igraph callers stay correctly silent — the point of a soft deprecation. The matching itself is delegated to a small hand-written helper, migrate_recover_args() (R/migrate-args.R) — a plain, debuggable function that takes the per-function maps and returns the recovered values plus message parts (or NULL); the generated block just supplies the configuration and assigns the results.

Reordering or removing an argument

Positional recovery only covers old slots whose order is preserved. To reorder an argument, or to drop a positional slot without breaking old calls, place the affected argument after ... in the new signature. Arguments past the ellipsis are keyword-only and are recovered by (partial) name rather than by position, so they side-step the position math entirely.

The one case the generator cannot rescue is a positional slot that is removed and whose old position is reused by a different surviving argument — a legacy positional call would then rebind to the wrong slot. No migration planned for 3.0.0 does this (they are pure renames).

Additional Documentation

For detailed information on implementing R wrappers for callback functions, see AGENTS.md in this directory.