use sets instead of lists to store CH and PH commands#2890
use sets instead of lists to store CH and PH commands#2890KH4St3H wants to merge 61 commits intopython-telegram-bot:masterfrom
Conversation
…t#2612) Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
Signed-off-by: starry69 <starry369126@outlook.com> Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
…m-bot#2634) Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com> Co-authored-by: poolitzer <25934244+Poolitzer@users.noreply.github.com>
Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com> Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com> Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
|
Hi, are you working on #2885 with this? |
yes |
Bibo-Joshi
left a comment
There was a problem hiding this comment.
Hi. thanks for the PR! IISC so far you have only allowed for the PrefixHandler.prefix/command setters to accept sets.
I think I should clarify a bit what the intention of #2885 was:
- The main idea of the issue was to rename
CommandHandler.commandtoCommandHandler.commands(plural) since that attribute is currently actually always a list. Extending this toPrefixHandlers.prefixis surely reasonable. - Since I noticed that we only ever need
x in self.command, my idea was to makeCommandHandler.commandsa set instead of a list. Accepting sets as input in addition is surely a logical extension. So
Additionally, while reviewing your PR, I noticed that unlike PrefixHandler.command, CommandHandler.command doesn't have a setter. I.e. if you do command_handler.command = 'NewCommand', it won't be converted to [newcommand]. I think we should fix that. So what I would ask you to do is:
- Make sure that sets are accepted as input for
Prefix/CommandHandler.commandandPrefixHandler.prefix- this includes type hints & documentation - rename
CommandHandler.commandtoCommandHandler._commandsand add getter + setter proprtiesCommandHandler.commands. The getter should provide access to the setCH._commandsand the setter should accept a single string or tuple/list/set and convert it as it is converted on__init__ - Apply analogous changes to
PrefixHandler
telegram/ext/_commandhandler.py
Outdated
|
|
||
| Attributes: | ||
| command (:obj:`str` | Tuple[:obj:`str`] | List[:obj:`str`]): | ||
| command (:obj:`str` | Tuple[:obj:`str`] | List[:obj:`str`] | Set[:obj:`str`]): |
There was a problem hiding this comment.
In the current implementation, CommandHandler.command is actually always a List …
There was a problem hiding this comment.
Hi. thanks for the PR! IISC so far you have only allowed for the
PrefixHandler.prefix/commandsetters to accept sets. I think I should clarify a bit what the intention of #2885 was:
- The main idea of the issue was to rename
CommandHandler.commandtoCommandHandler.commands(plural) since that attribute is currently actually always a list. Extending this toPrefixHandlers.prefixis surely reasonable.- Since I noticed that we only ever need
x in self.command, my idea was to makeCommandHandler.commandsa set instead of a list. Accepting sets as input in addition is surely a logical extension. SoAdditionally, while reviewing your PR, I noticed that unlike
PrefixHandler.command,CommandHandler.commanddoesn't have a setter. I.e. if you docommand_handler.command = 'NewCommand', it won't be converted to[newcommand]. I think we should fix that. So what I would ask you to do is:
- Make sure that sets are accepted as input for
Prefix/CommandHandler.commandandPrefixHandler.prefix- this includes type hints & documentation- rename
CommandHandler.commandtoCommandHandler._commandsand add getter + setter proprtiesCommandHandler.commands. The getter should provide access to the setCH._commandsand the setter should accept a single string or tuple/list/set and convert it as it is converted on__init__- Apply analogous changes to
PrefixHandler
Thanks for the review, I'm working on it. should I change the name in arguments from command to commands too?
There was a problem hiding this comment.
should I change the name in arguments from command to commands too?
no need IMO, since either way the arguments will have to accept both single strings or colletcions of strings.
Bibo-Joshi
left a comment
There was a problem hiding this comment.
hi, thanks for the updates! I left a few comments below
Also please add a .. versionchanged:: directive just before the Args: of CommandHandler saying that command was replaced by commands
telegram/ext/_commandhandler.py
Outdated
| commands (Set[:obj:`str`]): | ||
| The set of command(s) this handler should listen for. |
There was a problem hiding this comment.
the property already has a docstring, so you can remove this documentation of the attribute
telegram/ext/_commandhandler.py
Outdated
|
|
||
| .. versionchanged:: 14.0 | ||
| :attr:`commands` is now a set and can be assinged after construction of model. |
telegram/ext/_commandhandler.py
Outdated
| if isinstance(command, str): | ||
| self._commands = {command.lower()} | ||
| else: | ||
| self._commands = {i.lower() for i in command} |
There was a problem hiding this comment.
THis is the same logic that's used on __init__. Could you extract it to a new protected method (e.g. _parse_commands()) that you call both here and on __init__? Then you can also move the regexing from __init__ to that function. THat will make sure that also the setter only accepts valid bot commands (please add a unit test for this)
There was a problem hiding this comment.
Sure, I first tried to do it like this
self.commands = command
But it wouldn't work when PrefixHandler tried to call it because it has a different setter than CommandHandler with the same name. But I'll make a function for it.
There was a problem hiding this comment.
I noticed we don't have CommandHandler command limitations for PrefixHandler, so illegal commands in prefixhandler should work properly shouldn't they?
There was a problem hiding this comment.
I noticed we don't have CommandHandler command limitations for PrefixHandler, so illegal commands in prefixhandler should work properly shouldn't they?
yes, since PrefixHandler is not limited to the commands recognized by Telegram. Actually the fact that PrefixHandler is a subclass of CommandHandler is more or less an implementation detail …
|
|
||
| Args: | ||
| command (:obj:`str` | Tuple[:obj:`str`] | List[:obj:`str`]): | ||
| command (:obj:`str` | Tuple[:obj:`str`] | List[:obj:`str`] | Set[:obj:`str`]): |
There was a problem hiding this comment.
please add a "versionchanged" directive saying that it now also accepts sets
telegram/ext/_commandhandler.py
Outdated
| .. versionchanged:: 14.0 | ||
| :attr:`prefix` has been changed to :attr:`prefixes` and accepts sets as well. | ||
| :attr:`command` has been changed to :attr:`commands` and accepts sets as well. |
There was a problem hiding this comment.
Please move the "changed to" notes to a versionchanged directive right before the Args:. Please move the "now accepts sets" to a versionchanged directive in the documentation of the respective arguments
telegram/ext/_commandhandler.py
Outdated
| @property # type: ignore[override] | ||
| def command(self) -> List[str]: # type: ignore[override] | ||
| @property | ||
| def commands(self) -> Set[str]: |
There was a problem hiding this comment.
Is this override still necessary? IISC, this does exatly the same as CommandHandler.commands
There was a problem hiding this comment.
yes, because this class uses a different setter than the parent one and needs a property to set it.
There was a problem hiding this comment.
IIRC using @CommandHandler.commands.setter instead of @commands.setter should do the trick and then you don't need to re-implement the property
| handler = CommandHandler(['test', 'star'], self.callback_basic) | ||
| assert is_match(handler, make_command_update('/test')) | ||
| handler.commands = "help" | ||
| assert is_match(handler, make_command_update('/help')) |
There was a problem hiding this comment.
| assert is_match(handler, make_command_update('/help')) | |
| assert is_match(handler, make_command_update('/help')) | |
| assert not is_match(handler, make_command_update('/test)) |
| handler.commands = "help" | ||
| assert is_match(handler, make_command_update('/help')) | ||
| handler.commands = ['exit', 'happy'] | ||
| assert is_match(handler, make_command_update('/happy')) |
There was a problem hiding this comment.
| assert is_match(handler, make_command_update('/happy')) | |
| assert is_match(handler, make_command_update('/happy')) | |
| assert not is_match(handler, make_command_update('/help')) |
|
|
||
| .. versionchanged:: 14.0 | ||
| :attr:`commands` is now a set and can be assinged after construction of model. | ||
|
|
| attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. | ||
|
|
||
| .. versionchanged:: 14.0 | ||
| :attr:`commands` is now a set and can be assinged after construction of model. |
There was a problem hiding this comment.
The attribute command was replaced by :attr:commands
| and message.get_bot() | ||
| ): | ||
| command = message.text[1 : message.entities[0].length] | ||
| command = message.text[1: message.entities[0].length] |
There was a problem hiding this comment.
please revert - this is the reason the pre-commit test is failing
| for comm in self._commands: | ||
| if not re.match(r'^[\da-z_]{1,32}$', comm): | ||
| raise ValueError(f'Command `{comm}` is not a valid bot command') |
There was a problem hiding this comment.
when this is raised, self._commands has already been changed - it should be the other way around. You can even add a unit test along the lines
- bulid commandhandler with valid commands
- try to assign invalid commands
- assert that the commands where not changed
what we can do to make this easier here & also make the init a bit cleaner is:
- make
_parse_commandsreturn the parsed commands instead of assigning them directly toself._commands. It can be converted into a static method - apply the regexing for valid commands directly within
_parse_commands - do
self._commands = self._parse_commands(command)both here and in__init__.
| self._command = command | ||
| @CommandHandler.commands.setter # type: ignore[attr-defined,misc] | ||
| def commands(self, command: SLTS) -> None: | ||
| self._parse_commands(command) |
There was a problem hiding this comment.
IISC this doesn't rebuilt self._combinations, but that's needed. I guess it should be self._build_commands instead. If you want to reduce code duplication some more, you can add a method _slts_to_set that returns {arg.lower()} if isinstance(arg, str) else {item.lower() for item in arg}. This can be a static method of CommandHandler or even just a module level function. You can then use it in CH._parse_commands, in the setter of prefixes and here.
| :attr:`prefixes` and :attr:`commands` are always a set and can be | ||
| assigned with sets as well. |
There was a problem hiding this comment.
| :attr:`prefixes` and :attr:`commands` are always a set and can be | |
| assigned with sets as well. | |
| The attributes ``prefix`` and ``command`` where replaced by :attr:`prefixes` and :attr:`commands`, respectively. |
fix CommandHandler and PrefixHandler to use lists instead of sets
.. versionadded:: version,.. versionchanged:: versionor.. deprecated:: versionto the docstrings for user facing changes (for methods/class descriptions, arguments and attributes)AUTHORS.rst(optional)Closes Rename CommandHandler.command to CH.commands (plural) … #2885