-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Add default values #1490
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
Add default values #1490
Conversation
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 didnt check the tests because I feel nowhere save with them.
@property | ||
def _has_input_message_content(self): | ||
return hasattr(self, 'input_message_content') |
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 dont exactly see what this has to do with the PR?
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 question … Don't recall, why I added it …
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.
Mh, actually it seems that the InlineQueryResult…
classes don't always have an input_message_content
attribute. This is due to the fact, that the base class InlineQueryResult
doesn't have one and in the child classes the optionals are parsed in the manner
if input_message_content:
self.input_message_content = input_message_content
without preceding self.input_message_content = None
.
I.e. if the optional input_message_content
is not passed, the class instance won't have such an attribute.
I'm not sure that this is acutally wanted behavior, but changing that would surely be the content of another PR …
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.
Okay, above mentioned issue is solved by #1600. But this is still needed, since e.g. InlineQueryResultGame
has neither a parse_mode
nor ar input_message_content
attribute.
What I saw from my little dive into your changes is that this is a good solutions which tackels all the problems we had, everything is covered. If the other maintainer agree, we can built the other default ideas we had (timeout, disable web preview mainly) on top of this. Lets use #1527 to track this if we come so far. Good job though, I really like this :) Since this is such a big change and I bet that there will be a lot of discussion around this, I feel like we should give it it's own version, so I added it to the 12.3 milestone. |
Hey, I was wondering if it was possible to move this check: from all the different so that we wouldn't have as much code duplication. |
Regarding the Assuming we want to be able to have defaults for other parameters, I suggest a class Defaults():
def __init__(parse_mode=None, disable_notification=False):
self.parse_mode = parse_mode
self.disable_notification = disable_notification Then, we can use it like this: def Bot(token, base_url=None, base_file_url=None, request=None, private_key=None,
private_key_password=None, defaults=Defaults()):
class Bot(TelegramObject):
[...]
@log
def send_message(self,
chat_id,
text,
parse_mode=defaults.parse_mode,
disable_web_page_preview=None,
disable_notification=defaults.disable_notification,
reply_to_message_id=None,
reply_markup=None,
timeout=None,
**kwargs):
[...]
return Bot(token, base_url, base_file_url, request, private_key, private_key_password) which means you can create a bot like Now, i realize this seems a little janky with the factory and all, but i still wanted to put it out there as an idea. |
Your idea for a My main concern is usability. To me it seems a bit harder to understand
than
Still, I guess I'd put maintainability before usability 😄 |
Here's a solution that does the best of both worlds (maybe?) import functools
import inspect
class Defaults:
parse_mode = None
disable_notification = False
class Bot:
def __new__(cls, *args, **kwargs):
# Transform default_x=y kwargs into Defaults.x=y
defaults = Defaults()
for kwarg in list(kwargs.keys()):
if kwarg.startswith('default_'):
defaults.__dict__[kwarg[8:]] = kwargs[kwarg]
del kwargs[kwarg]
# Make an instance of the class
instance = super(Bot, cls).__new__(cls, *args, **kwargs)
# For each method
for method_name, method in inspect.getmembers(instance, predicate=inspect.ismethod):
# Get kwargs
argspec = inspect.getfullargspec(method)
kwarg_names = argspec.args[-len(argspec.defaults or []):]
# Check if Defaults has a attribute that matches the kwarg name
needs_default = [kwarg_name for kwarg_name in kwarg_names if kwarg_name in defaults.__dict__.keys()]
# Make a dict of kwarg name and the default value
default_kwargs = {kwarg_name: defaults.__dict__[kwarg_name] for kwarg_name in needs_default}
# Apply the defaults using a partial
if default_kwargs:
setattr(instance, method_name, functools.partial(method, **default_kwargs))
return instance
def __init__(self, *args, **kwargs):
pass
def test(self, parse_mode=None, disable_notification=False):
print('parse_mode=', parse_mode, ' ', 'disable_notification=', disable_notification)
print('bot1') # bot1
bot1 = Bot()
bot1.test() # parse_mode= None disable_notification= False
bot1.test(parse_mode='md') # parse_mode= md disable_notification= False
bot1.test(parse_mode='html') # parse_mode= html disable_notification= False
bot1.test(parse_mode=None) # parse_mode= None disable_notification= False
bot1.test(disable_notification=False) # parse_mode= None disable_notification= False
bot1.test(disable_notification=None) # parse_mode= None disable_notification= None
bot1.test(disable_notification=True) # parse_mode= None disable_notification= True
print('bot2') # bot2
bot2 = Bot(default_parse_mode='md')
bot2.test() # parse_mode= md disable_notification= False
bot2.test(parse_mode='md') # parse_mode= md disable_notification= False
bot2.test(parse_mode='html') # parse_mode= html disable_notification= False
bot2.test(parse_mode=None) # parse_mode= None disable_notification= False
bot2.test(disable_notification=False) # parse_mode= md disable_notification= False
bot2.test(disable_notification=None) # parse_mode= md disable_notification= None
bot2.test(disable_notification=True) # parse_mode= md disable_notification= True |
@jsmnbom Seems equally janky, if not more so, but it's really cool ^^ |
Your approach looks good, @jsmnbom, but it only seems to work for bot methods that get the
and
within Or am I missing something? |
@Bibo-Joshi We could still save the |
Simply storing |
@Bibo-Joshi In my mind, there are two different problems here:
Your PR already handles the second problem pretty well, IMO, with the help of the Is there a third case or am i missing some other problem with this? |
Yea in my opinion the best way to do it is using the snippet i posted, but then keeping a lot of your code for the InlineQuery stuff ^^ |
Okidok, then we're all on the same page :) Just wanted to clarify, before starting to work on it ;) |
mh … I implemented the changes @jsmnbom suggested for the default parameters, but now I'm having trouble adjusting the tests. Since I can't just alter
in the routines (and reset to 'None' afterwards). However that causes (at least i thinks that's the connection) other tests to fail. E.g. after Do you have any hint on how to handle this, @jh0ker @jsmnbom ? Update:
instead of above snipped and that seems to work 💃 |
|
Well, a |
Latest commits add Regarding failed tests:
|
This reverts commit 1baa91a.
* Rename Test suite * Actually change the badge in readme * fix download without path arguments (#1591) * fix download without path arguments * fix download without path arguments * solved downloading a file without file_path or custom_path * if no file_path, download as file_id * Add test case * Elaborate doc string Co-authored-by: Bibo-Joshi <hinrich.mahler@freenet.de> * Add default values (#1490) * added parse_mode parameter in Updater and in Bot class to set a default parse mode for bot * DefaultValue * Add default parse_mode everywhere * Renome to default_parse_mode * Test def parse_mode for send_*, edit_message_* * Remove duplicate code in Input* classes * Improve handling of def_p_m for Bot methods * Remove unneeded deletion of kwargs * Make @log preserve signature, add bots with defaults to tests * Fix Codacy * Fix Travis Error * Add default_disable_notification * Add default_disable_web_page_preview * Add default_disable_timeout * add default_disable_web_page_preview for InputTextMessageContent * add default_quote * Try fixing test_pin_and_unpin * Just run 3.5 tests for paranoia * add tests for Defaults * Revert "Just run 3.5 tests for paranoia" This reverts commit 1baa91a. * Tidy up parameters, move Defaults to ext * Stage new files, because im an idiot * remove debug prints * change equality checks for DEFAULT_NONE * Some last changes * fix S&R error so that tests actually run Co-authored-by: Ak4zh <agwl.akash@gmail.com> Co-authored-by: Eldinnie <Eldinnie@users.noreply.github.com> * Skip test relying on ordered dicts for 3.5 (#1752) * Rename Test suite * Actually change the badge in readme Co-authored-by: Gabriel Simonetto <42247511+GabrielSimonetto@users.noreply.github.com> Co-authored-by: Ak4zh <agwl.akash@gmail.com> Co-authored-by: Eldinnie <Eldinnie@users.noreply.github.com>
Supersedes/Closes #1226 and closes #1527
To meet the requirements stated by @Eldinnie in #1226, I added a new class
DefaultValue
to thehelpers
module. It's purpose is to allow to differentiate check if a value is just the default or was set specifically:With
def f(arg=None)
we don't know, ifarg is None
results fromf()
or fromf(None)
.With
def f(arg=DefaultValue(None)
, we can handle those differently.This makes it necessary to add some stuff to the
InlineQueryResult*
forbot.answer_inline_query
to parse the newdefault_parse_mode
correctly. But it does the job :) MaybeDefaultValue
also comes in handy with a future use case …How do the @python-telegram-bot/maintainers feel about this?
For the tests, I tried and added them for the
send_*
andedit_message_*
methods. I'm still not too familiar withpytest
, so please tell if those can be improved.However, I couldn't find a clever way to test the
default_parse_mode
foranswer_inline_query
since I'd somehow have to get hold of the resulting message. Any idea is appreciated :)