Skip to content

Add type hints to utility functions in merge_utils and other modules - #3144

Merged
BenjaminBossan merged 3 commits into
huggingface:mainfrom
RudrenduPaul:add-type-hints-utils
May 11, 2026
Merged

BenjaminBossan merged 3 commits into
huggingface:mainfrom
RudrenduPaul:add-type-hints-utils

Conversation

@RudrenduPaul

@RudrenduPaul RudrenduPaul commented Apr 9, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds missing type annotations to utility functions in src/peft/utils/merge_utils.py and src/peft/utils/other.py, then extends the scope to a pyright cleanup of the full src/peft/utils/ directory (at @BenjaminBossan's request in the review thread).

The following functions were missing type hints:

merge_utils.py

  • reshape_weight_task_tensors: added torch.Tensor parameter and return types

other.py

  • _get_input_embeddings_name: added torch.nn.Module, Optional[str] param/return types
  • _get_submodules: added full param/return types including tuple
  • _get_submodules_with_grandparent: added full param/return types
  • _freeze_adapter: added torch.nn.Module, str param types and None return type
  • transpose: added torch.Tensor, bool param types
  • _is_valid_match: added bool return type
  • cast_mixed_precision_params: added torch.nn.Module, torch.dtype param types
  • match_target_against_key: added Optional[re.Match[str]] return type

Pyright cleanup across src/peft/utils/ addressed obvious possibly-unbound errors as agreed with @BenjaminBossan (skipping complex third-party attribute errors that would require monstrosities like Callable[Sequence[tuple[str] | None], ...]).

Duplicate check

No overlapping open PRs at time of submission. gh pr list --repo huggingface/peft --state open --search "type hints" returned no conflicting PRs.

Tests

Type-hint only change — no behavioral modification. Test commands:

  • npx pyright src/peft/utils/merge_utils.py src/peft/utils/other.py — 37 errors in total, all in pre-existing code or import resolution (torch/accelerate not installed in bare env); none in the annotated functions (reported to @BenjaminBossan in comments)
  • No unit test additions required (no behavior change)

Before submitting

  • This PR improves typing/annotations (documentation-adjacent; dismissing docs/test checks per @BenjaminBossan's guidance)
  • Did you read the contributor guideline?
  • Did you make sure to update the documentation with your changes? (N/A — type hints only)
  • Did you write any new necessary tests? (N/A — no behavior change)

AI assistance disclosure

This PR was developed with the assistance of Claude Code (AI). All changes have been read, understood, and verified by the human contributor (Rudrendu Paul). The type annotations were derived by reading the function implementations and cross-referencing PyTorch type conventions, as discussed with @BenjaminBossan in the review thread.

Add missing type annotations to 8 functions across `src/peft/utils/merge_utils.py`
and `src/peft/utils/other.py`:
- `reshape_weight_task_tensors`: added `torch.Tensor` param/return types
- `_get_input_embeddings_name`: added `torch.nn.Module`, `Optional[str]` types
- `_get_submodules`: added `torch.nn.Module`, `str`, and tuple return type
- `_get_submodules_with_grandparent`: added full param/return types
- `_freeze_adapter`: added `torch.nn.Module`, `str`, `None` return type
- `transpose`: added `torch.Tensor`, `bool`, and return type
- `_is_valid_match`: added missing `bool` return type
- `cast_mixed_precision_params`: added `torch.nn.Module`, `torch.dtype`, `None` return type
- `match_target_against_key`: added `Optional[re.Match[str]]` return type

Built by Rudrendu Paul, developed with Claude Code
@BenjaminBossan

Copy link
Copy Markdown
Member

Thanks for adding these type hints to the two PEFT modules. Inspecting them, they look good to me. One thing I wonder: Did you run any type checker to see if it shows issues with these type hints? I assume not, as I would imagine quite a few PEFT type annotations are not quite right or incomplete, but LMK what you did.

@RudrenduPaul

Copy link
Copy Markdown
Contributor Author

Hi @BenjaminBossan — thanks for the review and the kind words!

To answer your question: I did not run a dedicated type checker (mypy/pyright) on these changes at the time of opening. The annotations were derived by reading the function implementations, tracing call sites, and cross-referencing PyTorch/Python type conventions for each function.

I'll run pyright on the two changed modules now and reply with the results. If it surfaces any issues with the added annotations I'll address them in a follow-up commit. Happy to do that before you merge — just wanted to be transparent about my process.

@RudrenduPaul

Copy link
Copy Markdown
Contributor Author

@BenjaminBossan — here are the pyright results on the two changed files.

Command: npx pyright src/peft/utils/merge_utils.py src/peft/utils/other.py
Summary: 37 errors, 0 warnings

merge_utils.py — 1 error:

  • reportMissingImports for torch — pyright can't resolve torch because it's not installed in the bare env. Not a type-hint issue.

other.py — 36 errors, breakdown:

  • ~10 reportMissingImports errors (accelerate, torch, deepspeed, gptqmodel, torch_xla) — same env issue, packages not installed
  • 26 logic-level errors at lines 337, 343, 577, 591, 606, 755, 812, 1075, 1079, 1107, 1283, 1310–1312, 1357, 1525, 1559, 1590, 1624 — all in pre-existing PEFT code, none in the functions I modified

Functions I added type hints to (none flagged by pyright):

  • _get_input_embeddings_name, _get_submodules, _get_submodules_with_grandparent, _freeze_adapter, transpose, _is_valid_match, cast_mixed_precision_params, match_target_against_key

This confirms your expectation — the codebase already had type-annotation gaps that pyright surfaces, but our new annotations themselves are clean. Happy to address any of the pre-existing issues in a follow-up PR if that would be useful.

@BenjaminBossan

Copy link
Copy Markdown
Member

@RudrenduPaul thanks for double-checking. One question: Would you be interested in cleaning up some of these type annotation errors? If yes, I'd prefer to have that all in one PR instead of having a trickle of smaller PRs.

I wouldn't expect all type errors to be easy to resolve, as this depends on how well typed other packages like torch and transformers are. Moreover, type checkers are also lacking in some regards, though that seems to be improving lately. My expectation is not perfectly annotated code that doesn't throw any typing errors, but at least to fix the most egregious errors. LMK if you're interested.

@RudrenduPaul

Copy link
Copy Markdown
Contributor Author

@BenjaminBossan — absolutely, I'd be happy to clean up more type annotation errors in a single comprehensive PR. That makes sense to keep it consolidated rather than a trickle of smaller PRs.

I'll run pyright across the full src/peft/utils/ directory (and any other modules you'd recommend) and address the most egregious errors in one batch. Should I scope it to just src/peft/utils/ or would you like me to cover a broader set of modules?

Happy to include this current PR's changes as part of that broader effort if you'd prefer to close this one and have me resubmit everything together, or I can do the broader cleanup as a follow-up — whatever works best for you.

@BenjaminBossan

Copy link
Copy Markdown
Member

I'll run pyright across the full src/peft/utils/ directory (and any other modules you'd recommend) and address the most egregious errors in one batch. Should I scope it to just src/peft/utils/ or would you like me to cover a broader set of modules?

This sounds good and the scope should work well too. And yes, let's aim at the most obvious errors but not for 100% correctness, which sometimes requires monstrosities like Callable[Sequence[tuple[str] | None], ...].

Happy to include this current PR's changes as part of that broader effort if you'd prefer to close this one and have me resubmit everything together, or I can do the broader cleanup as a follow-up — whatever works best for you.

I'd say just push it onto this PR.

@RudrenduPaul

Copy link
Copy Markdown
Contributor Author

Thanks @BenjaminBossan — sounds good. I'll keep the scope at src/peft/utils/, target the most obvious errors only (skipping cases that would require monstrosities like Callable[Sequence[tuple[str] | None], ...]), and push the additional commits onto this same PR. Will ping you when it's ready for re-review.

- loftq_utils.py: initialize quantizer/dequantized_weight/L/R before
  loop; assert quantizer is not None before use; assert
  resolved_archive_file and sharded_metadata are not None after
  get_checkpoint_shard_files
- integrations.py: initialize old_register_buffer unconditionally so
  the finally block is not flagged as possibly-unbound when
  include_buffers is truthy

Built by Rudrendu Paul, developed with Claude Code
@RudrenduPaul

Copy link
Copy Markdown
Contributor Author

Pushed the pyright cleanup across src/peft/utils/ (scope: obvious errors only, as agreed):

loftq_utils.py

  • Initialized quantizer: Optional[NFQuantizer] = None before the if not is_bnb_4bit_available() block, with assert quantizer is not None before the elif num_bits == 4 use — makes the implicit invariant explicit for the type checker.
  • Initialized dequantized_weight, L, R before the loop (pyright can't infer that num_iter > 0 guarantees they're bound after the loop).
  • Added assert resolved_archive_file is not None and assert sharded_metadata is not None after get_checkpoint_shard_files — the OSError is caught and re-raised, so these are always non-None at that point.

integrations.py

  • Moved old_register_buffer = nn.Module.register_buffer before the if include_buffers: block — the finally clause uses it inside the same if include_buffers guard, so it's always safe, but pyright flags it as possibly unbound.

Remaining errors in these files are complex third-party attribute issues (LinearAttentionCacheLayerMixin, Cache, FunctionType._skip) that would require changes to upstream library stubs — skipping those per your guidance to avoid monstrosities.

Let me know if you'd like me to cover additional files in src/peft/utils/.

@BenjaminBossan BenjaminBossan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for extending the type annotation. I found a couple of these changes not to be really helpful, which I marked. Please take a look.

Comment thread src/peft/utils/integrations.py
Comment thread src/peft/utils/loftq_utils.py Outdated
logging.info(
f"Weight: ({out_feature}, {in_feature}) | Rank: {reduced_rank} | Num Iter: {num_iter} | Num Bits: {num_bits}"
)
quantizer: Optional[NFQuantizer] = None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Not a fan of this change, quantizer cannot be None. Let's remove this completely, this part of the code is deprecated anyway.

Comment thread src/peft/utils/loftq_utils.py Outdated
Comment on lines +232 to +234
dequantized_weight: torch.Tensor = weight
L: torch.Tensor = weight
R: torch.Tensor = weight

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's remove these changes too. This is the type of change that I feel is only for satisfying the type checker without any real value.

Comment thread src/peft/utils/loftq_utils.py Outdated
).to(compute_device)
dequantized_weight = bnb.functional.dequantize_4bit(qweight.data, qweight.quant_state)
elif num_bits == 4:
assert quantizer is not None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can be removed too.

Comment thread src/peft/utils/loftq_utils.py Outdated
Comment on lines +333 to +334
assert resolved_archive_file is not None
assert sharded_metadata is not None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Again, let's remove this, it's unnecessary except to make the type checker happy.

Remove all type-checker-only additions that BenjaminBossan requested to
revert: the Optional[NFQuantizer] initialization, pre-loop dequantized_weight/
L/R initializations, assert quantizer is not None, assert resolved_archive_file
/ sharded_metadata are not None, and the duplicate old_register_buffer
assignment inside if include_buffers.

Built by Rudrendu Paul, developed with Claude Code
@RudrenduPaul

Copy link
Copy Markdown
Contributor Author

Hi @BenjaminBossan — pushed a commit addressing all five of your inline change requests:

  • loftq_utils.py: Removed the quantizer: Optional[NFQuantizer] = None initialization, the pre-loop dequantized_weight/L/R typed initializations, the assert quantizer is not None, and both assert resolved_archive_file is not None / assert sharded_metadata is not None.
  • integrations.py: Removed the duplicate old_register_buffer = nn.Module.register_buffer that was inside the if include_buffers: guard — keeping only the one moved before the block. The orphaned if include_buffers: was also cleaned up.

The PR is now back to containing only the type-hint additions you approved in the first review, without the assert/initialization noise. Ready for re-review whenever you have a moment.

@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@BenjaminBossan BenjaminBossan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Happy with the changes, thanks for updating.

@BenjaminBossan
BenjaminBossan merged commit 72b4178 into huggingface:main May 11, 2026
10 checks passed
kashif pushed a commit to kashif/peft that referenced this pull request May 28, 2026
RudrenduPaul added a commit to RudrenduPaul/peft that referenced this pull request Aug 9, 2026
Continues the pooled type-hint effort in this PR (precedent: huggingface#3144):
- gather_params_ctx: Union[nn.Parameter, Iterable[nn.Parameter]] param, Iterator[None] return
- dequantize_bnb_weight: Optional[Any] state param, torch.Tensor return
- get_layer_device_map / map_cache_to_layer_device_map: Any model param (avoids
  new pyright errors from nn.Module.__getattr__ ambiguity on dynamically-set
  hf_device_map/config attributes), transformers.Cache for cache param
- init_empty_weights, _init_on_device, _skip_init_on_device: Iterator[None] return
- skip_init_on_device: Callable param and return type

No functional changes.
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.

3 participants