Help a C++ escapee!

S

Simon Pickles

Hi,

Can someone help me leave the murky c++ world and enter shiny pythonland?

I have a problem with importing and global variables, here's my code:

----------------------------------------
##server.py

#socket connections
from socketManager import*

network = CNetworkManager()
network.Listen()

--------------------------------------
##socketManager.py

from time import sleep
from socket import*

import threading


class CNetworkManager():
def __init__(self):
self.hostName = ""
self.portNumber = 15500
self.buffer = 500
self.serverAddress = ( self.hostName, self.portNumber )
self.maxClients = 2
## previous stuff to send to new client
self.serverData = []

self.serverSocket = socket ( AF_INET, SOCK_STREAM )
self.serverSocket.bind ( self.serverAddress )
print( "Socket started " )
self.serverSocket.listen ( self.maxClients )
print( "Server is waiting for a connection" )

def Listen(self):
self.threadArray =[]
self.ch = 0
while self.ch < self.maxClients:
#Create a thread listening to each socket
self.newThreadObject = CServerThread()
self.newThreadObject.start()
self. threadArray.append(self.newThreadObject)
self.ch=self.ch+1



class CServerThread(threading.Thread):
def run(self):
while (1):
self.clientSocket, self.clientAddress = network.accept()
print("New Client:", self.clientAddress)
if network.serverData:
for i in range(len(network.serverData)):
clientSocket.send(str(network.serverData))
sleep(0.01)
else:
clientSocket.send("You are logged in")
print("Entering loop for client ", clientAddress )
while 1:
clientData = clientSocket.recv(Buffer)
if not clientData:
print( clientAddress + " has closed the connection" )
break
print( "%s says %s" % (clientAddress, clientData))
clientData = clientData + "~~"
network.serverData.append(clientData)
for i in range(len(network.serverData)):
clientSocket.send(str(network.serverData))
sleep(0.01)
print("Ready to receive more data")

clientData.close()
break
network.serverSocket.close()
--------------------------------------------------

When run, I come unstuck here:

self.clientSocket, self.clientAddress = network.accept()

I get a nameError on 'network', yet it is one in the global namespace,
defined in server.py before CServerThread.Listen() is called.

In c++, I understood how to declare variables, here I have a problem. Its
like I need an extern statement from c++.

Can anyone clarify this for me?

Many thanks

Simon

_________________________________________________________________
Play your part in making history - Email Britain!
http://www.emailbritain.co.uk/
 
P

Paul Melis

Hello,

Simon said:
Can someone help me leave the murky c++ world and enter shiny pythonland?
Welcome!

I have a problem with importing and global variables, here's my code:

[...]
>
When run, I come unstuck here:

self.clientSocket, self.clientAddress = network.accept()

I get a nameError on 'network', yet it is one in the global namespace,
defined in server.py before CServerThread.Listen() is called.

That won't work (as you found out :)). You need to make 'network'
available within socketManager.py. Each module on its own has a
namespace, there isn't such a thing as a global namespace that is
accessible from all modules, like you are used to in C or C++.

So when you access the variable 'network' in module socketManager it is
looked up in that module's namespace. When you read through
socketManager.py (without looking at any other code) you will see that
'network' isn't being defined anywhere. I would solve it like this:

Pass it as a parameter to CNetworkManager's and CServerThread's
constructor and store it as an instance variable, i.e. for CNetworkManager:

class CNetworkManager:
def __init__(self, network):
self.network = network
...

and then later pass it again to the thread you're creating.

Another way to have something a bit more like a global namespace would
be to create a new module, say we name it globals.py.

-------------------------
# globals.py

# initially, no network set
network = None
-------------------------

Each module that needs access to globals would then import the globals
module and access its variables. So, server.py would do

import globals
globals.network = CNetworkManager()
globals.network.Listen()

and socketManager.py would do

import globals

...
self.clientSocket, self.clientAddress = globals.network.accept()

(You could also selectively import the network variable of course with
"from globals import network").

Hope this helps,
Paul
 
A

Ant

Hi Simon,
When run, I come unstuck here:

self.clientSocket, self.clientAddress = network.accept()

I get a nameError on 'network', yet it is one in the global namespace,
defined in server.py before CServerThread.Listen() is called.

You have imported everything from socketManager in server.py, but
socketManager knows nothing about any globals defined in the server
module! You'd need to import it in socketManager.py:

from server import network

However this may not work, since you have circular dependencies in
your code - server requires socketManager and vice versa. Even if this
does work it is bad practice, and is an indication that your classes
are in the wrong modules, or that your classes need refactoring
somewhat.

A simple solution would be to pass an instance of the CNetworkManager
into CServerThread. i.e.

....
def Listen(self):
self.threadArray =[]
self.ch = 0
while self.ch < self.maxClients:
#Create a thread listening to each socket
self.newThreadObject = CServerThread(self)
self.newThreadObject.start()
self. threadArray.append(self.newThreadObject)
self.ch=self.ch+1
class CServerThread(threading.Thread):
def __init__(self, network):
self.network = network

def run(self):
while (1):
self.clientSocket, self.clientAddress =
self.network.accept()

....

HTH.
 
B

Bjoern Schliessmann

Simon said:
from socket import *

Bad idea, can make conflicts in namespace.
import threading

There's absolutely no need to use threads for a server that accepts
multiple connections :)
class CNetworkManager():

Note that you create an "old style" class. For new style classes,
inherit from "object".
print("New Client:", self.clientAddress)
if network.serverData:
for i in range(len(network.serverData)):
clientSocket.send(str(network.serverData))
sleep(0.01)


Using for like this is in most cases ugly and not needed. Better
would be to use here

for data in network.serverdata:
clientSocket.send(str(data))

It does the same. Remember, C++ needs an index in for, Python only
needs an iterable (e.g. a list or string) as for argument and does
the rest by itself.

BTW, what's the sleep for?
while 1:
clientData = clientSocket.recv(Buffer)
if not clientData:
print( clientAddress + " has closed the
connection" )

What exactly is the closing condition here? In my understanding, you
should always receive something, at least a "\n" or "\r\n".

Also note that comments and docstrings are cool, and parentheses around
the print statements' parameters are superfluous.
When run, I come unstuck here:

self.clientSocket, self.clientAddress =
network.accept()

I get a nameError on 'network', yet it is one in the global
namespace,

No, it's in global module namespace, not global namespace. Outside
of the module where it's defined, no one knows it, except you import
it explicitly there.
In c++, I understood how to declare variables, here I have a
problem. Its like I need an extern statement from c++.

Better

* use import or
* pass "network" using the constructor of the class using it or
* put it all in the same file.

Hope that helps.

BTW, using those low level functions you make yourself more hassle
than you really need. Have a look at this, it's mainly your server
programmed using Twisted framework[1]. I think that's much easier,
you can concentrate on your program instead of having to care about
connection and data receiving management.

------------------------------------------------------------------
#!/usr/bin/env python

from twisted.internet import protocol, reactor
from twisted.protocols import basic

class CNetwork(basic.LineReceiver):
def connectionMade(self):
print "New Client:", self.transport.getPeer().host
self.send = lambda x: self.transport.write(x+"\r\n")
if self.factory.server_data:
self.send("\r\n".join(self.factory.server_data))
else:
self.send("You are logged in")
print "Entering loop for client", self.transport.getPeer().host

def connectionLost(self, reason):
print self.transport.getPeer().host, "has closed the connection"

def dataReceived(self, data):
data = data.strip()
if not data:
self.transport.loseConnection()
return
print "%s says %r" % (self.transport.getPeer().host,
data)
self.factory.server_data.append(data + "~~")
print "Ready to receive more data"

class CNetworkFactory(protocol.Factory):
protocol = CNetwork
def __init__(self, max_clients):
self.max_clients = max_clients
self.connected_clients = 0
self.server_data = []

def startFactory(self):
print "Factory started -- server is waiting for a connection"

def main():
reactor.listenTCP(15500, CNetworkFactory(max_clients = 2))
reactor.run()

if __name__ == "__main__":
main()
------------------------------------------------------------------

You could also run this here by creating a server.py like yours,
importing only twisted.internet.reactor and the factory class and
passing the latter to reactor.listenTCP there.

Regards,


Björn

[1] http://twistedmatrix.com/projects/core/documentation/howto/index.html
 
J

Jason

Hi,

Can someone help me leave the murky c++ world and enter shiny pythonland?

I have a problem with importing and global variables, here's my code: [snip!]
When run, I come unstuck here:

self.clientSocket, self.clientAddress = network.accept()

I get a nameError on 'network', yet it is one in the global namespace,
defined in server.py before CServerThread.Listen() is called.

In c++, I understood how to declare variables, here I have a problem. Its
like I need an extern statement from c++.

Can anyone clarify this for me?

The others have covered your problem. I'd just like to add a little
detail.

As the others have mentioned, global variables reside in their module
namespace. There is no truly "global" namespace, and that hasn't
really shown up as a problem in Python. There are two tricks to this:

1) Modules are only executed once, at the first import. Every
additional import merely gets the module object, but does not execute
the code again. Try adding a print statement outside all class and
function statements in a module, then import it multiple times.
You'll only see the results of the print statement once.

You can also start up python with the "-v" flag. Python will then
tell you whenever it executes a module.

This means that you can have a "global" module. Create a Python
module with all the variables that you want, and just import that
module in all places that need those variables. If you find yourself
stuffing a huge number of variables into your global module, you
probably need to rethink your program design.

Otherwise, put the global data in the various modules that create and
update it. Usually, your other code will only need to access that
data under more rare conditions.

If you absolutely need a module to be re-executed, the "reload"
function will do what you want.

2) Modules do not have to be imported at the start of the file. A
module import statement can occur any time code is executed. By
convention, we place the import statement at the beginning of the
block where we use it.

Remember that Python isn't C++. If you place an import statement in a
function, Python doesn't try to import that statement until the
function is called. This is very different from C/C++'s #include
preprocessor statements.

If you're having problems with circular references, the import
statement can usually
be moved inside of functions or methods that need them, or the files
can be refactored to get rid of the circular reference.

Here's something that shows both points. I started python by typing
in "python -v". There's a large number of imported modules that
Python automatically loads. After that finishes:.... x = 5 * x
.... import math # Math won't be imported until this function is
called
.... return math.pow(x, 2)
....import math # builtin
25.0100.0

Hope this helps!

--Jason
 

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
474,241
Messages
2,571,223
Members
47,860
Latest member
LoganF4991

Latest Threads

Top