Indented namespacing with python - python

I have a config file with loads of paths and I want to organize them in a way. So I decided using types.SimpleNamespace to do that like:
paths = SimpleNamespace()
paths.base = '...'
paths.dataset.encoded = '...'
and I got:
AttributeError: 'types.SimpleNamespace' object has no attribute 'dataset'
I tried to define paths.dataset even though I didn't need it yet it didn't work:
paths = SimpleNamespace()
paths.base = '...'
paths.dataset = '...'
paths.dataset.encoded = '...'
AttributeError: 'str' object has no attribute 'encoded'
I also tried this:
_ = {
'base': '...',
'dataset': {
'encoded': '...',
}
}
paths = SimpleNamespace(**_)
and here is the result:
>>> paths.dataset.encoded # Error
AttributeError: 'dict' object has no attribute 'encoded'
>>> paths.dataset['encoded'] # works
'...'
This means that SimpleNamespace only works for one layer namespacing, right?
Is there another solution to this? I mean a solution other than using SimpleNamespace for every layer like this:
dataset = SimpleNamespace()
dataset.encoded = '...'
paths = SimpleNamespace()
paths.base = '???'
paths.dataset = dataset
>>> paths.base
'???'
>>> paths.dataset.encoded
'...'
Any ideas?

I came up with this solution:
def create_namespace(dictionary: dict):
"""Create a namespace of given dictionary
the difference between create_namespace and python's types.SimpleNamespace
is that the former will create name space recursively, but the later will
create the namespace in one layer indentation. See the examples to see the
difference.
Parameters
----------
dictionary : dict
A dict to be converted to a namespace object
Returns
-------
types.SimpleNamespace
A combination of SimpleNamespaces that will have an multilayer
namespace
Examples
--------
>>> dictionary = {
... 'layer1_a': '1a',
... 'layer1_b': {
... 'layer2_a': '2a',
... },
... }
>>> # types.SimpleNamespace
>>> simple = SimpleNamespace(**dictionary)
>>> simple.layer1_a
'1a'
>>> simple.layer1_b.layer2_a
AttributeError: 'dict' object has no attribute 'layer2_a'
# because layer1_b is still a dictionary
>>> # create_namespace
>>> complex = create_namespace(dictionary)
>>> complex.layer1_a
'1a'
>>> complex.layer1_a.layer2_a
'2a'
"""
space = {}
for key, value in dictionary.items():
if isinstance(value, dict):
value = create_namespace(value)
space[key] = value
return SimpleNamespace(**space)
but I think there is a better way that I'm not seeing. I appreciate any comments on this.

Related

Flask: iterable expected, not InstanceState

I'm just trying to look through a queryset of objects and write to a csv export:
#expose('/csv-export')
def csv_export(self):
batch_num = request.args.get('batch_num')
if not batch_num:
flash('Invalid batch id', 'danger')
abort(404)
si = io.StringIO()
cw = csv.writer(si)
# array
my_objects = MyObject.query.filter_by(batch_num=batch_num).all()
row_headers = my_objects[0].serialize()
cw.writerow(row_headers)
object_rows = []
for my_object in my_objects:
for k, v in vars(my_object).items():
object_rows.append(v)
cw.writerows(object_rows)
output = make_response(si.getvalue())
output.headers["Content-Disposition"] = "attachment; filename=export.csv"
output.headers["Content-type"] = "text/csv"
return output
If I inspect what object_rows is it looks like this:
[<sqlalchemy.orm.state.InstanceState object at 0x7f92a791a390>, 2, None, '3312363552684', 551, None, '24daae41-82f5-42bf-b12f-9762554ee394', <sqlalchemy.orm.state.InstanceState object at 0x7f92a791a588>, ]
What am I doing wrong?
for my_object in my_objects:
for k, v in vars(my_object).items():
object_rows.append(v)
SQLAlchemy creates an attribute on each instance of an instrumented ORM object called _sa_instance_state which is used by SQLAlchemy internally. When you iterate over the object’s __dict__ using vars() this key/value pair is traversed along with all other attribute key/values.
So the solution is to filter it out. A common way to do this is to filter keys out based on whether they start with a leading underscore, e.g:
for my_object in my_objects:
for k, v in vars(my_object).items():
if not k.startswith(‘_’):
object_rows.append(v)
This will filter out all private attributes but you can be more specific with your filtering if there are other private attribute values that you want to capture.

XML generation Python reading from DB

I have generate an xml, that take a param value from the model.
The result xml should be like this <read><test><value string/></test></read>
root = etree.Element('read')
node = etree.Element('test')
value = etree.SubElement(node, "value" )
for dict in Listing.objects.values('string'):
value.text = dict['string']
I get like this :<read><test><value>string</value></test></read>
How should I add the string to the value tag rather than as a TEXT?
Firstly, as #parfait mentioned, the XML result you want (<read><test><value string/></test></read>) is not valid XML.
Using the code you provided, the node test doesn't get added to the root node read.
1. Code to get your current result:
If you want something like this as the result: <read><test><value>string</value></test></read> (which you said you don't), then the code for it would be:
>>> root = etree.Element('read')
>>> node = etree.SubElement(root, 'test') # `node` should be a `SubElement` of root
>>> # or create as `Element` and use `root.append(node)` to make it a `SubElement` later
... value = etree.SubElement(node, 'value')
>>> value.text = 'string' # or populate based on your `Listing.objects`
>>> etree.dump(root)
<read><test><value>string</value></test></read>
2. "string" as a value (text) of "test":
If you want 'string' to be the value of test and not as a node 'value' under 'test', then you should set 'string' as the .text attribute of 'test':
>>> root = etree.Element('read')
>>> node = etree.SubElement(root, 'test')
>>> node.text = 'string'
>>> etree.dump(root)
<read><test>string</test></read>
3. "value" as an attribute of "test" with the value "string":
The one I think you're trying to get:
>>> root = etree.Element('read')
>>> node = etree.SubElement(root, 'test')
>>> node.attrib # the current attributes of the node, nothing, empty dict
{}
>>> node.attrib['value'] = 'string' # this is how you set an attribute
>>> etree.dump(root)
<read><test value="string" /></read>
Btw, in XML good-ness, the 2nd option is nicer than the 3rd; but botha/all are valid XML.

Layered Classes and JSON encoding

I have some Data that I need to write to a JSON string.
I have it working with dict items but want to encompass it all in classes to help ensure the correct data.
The following code is a comparison between the dict items and the class item output. They don't match, and I can't figure out what I am missing.
I get a "bound method Event.encode of Event..." in my JSON string.
from collections import namedtuple
import json
class Event(namedtuple('Event', 'itemName, itemID')):
def encode(self):
obj = {}
obj['itemName'] = str(self.itemName)
obj['itemID'] = int(self.itemID)
return json.dumps(obj)
curEv = Event('MyName', 5)
print 'ClassEv : ', curEv.encode()
curEv2 = {'itemName':'MyName', 'itemID':5}
print 'DictEv : ', json.dumps(curEv2)
class Packet(namedtuple('Packet', 'pID, itemType, itemData')):
def encode(self):
obj = {}
obj['pID'] = int(self.pID)
obj['itemType'] = int(self.itemType)
obj['itemData'] = str(self.itemData.encode)
return json.dumps(obj)
packet = Packet(11, 0, curEv)
print 'ClassPacket: ', packet.encode()
packet2 = {'pID':11, 'itemType':0}
packet2['itemData'] = curEv2
print 'DictPacket : ', json.dumps(packet2)
You are failing to call the itemData.encode() function. Instead you are simply returning a reference to it.
Try:
obj['itemData'] = str(self.itemData.encode())
Note the extra () at the end.

serializing sqlalchemy class to json

I'm trying to serialize the result (a list) of an sqlalchemy query to json.
this is the class:
class Wikilink(Base):
__tablename__='Wikilinks'
__table_args__={'extend_existing':True}
id = Column(Integer,autoincrement=True,primary_key=True)
title = Column(Unicode(350))
user_ip = Column(String(50))
page = Column(String(20))
revision = Column(String(20))
timestamp = Column(String(50))
and I guess my problem is with the __repr__(self): function.
I tried something like:
return '{{0}:{"title":{1}, "Ip":{2}, "page":{3} ,"revision":{4}}}'.format(self.id,self.title.encode('utf-8'),self.user_ip,self.page,self.revision)
or:
return '{"id"={0}, "title"={1}, "Ip"={2}}'.format(self.id,self.title.encode('utf-8'),self.user_ip.encode('utf-8'),self.page,self.revision)
and I got:
TypeError(repr(o) + " is not JSON serializable")
ValueError: Single '}' encountered in format string
I tried:
return '{id=%d, title=%s, Ip=%s}'%(self.id,self.title.encode('utf-8'),self.user_ip.encode('utf-8'))
and I got:
TypeError: {id=8126, title=1 בדצמבר, Ip=147.237.70.106} is not JSON serializable
adding "" around (according to the JSON formatting) like this: "id"="%d", "title"="%s", "Ip"="%s" didn't help either.
I know this is supposed to be dead simple but I just can't get this right
actually bottle is handling the jsonification part automatically, but trying to call json.dumps on the result gives me the same errors.
Instead of trying to convert to json a string, you could define, for example, your own to_dict method that returns the dictionary structure it seems you're trying to create and, after that, generate the json from that structure:
>>> import json
>>> d = {'id':8126, 'title':u'1 בדצמבר', 'ip':'147.237.70.106'}
>>> json.dumps(d)
'{"ip": "147.237.70.106", "id": 8126, "title": "1 \\u05d1\\u05d3\\u05e6\\u05de\\u05d1\\u05e8"}'
I'm not sure I understand what you tried. Couldn't you build the dict and let json.dumps() do the work for you?
Something like:
>>> class Foo:
... id = 1
... title = 'my title'
... to_jsonize = ['id', 'title']
>>>
>>> dct = {name: getattr(Foo,name) for name in Foo.to_jsonize}
>>> import json
>>> json.dumps(dct)
'{"id": 1, "title": "my title"}'

dictionary reading from module

I have following module root_file.py. This file contains number of blocks like.
Name = {
'1':'a'
'2':'b'
'3':'c'
}
In other file I am using
f1= __import__('root_file')
Now the requirement is that I have to read values a,b,c at runtime using variables like
for reading a
id=1
app=Name
print f1[app][id]
but getting error that
TypeError: unsubscriptable object
How about
import root_file as f1
id = 1
app = 'Name'
print getattr(f1, app)[id] # or f1.Name[id]
Uh, well, if I understand what you are trying to do:
In root_file.py
Name = {
'1':'a', #note the commas here!
'2':'b', #and here
'3':'c', #the last one is optional
}
Then, in the other file:
import root_file as mymodule
mydict = getattr(mymodule, "Name")
# "Name" could be very well be stored in a variable
# now mydict eqauls Name from root_file
# and you can access its properties, e.g.
mydict['2'] == 'b' # is a True statement

Categories