So I'm trying to make a very basic MUD and I may be going about it the wrong way but I just need some coding help. This code includes twisted that I've tweaked for testing means to try and understand it, however I have run into a road block.
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor
import time
class Chat(LineReceiver):
def __init__(self, users):
self.users = users
self.name = None
self.health = 10
self.state = "GETNAME"
def connectionMade(self):
self.sendLine("What's your name?")
def connectionLost(self, reason):
if self.users.has_key(self.name):
print self.name, "has disconnected"
del self.users[self.name]
def lineReceived(self, line):
if self.state == "GETNAME":
self.handle_GETNAME(line)
else:
if self.state == "CHAT":
self.handle_CHAT(line)
else:
if self.state == "ATTACK":
self.handle_ATTACK(line)
def handle_GETNAME(self, name):
if self.users.has_key(name):
self.sendLine("Name taken, please choose another.")
return
self.sendLine("Welcome, %s!" % (name,))
print name, "has connected"
self.sendLine("You currently have %s health..." % (self.health))
self.name = name
self.users[name] = self
for name, protocol in self.users.iteritems():
if protocol != self:
message = "%s has joined" % (self.name,)
protocol.sendLine(message)
self.state = "CHAT"
def handle_CHAT(self, message):
if(message[0:3] == 'say'):
try:
message = message[4:]
message = "<%s> %s" % (self.name, message)
print message
for name, protocol in self.users.iteritems():
if protocol != self:
protocol.sendLine(message)
except:
print "Chat failed"
if(message == 'test'):
try:
self.handle_TEST()
except:
print "Testing Failed"
if(message == 'attack'):
try:
self.handle_ATTACKINIT()
except:
print "Attack Failed"
def handle_ATTACKINIT(self):
self.sendLine("Who are you attacking?")
self.state = "ATTACK"
def handle_ATTACK(self, target):
for target, protocol in self.users.iteritems():
if protocol == target:
target.sendLine("You have been attacked!")
protocol.sendLine("You now have %s health remaining..." % (self.health,))
else:
self.sendLine("No target with that name")
def handle_TEST(self):
print name, "is Testing"
self.sendLine("This is a test")
self.state = "CHAT"
class ChatFactory(Factory):
def __init__(self):
self.users = {} # maps user names to Chat instances
def buildProtocol(self, addr):
return Chat(self.users)
reactor.listenTCP(8123, ChatFactory())
reactor.run()
The main function I need help with is this function...
def handle_ATTACK(self, target):
for target, protocol in self.users.iteritems():
if protocol == target:
target.sendLine("You have been attacked!")
protocol.sendLine("You now have %s health remaining..." % (self.health,))
else:
self.sendLine("No target with that name")
I need to find the 'target's' protocol to send it a message and deal damage to it.
I figured out that it is saving the Name/Protocol match in the list self.users, and I'm guessing I look for "target's" protocol in the set self.users.iteritems() but I'm having trouble accessing a specific protocol and utilizing it.
Any help for the beginning novice that is messing with twisted?
You've shadowed your target parameter with a loop variable:
def handle_ATTACK(self, target):
for target, protocol in self.users.iteritems():
if protocol == target:
Before the loop starts, target identifies the user to attack. After the loop starts, it identifies one of the keys in the self.users dictionary.
Then, you compare that key to protocol - which is one of the values of the self.users dictionary. This probably isn't what you intended.
Try comparing the target parameter to keys from self.users and then using the corresponding value as the protocol to use to send data.
Related
everyone.
app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0'
app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0'
celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)
def upload():
if checkDouble == None:
do_async_upload.apply_async(args=[checkDouble], countdown=15)
return 'Saved ' + file.filename
else:
return "File exists"
#celery.task
def do_async_upload(pic):
print("hello2")
db.session.add(pic)
db.session.commit()
return 0
But do_sync_upload doesn't work (hello2 doesn't print), it just skips. Redis server is working correctly
Updated:
if checkDouble == None:
print('Hello')
async_result = do_async_upload.delay(checkDouble)
print(async_result)
result = async_result.get()
return 'Saved ' + file.filename
else:
return "File exists"
But do_async_upload.apply_async doesn't seem to execute at all.
Maybe there is a problem in my Redis? Server is working, but after I try to submit something it turns from 0 to 4 clients connected.
Or even I have a problem with my datetime settings.
UPDATE 2:
I tried 2 different examples with Flask and Celery and none of them work. Redis server shows some activity, but no.
Both of them are using the same function:
Code from https://github.com/ro6ley/flask-celery-demo:
#client.task
def send_mail(data):
with app.app_context():
msg = Message("Ping!",
sender="admin.ping",
recipients=[data['email']])
msg.body = data['message']
mail.send(msg)
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
return render_template('index.html')
elif request.method == 'POST':
data = {}
data['email'] = request.form['email']
data['first_name'] = request.form['first_name']
data['last_name'] = request.form['last_name']
data['message'] = request.form['message']
duration = int(request.form['duration'])
duration_unit = request.form['duration_unit']
print("1")
send_mail.apply_async(args=[data], countdown=duration)
print("2")
flash(f"Email will be sent to {data['email']} in {request.form['duration']} {duration_unit}")
It shows flash message, but message isn't coming. Config.py is set correctly.
Next application:
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
return render_template('index.html', email=session.get('email', ''))
email = request.form['email']
session['email'] = email
# send the email
email_data = {
'subject': 'Hello from Flask',
'to': email,
'body': 'This is a test email sent from a background Celery task.'
}
if request.form['submit'] == 'Send':
# send right away
send_async_email.delay(email_data)
flash('Sending email to {0}'.format(email))
else:
# send in one minute
send_async_email.apply_async(args=[email_data], countdown=60)
flash('An email will be sent to {0} in one minute'.format(email))
return redirect(url_for('index'))
#celery.task
def send_async_email(email_data):
"""Background task to send an email with Flask-Mail."""
msg = Message(email_data['subject'],
sender=app.config['MAIL_DEFAULT_SENDER'],
recipients=[email_data['to']])
msg.body = email_data['body']
with app.app_context():
mail.send(msg)
None of the options work.
apply_async() returns an AsyncResult object IMMEDIATELY so no wonder you do not see anything and think do_async_upload() did not execute...
So here are two things I suggest you do:
1) Modify the upload() to have something like:
async_result = do_async_upload.apply_async(args=[checkDouble], countdown=15)
# .get() blocks until the task is done and returns
result = async_result.get()
2) Do not call print() in Celery tasks. Use the get_task_logger() function to obtain Celery's logger object and use it in your tasks. There is a whole section in the documentation about it.
I am total beginner in tornado and generally websockets/networking.
What I am trying to do is to implement in desktop windowed app a websocket client, that:
receives some random data from server, reads it, checks data and
emits signal with it outside to some widgets
sends data to server (with no response from server)
(up to this point I managed to this)
sends data to server in some outside function (like in widget):
def request_masks_export(self, export_dir):
wrapped_command = {
"clientAction": "exportMasks",
"clientData": {
"client": "Remote Browser is requesting to export masks files.",
"exportDir": export_dir
}
}
response = self.current_connection_manager.send_sync(wrapped_command)
# response from send_sync here
...and waits until it will get response, and then proceeds within function.
I can't get my head around it to do it...
Here is my code:
class ConnectionManager(QThread):
on_data = Signal(dict)
connection_interrupted = Signal()
_default_url = "ws://localhost:12345/"
_identifier = {
"clientAction": "newClientConnected",
"clientData": {
"client": "connected from python app: {}".format(os.path.basename(__file__))
}
}
def __init__(self, url=None, timeout=None, parent=None):
QThread.__init__(self, parent)
self.ioloop = IOLoop.current()
if url is not None:
self.url = url
else:
self.url = self._default_url
if timeout is not None:
self.timeout = timeout
else:
self.timeout = 1
self.ws_connection = None
#gen.coroutine
def connect_to_server(self):
try:
start_time = time.time()
self.ws_connection = yield websocket_connect(self.url)
self.ws_connection.connect_future.add_done_callback(self.connect_callback)
print 'Elapsed time of connection: %.1f msec' % (time.time() - start_time) * 1000
self.send(self._identifier)
except socket.error as e:
print e
if e.errno == errno.ECONNREFUSED:
print 'Connection has been refused. Awaiting connection...'
yield gen.sleep(self.timeout)
self.connect_to_server()
except Exception as e:
print "unable to connect websocket server"
print e
def run(self):
self.ioloop.spawn_callback(self.connect_to_server)
self.ioloop.start()
def connect_callback(self, future):
if future.exception() is None:
self.ws_connection = future.result()
self._on_connection_success()
self.read_message()
else:
self.on_connection_error(future.exception())
#gen.coroutine
def read_message(self):
# reading here all messages, except those that are result of def send_sync
while True:
msg = yield self.ws_connection.read_message()
if msg is None:
self.on_connection_close()
break
self.check_data(msg)
#gen.coroutine
def send(self, data):
if isinstance(data, dict):
json_object = json.dumps(data)
res = yield self.ws_connection.write_message(json_object)
# from here I just send messages that don't need response
#gen.coroutine
def send_sync(self, data):
if isinstance(data, dict):
json_object = json.dumps(data)
response = yield self.ws_connection.write_message(json_object)
# I want to get response messege from server here to be able to return it 'somehow'
def check_data(self, raw_msg):
"""
Callback function when message is received from server.
Emits signal
:param raw_msg: unicode message received from server
:return:
"""
try:
if raw_msg is None:
raise WebSocketNoneError("raw_msg received is None!", "none data")
try:
raw_data_dict = json.loads(raw_msg)
except ValueError as e:
# handles json decode error from raw_msg(string)
print e
raise WebSocketMessageError("Incompatible message type!", "JSON only")
if raw_data_dict.has_key("serverAction"):
self.on_data.emit(raw_data_dict)
return raw_data_dict
else:
raise WebSocketMessageError("Incompatible message structure!", "missing serverAction")
except WebSocketMessageError as e:
print e.args
raise
except WebSocketNoneError as e:
print e.args
raise
def close_manager(self):
"""
Method for stopping websocket, waiting for thread to finish and stop it.
:return:
"""
self.close_websocket()
self.ioloop.stop()
self.stop()
def is_connected(self):
if not self.ws_connection:
return False
else:
return True
def stop(self):
"""
Stop thread.
:return:
"""
self.quit()
self.wait()
def close_websocket(self):
"""
Close websocket.
:return:
"""
try:
self.ws_connection.close()
except Exception as e:
print e
pass
def on_connection_success(self):
print "_on_connection_success"
pass
def on_connection_close(self):
print "_on_connection_close"
self.connection_interrupted.emit()
self.connect_to_server()
pass
I am using:
class ConnectionManager(QThread):
Because it was blocking the thread that was used for rest of the app... Is this proper way to do it? Correct me if I am wrong but I wouldn't do it in QThread then whole app would load up to the point of iolopp.start() and wouldn't execute past that element, instead it would wait/listen for incoming messages, etc...
Also I am a bit confused about connection types in tornado. There isn't many examples of websocket connections, instead there is a lot about HTTP. Since all of this stuff is new for me, then maybe I misunderstand some things, but I thought that only initial handshake is http based in websockets and I can't use classes like RequestHandler, tornado.httpclient.
I don't know how to do it in a yield-way, but I can see a pretty simple callback-way solution, if you are ok with that. You can achieve this through the interaction between a Tornado's event loop and QT event loop. I don't know PyQT, so I'll show some abstraction and just general idea, not working code.
def request_masks_export(self, export_dir):
# ...
IOLoop.current().add_callback(send_sync,
wrapped_command,
some_func_to_call_after_request_is_finished)
...
#gen.coroutine
def send_sync(self, data, callback_func):
if isinstance(data, dict):
json_object = json.dumps(data)
response = yield self.ws_connection.write_message(json_object)
QTEventLoop.add_callback(callback_func, response)
I am attempting to build a server that takes user requests for long(ish)-running jobs, updates the user as the job progresses, and returns some data for the client to use. I am attempting to use tornado's WebSocketHandler to do this. Is there a reason I can't call a WebSocketHandler's write_message method from another object?
import tornado.ioloop
import tornado.websocket
import json, sys, os
from uuid import uuid4
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write('Welcome to the site. Requests cannot be made to the main page.')
class WSInvalidRequest(Exception):
"""Called when user sends invalid request to the server."""
pass
class WSRequestQueue:
def __init__(self):
self._items = []
def put(self, item):
self._items.append(item)
return self._items.length()
def get(self):
return self._items.pop(0)
def get_position(self, item):
return self._items.index(item)
QUEUE = WSRequestQueue()
class WSRequest:
def __init__(self, message, websocket):
self.websocket = websocket
self.ran = False
self.valid = False
self.write(u'Request received.')
try:
self.request = WSRequest.parse_message(message)
self.valid = True
self.write(u'Request validated.')
position = QUEUE.put(self)
self.write(u'Added request to queue behind %i other requests.' % position)
except WSInvalidRequest as e: self.write(e.message)
#staticmethod
def validate_request_dict(request):
if not isinstance(messageDict, dict):
raise WSInvalidRequest(u'Invalid request. Should be JSON dict string.')
if 'arg' not in request:
raise WSInvalidRequest(u'Invalid request. No arg found')
#staticmethod
def parse_message(message):
messageDict = json.loads(message)
validate_request_dict(messageDict)
argument = messsageDict['arg']
return {'argumet': argument}
def write(self, message):
self.websocket.write_messsage(unicode(message))
def run(self):
self.ran = True
def destroy(self):
if self.valid:
if not self.ran: QUEUE.pop(QUEUE.get_position(self))
self.websocket.requests.remove(self)
self.write(u'Removed request from queue.')
class RequestWebSocket(tornado.websocket.WebSocketHandler):
def open(self):
self.id = uuid4()
self.requests = set()
print("WebSocket opened")
def on_message(self, message):
self.write_message(u'You sent: %s' % message)
self.write_message(u'Attempting to add your request to the queue.')
newRequest = WSRequest(message, self)
if newRequest.valid: self.requests.add(newRequest)
else: newRequest.destroy
def on_close(self):
print("WebSocket closed. Removing all requests from the queue.")
for request in self.requests: request.destroy()
def check_origin(self, origin):
return True
if __name__ == "__main__":
# Create the web server
application = tornado.web.Application([
(r'/', MainHandler),
(r'/websocket', RequestWebSocket)
], debug=True)
application.listen(80)
tornado.ioloop.IOLoop.instance().start()
There's a spelling mistake in write_messsage. There's an extra s.
It's at:
class WSRequest:
def write(self, message):
self.websocket.write_messsage(unicode(message))
# ^ extra 's'
I am using Vimeo's API for the users of my app to upload videos, or replace their existing video with a new one. I am using a Vimeo Client to help me make the calls in my Django Application. Uploading works without any issues, but when I try to replace an existing video with a new one, the thumbnail stays as the old video. If you play the video, it will play the new one, but the thumbnail never changes.
Model Method that Uploads/Replaces
def vimeo_upload(self):
media_file = self.video_file
if media_file and os.path.exists(media_file.path):
v = vimeo.VimeoClient(token=settings.VIMEO_ACCESS_TOKEN, key=settings.VIMEO_API_KEY,
secret=settings.VIMEO_API_SECRET)
if self.video_url is None:
try:
video_uri = v.upload(media_file.path)
except AssertionError as exc:
logging.error('Vimeo Error: %s Video: %s' % (exc, media_file.path))
else:
self.video_url = video_uri
else:
try:
v.replace(video_uri=self.video_url, filename=media_file.path)
except Exception as exc:
self.video_url = None
logging.error('Vimeo Replace Error: %s Video: %s' % (exc, media_file.path))
# set the video title, description, etc.
if self.video_url:
try:
# convert locale from en-us form to en
v.patch(self.video_url, data={'description': self.customer.full_name, })
except Exception as exc:
logging.error('Vimeo Patch Error: %s Video: %s' % (exc, media_file.path))
Vimeo Client Model, and UploadVideoMixin
class UploadVideoMixin(object):
"""Handle uploading a new video to the Vimeo API."""
UPLOAD_ENDPOINT = '/me/videos'
REPLACE_ENDPOINT = '{video_uri}/files'
def upload(self, filename, upgrade_to_1080=False):
"""Upload the named file to Vimeo."""
ticket = self.post(
self.UPLOAD_ENDPOINT,
data={'type': 'streaming',
'upgrade_to_1080': 'true' if upgrade_to_1080 else 'false'},
params={'fields': 'upload_link,complete_uri'})
return self._perform_upload(filename, ticket)
def replace(self, video_uri, filename, upgrade_to_1080=False):
"""Replace the video at the given uri with the named source file."""
uri = self.REPLACE_ENDPOINT.format(video_uri=video_uri)
ticket = self.put(
uri,
data={'type': 'streaming',
'upgrade_to_1080': 'true' if upgrade_to_1080 else 'false'},
params={'fields': 'upload_link,complete_uri'})
return self._perform_upload(filename, ticket)
def _perform_upload(self, filename, ticket):
"""Take an upload ticket and perform the actual upload."""
if ticket.status_code != 201:
raise UploadTicketCreationFailure(ticket, "Failed to create an upload ticket")
ticket = ticket.json()
# Perform the actual upload.
target = ticket['upload_link']
last_byte = 0
# Try to get size of obj by path. If provided obj is not a file path
# find the size of file-like object.
try:
size = os.path.getsize(filename)
with io.open(filename, 'rb') as f:
while last_byte < size:
try:
self._make_pass(target, f, size, last_byte)
except requests.exceptions.Timeout:
# If there is a timeout here, we are okay with it, since
# we'll check and resume.
pass
last_byte = self._get_progress(target, size)
except TypeError:
size = len(filename.read())
f = filename
while last_byte < size:
try:
self._make_pass(target, f, size, last_byte)
except requests.exceptions.Timeout:
# If there is a timeout here, we are okay with it, since
# we'll check and resume.
pass
last_byte = self._get_progress(target, size)
# Perform the finalization and get the location.
finalized_resp = self.delete(ticket['complete_uri'])
if finalized_resp.status_code != 201:
raise VideoCreationFailure(finalized_resp, "Failed to create the video")
return finalized_resp.headers.get('Location', None)
def _get_progress(self, upload_target, filesize):
"""Test the completeness of the upload."""
progress_response = self.put(
upload_target,
headers={'Content-Range': 'bytes */*'})
range_recv = progress_response.headers.get('Range', None)
_, last_byte = range_recv.split('-')
return int(last_byte)
def _make_pass(self, upload_target, f, size, last_byte):
"""Make a pass at uploading.
This particular function may do many things. If this is a large upload
it may terminate without having completed the upload. This can also
occur if there are network issues or any other interruptions. These
can be recovered from by checking with the server to see how much it
has and resuming the connection.
"""
response = self.put(
upload_target,
timeout=None,
headers={
'Content-Length': str(size),
'Content-Range': 'bytes: %d-%d/%d' % (last_byte, size, size)
}, data=f)
if response.status_code != 200:
raise VideoUploadFailure(response, "Unexpected status code on upload")
class VimeoClient(ClientCredentialsMixin, AuthorizationCodeMixin, UploadMixin):
"""Client handle for the Vimeo API."""
API_ROOT = "https://api.vimeo.com"
HTTP_METHODS = set(('head', 'get', 'post', 'put', 'patch', 'options', 'delete'))
ACCEPT_HEADER = "application/vnd.vimeo.*;version=3.2"
USER_AGENT = "pyvimeo 0.3.10; (http://developer.vimeo.com/api/docs)"
def __init__(self, token=None, key=None, secret=None, *args, **kwargs):
"""Prep the handle with the authentication information."""
self.token = token
self.app_info = (key, secret)
self._requests_methods = dict()
# Make sure we have enough info to be useful.
assert token is not None or (key is not None and secret is not None)
# Internally we back this with an auth mechanism for Requests.
#property
def token(self):
return self._token.token
#token.setter
def token(self, value):
self._token = _BearerToken(value) if value else None
def __getattr__(self, name):
"""This is where we get the function for the verb that was just
requested.
From here we can apply the authentication information we have.
"""
if name not in self.HTTP_METHODS:
raise AttributeError("%r is not an HTTP method" % name)
# Get the Requests based function to use to preserve their defaults.
request_func = getattr(requests, name, None)
if request_func is None:
raise AttributeError(
"%r could not be found in the backing lib" % name
)
#wraps(request_func)
def caller(url, jsonify=True, **kwargs):
"""Hand off the call to Requests."""
headers = kwargs.get('headers', dict())
headers['Accept'] = self.ACCEPT_HEADER
headers['User-Agent'] = self.USER_AGENT
if jsonify \
and 'data' in kwargs \
and isinstance(kwargs['data'], (dict, list)):
kwargs['data'] = json.dumps(kwargs['data'])
headers['Content-Type'] = 'application/json'
kwargs['timeout'] = kwargs.get('timeout', (1, 30))
kwargs['auth'] = kwargs.get('auth', self._token)
kwargs['headers'] = headers
if not url[:4] == "http":
url = self.API_ROOT + url
response = request_func(url, **kwargs)
if response.status_code == 429:
raise APIRateLimitExceededFailure(
response, 'Too many API requests'
)
return response
return caller
class _BearerToken(requests.auth.AuthBase):
"""Model the bearer token and apply it to the request."""
def __init__(self, token):
self.token = token
def __call__(self, request):
request.headers['Authorization'] = 'Bearer ' + self.token
return request
I am trying to learn twisted library from its documentation. I've created a simple chat server that allows users to public chat. However, I wonder if I can add private chat functionality to that. I am looking for your ideas. Thanks.
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor
class Chat(LineReceiver):
def __init__(self,users):
self.users = users
self.name = None
self.state = "GETNAME"
def connectionMade(self):
self.sendLine("What's your name?")
def connectionLost(self, reason):
if self.users.has_key(self.name):
del self.users[self.name]
def lineReceived(self,line):
if self.state == "GETNAME":
self.handle_GETNAME(line)
else:
self.handle_CHAT(line)
def handle_GETNAME(self,name):
if self.users.has_key(name):
self.sendLine("Name taken, please choose another.")
return
self.sendLine("Welcome, %s!" % (name))
self.name = name
self.users[name] = self
self.state = "CHAT"
def handle_CHAT(self,msg):
msg = "<%s> %s" % (self.name,msg)
for name,protocol in self.users.iteritems():
if protocol != self:
protocol.sendLine(msg)
class ChatFactory(Factory):
def __init__(self):
self.users = {}
def buildProtocol(self, addr):
return Chat(self.users)
reactor.listenTCP(8123,ChatFactory()) ##UndefinedVariable
reactor.run() ##UndefinedVariable
Actually I figured it out by changing the for loop in handle_CHAT. As I want the program to send the message to the specific user, a "talkwith" string can be added to the class and this string can hold that specific user's name. Then this change turns the program from public to private chatting.
msg = "<%s> %s" % (self.name,msg)
for name,protocol in self.users.iteritems():
if name == self.talkwith:
protocol.sendLine(msg)