-
Notifications
You must be signed in to change notification settings - Fork 120
SG-38756 Fix parent property of PathTemplate in case of optional key #1016
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
SG-38756 Fix parent property of PathTemplate in case of optional key #1016
Conversation
|
Thanks for your contribution @ClementHector! We are going to review and test. |
|
With pleasure @julien-lang , don't hesitate to ask me for corrections. |
|
Hi @julien-lang, Did you have time to do a review? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes a bug in the PathTemplate.parent property where optional keys (enclosed in square brackets) were being omitted from the parent path. The fix ensures that when a template like shots/{Sequence}[-{seq_num}]/{Shot}/{Step}/work has its parent calculated, the optional key structure is preserved.
Key changes:
- Introduced a new
_dirname()method to properly handle optional keys when extracting the directory portion of a template path - Modified the
parentproperty to use_dirname()instead ofos.path.dirname(self.definition) - Added comprehensive test coverage for the
parentproperty with optional keys and a newdirnameproperty test suite
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| python/tank/template.py | Added _dirname() helper method and updated parent property to correctly preserve optional keys in parent templates |
| tests/core_tests/test_templatepath.py | Added test cases for parent property with optional keys and new TestDirname test class with multiple optional key scenarios |
| azure-pipelines.yml | Added mock package installation step to the CI pipeline |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Returns the directory part of the template. | ||
| :returns: String | ||
| """ | ||
| dirname, basename = os.path.split(self._repr_def) | ||
| if "[" in basename and "]" not in basename: | ||
| return dirname.split("[")[0] | ||
| return dirname | ||
|
|
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for handling optional keys in _dirname() is incomplete. If the basename contains a [ but no ], it splits by [ and takes only the first part, but this doesn't account for cases where the opening bracket is in the dirname portion. For example, with shots/{Sequence}/[{Shot}]-{Step}, after os.path.split, basename would be [{Shot}]-{Step} (containing both [ and ]), and the condition would be false, but the dirname shots/{Sequence} would be incorrect if it also had an unclosed bracket. Consider a more robust approach that properly handles nested or partial optional sections.
| Returns the directory part of the template. | |
| :returns: String | |
| """ | |
| dirname, basename = os.path.split(self._repr_def) | |
| if "[" in basename and "]" not in basename: | |
| return dirname.split("[")[0] | |
| return dirname | |
| Returns the directory part of the template, robustly handling optional sections. | |
| :returns: String | |
| """ | |
| return self._split_path_outside_brackets(self._repr_def) | |
| @staticmethod | |
| def _split_path_outside_brackets(path): | |
| """ | |
| Splits the path at the last path separator that is outside any brackets. | |
| Returns the directory part. | |
| """ | |
| bracket_depth = 0 | |
| last_sep_outside = -1 | |
| for i, c in enumerate(path): | |
| if c == "[": | |
| bracket_depth += 1 | |
| elif c == "]": | |
| bracket_depth = max(0, bracket_depth - 1) | |
| elif c == os.sep and bracket_depth == 0: | |
| last_sep_outside = i | |
| if last_sep_outside == -1: | |
| return "" | |
| return path[:last_sep_outside] |
| definition = "shots/{Sequence}[-{seq_num}]/{Shot}/{Step}/work" | ||
| parent_expected_definitions = [ | ||
| "shots/{Sequence}-{seq_num}/{Shot}/{Step}", | ||
| "shots/{Sequence}/{Shot}/{Step}", | ||
| ] | ||
| parent_expected__repr_def = os.path.dirname(definition) |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test verifies _definitions and _repr_def but doesn't explain why both are necessary or what they represent. Add a comment explaining that _definitions contains all possible expanded forms (with and without optional keys) while _repr_def is the original representation-preserving form with optional syntax intact.
|
|
||
| def test_dirname_with_optional(self): | ||
| definition = "shots/{Sequence}/{Shot}[/{Step}]" | ||
| template = TemplatePath(definition, {}, root_path=self.project_root) |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TestDirname test cases pass an empty dictionary for keys instead of self.keys. This may not fully test the interaction between the dirname property and key validation. Consider using self.keys consistently to ensure the tests validate behavior with actual key definitions, or add a comment explaining why empty keys are intentional for these specific tests.
|
|
||
| def test_dirname_with_optional_word_and_key(self): | ||
| definition = "shots/{Sequence}/{Shot}/[abc_{Step}]" | ||
| template = TemplatePath(definition, {}, root_path=self.project_root) |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TestDirname test cases pass an empty dictionary for keys instead of self.keys. This may not fully test the interaction between the dirname property and key validation. Consider using self.keys consistently to ensure the tests validate behavior with actual key definitions, or add a comment explaining why empty keys are intentional for these specific tests.
|
|
||
| def test_dirname_with_optional_key_and_key(self): | ||
| definition = "shots/{Sequence}/[{Shot}]-{Step}" | ||
| template = TemplatePath(definition, {}, root_path=self.project_root) |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TestDirname test cases pass an empty dictionary for keys instead of self.keys. This may not fully test the interaction between the dirname property and key validation. Consider using self.keys consistently to ensure the tests validate behavior with actual key definitions, or add a comment explaining why empty keys are intentional for these specific tests.
| parent_definition = os.path.dirname(self.definition) | ||
| if parent_definition: | ||
| parent_definition = self._dirname() | ||
| if parent_definition and parent_definition != "/": |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check parent_definition != \"/\" is Unix-specific and may not work correctly on Windows where root paths use drive letters (e.g., C:\\). Consider using os.path.dirname(parent_definition) != parent_definition or a more platform-agnostic approach to detect root paths.
| if parent_definition and parent_definition != "/": | |
| if parent_definition and os.path.dirname(parent_definition) != parent_definition: |
Context
The parent property of PathTemplate doesn't create a correct PathTemplate when optional keys are used. For example, if you create a template
shots/{Sequence}[-{seq_num}]/{Shot}/{Step}/work, the parent property will return a templatePath without the optional[-{seq_num}]part.Problem
When creating a PathTemplate with optional keys, the parent property omits these keys in the generated path. This can result in incorrect or incomplete paths.
Solution
To solve this issue, instead of passing self.definition to the PathTemplate constructor, we use self._repr_def, which contains the optional keys.
Testing
A test has been added to verify that the parent property now correctly includes optional keys in the generated PathTemplate.