-
Notifications
You must be signed in to change notification settings - Fork 559
Update stack and stack components via the CLI #497
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
Conversation
schustmi
left a comment
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.
Nice work! Only left a few small comments
| def __check_component( | ||
| component: StackComponentWrapper, | ||
| ) -> Tuple[StackComponentType, str]: | ||
| try: | ||
| _ = self.get_stack_component( | ||
| component_type=component.type, name=component.name | ||
| ) | ||
| except KeyError: | ||
| self.register_stack_component(component) | ||
| return component.type, component.name | ||
|
|
||
| stack_configuration = { | ||
| typ: name for typ, name in map(__check_component, stack.components) | ||
| } |
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.
Maybe we can extract this common code from both registering and updating a stack into some separate function that both of them can call?
jwwwb
left a comment
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.
Oops, I wrote a few comments but forgot to submit the review. Looks good, just have a few small ideas that could be changed.
src/zenml/cli/stack.py
Outdated
| try: | ||
| current_stack = repo.get_stack(stack_name) | ||
| except KeyError as e: | ||
| console.print( |
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.
Shouldn't this also return from the function? It seems like all the stack_components key access below will fail
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.
I'll just raise a cli_utils.error instead of printing. Good catch.
| @abstractmethod | ||
| def update_stack_component( | ||
| self, | ||
| current_component: StackComponentWrapper, |
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.
Since this is all unimplemented, Maybe consider leaving it out? But if it does stay in, I think the function signature should not take 2 StackComponentWrappers, but rather a type and name of the component to update, and a wrapper to update it with (same signature as the create stack component)
tests/unit/test_repository.py
Outdated
| ) | ||
|
|
||
|
|
||
| def test_updating_a_new_stack_with_already_registered_components(clean_repo): |
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.
I would suggest maybe testing in the stack store tests, because those are parameterized to test every implementation, whereas this will only test the yaml version I believe.
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.
Good idea. I've moved it over.
…date-stack-components
…date-stack-components
…date-stack-components
src/zenml/cli/stack.py
Outdated
| stack_components = current_stack.components | ||
|
|
||
| if container_registry_flag: | ||
| stack_components = { |
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.
I think this could also be solved by a stack_components.pop(StackComponentType.CONTAINER_REGISTRY, None) as well to make it a little shorter. That will also remove a key-value pair from a dictionary in case a value for that key exists.
src/zenml/cli/stack.py
Outdated
| ) | ||
| stack_components = current_stack.components | ||
|
|
||
| registered_stacks = [stack.name for stack in repo.stacks] |
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.
| registered_stacks = [stack.name for stack in repo.stacks] | |
| registered_stacks = {stack.name for stack in repo.stacks} |
I would suggest using a set here as it makes conceptually more sense for a lookup
src/zenml/cli/stack_components.py
Outdated
| command_group.command( | ||
| "update", | ||
| context_settings=context_settings, | ||
| help=f"Update a new {singular_display_name}.", |
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.
| help=f"Update a new {singular_display_name}.", | |
| help=f"Update a registered {singular_display_name}.", |
src/zenml/cli/stack_components.py
Outdated
| registered_components = [ | ||
| component.name | ||
| for component in repo.get_stack_components(component_type) | ||
| ] |
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.
| registered_components = [ | |
| component.name | |
| for component in repo.get_stack_components(component_type) | |
| ] | |
| registered_components = { | |
| component.name | |
| for component in repo.get_stack_components(component_type) | |
| } |
src/zenml/cli/stack_components.py
Outdated
| for prop in required_properties: | ||
| if prop not in parsed_args: | ||
| parsed_args[prop] = getattr(current_component, prop) | ||
| updated_component = component_class(name=name, **parsed_args) |
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.
If the component.update worked in the rename method, it might be worth trying it here as well like this:
updated_component = current_component.copy(update=parsed_args)
| self._delete_stack_component(component_type, name) | ||
|
|
||
| # add the component to the stack store dict and write it to disk | ||
| components[component.name] = component.flavor |
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.
I know that you're already checking for the case that a component exists in the CLI methods (and I definitely think it should be there with a CLI error message), however I think we should have it here as well. Users (or us) might still call this function directly in which case we would just overwrite the existing one without noticing. Maybe a StackComponentExistsError?
| try: | ||
| self.get_stack(name) | ||
| except KeyError: | ||
| raise DoesNotExistException( |
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.
I would keep this as a KeyError (and get rid of the DoesNotExistException entirely) as we use that in all other cases as well.
| except KeyError: | ||
| self.register_stack_component(component) | ||
| return component.type, component.name | ||
|
|
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.
Same as the stack component, I think we should still have a check here if something with the new name already exists. Probably a StackExistsError?
| component_type, | ||
| component.name, | ||
| ) | ||
| # tc = session.exec( |
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.
Can probably be removed? Similar to the local store, I think we should handle a component with that name already existing
| session.add(stack) | ||
| stack = session.exec( | ||
| select(ZenStack).where(ZenStack.name == name) | ||
| ).first() |
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.
Similar to the local store, I think we should handle a stack with that name already existing
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.
Yes I think this is now handled.
|
@schustmi Addressed your comments in these latest commits. |
schustmi
left a comment
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.
This PR was great a few iterations ago already and it got even better!
stefannica
left a comment
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.
Awesome work ! Thanks so much for considering my suggestions.
| f"found with this name." | ||
| ) | ||
| elif name != component.name and component.name in components: | ||
| breakpoint() |
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.
You forgot a breakpoint here :)
I added functionality to allow users to update stacks that already exist. This shows the basic workflow:
More details are in the CLI docs (updated as part of this PR). Users can add new stack components to a pre-existing stack, or they can modify already-present stack components. They can also rename their stack and individual stack components.
Remaining tasks:
test_stack_storewhich is causing wonky functionalityPre-requisites
Please ensure you have done the following:
Types of changes