Skip to content

Type-annotate ansible.galaxy.collection - #85961

Open
webknjaz wants to merge 1 commit into
ansible:develfrom
webknjaz:typing/ansible.galaxy.collection
Open

Type-annotate ansible.galaxy.collection#85961
webknjaz wants to merge 1 commit into
ansible:develfrom
webknjaz:typing/ansible.galaxy.collection

Conversation

@webknjaz

@webknjaz webknjaz commented Oct 7, 2025

Copy link
Copy Markdown
Member

This patch is a result of pyrefly autotype with a lot of post-processing.

ISSUE TYPE
  • Test Pull Request

@ansibot ansibot added test This PR relates to tests. needs_triage Needs a first human triage before being processed. pending_ci needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR. and removed pending_ci labels Oct 7, 2025
@ansibot

This comment was marked as outdated.

@webknjaz
webknjaz force-pushed the typing/ansible.galaxy.collection branch 2 times, most recently from a59e959 to dd991a4 Compare October 8, 2025 15:07
@webknjaz
webknjaz marked this pull request as draft October 8, 2025 15:17
@webknjaz webknjaz added the ci_verified Changes made in this PR are causing tests to fail. label Oct 8, 2025
@webknjaz
webknjaz force-pushed the typing/ansible.galaxy.collection branch from dd991a4 to d55b934 Compare October 8, 2025 15:38
@ansibot ansibot added pending_ci and removed needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR. ci_verified Changes made in this PR are causing tests to fail. pending_ci labels Oct 8, 2025
@webknjaz
webknjaz marked this pull request as ready for review October 8, 2025 21:29
@webknjaz webknjaz moved this to ⏰ Review reminder needed 👀 in 📅 Procrastinating in public 😵‍💫 Oct 8, 2025
@dataclass
class ManifestControl:
directives: list[str] = None
directives: list[str]

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.

This seems like a user-facing change, shouldn't it remain optional?

Suggested change
directives: list[str]
directives: list[str] | None = None

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This class is only used to pass things between calls internally, though. But I get your point. MyPy wanted me to do something where it's initialized, I think. I'll double-check if I can revert this bit.

def __init__(self, reasons=None, stdout=None, rc=None, ignore=False):
def __init__(
self,
reasons: t.Iterable | None = None,

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.

Suggested change
reasons: t.Iterable | None = None,
reasons: t.Iterable[str] | None = None,

Comment on lines +495 to +499
t.cast(list[str], collection_meta.get('build_ignore', [])),
t.cast(
ManifestMetadataType | t.Type[Sentinel],
collection_meta.get('manifest', Sentinel),
),

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.

_normalize_galaxy_yml_manifest populates the default value for the optional keys (so it contains both 'manifest' and 'build_ignore'). Can we fix the type hint to match, instead of using collection_meta.get()?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I can try. MyPy was mad at me when I was trying to fix it w/o a cast (it thought that the _build_files_manifest() call gets an object which I couldn't explain). I'd prefer that but couldn't make it work for hours. I'd be happy to get rid of the casts if we find a way.

Comment on lines -1084 to +1095
if manifest_control is None:
manifest_control = {}

try:
control = ManifestControl(**manifest_control)
control = ManifestControl(**(manifest_control or {'directives': []}))

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.

This works if manifest_control is None or empty, but if it's truthy and doesn't contain 'directives', it will fail since ManifestControl now requires directives.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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.

It's a change in behavior. I get a crash now when I using a manifest containing the option other than directives.

Comment on lines -1295 to +1331
collection_manifest['file_manifest_file']['chksum_sha256'] = secure_hash_s(files_manifest_json, hash_func=sha256)
collection_manifest_json = to_bytes(json.dumps(collection_manifest, indent=True), errors='surrogate_or_strict')
collection_manifest_with_hash = {
**collection_manifest,
'file_manifest_file': {
**collection_manifest['file_manifest_file'],
'chksum_sha256': secure_hash_s(
files_manifest_json,
hash_func=sha256,
),
},
}
collection_manifest_json = to_bytes(
json.dumps(collection_manifest_with_hash, indent=True),
errors='surrogate_or_strict',
)

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.

I think the original is easier to read, especially the first line versus the replacement.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@s-hertel yes, but we can't mutate read-only fields. I was trying to make the data structs immutable. Perhaps, we need structs with and w/o the check sum computed. Dunno how to best handle this.

def _get_direct_collection_namespace(
self,
collection: Collection,
) -> str | None:

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.

Suggested change
) -> str | None:
) -> str:

def _get_direct_collection_name(
self,
collection: Collection,
) -> str | None:

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.

Suggested change
) -> str | None:
) -> str:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

get_direct_collection_meta() sets name+namespace to None in a few cases and so this can just represent that. Otherwise, MyPy will be unhappy about non-matching typing declarations.

@s-hertel s-hertel Oct 10, 2025

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.

If these return None, the only caller will crash with a TypeError.

# type: (t.Union[Candidate, Requirement]) -> dict[str, str]
def get_direct_collection_dependencies(
self,
collection: Candidate | Requirement,

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.

Can this also use Collection, for consistency?

Suggested change
collection: Candidate | Requirement,
collection: Collection,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Maybe, I think in some places I just got the comments converted but in some places it was problematic. I'll check locally, thanks!

sentinel_keys = set() # type: set[str]
galaxy_yml: dict[
str,
str | list[str] | dict[str, str] | None | t.Type[Sentinel],

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.

The manifest boolean option omit_default_directives is not represented here.

Suggested change
str | list[str] | dict[str, str] | None | t.Type[Sentinel],
str | list[str] | dict[str, str | bool] | None | t.Type[Sentinel],

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah, I temporarily gave up on this function as it's non-trivial to address (hence the cast at the end). It should definitely be refactored more in the future. But I didn't want to make the PR scope too big. I'll accept this suggestion but it'll only cover a little bit of what needs to be improved.

): # type: (...) -> dict
b_path: bytes,
filename: str,
) -> dict:

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.

We don't actually validate the MANIFEST.json is a dictionary before we use it as such.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

True. But I also just converted the type from comment to explicit syntax. So I didn't look into it. I was trying not to get into making runtime changes unless absolutely necessary. Do you feel like the PR should make such changes?

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.

I just noticed the bug while reviewing, you don't need to fix it.

SIGNATURE_COUNT_RE = r"^(?P<strict>\+)?(?:(?P<count>\d+)|(?P<all>all))$"


class DisplayThread:

@webknjaz webknjaz Oct 10, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Note: I should try to move this back into the original place now that I've solved the display type with a protocol. Originally, I was going to use this class in the union.

@webknjaz

This comment was marked as resolved.

@ansibot ansibot added the stale_ci This PR has been tested by CI more than one week ago. Close and re-open this PR to get it retested. label Oct 17, 2025
@s-hertel s-hertel removed the needs_triage Needs a first human triage before being processed. label Oct 21, 2025
@webknjaz

This comment was marked as resolved.

@webknjaz webknjaz moved this from ⏰ Review reminder needed 👀 to 🚧 In progress 🚧 in 📅 Procrastinating in public 😵‍💫 Nov 28, 2025
@ansibot ansibot added the needs_rebase https://docs.ansible.com/ansible/devel/dev_guide/developing_rebasing.html label Dec 10, 2025
This patch is a result of `pyrefly autotype` with *a lot* of
post-processing.
@webknjaz
webknjaz force-pushed the typing/ansible.galaxy.collection branch from d55b934 to 00eabe3 Compare December 10, 2025 17:10
@ansibot ansibot removed the needs_rebase https://docs.ansible.com/ansible/devel/dev_guide/developing_rebasing.html label Dec 10, 2025

if t.TYPE_CHECKING:
from ansible.utils.display import Display
from ._types import DisplayQueueType, DisplayThreadProto

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

(no idea why I have DisplayQueueType here still; will probably have to drop it before merge)

Suggested change
from ._types import DisplayQueueType, DisplayThreadProto
from ._types import DisplayThreadProto

@webknjaz

Copy link
Copy Markdown
Member Author

(last push is just a rebase on top of the moved-out portions of the initial patch; no new changes)

DisplayQueueType: _t.TypeAlias = _q.Queue[DisplayQueueItemType]


class DisplayThreadProto(_t.Protocol):

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.

does this make sense to have here or should it go to a more general _types file?

@webknjaz webknjaz Dec 10, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It may make sense elsewhere. But most of this patch is trying to figure out bits and pieces within the scope of a subpackage/module. I'd definitely consider moving it in a follow-up, but I wouldn't want to include anything beyond ansible.galaxy.collection in the PR scope — it's too fragile atm.

@ansibot ansibot removed the stale_ci This PR has been tested by CI more than one week ago. Close and re-open this PR to get it retested. label Dec 10, 2025
@ansibot ansibot added the stale_ci This PR has been tested by CI more than one week ago. Close and re-open this PR to get it retested. label Dec 24, 2025

@mattclay mattclay 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.

There's a lot going on in this PR. Could you split it up to make it easier to review?

There are quite a few "simple" conversions from type comments to native type annotations. That seems like a good place to start, as those should be easy to review. That would make it easier to review the other, more involved changes (which may also benefit from being split up).

@webknjaz

webknjaz commented Jan 9, 2026

Copy link
Copy Markdown
Member Author

@mattclay agreed. I've actually done that with 3 PRs before PTO and was thinking about doing it again, just haven't returned to this one yet. It's on my list.

@ansibot ansibot added the needs_rebase https://docs.ansible.com/ansible/devel/dev_guide/developing_rebasing.html label Mar 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs_rebase https://docs.ansible.com/ansible/devel/dev_guide/developing_rebasing.html stale_ci This PR has been tested by CI more than one week ago. Close and re-open this PR to get it retested. test This PR relates to tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants