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
Dictionary expansion for keyword arguments #3820
Conversation
That's arguable, and we should IMHO be a hundred percent sure about that before picking a syntax that close yet different to python's. Consider the following use case:
I think this could prove useful, though we don't necessary want to support that for now, I would err towards the side of caution and use Apart from that, I would indeed want the feature, +1 for me :D |
As far as I know, all meson functions that take positional arguments also accept lists for those and flatten them automatically. Is there a real counterexample to that, or a potential non-abstract use case that couldn't be handled the same way? |
That's not always the case, see all functions with the noArgsFlattening decorator :) |
Okay, I just took a look at them. All of them fall into one of these groups:
Every use case for list expansions that I can think of is not applicable to either of those (in the sense that it doesn't achieve anything you can't easily do already). In Python it's mostly used to deal with variadic functions, which all flatten their arguments in Meson. Don't get me wrong though, I'm not religiously opposed to changing the syntax. But I'm a bit reluctant to add that extra asterisk without a solid reason to. |
I have two reasons for this:
On the other hand, afaiu the reason why python has both IMHO, it would probably be a good idea not to use @jpakkane , care to chime in? |
For dictionary updating we could probably do the same as for arrays. So you could do an union like this: joined = dict1 + dict2 Or you could do this:
Which would create a new dict in the same way as For passing the keyword dict I really don't like executable('foo', 'foo.c', some_option: 'foo', default_kwargs: some_dict) then it is clear that arguments override the values in the dictionary. On the other hand if we consider that this will most probably be used to specify different "target classes" we could do this: executable('foo', 'foo.c', some_option: 'foo', template: some_dict) where |
This looks interesting, I do something similar (manually) for VLC modules but the other way around: https://code.videolan.org/GSoC2018/ePirat/vlc/blob/meson/modules/meson.build#L36 |
I implemented dict + and += in #3905. Note that python itself doesn't support that, but I don't think we have to follow python here. |
There is a big question here on how we do overriding. As an example you might want to use this to have a include_directory in your dict and then use that in a target. Semantically what you probably want is to add the user specified include_directories kwargs to the ones in the dict. But there are other cases where you want to override the ones in the dict. |
I think overriding should be the default behavior. It is the most consistent and unambiguous. Just think about the can of worms you open when you consider that lists and dictionaries can be nested arbitrarily deeply. You can still satisfy the include_directories use-case with these semantics, albeit more verbosely: dict1 = { 'include_directories' : include_directories('foo'), 'some_list' : ['eggs'] }
dict2 = { 'include_directories' : include_directories('bar'), 'some_list': ['spam'] }
merged_dict = dict1 + dict2 + { 'include_directories' : dict1['include_directories'] + dict2['include_directories'] } |
Sorry for the delay, I've been busy with CppCon. The basic idea of this is sound and we should merge something like this definitely. I'll try to get a full review done within a day or two. |
Sorry for the delay, but let's see if we can come to an agreement on how this should behave. Suppose you have a dict with some default values you'd like to use. Call it executable('foo', 'foo.c',
c_args : '-DSOMETHING',
default_kwargs : def) Here current_def = def + {'c_args': def['c_args'] + ['-DSOMETHING']}
executable('foo', 'foo.c',
default_kwargs : current_def)
) Would this be a reasonable UI? Would you need some other kind of helper functionality? The additive dict model seems (to me at least) to work fairly well for this usage. |
This would be implemented for all function/methods calls in a generic way I guess? That shouldn't be too hard, basically:
Looks good to me. One possible caveat is it could get a bit confusing with #4159, where we have kwargs that take similar dict like |
this feels like it has a lot of overlap with |
@ePirat it fix real and important use-cases. If you have a better idea to fix them please comment on that PR. Otherwise I think it's ready to be merged. |
But |
I'm just concerned that this is going to create a case of two ways to do 90% of the same thing, but with subtly different behavior that will work in some cases by not others, take @jpakkane's second example: current_def = def + {'c_args': def['c_args'] + ['-DSOMETHING']}
executable('foo', 'foo.c',
default_kwargs : current_def)
) we could rewrite this as: defaults = declare_dependency(arguments : ['-DDEFAULT'])
executable('foo', 'foo.c',
c_args : ['-DSOMETHING'],
dependencies : defaults,
) but it's easy to come up with cases where they wouldn't be equivalent. I'm concerned this is going to create confusion and lots of "works for me" kind of bugs. At the very least we need to have some documentation clearly explaining the differences (because they are subtle in some cases) and guidance on when to use one vs the other. |
Thinking a bit more about this, I think we should raise exception in the case a kwarg is set and is also in default_kwargs, otherwise args can too easily be silently ignored, leading to hard to debug issues. That's also the behaviour of python If we do that, it wouldn't be "default" values anymore, so I would rename |
Seems reasonable. Being more strict is nice, because we can loosen it later if it is deemed useful. |
This is being done in #4578 so closing. |
This is a basic draft for an implementation of #3819. It should be considered an RFC more than anything.
A python-like syntax is chosen in this implementation:
The above is equivalent to:
Unlike Python, I went for a single asterisk. This is because Python has both positional and keyword arguments expansion (
*
and**
respectively). The former does not make much sense for meson, and the single asterisk syntax is otherwise unambiguous.As in Python, the expansion must be the last element in the argument list:
Keywords specified in the dictionary override those written directly in the call:
The expansion expression can be arbitrarily complex (at least, as far as meson's parser allows):
An implementation detail allows for a certain peculiarity with this syntax: the asterisk "operator" has the lowest precedence, so the parentheses are actually not necessary in the above example, which may not be obvious:
EDIT: Just checked, turns out Python handles it the same way.
As I mentioned on IRC earlier, this feature would be a lot more useful if we had some way of either modifying dictionaries, or creating a new one by merging two others. The later approach is consistent with the way arrays work.