Configuration Files in Python
Another day, another language; such is the life of a consultant. This time it was Python and I was parameterizing tests for a client so I could log into their system without having to change anything other an an external file. Their CI server will have its own file, as will each of the team members writing / maintaining the Selenium scripts.
So building on what was covered in One, and only one config file and If you really must write your Selenium framework in Java, here is how I am currently suggesting how people incorporate configuration files into their scripts and frameworks.
First, you need to tell your framework (which in this case I mean ‘your own custom runner’) where exactly to find your config file.
<pre lang="python">import getopt
# this is a custom module / class you can see below
import config
try:
opts, args = getopt.getopt(sys.argv[1:], "hc:", ["config="])
except:
print("crap")
sys.exit(2)
config_file = ""
for o, a in opts:
if o == "-h":
print("Usage:")
print("sh.[bat|sh] script_pattern")
print(" script_pattern can be either a script name (mco_rpt_wo_0001.py) or path part (mco_rpt)")
if o in ("-c", "--config"):
config_file = a
if config_file == "":
logger.info("using config/flyingmonkey.cfg as config")
config_file = "flyingmonkey.cfg"
# two ways to specify the config; either as a filename in the config dir or by path
cf = config.config()
if os.path.exists(os.path.abspath(config_file)):
cf.configure(config_file)
elif os.path.exists(os.path.join(os.path.dirname(__file__), "..", "config", config_file)):
cf.configure(os.path.join(os.path.dirname(__file__), "..", "config", config_file))
else:
print "config file (%s) does not exist" % config_file
sys.exit(1)
Basically, what is happening here is that the runner can take as an optional parameter -c or –config which both take the name of a config file. And if the parameter isn’t provided then a default of flyingmonkey.cfg is used.
In my original way of doing this almost three years ago, I had it read the config from an XML file, but that doesn’t appear to be the Pythonic way of doing things. In the standard modules that Python comes with is the ConfigParser module which is a lot nicer — and since it is there by default is implicitly blessed as the way to do this sort of thing. Here is my new class for parsing this file.
<pre lang="python">import ConfigParser
class config(object):
# singleton
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(config, cls).__new__(cls, *args, **kwargs)
return cls._instance
def configure(self, config_file):
# parse config
self.config = ConfigParser.RawConfigParser()
self.config.readfp(open(config_file))
A lot nicer.
And here is how it is used in a script.
<pre lang="python">import unittest, logging
from selenium import selenium
import config, rabbit
class createAlert(unittest.TestCase):
def setUp(self):
self.log = logging.getLogger("monkey.createAlert")
self.cf = config.config().config
self.verificationErrors = []
self.selenium = selenium("localhost", 4444, self.cf.get("General", "browser"), self.cf.get("General", "server"))
self.selenium.start()
rabbit.login(self.selenium, self.cf.get("Login", "username"), self.cf.get("Login", "password"))
As for the actual file that it reads from, for those who used to have to hack around in DOS and Windows 3.x, it is quite similar to the .ini format that everything used to be.
<pre lang="text">[General]
server: http://flying.monkey.com
environment: test
browser: *chrome
[Login]
username: adam@element34.ca
password: monkey
The observant reader will see that the way to reference a particular parameter is by “Section” and then “Key”.
I’m pretty happy with how easy, and nice, it is to do this now.