Skip to content

Refactor profiles filtering: add rank_profile, get_matching_profiles, get_highest_score_profiles#1705

Open
vincentullmann wants to merge 2 commits intoynput:developfrom
vincentullmann:enhancement/profiles-filtering-refactor
Open

Refactor profiles filtering: add rank_profile, get_matching_profiles, get_highest_score_profiles#1705
vincentullmann wants to merge 2 commits intoynput:developfrom
vincentullmann:enhancement/profiles-filtering-refactor

Conversation

@vincentullmann
Copy link
Copy Markdown
Contributor

Changelog Description

Refactored profile filtering and added new methods so callers can match multiple profiles instead of only picking the single best one.

Additional info

Unchanged

  • filter_profiles expected behaviour is unchanged and backwards compatible. (hopefully)

New

  • rank_profile: ranks a single profile using a lexicographic binary score.
    • Each key contributes one bit (1 = exact match, 0 = wildcard); results form a bitmask so earlier keys matter more.
    • e.g. 0b110 = 6 beats 0b101 = 5 (both have 2 value matches and 1 wildcard).
    • Total number of exact matches = number of 1 bits (e.g. score.bit_count()).
  • rank_profiles: helper to rank multiple profiles
  • get_matching_profiles: returns all profiles with a non‑negative score.
  • get_highest_score_profiles: Returns all profiles with the highest score.
  • Unit tests added for the new helpers and edge cases.

Use case

Useful for addons that want to compose profiles dynamically (eg. merge multiple rules into one).

Example (eg. dynamic profiles for deadline):

profiles = {
    # default priority
    ["host": ["*"], "priority": 50],

    # increase chunk size to run the entire playblast in one chunk
    ["host": ["maya"],  "productBaseTypes": ["playblast"], "chunk_size": 999, "overrides": ["chunk_size"]],

    # allow FX tasks to choose the machine pool + allow them to overwrite the default pool
    ["host": ["maya"],  "task": ["fx"], "pool": "high_memory", "overrides": ["pool"]],
}


def get_profile_for_instance(self, instance):
    values = {
        "host": "maya",
        "productBaseTypes": "playblast",
        "task": "fx",
    }
    # custom filter to only include profiles with at least two exact matches
    profiles = [profile for profile, score in rank_profiles(self.profiles, values) if score.bit_count() >= 2]

    # addon specific merge of profiles
    profile = self.combine_profiles(profiles)
    # {
    #     "priority": 50,
    #     "chunk_size": 999,
    #     "pool": "high_memory",
    #     "overrides": ["chunk_size", "pool"]
    # }

@ynbot ynbot added size/M type: enhancement Improvement of existing functionality or minor addition labels Feb 16, 2026

def _profile_exclusion(matching_profiles, logger):
"""Find out most matching profile byt host, task and family match.
def fullmatch(regex, string, flags=0):
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this function is unchanged. the diff is incorrect

]


def get_matching_profiles(
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.

What is the use-case for this function?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

a couple of use cases I currently can think of:

User selection from multiple valid profiles

eg.: a studio might have muliple playblast profiles defined (generic_maya_profile, anim_profile, low_quality_but_fast_profile, high_quality_but_slow_profile, ..)
Using this function plugins could present the users with a dropdown allowing them to select which profile to use

Profile composition

Some workflows could benefit from considering profiles as composable attributes rather than beeing mutually exclusive. Returning all matches could allow a plugin to dynamically merge them based on user input / context / other settings.

Copy link
Copy Markdown
Member

@iLLiCiTiT iLLiCiTiT Mar 17, 2026

Choose a reason for hiding this comment

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

Using this function plugins could present the users with a dropdown allowing them to select which profile to use

In that case you should not use profiles. Or rather do not call it profiles. Profiles are explicitly defined to match one or none for a set of filters. The use-case does not match the criteria.

What you are describing are presets, definitions, not profiles. In that cases you probably have all filtering values available and you're simply comparing left -> right, don't need to track score, just show preset which does match the filters. That is what this function does, but that should NOT be used for profiles.

return regexes


def _profile_exclusion(matching_profiles, logger):
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.

This functionality is not available anymore, I guess?

Copy link
Copy Markdown
Contributor Author

@vincentullmann vincentullmann Feb 17, 2026

Choose a reason for hiding this comment

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

The same functionally is now part of the ranking itself.

From how I understand the previous implemetiation:

  • filter_profiles would rank and filter profiles based on the number of matches (line ~199)
  • _profile_exclusion then selects the best match based on the position of the matched keys in order.

The new rank_profile function does both at the same time by generting a "order-aware-score"

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 sure, this is not about score, it is about filters order in profiles. If there is profile with filters:

  • product base type
  • host names
  • task types

When there are 2 profiles with score 1, one has matching task type and one has matching host name, this function would return the one which has matching host name because the filter has more "value" for the filtering.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It still does that

here is an variation of the test_order_of_keys_matters test case to match the variable names

profiles = [
    {"product_base_type": "model", "task": "modeling", "foo": "no"},  # 2 matches product_base_type + task
    {"product_base_type": "model", "host": "maya", "foo": "yes"},     # 2 matches product_base_type + host
]
key_values = {
    "product_base_type": "model", 
    "host": "maya",     # host listed first => higher value
    "task": "modeling",
}
matched = profiles_filtering.get_highest_score_profiles(profiles, key_values, log)
print(matched)
>>> [{'product_base_type': 'model', 'host': 'maya', 'foo': 'yes'}]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M type: enhancement Improvement of existing functionality or minor addition

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants