SCons problem - dont understand Variables class - python

I'm working on an SConstruct build file for a project and I'm trying to update from Options to Variables, since Options is being deprecated. I don't understand how to use Variables though. I have 0 python experience which is probably contributing to this.
For example, I have this:
opts = Variables()
opts.Add('fcgi',0)
print opts['fcgi']
But I get an error:
AttributeError: Variables instance has no attribute '__getitem__':
Not sure how this is supposed to work

Typically you would store the variables in your environment for later testing.
opts = Variables()
opts.Add('fcgi',0)
env = Environment(variables=opts, ...)
Then later you can test:
if env['fcgi'] == 0:
# do something

That specific error tells you that class Variables hasn't implemented python's __getitem__ interface which would allow you to use [ ...] on opts. If all you want to do is print out your keys, the Variables documentation seems to indicate that you can iterate over your keys:
for key in opts.keys():
print key
Or you can print out the help text:
print opts.GenerateHelpText()

Related

What is the purpose of declaring a variable with the format variable : class type = in python?

I am new to programming in general. I would like to know why this symbol is being used in a few codes. What I mean is, what is the purpose of this way of declaring a variable? Is it useful?
For example let's say I define
say_hello1 = "hello everybody"
I could also define the same variable using:
say_hello2 : str = "hello everybody"
Or ..
say_hello3 : float = "hello everybody" # I know this is not a float variable, I'm doing it on purpose
Each variable (say_hello1, say_hello2 and say_hello3 ) will be the same type (string). So what is the purpose of this way of declaring variables? Is it just to say that the variable will be a specific type? Or does it change anything else (i.e the efficency of the code)?
Thanks in advance.
It shows what variable you are expecting, which makes your code a lot easier to read and understand. It can also help with linters and other third party tools. Python itself won’t look at it afaik.
Take a look here: https://docs.python.org/3/library/typing.html
Python is not a strongly-typed language. The variable : type is just for readability, does not declare strongly typed variables. Later you can change the value, even in another type.
Type hints were introduced in PEP-484. The goal is to allow additional tools to do static linting based on the acceptable types for a variables. This is included for example in PyCharm, and is used by mypy.
But anyway, Python is a dynamic language, and the interpretor just ignores those declared type hints.
In this case the type is pretty obvious so it is not very helpful but assume that you have a base class BaseClass that some other classes inherit SubClassA and SubClassB, then:
a: SubClassA = SubClassA() # not very useful
a: BaseClass = SubClassA() # indicates that you expect a given interface, no necessarily SubClassA
### another dev can safely replace code later on
a: BaseClassA = SubClassB()

module['test'] isn't equal to module.test?

I'm currently creating a discord bot in which I use the mechanism of modules and I want to access from the modules imported with classes integrated the informations that are set in the beginning of the class. However when i try to go into the module
https://pastebin.com/ygLAgaWB
and try to access the class into it doing the following "module.help" works but not "module['help']". Since i'm replacing the "help" with a variable like : module[var], i can' t use module.var. So how can i solve this problem ? I want to access the 'help' class in the 'module' module using a variable where 'help' is stored in.
I've tried nothing because i don't know what to do.
module = self.Modules
var = tmp[0]
class_ = module[var][var]
I expect that it return the class object.
Yes, Python isn't JavaScript, module["help"] and module.help are two different concepts. Square brackets take indices or keys, attributes are retrieved with dot notation.
If you need to access an attribute using a variable you can use
getattr(module, var)

Creating an object from a string, is it possible?

Is it possible to do the following
This works
usethismodule.LoginUsername="my value"
usethismodule.LoginPassword="another value"
But I don't know the second part of the object until the code is being run, I think what I want is this
listofthings=[]
listofthings.append('usethismodule.LoginUsername')
listofthings.append('usethismodule.LoginPassword')
for value in listofthings:
value="A value I set"
This would have the same outcome as the above. Does that make sense?
Indeed, setattr and a dictionary would do the trick.
dictofthings = {
'usethismodule.LoginUsername': 'my value',
'usethismodule.LoginPassword': 'my other value'
}
for k in dictofthings:
setattr(eval(k.split('.')[0]), k.split('.')[1], dictofthings[k])
Hope this helps!
You keep saying usethismodule.<something>. So let's pretend you're right.
Python maintains a dict of currently imported modules in sys.modules. You can look up a module, by name, and get an "object". (A module object, in fact.)
If you have an object in hand, you can either hard-code an access to one of its attributes, or use getattr or setattr on it.
import sys
module_name = "usethismodule" # I don't know where this come from.
utm = sys.modules[module_name]
utm.LoginUsername = "xyzzy"
utm.LoginPassword = "secret123"
# or
pw_attr_name = "passWord" # Maybe you read this from a config file?
setattr(utm, pw_attr_name, "secret123")

How to initialize a class with data from a python file

I'd like to init a class from data stored in a simple python file specified while calling the script. The config file named myconfig.py is :
str='home'
val=2
flt=7.0
I'd like to call it during class initilization like so. One of the objectives is to define variable types as well in the file. I know of the configparser, but this method less verbose if it can be made to work.
class ClassInit(object):
def __init__(self, configFile):
fp, path, des = imp.find_module('',configFile)
imp.load_module(configFile, fp, path, des)
self.__dict__ = configFile.__dict__
fp.close()
def printVal(self):
print '%s %0.2f'%(self.str, self.val)
if __name__ == '__main__':
srcDir = 'src/'
config = osp.join(srcDir, argv[0]) # config for current run
ci = ClassInit(config)
ci.printVal()
Is anything like this possible?
Well, there are several ways to do this. The easiest way would be to use eval() or exec to evaluate this code within the class scope. But that's also the most dangerous way, especially if these files can be created by someone other than you. In that case, the creator can write malicious code that can pretty much do anything. You can override the __builtins__ key of the globals dictionary, but I'm not sure if this makes eval/exec entirely safe. For example:
class ClassInit(object):
def __init__(self, configFile):
f = open(configFile)
config = f.read()
f.close()
config_dic = { '__builtins__': None}
exec 'a = 4' in config_dic
for key, value in config_dic.iteritems():
if key != '__builtins__':
setattr(self, key, value)
This method kills the unsafe 'builtins' object, but it's still not quite safe. For instance, the file may be able to define a function which would override one of your class's functions with malicious code. So I really don't recommend it, unless you absolutely control thos .py files.
A safer but more complex way would be to create a custom interpreter that interprets this file but doesn't allow running any custom code.
You can read the following thread, to see some suggestions for parsing libraries or other safer alternatives to eval():
Python: make eval safe
Besides, if all you ever need your config.py file for is to initialize some variables in a nice way, and you don't need to be able to call fancy python functions from inside it, you should consider using JSON instead. Python 2.6 and up includes simplejson, which you can use to initialize an object from file. The syntax is Javascript and not Python, but for initializing variables there's little difference there.
Can you try self.__dict__.update(configFile.__dict__)? I don't see why that wouldn't work.

How to extract and then refer to variables defined in a python module?

I'm trying to build a simple environment check script for my firm's test environment. My goal is to be able to ping each of the hosts defined for a given test environment instance. The hosts are defined in a file like this:
#!/usr/bin/env python
host_ip = '192.168.100.10'
router_ip = '192.168.100.254'
fs_ip = '192.168.200.10'
How can I obtain all of these values in a way that is iterable (i.e. I need to loop through and ping each ip address)?
I've looked at local() and vars(), but trying do something like this:
for key, value in vars():
print key, value
generates this error:
ValueError: too many values to unpack
I have been able to extract the names of all variables by checking dir(local_variables) for values that don't contain a '__' string, but then I have a list of strings, and I can't figure out how to get from the string to the value of the same-named variable.
First off, I strongly recommend not doing it that way. Instead, do:
hosts = {
"host_ip": '192.168.100.10',
"router_ip": '192.168.100.254',
"fs_ip": '192.168.200.10',
}
Then you can simply import the module and reference it normally--this gives an ordinary, standard way to access this data from any Python code:
import config
for host, ip in config.hosts.iteritems():
...
If you do access variables directly, you're going to get a bunch of stuff you don't want: the builtins (__builtins__, __package__, etc); anything that was imported while setting up the other variables, etc.
You'll also want to make sure that the context you're running in is different from the one whose variables you're iterating over, or you'll be creating new variables in locals() (or vars(), or globals()) while you're iterating over it, and you'll get "RuntimeError: dictionary changed size during iteration".
You need to do vars().iteritems(). (or .items())
Looping over a dictionary like vars() will only extract the keys in the dictionary.
Example.
>>> for key in vars(): print key
...
__builtins__
__name__
__doc__
key
__package__
>>> for key, value in vars().items(): print key, value
...
__builtins__ <module '__builtin__' (built-in)>
value None
__package__ None
key __doc__
__name__ __main__
__doc__ None
Glenn Maynard makes a good point, using a dictionary is simpler and more standard. That being said, here are a couple of tricks I've used sometimes:
In the file hosts.py:
#!/usr/bin/env python
host_ip = '192.168.100.10'
router_ip = '192.168.100.254'
fs_ip = '192.168.200.10'
and in another file:
hosts_dict = {}
execfile('hosts.py', hosts_dict)
or
import hosts
hosts_dict = hosts.__dict__
But again, those are both rather hackish.
If this really is all that your imported file contains, you could also read it as just a text file, and then parse the input lines using basic string methods.
iplistfile = open("iplist.py")
host_addr_map = {}
for line in iplistfile:
if not line or line[0] == '#':
continue
host, ipaddr = map(str.strip, line.split('='))
host_addr_map[host] = ipaddr
iplistfile.close()
Now you have a dict of your ip addresses, addressable by host name. To get them all, just use basic dict-style methods:
for hostname in host_addr_map:
print hostname, host_addr_map[hostname]
print host_addr_map.keys()
This also has the advantage that it removes any temptation any misguided person might have to add more elaborate Python logic to what you thought was just a configuration file.
Here's a hack I use all the time. You got really close when you returned the list of string names, but you have to use the eval() function to return the actual object that bears the name represented by the string:
hosts = [eval('modulename.' + x) for x in dir(local_variables) if '_ip' in x]
If I'm not mistaken, this method also doesn't pose the same drawbacks as locals() and vars() explained by Glen Maynard.

Categories