Variable method name in python - python

I have a dictionnary fields={} which contains some field names that I named the Qt comobox objects after. For example keys combobox1 and combobox2, both contain a list with values that I would like to add to the
fields={}
fields['combobox1']=['value1','someting else']
fields['combobox2']=['bla','another value']
for key,values in fields.items():
for value in values:
Qt_ui. _____key_____ .addItem(value)
what is the correct syntax for the last line, so that ____key____ is replaced with the keys from the dictionary? I tried ui.__getattribute__(key).addItem(value) but it does't seem to work. Any suggestions are appreciated.
TypeError: 'PySide.QtGui.QComboBox.addItem' called with wrong argument types:
PySide.QtGui.QComboBox.addItem(float)
Supported signatures:
PySide.QtGui.QComboBox.addItem(PySide.QtGui.QIcon, unicode, QVariant = QVariant())
PySide.QtGui.QComboBox.addItem(unicode, QVariant = QVariant())

In fact the problem was somewhere else getattribute(key) is correct but the added item needs to be a string. Any way, I thought this is an interesting problem and will leave the post anyway.
ui.__getattribute__(key).addItem(str(value))

Related

Parse numbers and lists from config files with configparser

I'm doing a relatively big project for my final thesis and therefore I am using a .ini file for storing and retrieving settings. However, I am unable to find an elegant solution for how to convert the strings (well, actually the strings inside the dictionary) that Configparser returns to numbers (ints and floats) and/or lists.
Googling the issue, I came across this SO thread which only tackles the 'list' part of my problem but using the top rated solution (defining lists inside the .ini file like that: list=item1,item2) didn't do anything for me, as the 'list' still shows up as a string after parsing. Also, I do not want to change format.
So I decided I would try it myself and came up with this solution:
import configparser
# create a new instance of a 'ConfigParser' class
config = configparser.ConfigParser()
# create mock-content for the config file
config["Test"] = {
"test_string":"string",
"test_int":"2",
"test_float":"3.0",
"test_list":"item1, item2"
}
# get the relevant settings
settings = config["Test"]
# pack the relevant settings into a dictionary
settings = dict(settings)
# iterate through all the key-value pairs and convert them, if possible
for key, value in settings.items():
# try to convert to int
try:
settings[key] = int(value)
# if the value can't be converted to int, it might be a float or a list
except ValueError:
# try converting it to a float
try:
settings[key] = float(value)
# if the value can't be converted to float, try converting to list
except ValueError:
if "," in value:
cont = value.split(",")
settings[key] = [item.strip() for item in cont]
else:
settings[key] = value
print(type(settings["test_string"]))
print(settings)
However, this seems so very inelegant and is so heavily nested and the task itself seems so important that I cannot believe that there is no "more official" solution to this that I am simply unable to find.
So, could please someone help me out here and tell me if there really is no better, more straightforward way to achieve this!?
Best I can do is this (though it's kinda hacky and probably dangerous too):
for key, value in settings.items():
try: # will handle both ints and floats, even tuples with ints/floats
settings[key] = eval(value)
except NameError: # this means it's a string or a tuple with strings
get_val = list(map(str.strip, value.split(",")))
settings[key] = get_val if get_val[1:] else get_val[0]
This will work correctly for ints, floats as well as your comma separated values (it will evaluated it as a tuple, I guess that should be fine though I added a condition for that anyway).

ldap3 library: modify attribute with multiple values

Trying to modify an ldap attribute that has multiple values, can't seem to figure out the syntax.
I'm using the ldap3 library with python3.
The documentation gives an example which modifies two attributes of an entry - but each attribute only has one value.
The dictionary from that example is the bit I'm having trouble with:
c.modify('cn=user1,ou=users,o=company',
{'givenName': [(MODIFY_REPLACE, [<what do I put here>])]})
Instead of 'givenname' which would have one value I want to modify the memberuid attribute which obviously would have many names as entries.
So I spit all my memberuids into a list, make the modification, and then am trying to feed my new usernames/memberuid list to the MODIFY command.
Like so:
oldval = 'super.man'
newval = 'clark.kent'
existingmembers = ['super.man', 'the.hulk', 'bat.man']
newmemberlist = [newval if x==oldval else x for x in existingmembers]
# newmemberlist = ", ".join(str(x) for x in newmemberlist)
I've tried passing in newmemberlist as a list
'memberuid': [(MODIFY_REPLACE, ['clark.kent', 'the.hulk','bat.man'])]
which gives me TypeError: 'str' object cannot be interpreted as an integer
or various combinations (the commented line) of one long string, separated with spaces, commas, semi colons and anything else I can think of
'memberuid': [(MODIFY_REPLACE, 'clark.kent, the.hulk, bat.man')]
which does the replace but I get one memberuid looking like this
'clark.kent, the.hulk, bat.man'
You need to ensure you are passing in the DN of the ldapobject you wish to modify.
c.modify(FULL_DN_OF_OBJECT, {'memberuid': [(MODIFY_REPLACE, ['clark.kent', 'the.hulk','bat.man'])]})
Then you should be able just to pass in newmemberlist instead of ['clark.kent', 'the.hulk','bat.man']
c.modify(FULL_DN_OF_OBJECT, {'memberuid': [(MODIFY_REPLACE, newmemberlist )]})
I believe MODIFY_REPLACE command would not accept multiple values, as it would not understand which values would be replaced with new ones. Instead you should try doing MODIFY_DELETE old values first and MODIFY_ADD new values afterwards.

Python for loop over json data throws 'TypeError: string indices must be integers' only when a single element of data

I've inherited the following code which is working great, apart from when only a single data item is return from the original xml. When that occurs the following error is thrown: 'TypeError: string indices must be integers'
result = xmltodict.parse(get_xml())
latest_result = result['Response']['Items']['Item']
myJsonData = json.dumps(latest_result)
j= json.loads(myJason)
print type(j)
for item in j:
print (item['Id'])
print (item['OrderId'])
I have narrowed the change in behaviour to a difference in datatype here:
print type(j)
When only a single ['Item'] is returned from the source XML the datatype of j is a 'dict', whilst the rest of the time (greater than one ['Item']) its a 'list'.
Hope someone can help.
Encoding to JSON then decoding again has nothing to do with your question. It is a red herring, you can use latest_result and still get the same error.
The result['Response']['Items']['Item'] can evidently be either a list of dictionaries, or a single dictionary. When iterating over a list, you'll get contained elements, while iteration over a dictionary gives you the keys. So your item elements are strings (each a key in the dictionary) and you can't address elements in that string with 'Id' or 'OrderId'.
Test for the type or use exception handling:
if isinstance(latest_result, dict):
# just one result, wrap it in a single-element list
latest_result = [latest_result]
Alternatively, fix up the xmltodict code (which you didn't share or otherwise identify) to always return lists for elements, even when there is just a single one.
This is a common xmltodict module usage problem. When there is a single child, by default, it makes a dict out of it and not a list with a single item. Relevant github issue:
xml containing 1 child
To workaround it, one option would be to set the dict_constructor argument:
from collections import defaultdict
xmltodict.parse(xml, dict_constructor=lambda *args, **kwargs: defaultdict(list, *args, **kwargs))

Python - If value in dictionary then

Here's my code
if "value" not in dictionary():
do something
else:
do something else
I get the error 'TypeError: 'dict' object is not callable.
I've tried changing the first line to
if dictionary["value"]:
But get a different error. Where am I going wrong here?
Assuming dictionary is in fact a dict() composed of key-values then it would be
if 'value' not in dictionary:
...etc
The error is essentially telling you that you are erroneously attempting to call a non-callable object as if it were a method/function.
If you are not particularly interested in whether or not a value exists you may use a method I am personally a fan of:
some_value = dictionary.get('value', 'valueIfNotPresent')
do_something(some_value)
The above allows you to provide a sentinel value (which is provided when the key does not exist). This can help with branch elimination (e.g. not having to if/else it, just allowing your code to act upon the sentinel) or at least reduce logic in checking for the existence of a key (unless the absence of a key is important).
Both are quite readable however and your mileage may vary.
EDIT:
#user1857805 is correct, if you are attempting to find out if a value is in a dictionary then the above (while still good and valid to know) is not enough. You will need to get the .values() as a list from the dictionary; however, as a dictionary may contain dictionaries you will need to recurse the dictionary to find all of the possibly stored values.
try using the following:
if 'value' not in dictionary.values():
do something
else:
do something else.

Reducing menu.add_command() clutter/repeat lines

I would like to do the following (just an example, the real code has more menu's and more add_command's):
editmenu.add_command(label="Cut",state="disabled")
editmenu.add_command(label="Copy",state="disabled")
editmenu.add_command(label="Paste",state="disabled")
editmenu.add_command(label="Delete",state="disabled")
But on fewer lines, In fact, just one line if possible. I have menus that are taking up a considerable amount of space in my program and would like to reduce the clutter. Plus the programmer in me sees a bunch of similar lines and feels there must be a way to reduce them.
I tried the following code to no avail; I obviously got a nameerror because label and state aren't defined...
for labeldic in [{label:"Cut"},{label:"Copy"},{label:"Paste"},{label:"Delete"}]: editmenu.add_command(labeldic+{state:"disabled"})
Thanks in advance for any suggestions!
Here's a translation of what you wanted to do:
for labeldic in [{"label":"Cut"},{"label":"Copy"},{"label":"Paste"},{"label":"Delete"}]:
labeldic.update({"state": "disabled"})
editmenu.add_command(**labeldic)
There were three problems I fixed.
The first is that dictionary keys need to be quoted if they are strings. If you want a dict mapping the string 'label' to the the string 'cut', you can do it using the dict literal {'label': 'cut'}, or else possibly with the dict() constructor which expands keyword arguments that way: dict(label='cut'). As you discovered, {label: 'cut'} wouldn't work, because it tries to use a variable's value for the key, but there is no such variable.
The second is that you can't merge dictionaries using the + operator. It doesn't work, unfortunately. There is, however, an update method that mutates the dict it's called on. Since it doesn't return a merged dict, it can't be used inline the way you used +.
The third problem is that passing a dict is not the same as passing in keyword arguments. foo(bar='baz') is not the same as foo({'bar':'baz'}), but it is the same as foo(**{'bar':'baz'}). The ** syntax in function calling "unpacks" a dictionary into keyword arguments.
Regardless it's sort of weird style. Here's what I would do instead:
for label in ['Cut', 'Copy', 'Paste', 'Delete']:
editmenu.add_command(label=label, state='disabled')

Categories

Resources