Skip to content

Conversation

@mikeleppane
Copy link
Contributor

@mikeleppane mikeleppane commented Dec 20, 2025

Summary

This PR adds complete type annotations to the entire src/robocop/ codebase and enables strict Mypy checks with no type: ignore comments. Additionally, it drops support for Python 3.9 and Robot Framework 4.*.

Sorry, this is a large diff due to the nature of the change.

Changes

  • Type Safety: Added comprehensive type annotations to all modules in src/robocop/
  • Strict Mypy: Enabled strict mypy configuration with --strict flag
  • Version Bump: Minimum versions now Python 3.10+ and Robot Framework 5.0+
  • Tooling: Added mypy to pre-commit hooks and CI/CD workflows
  • Testing: Updated test matrix to remove Python 3.9 and Robot Framework 4.* combinations

Breaking Changes

⚠️ Python 3.9 and Robot Framework 4. are no longer supported*

Users must upgrade to:

  • Python 3.10 or newer
  • Robot Framework 5.0 or newer

self.up_to_column = int(up_to_column) - 1
self.argument_indent = int(argument_indent)
self.min_width: int | None = int(min_width) if min_width is not None else None
self.fixed_width: int | None = int(fixed_width) if fixed_width is not None else None
Copy link
Member

Choose a reason for hiding this comment

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

I need to check it - I was under impression that we use typing to parse the input argument when creating instance of the class. But I even if its not the case, I agree that unclear typing made it ambigious.

Copy link
Member

Choose a reason for hiding this comment

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

I've made quick and dirty test for it (I will need to add it to our tests later):

from robot.utils.importer import Importer

from robocop.formatter.formatters import resolve_args
from robocop.formatter.formatters.AlignKeywordsSection import AlignKeywordsSection
from robocop.formatter.skip import SkipConfig


def test_arguments_are_resolved_with_types():
    args = {"compact_overflow_limit": "2"}
    skip_config = SkipConfig()
    importer = Importer()
    spec = importer._get_arg_spec(AlignKeywordsSection)
    positional, named, argument_names = resolve_args("AlignKeywordsSection", spec, args, skip_config, handles_skip={})
    assert named["compact_overflow_limit"] == 2

So it does resolve the types. Hovewer if we're adding None it fails to resolve the type because of how the Robot Framework type resolver works (| None stops resolving the type). We have following options:

First, we can go with the current implementation (yours from the PR) since it's correct and works.
Second, in the future we may improve the type resolver - maybe possibly instead of using robot one, we can create our own as it's not that complex. Once it's done, we may be able to simplify this code to avoid double parsing the type.

OR

We can always assume that whatever we're passing is string, and leave converting the type to the formatter __init__ method. It's not that convenient and would be breaking change, that's why I am not considering it for now.

if index < up_to:
if self.fixed_width:
return max(self.fixed_width - len(token.value), self.formatting_config.space_count) * " "
space_count = int(self.formatting_config.space_count) # type: ignore[union-attr,arg-type]
Copy link
Member

Choose a reason for hiding this comment

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

Instead of calling int on space count repeatedly, wouldnt it better to just ensure that it has proper type in formatting config? I saw few int() calls on it in the same file.

Copy link
Member

Choose a reason for hiding this comment

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

Ok now I see where it's coming from - it's bit of poor design where class accept either value or None (so it should take default instead) and then it's converted in post processing. This is causing typing issues along the way. You can ignore my comment and leave it as it is and it can be addressed later (so improve design of this class -> replace those additional converts).

@bhirsz
Copy link
Member

bhirsz commented Dec 20, 2025

Thanks for the great work! I went through the changes and they are good in general. I saw not only pure typing changes but also code changes, but they usually are because we didnt have fallbacks to default values.

It looks like ready to be merged (after rebasing and minor discussions) but I will wait with it until we release next robocop (7.1) since this change will go as 8.0. I need to add some documentation for 7.1, but it should be ready tommorrow.

if self.config.silent:
return
if hasattr(sys.stdout, "reconfigure"):
# reconfigure is available from Python 3.7+
Copy link
Member

Choose a reason for hiding this comment

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

Since you noticed its available anyway, maybe we just need to remove this condition?

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.

2 participants