How the flask app.config.from_object work? - python

Currently! I have write a python project, and I refer to flask. When I have write the config module, i got a problem. the example code below will not work as I expect!
class ConfigSample:
PREFIX = ''
MAIN_SPLIT = []
MESSAGE_ID = 'message_id'
DEVICE_ID = 'device_id'
HOST = '127.0.0.1'
PORT = 5555
DEBUG = True
ESCAPE_LIST = {}
class Config(dict):
def from_object(self, obj):
for key in dir(obj):
if key.isupper():
self[key] = getattr(obj, key)
class SampleForTestConfigDict:
config = Config()
def __init__(self):
print self.config.get('HOST', 'default host')
if __name__ == '__main__':
p = SampleForTestConfigDict()
#: This config method can be easy load!
p.config.from_object(ConfigSample)
#: There also another config method
p.config['ak'] = 'kkkk'
print p.config
I want to use the config inside the SampleForTestConfigDict init method. But the init method have run before i assign it by 'p.config.from_object' , How can i fix this problem?

This code is taken from Flask source code
def from_object(self, obj):
if isinstance(obj, string_types):
obj = import_string(obj)
for key in dir(obj):
if key.isupper():
self[key] = getattr(obj, key)
Here string_types = (str, unicode)

Related

How to convert dict to class attributes in Python

Instead of using a dict to store and pass data we are going completely OOPS approach of storing the data as class attributes and call the get methods defined according to need.
In Java i was able to achieve this but having some trouble in Python. Any Solution would be helpful.
import json
class InputModel:
def __init__(self, input_payload):
self.id1 = input_payload["id1"]
self.route = RouteModel(input_payload["route"])
self.id2 = input_payload["id2"]
self.id3 = input_payload["id3"]
self.id4 = input_payload["id4"]
self.id5 = input_payload["id5"]
def get_id1(self):
return self.id1
#similar for other ids
class RouteModel:
def __init__(self, input_payload_route):
self.id6 = input_payload_route["id6"]
self.id7 = input_payload_route["id7"]
def get_id6(self):
return self.id6
#similar for other ids
json_str = '{"id1":"string","route":{"id6":"string","id7":"string"},"id2": "string","id3": "string","id4": "string","id5": "string"}'
json_dict = json.loads(json_str)
im = InputModel(json_dict)
print(im.get_id1())
print(im.get_id6())
not able to access the nested class attributes
Seems like you went for 1 extra indent in your class methods, thus you couldn't reach them.
Also, to reach id6 of RouteModel, you had to refer to 'route' first:
import json
class InputModel:
def __init__(self, input_payload):
self.id1 = input_payload["id1"]
self.route = RouteModel(input_payload["route"])
self.id2 = input_payload["id2"]
self.id3 = input_payload["id3"]
self.id4 = input_payload["id4"]
self.id5 = input_payload["id5"]
def get_id1(self):
return self.id1
#similar for other ids
class RouteModel:
def __init__(self, input_payload_route):
self.id6 = input_payload_route["id6"]
self.id7 = input_payload_route["id7"]
def get_id6(self):
return self.id6
#similar for other ids
json_str = '{"id1":"string","route":{"id6":"string","id7":"string"},"id2": "string","id3": "string","id4": "string","id5": "string"}'
json_dict = json.loads(json_str)
im = InputModel(json_dict)
print(im.get_id1())
print(im.route.get_id6())
Output:
string
string
The problem is that you are only defining get_id* in your local scope, you need to assign it to the instance if you insist on defining it inside the __init__ method.
I minimized your code example to isolate your issue.
class RouteModel:
def __init__(self):
self.id6 = "foo"
def get_id6(self_=self):
return self_.id6
self.get_id6 = get_id6
rm = RouteModel()
print(rm.get_id6())
>>> "foo"
If I understand your question correctly, you want to be able to access the ids directly as attributes, no matter how deep they are nested in the dictionary.
This solution creates the attributes recursively:
import json
class InputModel:
def __init__(self, payload):
self.create_attrs(payload)
def create_attrs(self, d):
for key, value in d.items():
# if the value is a dict, call create_attrs recursively
if isinstance(value, dict):
self.create_attrs(value)
else:
# create an attribute key=value, e.g. id1="string"
setattr(self, key, value)
json_str = '{"id1":"string","route":{"id6":"string","id7":"string"},"id2": "string","id3": "string","id4": "string","id5": "string"}'
json_dict = json.loads(json_str)
im = InputModel(json_dict)
print(im.id1)
print(im.id6)
After going through answers provided, mostly have defined instance attributes and not class attributes.
Correct me if I'm wrong here but I think this is how class attributes are defined right?
import json
class InputModel:
def __init__(self, input_payload):
InputModel.id1 = input_payload["id1"]
InputModel.route = RouteModel(input_payload["route"])
InputModel.id2 = input_payload["id2"]
InputModel.id3 = input_payload["id3"]
InputModel.id4 = input_payload["id4"]
InputModel.id5 = input_payload["id5"]
def get_id1():
return InputModel.id1
#OR
##classmethod
#def get_id1(cls):
# return cls.id1
#similar for other ids
class RouteModel:
def __init__(self, input_payload_route):
RouteModel.id6 = input_payload_route["id6"]
RouteModel.id7 = input_payload_route["id7"]
def get_id6():
return RouteModel.id6
#similar for other ids
json_str = '{"id1":"string","route":{"id6":"string","id7":"string"},"id2": "string","id3": "string","id4": "string","id5": "string"}'
json_dict = json.loads(json_str)
InputModel(json_dict)
print(InputModel.get_id1())
print(InputModel.route.get_id6())
print(RouteModel.get_id6())

How do I use the functions within this Python script?

I have this Python script to control a PfSense router via FauxAPI. The problem is that when i call a function it gives an error. I think i'm calling the function wrong. Does anyone know how to call them?
Here is a link to the API i'm using: https://github.com/ndejong/pfsense_fauxapi
I have tried calling config_get(self, section=none) but that does not seem to work.
import os
import json
import base64
import urllib
import requests
import datetime
import hashlib
class PfsenseFauxapiException(Exception):
pass
class PfsenseFauxapi:
host = '172.16.1.1'
proto = None
debug = None
version = None
apikey = 'key'
apisecret = 'secret'
use_verified_https = None
def __init__(self, host, apikey, apisecret, use_verified_https=False, debug=False):
self.proto = 'https'
self.base_url = 'fauxapi/v1'
self.version = __version__
self.host = host
self.apikey = apikey
self.apisecret = apisecret
self.use_verified_https = use_verified_https
self.debug = debug
if self.use_verified_https is False:
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
def config_get(self, section=None):
config = self._api_request('GET', 'config_get')
if section is None:
return config['data']['config']
elif section in config['data']['config']:
return config['data']['config'][section]
raise PfsenseFauxapiException('Unable to complete config_get request, section is unknown', section)
def config_set(self, config, section=None):
if section is None:
config_new = config
else:
config_new = self.config_get(section=None)
config_new[section] = config
return self._api_request('POST', 'config_set', data=config_new)
def config_patch(self, config):
return self._api_request('POST', 'config_patch', data=config)
def config_reload(self):
return self._api_request('GET', 'config_reload')
def config_backup(self):
return self._api_request('GET', 'config_backup')
def config_backup_list(self):
return self._api_request('GET', 'config_backup_list')
def config_restore(self, config_file):
return self._api_request('GET', 'config_restore', params={'config_file': config_file})
def send_event(self, command):
return self._api_request('POST', 'send_event', data=[command])
def system_reboot(self):
return self._api_request('GET', 'system_reboot')
def system_stats(self):
return self._api_request('GET', 'system_stats')
def interface_stats(self, interface):
return self._api_request('GET', 'interface_stats', params={'interface': interface})
def gateway_status(self):
return self._api_request('GET', 'gateway_status')
def rule_get(self, rule_number=None):
return self._api_request('GET', 'rule_get', params={'rule_number': rule_number})
def alias_update_urltables(self, table=None):
if table is not None:
return self._api_request('GET', 'alias_update_urltables', params={'table': table})
return self._api_request('GET', 'alias_update_urltables')
def function_call(self, data):
return self._api_request('POST', 'function_call', data=data)
def system_info(self):
return self._api_request('GET', 'system_info')
def _api_request(self, method, action, params=None, data=None):
if params is None:
params = {}
if self.debug:
params['__debug'] = 'true'
url = '{proto}://{host}/{base_url}/?action={action}&{params}'.format(
proto=self.proto, host=self.host, base_url=self.base_url, action=action, params=urllib.parse.urlencode(params))
if method.upper() == 'GET':
res = requests.get(
url,
headers={'fauxapi-auth': self._generate_auth()},
verify=self.use_verified_https
)
elif method.upper() == 'POST':
res = requests.post(
url,
headers={'fauxapi-auth': self._generate_auth()},
verify=self.use_verified_https,
data=json.dumps(data)
)
else:
raise PfsenseFauxapiException('Request method not supported!', method)
if res.status_code == 404:
raise PfsenseFauxapiException('Unable to find FauxAPI on target host, is it installed?')
elif res.status_code != 200:
raise PfsenseFauxapiException('Unable to complete {}() request'.format(action), json.loads(res.text))
return self._json_parse(res.text)
def _generate_auth(self):
# auth = apikey:timestamp:nonce:HASH(apisecret:timestamp:nonce)
nonce = base64.b64encode(os.urandom(40)).decode('utf-8').replace('=', '').replace('/', '').replace('+', '')[0:8]
timestamp = datetime.datetime.utcnow().strftime('%Y%m%dZ%H%M%S')
hash = hashlib.sha256('{}{}{}'.format(self.apisecret, timestamp, nonce).encode('utf-8')).hexdigest()
return '{}:{}:{}:{}'.format(self.apikey, timestamp, nonce, hash)
def _json_parse(self, data):
try:
return json.loads(data)
except json.JSONDecodeError:
pass
raise PfsenseFauxapiException('Unable to parse response data!', data)
Without having tested the above script myself, I can conclude that yes you are calling the function wrong. The above script is rather a class that must be instantiated before any function inside can be used.
For example you could first create an object with:
pfsense = PfsenseFauxapi(host='<host>', apikey='<API key>', apisecret='<API secret>')
replacing <host>, <API key> and <API secret> with the required values
Then call the function with:
pfsense.config_get() # self is not passed
where config_get can be replaced with any function
Also note
As soon as you call pfsense = PfsenseFauxapi(...), all the code in
the __init__ function is also run as it is the constructor (which
is used to initialize all the attributes of the class).
When a function has a parameter which is parameter=something, that something is the default value when nothing is passed for that parameter. Hence why use_verified_https, debug and section do not need to be passed (unless you want to change them of course)
Here is some more information on classes if you need.
You need to create an object of the class in order to call the functions of the class. For example
x = PfsenseFauxapi() (the init method is called during contructing the object)
and then go by x.'any function'. Maybe name the variable not x for a good naming quality.

How to merge outputs of multiple function in a dictionary format using python?

I need to return the output from multiple functions inside a class in a dictionary format
I have tried using Python.
dict={}
class Compute():
def vm(self):
for obj in data['profile']:
for region_name in obj['region']:
conn = boto3.resource('ec2', aws_access_key_id=obj["access_key"], aws_secret_access_key=obj["secret_key"],
region_name=region_name)
instances = conn.instances.filter(Filters=[{'Name': 'instance-state-name', 'Values': ['running', 'stopped']}])
for instance in instances:
instance_count.append(instance)
instanceCount = str(len(instance_count))
dict['VM'] = len(instance_count)
#Subnet
def subnet(self):
subnet_count=0
for obj in data['profile']:
for region_name in obj['region']:
conn = boto3.client('ec2', aws_access_key_id=obj["access_key"], aws_secret_access_key=obj["secret_key"],
region_name=region_name)
subnet = conn.describe_subnets()
#print('subnet'+ ' '+ region_name + ' ' +str(len(subnet['Subnets'])))
subSize = len(subnet['Subnets'])
subnet_count+=subSize
dict['Networks'] = subnet_count
#VPCS
def vpc(self):
for obj in data['profile']:
for region_name in obj['region']:
conn = boto3.resource('ec2', aws_access_key_id=obj["access_key"], aws_secret_access_key=obj["secret_key"],
region_name=region_name)
vpcs = conn.vpcs.filter()
for vpc in vpcs:
vpc_count.append(vpc)
vpcCount = str(len(vpc_count))
dict['VPCs'] = len(vpc_count)
print(dict) #this only prints {}
def runcompute(self):
if __name__ == '__main__':
Thread(target=self.vm).start()
Thread(target=self.subnet).start()
Thread(target=self.vpc).start()
if __name__ == '__main__':
try:
if sys.argv[1]=='compute':
run = Compute()
run.runcompute()
"Now How to print the results in json/ dict format in the console.
I expect out put in
{"VM": 45, "VPCs": 23, "Networks": 35} format
But it print {} but that is wrong."
For what I understood you need to actually define a constructor for your class. Since it seems to be a simple dictionary we can inherit directly.
class Compute(dict):
def __init__(self):
super().__init__(self)
def my_method(self): # equivalent of your methods in your class
self["foo"] = 1
So when I do
run = Compute()
print(run)
>> {} # we just created the object
And when I call the methods
run.my_method()
print(run)
>> { 'foo': 1 } # and here we are
A complete simple example:
import sys
from threading import Thread
class Compute(dict):
def __init__(self):
super().__init__(self) # short version
# super(Compute, self).__init__(self) # long version
def _vm(self):
instance_count = [0] * 45 # do your stuff
self["VM"] = len(instance_count)
def _subnet(self):
subnet_count = 35 # do your stuff
self["Networks"] = subnet_count
def _vpc(self):
vpc_count = [0] * 23 # do your stuff
self["VPCs"] = len(vpc_count)
def runcompute(self):
# Create the threads
vm = Thread(target=self._vm)
subnet = Thread(target=self._subnet)
vpc = Thread(target=self._vpc)
# Actually start the threads
vm.start()
subnet.start()
vpc.start()
print(self) # If you really want to print the result here
if __name__ == "__main__":
if sys.argv[1] == "compute":
run = Compute()
run.runcompute()
Notice that I added the _ in front of _vm, _subnet and _vpc. This is mostly a naming convention (read more here and here) used to declare something "private". Since you only want to use those methods through runcompute() it fits the usage perfectly.

Using var from from function A to function B

On this sample code i want to use the variables on the function db_properties at the function connect_and_query. To accomplish that I choose the return. So, using that strategy the code works perfectly. But, in this example the db.properties files only has 4 variables. That said, if the properties file had 20+ variables, should I continue using return? Or is there a most elegant/cleaner/correct way to do that?
import psycopg2
import sys
from ConfigParser import SafeConfigParser
class Main:
def db_properties(self):
cfgFile='c:\test\db.properties'
parser = SafeConfigParser()
parser.read(cfgFile)
dbHost = parser.get('database','db_host')
dbName = parser.get('database','db_name')
dbUser = parser.get('database','db_login')
dbPass = parser.get('database','db_pass')
return dbHost,dbName,dbUser,dbPass
def connect_and_query(self):
try:
con = None
dbHost=self.db_properties()[0]
dbName=self.db_properties()[1]
dbUser=self.db_properties()[2]
dbPass=self.db_properties()[3]
con = None
qry=("select star from galaxy")
con = psycopg2.connect(host=dbHost,database=dbName, user=dbUser,
password=dbPass)
cur = con.cursor()
cur.execute(qry)
data = cur.fetchall()
for result in data:
qryResult = result[0]
print "the test result is : " +qryResult
except psycopg2.DatabaseError, e:
print 'Error %s' % e
sys.exit(1)
finally:
if con:
con.close()
operation=Main()
operation.connect_and_query()
Im using python 2.7
Regards
If there are a lot of variables, or if you want to easily change the variables being read, return a dictionary.
def db_properties(self, *variables):
cfgFile='c:\test\db.properties'
parser = SafeConfigParser()
parser.read(cfgFile)
return {
variable: parser.get('database', variable) for variable in variables
}
def connect_and_query(self):
try:
con = None
config = self.db_properties(
'db_host',
'db_name',
'db_login',
'db_pass',
)
#or you can use:
# variables = ['db_host','db_name','db_login','db_pass','db_whatever','db_whatever2',...]
# config = self.db_properties(*variables)
#now you can use any variable like: config['db_host']
# ---rest of the function here---
Edit: I refactored the code so you can specify the variables you want to load in the calling function itself.
You certainly don't want to call db_properties() 4 times; just call it once and store the result.
It's also almost certainly better to return a dict rather than a tuple, since as it is the caller needs to know what the method returns in order, rather than just having access to the values by their names. As the number of values getting passed around grows, this gets even harder to maintain.
e.g.:
class Main:
def db_properties(self):
cfgFile='c:\test\db.properties'
parser = SafeConfigParser()
parser.read(cfgFile)
configDict= dict()
configDict['dbHost'] = parser.get('database','db_host')
configDict['dbName'] = parser.get('database','db_name')
configDict['dbUser'] = parser.get('database','db_login')
configDict['dbPass'] = parser.get('database','db_pass')
return configDict
def connect_and_query(self):
try:
con = None
conf = self.db_properties()
con = None
qry=("select star from galaxy")
con = psycopg2.connect(host=conf['dbHost'],database=conf['dbName'],
user=conf['dbUser'],
password=conf['dbPass'])
NB: untested
You could change your db_properties to return a dict:
from functools import partial
# call as db_properties('db_host', 'db_name'...)
def db_properties(self, *args):
parser = SafeConfigParser()
parser.read('config file')
getter = partial(parser.get, 'database')
return dict(zip(args, map(getter, args)))
But otherwise it's probably best to keep the parser as an attribute of the instance, and provide a convenience method...
class whatever(object):
def init(self, *args, **kwargs):
# blah blah blah
cfgFile='c:\test\db.properties'
self._parser = SafeConfigParser()
self._parser.read(cfgFile)
#property
def db_config(self, key):
return self._parser.get('database', key)
Then use con = psycopg2.connect(host=self.db_config('db_host')...)
I'd suggest returning a namedtuple:
from collections import namedtuple
# in db_properties()
return namedtuple("dbconfig", "host name user password")(
parser.get('database','db_host'),
parser.get('database','db_name'),
parser.get('database','db_login'),
parser.get('database','db_pass'),
)
Now you have an object that you can access either by index or by attribute.
config = self.db_properties()
print config[0] # db_host
print config.host # same

Returning useable values from ConfigParse

Using the code described bellow, i can sucessfully retrieve the properties stored into the file.cfg, but how can i use the output into others variables?
from ConfigParser import SafeConfigParser
class Main:
def get_properties(self, section, *variables):
cfgFile = 'c:\file.cfg'
parser = SafeConfigParser()
parser.read(cfgFile)
properties= variables
return {
variable : parser.get(section,variable) for variable in properties
}
def run_me(self):
config_vars= self.get_properties('database','host','dbname')
print config_vars
op=Main()
op.run_me()
Im still learning Python, but i'm not sure what i need to do to set the output into individual variables:
current output:
{'host': 'localhost', 'dbname': 'sample'}
what i would like to have:
db_host = localhost
db_name = sample
def run_me(self):
config_vars= self.get_properties('database','host','dbname')
for key, value in config_vars.items():
print key, "=", value
You recieved dict-object config_vars, so your can using config variables as values of the dict:
>>> print config_vars["dbname"]
sample
>>> print config_vars["host"]
localhost
Read more about python dictionaries in documentation.
I would suggest this approach:
import ConfigParser
import inspect
class DBConfig:
def __init__(self):
self.host = 'localhost'
self.dbname = None
def foo(self): pass
class ConfigProvider:
def __init__(self, cfg):
self.cfg = cfg
def update(self, section, cfg):
for name, value in inspect.getmembers(cfg):
if name[0:2] == '__' or inspect.ismethod(value):
continue
#print name
if self.cfg.has_option(section, name):
setattr(cfg, name, self.cfg.get(section, name))
class Main:
def __init__(self, dbConfig):
self.dbConfig = dbConfig
def run_me(self):
print('Connecting to %s:%s...' % (self.dbConfig.host, self.dbConfig.dbname))
config = ConfigParser.RawConfigParser()
config.add_section('Demo')
#config.set('Demo', 'host', 'domain.com')
config.set('Demo', 'dbname', 'sample')
configProvider = ConfigProvider(config)
dbConfig = DBConfig()
configProvider.update('Demo', dbConfig)
main = Main(dbConfig)
main.run_me()
The idea is that you collect all important properties in a class (where you can also set the defaults).
The method ConfigProvider.update() will then overwrite those with the values from the config (if they exist).
This allows you to access properties with the simple obj.name syntax.
gist

Categories

Resources