How to get property names from .py file which contains class definition - python

I get on input file which contains only one class definition (class is just constants container, contains keys for json, similar file is used on Java client to decode json) looks like:
class Constants(object):
VERSION= 'version'
OS = 'os'
PROGRAM = 'program'
# more constants .....
How to get dictionary of all properties defined inside Constants, how to parse file to dictionary ?
I want to compress keys and generate new .py and .java files with same constants keys but shorter keys.

Import the module
I used imp.load_module instead of __import__ in the following code to import abitrary file path.
Find the class object.
Iterate the class attribute using vars:
import imp
path = '/path/to/file'
with open(path, 'U') as f:
mod = imp.load_module('temporary', f, path, ('.py', 'U', imp.PY_SOURCE))
builtins = vars(__builtins__)
cls = next(value for name, value in vars(mod).items() if name not in builtins)
const_dict = {name: value for name, value in vars(mod.Constants).items()
if not name.startswith('_')}
print(const_dict)
# => {'OS': 'os', 'VERSION': 'version', 'PROGRAM': 'program'}
Tested in Python on 2.7.6, 3.3.2, 3.4.0b2.

>>> [elem for elem in dir(Constants) if not elem.startswith("_")]
['OS', 'PROGRAM', 'VERSION']

Expanding on answer number one:
# dir(yourClass) will get you all the methods and properties of yourClass and parents wheather yourClass
# is a definition or an instance
elements = [elem for elem in dir(Constants) if not elem.startswith("_")]
# Using yourClass.__dict__.keys() will give you the same of dir if applied to a definition but only instance members
# if applied to an instance
elements = [elem for elem in Constants.__dict__.keys() if not elem.startswith("_")]
# You can get to the values of the properties with
for el in elements:
print Constants.__dict__[el]
# plus whatever you want to do to those elements
# Or if you're using the __dict__ way
Constants.__dict__.items()

Here is an example of using execfile and python 2.6 (I work on Debian Wheezy). A shorter version to build the dictionary for python version 2.7 and higher is given too. The constants.py file can define several classes, all of them will be parsed.
#!/usr/bin/env python
d = {}
const_d = {}
execfile("constants.py", d)
for k,cls in d.items():
if k not in vars(__builtins__):
if type(cls) is type:
# Python version < 2.7
attributes = {}
for name, value in vars(cls).items():
if not name.startswith('__'):
attributes[name] = value
# Python version >= 2.7
#attributes = {name: value for name, value in vars(cls).items() if not name.startswith('__')}
const_d[cls.__name__] = attributes
pass
pass
pass
print(const_d)

Related

Get information of Active Directory object using LDAP path

How to get all attributes and values of an Active Directory object using LDAP path with win32com package ?
Example:
import win32com.client
ldap_path = "LDAP://CN=aaa,DC=bbb,DC=ccc,DC=eee"
ldap_object = win32com.client.GetObject(ldap_path)
My own answer:
import win32com.client
def get_attributes_and_values(ldap_path):
ldap_object = win32com.client.GetObject(ldap_path)
ldap_object.GetInfo()
attributes = [
ldap_object.Item(x).Name
for x in range(0, ldap_object.PropertyCount)
]
for attribute in attributes:
value = getattr(ldap_object, attribute)
yield {attribute: value}
ldap_path = "LDAP://CN=aaa,DC=bbb,DC=ccc,DC=eee"
attr_val = get_attributes_and_values(ldap_path)
for x in attr_val:
print(x)
Item, Name, PropertyCount are objects/attributes of "COM" object that has been wrapped by win32com.client.

Assign a short name to a class attribute

I am using a Python package which read some type of data. From the data, it creates attributes to easily access meta-information related to the data.
How can create a short name to an attribute?
Basically let's assume the package name is read_data and it has an attribute named data_header_infomation_x_location
import read_data
my_data = read_data(file_path)
How can I instead create a short name to this attribute?
x = "data_header_infomation_x_location"
my_data[1].x gives an error no attribute
Here is a full example from my case
from obspy.io.segy.core import _read_segy
file_path = "some_file_in_my_pc)
sgy = _read_segy(file_path, unpack_trace_headers=True)
sgy[1].stats.segy.trace_header.x_coordinate_of_ensemble_position_of_this_trace
The last line gives a number. e.g., x location
what I want is to rename all this long nested attribute stats.segy.trace_header.x_coordinate_of_ensemble_position_of_this_trace with a short name.
trying for example
attribute = "stats.segy.trace_header.x_coordinate_of_ensemble_position_of_this_trace"
getattr(sgy[1], attribute )
does not work
how about:
from obspy.io.segy.core import _read_segy
attribute_tree_x = ['stats', 'segy', 'trace_header', 'x_coordinate_of_ensemble_position_of_this_trace']
def get_nested_attribute(obj, attribute_tree):
for attr in attribute_tree:
obj = getattr(obj, attr)
return obj
file_path = "some_file_in_my_pc"
sgy = _read_segy(file_path, unpack_trace_headers=True)
sgy[1].stats.segy.trace_header.x_coordinate_of_ensemble_position_of_this_trace
x = get_nested_attribute(sgy[1], attribute_tree_x) # should be the same as the line above
You cannot request the attribute of the attribute in one go, but this loops through the layers to obtain the final value you are looking for.

Updating python module varaibles from another python module

I need to update the python module's variable values from another python module.
Values need to be updated permanently to the module, not just for run-time.
So need to update the module file. How should I do that?
VersionInfo.py file has variables with some default values.
This file should be updated while executing another python file ReleaseVersion.py.
VersionInfo.py
__app__ = 'MyApp'
__appName__ = 'My Classic Application'
__version__ = 0.1
__updater__ = 'Kumaresan Lakshmanan'
__updatedOn__ = '2017-08-29'
#Other Lookups
versionStr = "v%s" % __version__
versionInfo ='%s (%s)' % (versionStr, __updatedOn__)
loggerName = __app__
stdLogFile = __app__ + '_log.txt'
errorLogFile = __app__ + '_error.txt'
ReleaseVersion.py
import VersionInfo
newVersion = VersionInfo.__version__
newVersion += .1
updatedOn = currentDate()
updater = 'Lakshmanan'
VersionInfo.__version__ = newVersion
VersionInfo.__updater__ = updater
VersionInfo.__updatedOn__ = getCurrentDate()
def updateVersionInfo():
# i m planning to go by followin step, need some better logic...
# 1. open versioninfo.py file into a str...
# 2. in str, search for old values and replace with new values
# 3. write back the str to versioninfo.py file
# Any other better logic i can do?
updateVersionInfo()
I do realize this is a very old question but maybe someone else has a question similar to this. You could attempt to make sort of a fake database with text files. Since python can easily write and read to text files, it would be very simple. You can create something such as
VersionInfo.txt::
__app__ = 'MyApp'
__appName__ = 'My Classic Application'
__version__ = 0.1
__updater__ = 'Kumaresan Lakshmanan'
__updatedOn__ = '2017-08-29'
And then import the file and split each line on the equal sign. This way you have a readable text file with all the information needed.

Reading array from config file in python

I have a problem. My program is using config file to set options, and one of those options is a tuple. Here's what i mean:
[common]
logfile=log.txt
db_host=localhost
db_user=root
db_pass=password
folder[1]=/home/scorpil
folder[2]=/media/sda5/
folder[3]=/media/sdb5/
etc...
Can i parse this into tuple with ConfigParser module in Python? Is there some easy way to do this?
if you can change config format like this:
folder = /home/scorpil
/media/sda5/
/media/sdb5/
then in python:
config.get("common", "folder").split("\n")
Your config could be:
[common]
logfile=log.txt
db_host=localhost
db_user=root
db_pass=password
folder = ("/home/scorpil", "/media/sda5/", "/media/sdb5/")
Assuming that you have config in a file named foo.cfg, you can do the following:
import ConfigParser
cp = ConfigParser.ConfigParser()
cp.read("foo.cfg")
folder = eval(cp.get("common", "folder"), {}, {})
print folder
print type(folder)
which should produce:
('/home/scorpil', '/media/sda5/', '/media/sdb5/')
<type 'tuple'>
-- EDIT --
I've since changed my mind about this, and would take the position today that using eval in this context is a bad idea. Even with a restricted environment, if the configuration file is under user control it may be a very bad idea. Today I'd probably recommend doing something interesting with split to avoid malicious code execution.
You can get the items list and use a list comprehension to create a list of all the items which name starts with a defined prefix, in your case folder
folders = tuple([ item[1] for item in configparser.items() if item[0].startswith("folder")])
Create configuration:
folders = ['/home/scorpil', '/media/sda5/', '/media/sdb5/']
config.set('common', 'folders', json.dumps(folders))
Load configuration:
tuple(json.loads(config.get('common', 'folders')))
I don't know ConfigParser, but you can easily read it into a list (perhaps using .append()) and then do myTuple = tuple(myList)
#!/usr/bin/env python
sample = """
[common]
logfile=log.txt
db_host=localhost
db_user=root
db_pass=password
folder[1]=/home/scorpil
folder[2]=/media/sda5/
folder[3]=/media/sdb5/
"""
from cStringIO import StringIO
import ConfigParser
import re
FOLDER_MATCH = re.compile(r"folder\[(\d+)\]$").match
def read_list(items,pmatch=FOLDER_MATCH):
if not hasattr(pmatch,"__call__"):
pmatch = re.compile(pmatch).match
folder_list = []
for k,v in items:
m = pmatch(k)
if m:
folder_list.append((int(m.group(1)),v))
return tuple( kv[1] for kv in sorted(folder_list) )
if __name__ == '__main__':
cp = ConfigParser.SafeConfigParser()
cp.readfp(StringIO(sample),"sample")
print read_list(cp.items("common"))
You could stick to json completely
tst.json
{
"common": {
"logfile":"log.txt",
"db_host":"localhost",
"db_user":"root",
"db_pass":"password",
"folder": [
"/home/scorpil",
"/media/sda5/",
"/media/sdb5/"
]
}
}
then work with it
$ python3
>>> import json
>>> with open("tst.json", "r", encoding="utf8") as file_object:
... job = json.load(file_object)
...
>>> job
{'common': {'db_pass': 'password', 'logfile':
'log.txt', 'db_user': 'root', 'folder':
['/home/scorpil', '/media/sda5/', '/media/sdb5/'],
'db_host': 'localhost'}}
>>> print (job["common"]["folder"][0])
/home/scorpil
>>> print (job["common"]["folder"][1])
/media/sda5/
print (job["common"]["folder"][2])
/media/sdb5/
>>> folder_tuple = tuple(job["common"]["folder"])
>>> folder_tuple
('/home/scorpil', '/media/sda5/', '/media/sdb5/')

Pythonic reading from config files

I have a python class which reads a config file using ConfigParser:
Config file:
[geography]
Xmin=6.6
Xmax=18.6
Ymin=36.6
YMax=47.1
Python code:
class Slicer:
def __init__(self, config_file_name):
config = ConfigParser.ConfigParser()
config.read(config_file_name)
# Rad the lines from the file
self.x_min = config.getfloat('geography', 'xmin')
self.x_max = config.getfloat('geography', 'xmax')
self.y_min = config.getfloat('geography', 'ymin')
self.y_max = config.getfloat('geography', 'ymax')
I feel that the last four lines are repetitive, and should somehow be compressed to one Pythonic line that would create a self.item variable for each item in the section.
Any ideas?
Adam
UPDATE:
Following your answers, I've modified my code to:
for item in config.items('geography'):
setattr(self, '_'+item[0], float(item[1]))
Now,
print self.__dict__
>>> {'_xmax': 18.600000000000001, '_ymax': 47.100000000000001,
'_ymin': 36.600000000000001, '_xmin': 6.5999999999999996}
I usually try to avoid external interactions in a constructor - makes it hard to test the code. Better pass a config parser instance or a fp-like object instead of a filename.
for line in ['x_min', 'x_max', 'y_min', 'y_max']:
setattr(self, line, config.getfloat('geography', line.replace('_', '')))
How about something like:
for key in ['xmin','xmax','ymin','ymax']:
self.__dict__[key] = config.getfloat('geography',key);
Note that the above will assign it to self.xmin instead of self.x_min... however, if you are fine with that naming, then this should work... otherwise, mapping between names would be more code than the original.

Categories

Resources