?

Log in

No account? Create an account
Using Python and XMLRPC to post to LJ - LiveJournal Client Discussions — LiveJournal [entries|archive|friends|userinfo]
LiveJournal Client Discussions

[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

Using Python and XMLRPC to post to LJ [May. 1st, 2006|04:05 am]
LiveJournal Client Discussions

lj_clients

[fallen_x_ashes]
[mood |curiouscurious]

I'm VERY new at this. (Both to using XMLRPC and Python, infact I wouldn't say I really know what I'm doing, merely coding from imitation and some basic knowledge of the general syntax of most programming languages.) Basically I started doing this when it seemed like the Blogger API had been removed from Livejournal. I couldn't make a post using it. So, I set about altering my application to use the LiveJournal XMLRPC. It's a very quick and dirty hack, I'm passing passwords in the clear essentially. I'm VERY close, it works for LJ.XMLRPC.login, but when I try LJ.XMLRPC.postevent I get the following error message:

XML-RPC Fault: ServerCan't use string ("1") as a HASH ref while "strict refs" in use at /home/lj/cgi-bin/ljprotocol.pl line 2527, [anglebracket]GEN2[anglebracket] line 41.

I added the [anglebracket] thing there. The line number following Gen2 changes with each try, so I'm not sure if it really matters. Here's the Code for the function responsible for creating the post:


def newPostLiveJournal(logger, audioFilePath, cgiAudioFilePath, AuthMethod, LineEndings,
xmlrpcServer, blogID, userName, password, publish, useNewMediaObject, nmoDestPath):
"""Adds a new post to a LiveJournal"""

logger.write('In newPostLiveJournal\n')
logger.flush()
postID = {}
postID['itemid'] = -1
LJEvent = {}
LJEvent['username'] = userName
LJEvent['auth_method'] = AuthMethod
LJEvent['password'] = password
LJEvent['event'] = '

This post was created with PhoneBlogger. ' \
+ 'Click to listen to the ' \
+ 'recorded message.

'
LJEvent['lineendings'] = LineEndings
LJEvent['subject'] = 'Post from PhoneBlogger'
LJEvent['year'] = time.localtime()[0]
LJEvent['mon'] = time.localtime()[1]
LJEvent['day'] = time.localtime()[2]
LJEvent['hour'] = time.localtime()[3]
LJEvent['min'] = time.localtime()[4]
logger.write('Set LJEvent\n')
logger.flush()
try:
postID = xmlrpcServer.LJ.XMLRPC.postevent(LJEvent and xmlrpclib.True or xmlrpclib.False)
except xmlrpclib.Fault, fault:
logger.write('XML-RPC Fault: ' + fault.faultCode + fault.faultString + '\n')
logger.flush()
return postID['itemid']


Does anyone have any ideas as to what could be wrong, or how I can go about debugging this?
linkReply

Comments:
[User Picture]From: fallen_x_ashes
2006-05-02 04:16 am (UTC)

And I have no idea what I'm doing there either.

That conditional section is actually something I mimiced from a function specifically meant to work with Movable Type. I have never seen such a structure for conditional statements before, as I'm used to operators ala C. In fact I think that you may have caught the reason for a bug in the program: Wordpress drafts are always posted even if the publish variable is set to 0. The originol line of coding is here:

postID = xmlrpcServer.metaWeblog.newPost(blogID, userName, password, content, publish and xmlrpclib.True or xmlrpclib.False)


The author of this software was himself new to python when he wrote it, so it's possible that he didn't realize what was going on when he wrote it. It does appear that you're right, this seems to evaluate to Y rather than X.

I actually don't even really follow the logic. How would this look like as an explicit if statement? It'd be really helpful if you could show me that.

Anyway, since it really didn't seem like that conditional statement was meant to do something other then work with the publish variable, and since it was absent from the function that posts to Blogger, I removed it from the code completely. And it works! Almost. The Post goes through but there's a runtime error that occurs shortly after it.

Now it seems I'm having a problem returning the itemid. I think that I may be taking for granted the way the struct gets passed back to me. Is the standard xmlrpc library in python good enough to work with it? I really don't have much of a clue as to why the error could be occuring other then perhaps itemid turns out to be undefined. As soon as I try to touch it anywhere in the code, such as printing out its value, the script terminates.

Here's the code as it looks like so far, as I've made a few changes, (I apologize in advance for any line-wrapping problems, they will hopefully be minimal):

def newPostLiveJournal(logger, audioFilePath, cgiAudioFilePath, AuthMethod, LineEndings, xmlrpcServer, blogID, userName, password, publish, useNewMediaObject, nmoDestPath):
"""Adds a new post to a LiveJournal"""

logger.write('In newPostLiveJournal\n')
logger.flush()
LJResponse = {}
LJEvent = {}
LJEvent['username'] = userName
LJEvent['auth_method'] = AuthMethod
LJEvent['password'] = password
LJEvent['event'] = '

This post was created with PhoneBlogger. ' + 'Click to listen to the ' + 'recorded message.

'
LJEvent['lineendings'] = LineEndings
LJEvent['subject'] = 'Post from PhoneBlogger'
LJEvent['year'] = time.localtime()[0]
LJEvent['mon'] = time.localtime()[1]
LJEvent['day'] = time.localtime()[2]
LJEvent['hour'] = time.localtime()[3]
LJEvent['min'] = time.localtime()[4]
logger.write('Set LJEvent\n')
logger.flush()
try:
LJResponse = xmlrpcServer.LJ.XMLRPC.postevent(LJEvent)
logger.write('Set LJResponse\n')
except xmlrpclib.Fault, fault:
logger.write('XML-RPC Fault: ' + fault.faultCode + fault.faultString + '\n')
logger.flush()
itemid = LJResponse['itemid']
logger.write(itemid)
logger.write('\n')
logger.write('Now Returning\n')
logger.flush()
return itemid
(Reply) (Parent) (Thread)
[User Picture]From: fallen_x_ashes
2006-05-02 04:22 am (UTC)

Re: And I have no idea what I'm doing there either.

Argh! The indentation has been stripped! Rest assured that this stuff is being indented as it should, with the first two lines after the try: and except: lines indented.

The problem occurs on logger.write(itemid), the script fails right there.
(Reply) (Parent) (Thread)
[User Picture]From: vulture23
2006-05-02 04:56 am (UTC)

Re: And I have no idea what I'm doing there either.

The move into html (as it gets posted to LJ) will likely eat any leading spaces. This is a common issue when posting Python code. :)

How that would look as an explicit if statement kinda depends on what the intention is. "X and Y or Z" is a well-known Python hack that's intended to emulate C's ternary operator (x ? y : z , if I'm remembering my C syntax correctly). However, it has some gotchas that C doesn't have, and it's really not obvious how it works until the trick has been pointed out to you -- and nonobvious code is dangerous code, because you *will* make mistakes with it sooner or later.

Here, if LJEvent is not an empty dict, then "LJEvent and xmlrpclib.True" evaluates to true, and takes on the value of the second operand. If LJEvent *is* empty, then the 'and' is false and evaluates to LJEvent, at which point "LJEvent or xmlrpclib.False" is evaluated. If we're here, LJEvent is false, so the 'or' evaluates to the second operand. Thus, in any case, "X and Y or Z" will evaluate to either Y or Z, but never X. Your code has this expression as the argument for postevent(), meaning that you will always submit either True or False as an argument, but never LJEvent. I can't guess what was intended by the original author of this code, but... in general this construct is better rendered as:

if X:
....return Y
else:
....return Z

(or using assignment or function call or whatever instead of return.)

Not sure where you're having problems with the itemid -- my LJ xmlrpclib code looks pretty similar at that point, and I haven't had any problems. When it terminates, are you getting a stack trace? If so, copy & paste the whole thing here; there's a lot of useful info in those stack traces.
(Reply) (Parent) (Thread)