Skip to content

tests: public-api surface drift detector (companion to test_import_fixes_drift.py)#5428

Merged
danielhanchen merged 2 commits into
mainfrom
sec/public-api-drift-detector
May 15, 2026
Merged

tests: public-api surface drift detector (companion to test_import_fixes_drift.py)#5428
danielhanchen merged 2 commits into
mainfrom
sec/public-api-drift-detector

Conversation

@danielhanchen
Copy link
Copy Markdown
Member

Summary

Adds tests/test_public_api_surface.py, a slim companion to PR #5414's tests/test_import_fixes_drift.py. That file catches drift in THIRD-PARTY libraries (transformers / trl / triton / peft / vllm / torchcodec / xformers); this one catches drift in unsloth's OWN public-surface API -- the top-9 classmethods + symbols that unslothai/notebooks calls at ~2000 cumulative sites.

Closes the gap where a refactor on this repo (e.g. renaming FastLanguageModel.from_pretrained -> .load) would pass unsloth CI green and surface only on the next unslothai/notebooks CI run, or worse, on a user's Colab crash report.

Coverage

Call-site counts measured against unslothai/notebooks main:

Test Symbol(s) Notebook call sites
test_fast_language_model_class_present FastLanguageModel (presence)
test_fast_language_model_from_pretrained_kwargs .from_pretrained accepts {model_name, max_seq_length, dtype, load_in_4bit} 506
test_fast_language_model_get_peft_model_kwargs .get_peft_model accepts {r, lora_alpha, lora_dropout, target_modules, bias, use_gradient_checkpointing, random_state} 304
test_fast_language_model_for_inference_callable .for_inference 370
test_fast_vision_model_class_and_methods FastVisionModel + 4 methods 60-183 each
test_fast_vision_model_get_peft_model_vision_kwargs .get_peft_model accepts vision-LoRA kwargs {finetune_vision_layers, finetune_language_layers, finetune_attention_modules, finetune_mlp_modules} 99
test_fast_model_class_and_methods FastModel + 2 methods 67
test_fast_model_from_pretrained_kwargs same 4 base kwargs 103
test_is_bf16_supported_or_alias_callable either current or legacy alias callable 48 + 8

Each test asserts the healthy public shape via inspect.signature; on regression fires pytest.fail("DRIFT DETECTED: ...") (never pytest.skip) so the Core matrix cell goes red. Mirrors the same skeleton used by tests/test_import_fixes_drift.py.

CI wiring

Wired as a new step in .github/workflows/consolidated-tests-ci.yml right after the import_fixes drift detectors step. Runs inside every Core matrix cell.

Local verification

$ pytest tests/test_public_api_surface.py -v
9 passed in 0.02s

Test plan

  • pytest tests/test_public_api_surface.py is 9/9 locally on transformers 4.57.6 + unsloth main.
  • CI passes on this PR in each Core matrix cell.

Companion to tests/test_import_fixes_drift.py (PR #5414): that file
catches drift in THIRD-PARTY libs (transformers / trl / triton / peft /
vllm / torchcodec / xformers); this file catches drift in unsloth's
OWN public-surface API -- the top-9 classmethods + symbols that
unslothai/notebooks calls at ~2000 cumulative sites.

Closes the gap where a refactor on this repo (e.g. renaming
FastLanguageModel.from_pretrained -> .load) would pass unsloth CI
green and surface only on the next unslothai/notebooks CI run, or
worse, on a user's Colab crash report.

Coverage (call-site counts measured against unslothai/notebooks main):
  test_fast_language_model_class_present
  test_fast_language_model_from_pretrained_kwargs        506 sites
  test_fast_language_model_get_peft_model_kwargs         304 sites
  test_fast_language_model_for_inference_callable        370 sites
  test_fast_vision_model_class_and_methods         (4 methods)
  test_fast_vision_model_get_peft_model_vision_kwargs    (4 kwargs)
  test_fast_model_class_and_methods                (2 methods)
  test_fast_model_from_pretrained_kwargs                 103 sites
  test_is_bf16_supported_or_alias_callable        48 + 8 sites

Each test asserts the healthy public shape via inspect.signature; on
regression fires pytest.fail("DRIFT DETECTED: ...") -- never
pytest.skip -- so the Core matrix cell goes red. Mirrors the same
skeleton used by tests/test_import_fixes_drift.py.

Wired as a new step in consolidated-tests-ci.yml right after the
import_fixes drift step, inside every Core matrix cell.

Local verification on transformers 4.57.6 + unsloth main:
  pytest tests/test_public_api_surface.py -v
  -> 9 passed in 0.02s
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9cf0d401cd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

unsloth = pytest.importorskip("unsloth")
has_new = callable(getattr(unsloth, "is_bf16_supported", None))
has_old = callable(getattr(unsloth, "is_bfloat16_supported", None))
if not (has_new or has_old):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require both bf16 aliases to stay importable

When only one alias is exported, this test still passes even though the other import form remains part of the public surface: the docstring cites notebook call sites for both is_bf16_supported and is_bfloat16_supported, and repo-wide search shows tests importing from unsloth import is_bf16_supported. In the scenario where only the legacy name remains (or only the new name remains), those imports crash while this new drift detector stays green, so this should assert both public names are callable unless the removed name is deliberately no longer supported.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds a new test suite in tests/test_public_api_surface.py to detect breaking changes in the public API of the unsloth library by checking for the existence of critical classes and their expected method signatures. Review feedback suggests removing the unused _signature_param_names helper function and hardening the _accepts function to correctly report drift if an object's signature cannot be inspected.

Comment on lines +48 to +53

def _signature_param_names(callable_obj) -> set[str]:
try:
sig = inspect.signature(callable_obj)
except (TypeError, ValueError):
return set()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The helper function _signature_param_names is defined but not used anywhere in this test file. It should be removed to keep the codebase clean and avoid maintaining dead code.

Comment on lines +62 to +63
sig = inspect.signature(callable_obj)
except (TypeError, ValueError):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Returning True, set() when inspect.signature fails is too permissive for a drift detector. If the object's signature cannot be inspected (for example, if the attribute was replaced by a non-callable value like None), the test would silently pass. Returning False and the set of required arguments ensures that such regressions are correctly identified as drift. Consider centralizing this signature validation logic into a helper function to ensure consistency across the codebase.

Suggested change
sig = inspect.signature(callable_obj)
except (TypeError, ValueError):
except (TypeError, ValueError):
return False, kwargs
References
  1. Centralize recurring or complex logical checks into a single helper function and reuse it across the codebase to ensure consistency and simplify maintenance.

@danielhanchen danielhanchen merged commit ab21dc2 into main May 15, 2026
13 of 16 checks passed
@danielhanchen danielhanchen deleted the sec/public-api-drift-detector branch May 15, 2026 02:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant