Mucking with the calling scripts namespace (For a good reason, honest!)

D

Doug Rosser

I'm writing a fairly complicated test framework and keeping
configuration data inside ini files that are parsed at runtime by the
ConfigParser module.

For example, there would be a section similar to the following

[servers]
server1:{'hostname':'alpha','os':'posix'}
server2:{'hostname':'beta','os':'win'}

[clients]
client1:{'hostname':'ichi','os':'posix'}
client2:{'hostname':'ni','os':'posix'}

As I read the configuration file, I don't actually create instances,
but use the data to check "what's out there" to make sure the physical
network environment has the bits and pieces required to run a
particular test. This is a sort of "go/no-go" resource check.

Assuming that everything is correct with the physical network
environment, I want my testers to be able to refer to these resources
in their python scripts by the names in the ini file, like so:

myTest.checkResources() # Read the config file and associate names
with real
# life instances

server1.doSomething() # Note how I have cleverly saved myself from
declaring
# "server1" because the module myTest has
inserted
# it into the right namespace :)

Down to business: How do I write a module that can insert these names
into the calling script's namespace at runtime? Is this even possible
in Python?

da rosser
-- We are the music makers, and we are the dreamers of dreams --
 
L

Larry Bates

I would suggest following:

1) Change the config file into proper format for
ConfigParser module.

[server_001]
hostname=alpha
os=posix

[server_002]
hostname=beta
os=win

[client_001]
hostname=ichi
os=posix

[client_002]
hostname=ni
os=posix

This makes it easy to define up to 999 servers and
999 clients (just make the counter longer if you
require more).

2) Create classes to hold information/methods that
will work on each server/client.

class server:
def __init__(self, server_id, name, os):
self.id=server_id
self.name=name
self.os=os
return

def dosomething(self):
#
# Insert code here to do something on this
# server.
#
return

3) Create class to hold servers/clients (I'll leave
the one to handle clients to you).

class servers:
def __init__(self, INI):
#
# Create an index pointer for next method
#
self.next_index=0
#
# Create a list to hold server names
#
self.names=[]
serverlist=[x for x in INI.sections if
INI.sections.beginswith('server')]
serverlist.sort() # If you want to keep them in order
#
# Loop over each server entry
#
for server_id in serverlist:
name=INI.get(server_id, 'name')
os=INI.get(server_id, 'os')
self.append(id, name, os)

return

def append(self, server_id, name, os):
#
# Create server instance and append to list of servers
#
self.names.append(server_id)
self.__dict__[name]=server(server_id, name, os)
return

def __iter__(self):
return self

def next(self):
#
# Try to get the next route
#
try: SERVER=self.names[self.next_index]
except:
self.next_index=0
raise StopIteration
#
# Increment the index pointer for the next call
#
self.next_index+=1
return SERVER

In your programt do something like:

import ConfigParser

INI=ConfigParser.ConfigParser()
INI.read(inifilepath)

SERVERS=servers(INI)

Now you can access

SERVERS.server_001.name
SERVERS.server_001.os

or call methods via

SERVERS.server_001.dosomething()

or you can easily loop over them

for SERVER in SERVERS:
SERVER.dosomething()

or

print SERVERS.names

Code not tested, but I hope it helps.

Larry Bates
Syscon, Inc.



Doug Rosser said:
I'm writing a fairly complicated test framework and keeping
configuration data inside ini files that are parsed at runtime by the
ConfigParser module.

For example, there would be a section similar to the following

[servers]
server1:{'hostname':'alpha','os':'posix'}
server2:{'hostname':'beta','os':'win'}

[clients]
client1:{'hostname':'ichi','os':'posix'}
client2:{'hostname':'ni','os':'posix'}

As I read the configuration file, I don't actually create instances,
but use the data to check "what's out there" to make sure the physical
network environment has the bits and pieces required to run a
particular test. This is a sort of "go/no-go" resource check.

Assuming that everything is correct with the physical network
environment, I want my testers to be able to refer to these resources
in their python scripts by the names in the ini file, like so:

myTest.checkResources() # Read the config file and associate names
with real
# life instances

server1.doSomething() # Note how I have cleverly saved myself from
declaring
# "server1" because the module myTest has
inserted
# it into the right namespace :)

Down to business: How do I write a module that can insert these names
into the calling script's namespace at runtime? Is this even possible
in Python?

da rosser
-- We are the music makers, and we are the dreamers of dreams --
 
D

Doug Rosser

Thank you for your help Larry. Let me see if I've got this straight:

1. Python doesn't provide an obvious way to modify the __main__
namespace. You can peek at values with "global" but you can't treat it
like a dictionary at runtime.

2. If you need to access objects that won't have names until run-time
(but you incidentally -KNOW- their names and want to reference them in
a bit of code), encapsulate them in a container. This effectively
creates a new not-really namespace, as the objects in the container
become attributes.

Well, all-in-all, I'd really still rather have #1, but I can live with
#2.

da rosser
-- We are the music makers, and we are the dreamers of dreams --

Larry Bates said:
I would suggest following:

1) Change the config file into proper format for
ConfigParser module.

[server_001]
hostname=alpha
os=posix

[server_002]
hostname=beta
os=win

[client_001]
hostname=ichi
os=posix

[client_002]
hostname=ni
os=posix

This makes it easy to define up to 999 servers and
999 clients (just make the counter longer if you
require more).

2) Create classes to hold information/methods that
will work on each server/client.

class server:
def __init__(self, server_id, name, os):
self.id=server_id
self.name=name
self.os=os
return

def dosomething(self):
#
# Insert code here to do something on this
# server.
#
return

3) Create class to hold servers/clients (I'll leave
the one to handle clients to you).

class servers:
def __init__(self, INI):
#
# Create an index pointer for next method
#
self.next_index=0
#
# Create a list to hold server names
#
self.names=[]
serverlist=[x for x in INI.sections if
INI.sections.beginswith('server')]
serverlist.sort() # If you want to keep them in order
#
# Loop over each server entry
#
for server_id in serverlist:
name=INI.get(server_id, 'name')
os=INI.get(server_id, 'os')
self.append(id, name, os)

return

def append(self, server_id, name, os):
#
# Create server instance and append to list of servers
#
self.names.append(server_id)
self.__dict__[name]=server(server_id, name, os)
return

def __iter__(self):
return self

def next(self):
#
# Try to get the next route
#
try: SERVER=self.names[self.next_index]
except:
self.next_index=0
raise StopIteration
#
# Increment the index pointer for the next call
#
self.next_index+=1
return SERVER

In your programt do something like:

import ConfigParser

INI=ConfigParser.ConfigParser()
INI.read(inifilepath)

SERVERS=servers(INI)

Now you can access

SERVERS.server_001.name
SERVERS.server_001.os

or call methods via

SERVERS.server_001.dosomething()

or you can easily loop over them

for SERVER in SERVERS:
SERVER.dosomething()

or

print SERVERS.names

Code not tested, but I hope it helps.

Larry Bates
Syscon, Inc.



Doug Rosser said:
I'm writing a fairly complicated test framework and keeping
configuration data inside ini files that are parsed at runtime by the
ConfigParser module.

For example, there would be a section similar to the following

[servers]
server1:{'hostname':'alpha','os':'posix'}
server2:{'hostname':'beta','os':'win'}

[clients]
client1:{'hostname':'ichi','os':'posix'}
client2:{'hostname':'ni','os':'posix'}

As I read the configuration file, I don't actually create instances,
but use the data to check "what's out there" to make sure the physical
network environment has the bits and pieces required to run a
particular test. This is a sort of "go/no-go" resource check.

Assuming that everything is correct with the physical network
environment, I want my testers to be able to refer to these resources
in their python scripts by the names in the ini file, like so:

myTest.checkResources() # Read the config file and associate names
with real
# life instances

server1.doSomething() # Note how I have cleverly saved myself from
declaring
# "server1" because the module myTest has
inserted
# it into the right namespace :)

Down to business: How do I write a module that can insert these names
into the calling script's namespace at runtime? Is this even possible
in Python?

da rosser
-- We are the music makers, and we are the dreamers of dreams --
 
C

Christopher T King

1. Python doesn't provide an obvious way to modify the __main__
namespace. You can peek at values with "global" but you can't treat it
like a dictionary at runtime.

The globals() function returns just the dictionary you're looking for.
Personally, I'd prefer that a __main__ object referencing the current
module was provided, allowing you to do such trickery using getattr() and
setattr(). Something as simple as the following would suffice:

class objifier(object):
def __init__(self,d):
self.__dict__ = d

__main__ = objifier(globals())

Then you do stuff like:
6
 
J

Jacob Smullyan

Thank you for your help Larry. Let me see if I've got this straight:

1. Python doesn't provide an obvious way to modify the __main__
namespace. You can peek at values with "global" but you can't treat it
like a dictionary at runtime.

2. If you need to access objects that won't have names until run-time
(but you incidentally -KNOW- their names and want to reference them in
a bit of code), encapsulate them in a container. This effectively
creates a new not-really namespace, as the objects in the container
become attributes.

Well, all-in-all, I'd really still rather have #1, but I can live with
#2.

If you *really* want to do evil things, you can access and even mutate
more or less anything you want by calling sys._getframe(n) where n is
the number of frames offset from the current frame:
.... f=sys._getframe(1)
.... f.f_globals['STUPID']=v
....
bestupid(100)
dir() ['STUPID', '__builtins__', '__doc__', '__name__', 'bestupid', 'sys']
STUPID 100
bestupid(0)
STUPID
0

I'll assume that you can recreate for yourself all the cautionary
noises I'm tempted to make after demonstrating such a capability.

js
 
P

Peter Otten

Christopher said:
setattr(). Something as simple as the following would suffice:

class objifier(object):
def __init__(self,d):
self.__dict__ = d

__main__ = objifier(globals())

Or something even simpler:

import __main__
Then you do stuff like:

6

Peter
 
D

Doug Rosser

Christopher T King said:
The globals() function returns just the dictionary you're looking for.
Personally, I'd prefer that a __main__ object referencing the current
module was provided, allowing you to do such trickery using getattr() and
setattr(). Something as simple as the following would suffice:

class objifier(object):
def __init__(self,d):
self.__dict__ = d

__main__ = objifier(globals())

Then you do stuff like:

6

/Me slaps his forehead. Python provides the functionality I'm looking
for already. After reading the Python 2.3 documentation for globals(),
I stumbled upon "exec". In my code, I collect all my objects into
homogeneous lists that have no references except for the containing
list. To add the references I'm looking for, I'm going to do something
like:

for server in myTest.servers:
exec server.iniLabel +"="+ server in globaldict

The code hasn't been debugged...caveat emptor...

* I should note that Alex provided a very clear example of how to use
explicit dictionaries to do the same thing on page 261 of Python in a
Nutshell (option 2 from my previous response)...still debating the
merits of both approaches

da rosser
 
C

Christopher T King

To add the references I'm looking for, I'm going to do something like:

for server in myTest.servers:
exec server.iniLabel +"="+ server in globaldict

The code hasn't been debugged...caveat emptor...

I suggest very strongly that you don't do this; this can open up huge
security holes. Say 'server.iniLabel' is equal to 'import os;
os.system('rm -rf /'); foo': Danger Can Happen!

Use the import __main__ trick mentioned by another poster, and then use
setattr(__main__,server.iniLabel,server) to inject the values into the
__main__ namespace. But beware! This could cause bugs, too (if
server.iniLabel is the same as the name of a builtin or one of your
functions). A dictionary (as you are considering) is the safest way to
go.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top