You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Create a issue board list by milestone_id still depends on label_id #1897
Description of the problem, including code/CLI snippet
I followed the API documentation which says that the usage of label_id, milestone_id and assignee_id is mutually exclusive. So I wanted to create a board which would relate on milestones to have a timeline. The function goes like:
@kurisukun I've assigned this to you for now for the initial simple change as you expressed interest, feel free to unassign if you don't feel like working on this though :)
IIUC this will technically also be incorrect since at least one of them is required. So (as a follow-up maybe) we could also introduce mutually exclusive/"requires one of" type of flow, ala Ansible:
@nejch .. where would we implement mutually exclusive/requires? argparse has add_mutually_exclusive_group(required=True) which does exactly how it sounds. The args are mutually exclusive, but one of them is required.
@walterrowe thanks for looking into this! For an initial fix, I think we'd be happy with first just making the arguments optional and let the API response guide the user (the user will get a 4xx response with the details I believe).
However if you're willing to work on a fix in the code itself, that'd be awesome :) We do already make some limited use of this in our CLI, see:
help=("GitLab CI job token [env var: CI_JOB_TOKEN]"),
required=False,
)
However, we would need to handle this in the API client itself, not just the CLI wrapper, and I'm not sure we should bring in argparse as I'd like to make the API independent of CLI-focused libraries. One place to store mutually exclusive args would be to extend our RequiredOptional class:
Currently, we validate args in _check_missing_create_attrs and _check_missing_update_attrs, so this would probably be the place to do it, but refactoring that might also make sense :)
I think we might even need a list/tuple of tuples here, because there could be several sets of mutually exclusive attributes for busier endpoints, but I'm not 100%.
refactor _check_missing_create_attrs and _check_missing_update_attrs
perhaps rename _check_missing_create_attrs and _check_missing_update_attrs (remove _missing from the name)?
Sounds great. Another option, since these two are mostly just copies of each other with the id removed for update, would be to extract them into a common validate_attrs helper with an optional exclude argument that can be supplied to ignore certain elements, if that makes sense. This would also make it useful later if we need to do the same for get() or list() filters.
@nejch took a simple stab at this for _check_missing_create_attrs()
def_check_missing_create_attrs(self, data: Dict[str, Any]) ->None:
missing= [ attrforattrinself._create_attrs.requiredifattrnotindata ]
iflen(missing) >0:
raiseAttributeError(f"Missing attributes: {', '.join(missing)}")
exclusives= [ attrforattrindataifattrinself._create_attrs.exclusive ]
iflen(exclusives) >1:
raiseAttributeError(f"Provide only one of these attributes: {', '.join(exclusives)}")
iflen(exclusives) ==0:
raiseAttributeError(f"Must provide one of these attributes: {', '.join(self._create_attrs.exclusive)}")
If exclusives has more than 1 item we raise an error on mutually exclusive attrs.
If exclusives is empty, we raise an error on missing attrs.
Does this achieve the desired effect?
I'm thinking about your proposal for a common validate_attrs(). That seems to make a lot of sense on the surface.
@nejch - is this what you are thinking for a common _validate_attrs() .. ?
How do we discern whether to reference _update_attrs vs _create_attrs? Any direction on this would be helpful in pushing this forward.
def_validate_attrs(self, data: Dict[str, Any], excludes=[]) ->None:
ifTYPE_CHECKING:
assertself._obj_clsisnotNone# Remove the id field from the required list as it was previously moved# to the http path.required=tuple(
[kforkinself._update_attrs.requiredifk!=excludes]
)
missing= [ attrforattrinrequiredifattrnotindata ]
iflen(missing) >0:
raiseAttributeError(f"Missing attributes: {', '.join(missing)}")
exclusives= [ attrforattrindataifattrinself._create_attrs.exclusive ]
iflen(exclusives) >1:
raiseAttributeError(f"Provide only one of these attributes: {', '.join(exclusives)}")
iflen(exclusives) ==0:
raiseAttributeError(f"Must provide one of these attributes: {', '.join(self._create_attrs.exclusive)}")
@walterrowe I was thinking we could supply attributes explicitly as an argument, e.g.:
def_validate_attrs(
self,
data: Dict[str, Any],
attributes: RequiredOptional,
excludes: Optional[list] =None
) ->None:
ifexcludesisNone:
excludes= []
required= [kforkinattributes.requiredifknotinexcludes]
missing= [attrforattrinrequiredifattrnotindata]
ifmissing:
raiseAttributeError(f"Missing attributes: {', '.join(missing)}")
exclusives= [attrforattrindataifattrinattributes.exclusive]
iflen(exclusives) >1:
raiseAttributeError(f"Provide only one of these attributes: {', '.join(exclusives)}")
ifnotexclusives:
raiseAttributeError(f"Must provide one of these attributes: {', '.join(attributes.exclusive)}")
So generally that looks good but I'm also thinking there might be some completely optional but mutually exclusive parameters, in which case we'd need to change this logic to simply check both required and optional data provided against the lists of mutually exclusive groups.
@nejch - I spent quite a bit of time reading Python docs and perusing base.py and mixin.py to get up to speed on how all of this is glued together. I feel pretty confident about my proposal above. I have a fork of python-gitlab with local changes I have not yet committed. Let me know if you agree with the above, let me know what else I should include (doc updates?), etc, and I will commit, push, and submit a merge request.
This doesn't handle the more complex case of multiple mutually exclusive sets of params. I think that can come later. We have people waiting on this specific enhancement.
feat: support mutually exclusive attributes, consolidate attribute validation, fix boards.py _create_attr
A paragraph or two giving more details
Closes: #1897
@walterrowe I'd say it's even simpler, you can remove the _check* methods completely and just have a normal function in utils.py that gets imported in client.py. This will have the argument passed explicitly instead of relying on self..
I think you can go ahead and open a PR, it's actually easier to do code review once we have something pushed ;)
Activity
JohnVillalovos commentedon Feb 22, 2022
This is due to:
python-gitlab/gitlab/v4/objects/boards.py
Line 26 in bd1ecdd
So that would need to be changed. Probably should make it
optionaland also add the other optional values to the tuple.kurisukun commentedon Feb 22, 2022
That's exactly what I was going to post. I can do the changes if it's possible
nejch commentedon Mar 31, 2022
@kurisukun feel free to go ahead :)
IIUC this will technically also be incorrect since at least one of them is required. So (as a follow-up maybe) we could also introduce mutually exclusive/"requires one of" type of flow, ala Ansible:
https://docs.ansible.com/ansible/latest/dev_guide/developing_program_flow_modules.html#dependencies-between-module-options
nejch commentedon Apr 4, 2022
@kurisukun I've assigned this to you for now for the initial simple change as you expressed interest, feel free to unassign if you don't feel like working on this though :)
walterrowe commentedon May 28, 2022
the same issue exists for creating assignee boards
There is explicit reference to being able to create assignee and milestone lists in the API docs.
https://gitlab.com/gitlab-org/gitlab/-/issues/6397
https://docs.gitlab.com/ee/api/boards.html#create-a-board-list
walterrowe commentedon May 29, 2022
@nejch .. where would we implement mutually exclusive/requires? argparse has
add_mutually_exclusive_group(required=True)which does exactly how it sounds. The args are mutually exclusive, but one of them is required.https://docs.python.org/3/library/argparse.html#mutual-exclusion
nejch commentedon May 29, 2022
@walterrowe thanks for looking into this! For an initial fix, I think we'd be happy with first just making the arguments optional and let the API response guide the user (the user will get a 4xx response with the details I believe).
However if you're willing to work on a fix in the code itself, that'd be awesome :) We do already make some limited use of this in our CLI, see:
python-gitlab/gitlab/cli.py
Lines 229 to 246 in 387a140
However, we would need to handle this in the API client itself, not just the CLI wrapper, and I'm not sure we should bring in
argparseas I'd like to make the API independent of CLI-focused libraries. One place to store mutually exclusive args would be to extend our RequiredOptional class:python-gitlab/gitlab/base.py
Lines 333 to 336 in 387a140
Currently, we validate args in
_check_missing_create_attrsand_check_missing_update_attrs, so this would probably be the place to do it, but refactoring that might also make sense :)walterrowe commentedon May 29, 2022
@nejch I agree.
_check_missing_create_attrsand_check_missing_update_attrs(remove _missing from the name)?Then in places like boards.pyL26 we would see:
I don't want to fork and work on it until we agree on direction.
nejch commentedon May 29, 2022
@walterrowe that sounds good!
I think we might even need a list/tuple of tuples here, because there could be several sets of mutually exclusive attributes for busier endpoints, but I'm not 100%.
Sounds great. Another option, since these two are mostly just copies of each other with the
idremoved forupdate, would be to extract them into a commonvalidate_attrshelper with an optionalexcludeargument that can be supplied to ignore certain elements, if that makes sense. This would also make it useful later if we need to do the same forget()orlist()filters.walterrowe commentedon May 29, 2022
@nejch took a simple stab at this for _check_missing_create_attrs()
Does this achieve the desired effect?
I'm thinking about your proposal for a common validate_attrs(). That seems to make a lot of sense on the surface.
walterrowe commentedon May 29, 2022
@nejch - is this what you are thinking for a common _validate_attrs() .. ?
How do we discern whether to reference _update_attrs vs _create_attrs? Any direction on this would be helpful in pushing this forward.
nejch commentedon May 29, 2022
@walterrowe I was thinking we could supply attributes explicitly as an argument, e.g.:
So generally that looks good but I'm also thinking there might be some completely optional but mutually exclusive parameters, in which case we'd need to change this logic to simply check both required and optional data provided against the lists of mutually exclusive groups.
We need to check the upstream API docs first though to see where this appears, e.g. https://docs.gitlab.com/search/?query=Mutually%20exclusive, I'll take a look today as well.
walterrowe commentedon May 29, 2022
Ah .. would _check_update_attrs and _check_create_attrs call this and pass in the requisite attributes and excludes lists?
walterrowe commentedon May 30, 2022
@nejch
trying to get a sense of right place to do this
this introduces the least change and implements it once. class inheritance is new to me so trying to get a handle on how this should be done.
in base.py RequiredOptional dataclass class add the exclusive attribute:
in base.py in class RESTManager we add:
in mixin.py we change _check_missing_create_attrs() to:
in mixin.py we change _check_update_create_attrs() to:
in gitlab/v4/objects/boards.py we change GroupBoardListManager class _create_attrs as follows:
as I said class inheritance is new to me so looking for some more seasoned guidance on proper syntax, etc.
walterrowe commentedon May 30, 2022
@nejch - I spent quite a bit of time reading Python docs and perusing base.py and mixin.py to get up to speed on how all of this is glued together. I feel pretty confident about my proposal above. I have a fork of python-gitlab with local changes I have not yet committed. Let me know if you agree with the above, let me know what else I should include (doc updates?), etc, and I will commit, push, and submit a merge request.
This doesn't handle the more complex case of multiple mutually exclusive sets of params. I think that can come later. We have people waiting on this specific enhancement.
walterrowe commentedon May 30, 2022
Here is a proposed commit message:
JohnVillalovos commentedon May 30, 2022
Probably will need to be something like this:
nejch commentedon May 30, 2022
Thanks @JohnVillalovos!
@walterrowe I'd say it's even simpler, you can remove the
_check*methods completely and just have a normal function inutils.pythat gets imported inclient.py. This will have the argument passed explicitly instead of relying onself..I think you can go ahead and open a PR, it's actually easier to do code review once we have something pushed ;)
feat: support mutually exclusive attributes and consolidate validatio…