Discussion:
[Scons-users] env.Dictionary bug? doc problem?
Mats Wichmann
2018-07-22 18:18:15 UTC
Permalink
=== manpage:

env.Dictionary([vars])

Returns a dictionary object containing copies of all of the
construction variables in the environment. If there are any variable
names specified, only the specified construction variables are returned
in the dictionary.

Example:

dict = env.Dictionary()
cc_dict = env.Dictionary('CC', 'CCFLAGS', 'CCCOM')


But testing exactly that example -

=== SConstruct:
env = Environment()
dict = env.Dictionary()
print("dict: " + str(type(dict)))
cc_dict = env.Dictionary('CC', 'CCFLAGS', 'CCCOM')
print("cc_dict: " + str(type(cc_dict)))
print(cc_dict)

=== output:
scons: Reading SConscript files ...
dict: <type 'dict'>
cc_dict: <type 'list'>
['gcc', [], '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES']
scons: done reading SConscript files.


And that's pretty easy to see in the code, which uses a list
comprehension if there are arguments, extracting only the values and
ignoring the keys.

def Dictionary(self, *args):
if not args:
return self._dict
dlist = [self._dict[x] for x in args]
if len(dlist) == 1:
dlist = dlist[0]
return dlist

If the documentation is correct, what we really want is a "dictionary
slicing" type of operation (maybe using itertools, maybe using a
dictionary view object).
Bill Deegan
2018-07-22 18:26:38 UTC
Permalink
Can you check if any internal usage depends on this slicing behavior?

-Bill
Post by Mats Wichmann
env.Dictionary([vars])
Returns a dictionary object containing copies of all of the
construction variables in the environment. If there are any variable
names specified, only the specified construction variables are returned
in the dictionary.
dict = env.Dictionary()
cc_dict = env.Dictionary('CC', 'CCFLAGS', 'CCCOM')
But testing exactly that example -
env = Environment()
dict = env.Dictionary()
print("dict: " + str(type(dict)))
cc_dict = env.Dictionary('CC', 'CCFLAGS', 'CCCOM')
print("cc_dict: " + str(type(cc_dict)))
print(cc_dict)
scons: Reading SConscript files ...
dict: <type 'dict'>
cc_dict: <type 'list'>
['gcc', [], '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES']
scons: done reading SConscript files.
And that's pretty easy to see in the code, which uses a list
comprehension if there are arguments, extracting only the values and
ignoring the keys.
return self._dict
dlist = [self._dict[x] for x in args]
dlist = dlist[0]
return dlist
If the documentation is correct, what we really want is a "dictionary
slicing" type of operation (maybe using itertools, maybe using a
dictionary view object).
_______________________________________________
Scons-users mailing list
https://pairlist4.pair.net/mailman/listinfo/scons-users
Mats Wichmann
2018-07-23 13:51:10 UTC
Permalink
Post by Bill Deegan
Can you check if any internal usage depends on this slicing behavior?
-Bill
Plenty of impacts.

A bunch of tests are written to the idea that Dictionary(key) returns
key's value, not a subset of the dictionary. I'm not yet clear if
anyone expects Dictionary(key1, key2) to return a list of values.

Test errors I'm seeing often look like:

TypeError: cannot concatenate 'str' and 'dict' objects
AttributeError: 'dict' object has no attribute 'startswith':

There are also things like these in src/engine/SCons/EnvironmentTests.py:

env3 = env1.Clone(XXX = 'x3', ZZZ = 'z3')
assert env3.Dictionary('XXX') == 'x3'

if Dictionary('XXX') returns a dict that fails.


It also affects Dump(), but in a good way (excepting, of course, test
expectations) - I actually got here wondering why Dump doesn't take a
list of variables like so many other environment functions do. Dump()
is like drinking from the firehose :), Dump(list-of-vars-I-care-about)
is not supported, and a series of Dump(key) calls is kind of
unsatisfying because you get back value strings instead of a pprinted
key:value pairs like in the full Dump(), so you have to do more work.
Mats Wichmann
2018-07-23 14:46:49 UTC
Permalink
Post by Mats Wichmann
Post by Bill Deegan
Can you check if any internal usage depends on this slicing behavior?
-Bill
Plenty of impacts.
A bunch of tests are written to the idea that Dictionary(key) returns
key's value, not a subset of the dictionary.
looks like there's also an expectation that Dictionary() returns a view,
not a new dictionary, such that modifying something through a Dictionary
call "sticks". See the below test code - the modification of env2
through its Dictionary() - this will not work if Dictionary returns a
newly constructed dict, as I had imagined.

# Ensure that lists and dictionaries are
# deep copied, but not instances.
class TestA(object):
pass
env1 = self.TestEnvironment(XXX=TestA(), YYY = [ 1, 2, 3 ],
ZZZ = { 1:2, 3:4 })
env2=env1.Clone()
env2.Dictionary('YYY').append(4)
env2.Dictionary('ZZZ')[5] = 6
assert env1.Dictionary('XXX') is env2.Dictionary('XXX')
assert 4 in env2.Dictionary('YYY')
assert not 4 in env1.Dictionary('YYY')
assert 5 in env2.Dictionary('ZZZ')
assert 5 not in env1.Dictionary('ZZZ')

Not really sure what to do with this.
Bill Deegan
2018-07-23 15:13:00 UTC
Permalink
Any internal non-test impact?
Post by Mats Wichmann
Post by Mats Wichmann
Post by Bill Deegan
Can you check if any internal usage depends on this slicing behavior?
-Bill
Plenty of impacts.
A bunch of tests are written to the idea that Dictionary(key) returns
key's value, not a subset of the dictionary.
looks like there's also an expectation that Dictionary() returns a view,
not a new dictionary, such that modifying something through a Dictionary
call "sticks". See the below test code - the modification of env2
through its Dictionary() - this will not work if Dictionary returns a
newly constructed dict, as I had imagined.
# Ensure that lists and dictionaries are
# deep copied, but not instances.
pass
env1 = self.TestEnvironment(XXX=TestA(), YYY = [ 1, 2, 3 ],
ZZZ = { 1:2, 3:4 })
env2=env1.Clone()
env2.Dictionary('YYY').append(4)
env2.Dictionary('ZZZ')[5] = 6
assert env1.Dictionary('XXX') is env2.Dictionary('XXX')
assert 4 in env2.Dictionary('YYY')
assert not 4 in env1.Dictionary('YYY')
assert 5 in env2.Dictionary('ZZZ')
assert 5 not in env1.Dictionary('ZZZ')
Not really sure what to do with this.
_______________________________________________
Scons-users mailing list
https://pairlist4.pair.net/mailman/listinfo/scons-users
Paweł Tomulik
2018-07-23 12:11:58 UTC
Permalink
Post by Mats Wichmann
env.Dictionary([vars])
Returns a dictionary object containing copies of all of the
construction variables in the environment. If there are any variable
names specified, only the specified construction variables are returned
in the dictionary.
dict = env.Dictionary()
cc_dict = env.Dictionary('CC', 'CCFLAGS', 'CCCOM')
[...]
And that's pretty easy to see in the code, which uses a list
comprehension if there are arguments, extracting only the values and
ignoring the keys.
return self._dict
Looks like it returns reference to the original internal dictionary, is
it the same as "a dictionary object containing copies of all of the
construction variables in the environment"
Post by Mats Wichmann
dlist = [self._dict[x] for x in args]
dlist = dlist[0]
return dlist
And this returns a list, not a dictionary.
Post by Mats Wichmann
If the documentation is correct, what we really want is a "dictionary
slicing" type of operation (maybe using itertools, maybe using a
dictionary view object).
Looks like the implementation is not in accordance with the documentation .
--
Paweł Tomulik
Instytut Techniki Lotniczej i Mechaniki Stosowanej
Politechnika Warszawska
pok. 204, tel. +48 22 234 5722
Loading...