leo ftp

75 views
Skip to first unread message

Ivanov Dmitriy

unread,
Oct 17, 2010, 4:51:06 PM10/17/10
to leo-editor
As a web programmer I have a local copy of files and the same files,
stored on server. So I made a button, that allows to upload the files
to server from leo:

from ftplib import FTP
ftp = FTP('host.ru') # connect to host, default port
ftp.login('user', 'pass')

files = [
['d:/www/dubai/modules/file1.pm', 'host.ru/cgi-bin/modules/file1.pm'],
['d:/www/dubai/modules/file2.pm', 'host.ru/cgi-bin/modules/file2.pm'],
]

for file in files:
FH = open(file[0],"rb")
ftp.storbinary('STOR ' + file[1], FH)
FH.close()
ftp.quit()
g.es("Upload complete")

------------------------------------------------------------------------
There is one enhancement, that I don't know how to do myself: I don't
what to upload the files, that weren't changed since the last upload,
so I need some kind of DB table, that will have this structure:

| local file name | server file name | last modify date time |

If last modify has changed, then upload. Now I have this simple DB as
a matrix in code - files array. What is the best way to implement this
feature in python inside leo? I don't think, that Mysql or Oracle
would be good choice for this. :)))

Terry Brown

unread,
Oct 17, 2010, 4:57:19 PM10/17/10
to leo-e...@googlegroups.com
On Sun, 17 Oct 2010 13:51:06 -0700 (PDT)
Ivanov Dmitriy <usr...@gmail.com> wrote:

> There is one enhancement, that I don't know how to do myself: I don't
> what to upload the files, that weren't changed since the last upload,
> so I need some kind of DB table, that will have this structure:
>
> | local file name | server file name | last modify date time |
>
> If last modify has changed, then upload. Now I have this simple DB as
> a matrix in code - files array. What is the best way to implement this
> feature in python inside leo? I don't think, that Mysql or Oracle
> would be good choice for this. :)))

I'd probably use sqlite, which comes with python since 2.5. Otherwise if there's not too many files a pickled dictionary would work, but sqlite is probably easier.

Cheers -Terry

zpcspm

unread,
Oct 17, 2010, 5:54:40 PM10/17/10
to leo-editor
On Oct 17, 11:51 pm, Ivanov Dmitriy <usr...@gmail.com> wrote:
> There is one enhancement, that I don't know how to do myself: I don't
> what to upload the files, that weren't changed since the last upload,
> so I need some kind of DB table, that will have this structure:
>
> | local file name | server file name | last modify date time |
>
> If last modify has changed, then upload. Now I have this simple DB as
> a matrix in code - files array. What is the best way to implement this
> feature in python inside leo? I don't think, that Mysql or Oracle
> would be good choice for this. :)))

Are you sure you need a relational database? Don't mix databases with
persistence, because that's what you need. I would use a database only
if I would have a couple of tables, a couple of relations and using
SQL would make it much easier.

In your case, I would just use a dictionary with file names as keys
and would store it using JSON. Recent versions of Python bundle JSON
parsing modules by default and converting between JSON and simple
Python data structures like list/dictionaries is the easiest possible
thing.

Ivanov Dmitriy

unread,
Oct 18, 2010, 5:09:16 AM10/18/10
to leo-editor
I decided to store the DB in an external file, called files.txt in
JSON format.

import json
import os
from ftplib import FTP

ftp = FTP('host.ru') # connect to host, default port
ftp.login('user', 'pass')

for file in files:
n = len(file)
if n < 3:
file.append(-1)


time = os.path.getmtime(file[0])
if time != file[2]:
file[2] = time
FH = open(file[0],"rb")
ftp.storbinary('STOR ' + file[1], FH)
FH.close()

ftp.quit()
FH = open('./files.txt', 'w')
json.dump(files, FH)
FH.close()
g.es("Upload complete")

--------------------------------------------------------------------------
I wrote the module in leo - does anybody know, how to attach the file
here? And is it possible to store files.txt contents inside a leo
node, so I won't need to have a separate file with each leo file?
--------------------------------------------------------------------------
files.txt:

[
['d:/www/dubai/modules/file1.pm', 'host.ru/cgi-bin/modules/file1.pm',
1287345652.84375],
['d:/www/dubai/modules/file2.pm', 'host.ru/cgi-bin/modules/file2.pm',
1287392182.886375],
]

Terry Brown

unread,
Oct 18, 2010, 9:13:25 PM10/18/10
to leo-e...@googlegroups.com
On Mon, 18 Oct 2010 02:09:16 -0700 (PDT)
Ivanov Dmitriy <usr...@gmail.com> wrote:

> I decided to store the DB in an external file, called files.txt in
> JSON format.
>

[code snipped]

Couldn't see where in the code you read the info. in, so it seemed you were just overwriting the info. each time - was that the intent?

Given a Leo node `nd` you can read and write JSON to it with:

files = json.loads(nd.b)

and

nd.b = json.dumps(files)

(loads and dumps are the *s*tring versions of their file counterparts)

Minor picky thing, built in open() returns a python file object, not a operating system file-handle, so calling it 'FH' potentially confusing. os.open() returns file handles, I think, but you probably need python file objects or at least something with a .write() method, so your code's correct but the name's odd.

Cheers -Terry

Ivanov Dmitriy

unread,
Nov 19, 2010, 2:12:26 AM11/19/10
to leo-editor
I decided to create a leo ftp plugin and need help. Please, someone
experienced in leo programming, read my pdf with scheme and questions:

http://live-wtr.ru/ftp.pdf

Terry Brown

unread,
Nov 19, 2010, 10:11:40 AM11/19/10
to leo-e...@googlegroups.com

So the goal is to make Leo log in to an ftp site and upload the derived files in the outline which have changed to that site?

Personally I use the linux command line tool `sitecopy` for this sort of thing.

But if you want to make Leo do it, there's a few considerations.

Does it need to be a plugin, or would a @button node be sufficient? It would probably be simpler to get it working in a @button node first, anyway.

I don't know I'd store the config data in JSON, although I guess it wouldn't look so bad if you used the pretty print mode to save it back into the body text.

You probably want a c.save() in there somewhere to make sure the derived files on disk are current.

And of course you need to scan the outline to fine a list of all the derived files, that's probably not too hard, although off the top of my head I'm not sure what the best way to ID them without listing the directive names (file, auto, shadow, edit) yourself is.

So really the fiddly stuff is in the plugin / button creation part, which can be avoided with a @button node. But there are plenty of plugins that do that sort of thing, which you could use as examples. The nav_qt plugin (on by default) is one example, although maybe not the best one, as it adds icon buttons, not regular text buttons.

Also, onCreate's only called one on a commander, so you shouldn't need to worry about the button already being created. I think following examples in other plugins will cover most of what you need.

Cheers -Terry

Ivanov Dmitriy

unread,
Dec 9, 2010, 11:39:23 AM12/9/10
to leo-editor
"But there are plenty of plugins that do that sort of thing, which you
could use as examples. The nav_qt plugin (on by default) is one
example, although maybe not the best one, as it adds icon buttons, not
regular text buttons."

Sorry, I looked for and didn't find. For example we have a leo file:

* node 1
* node 2
* @data ftp
here we have AJAX text, that must be parsed
* node 3

Can you tell me, what functions to use to find the node "@data ftp"
and read it's contents?

"It would probably be simpler to get it working in a @button node
first, anyway."

I made it work with @button node and wanna make it more generic.

Ivanov Dmitriy

unread,
Dec 10, 2010, 2:55:07 PM12/10/10
to leo-editor
Ville, can you tell me those 2 functions: for searching of the node
and getting it's value. I am stuck with the development because of
that. And you were very helpful during the maximize window plugin
fixing.

Terry Brown

unread,
Dec 10, 2010, 3:11:33 PM12/10/10
to leo-e...@googlegroups.com
On Thu, 9 Dec 2010 08:39:23 -0800 (PST)
Ivanov Dmitriy <usr...@gmail.com> wrote:

> * @data ftp
> here we have AJAX text, that must be parsed
> * node 3
>
> Can you tell me, what functions to use to find the node "@data ftp"
> and read it's contents?
>
> "It would probably be simpler to get it working in a @button node
> first, anyway."
>
> I made it work with @button node and wanna make it more generic.

p = g.findTopLevelNode(c, '@data ftp')

body content of p is p.b

there's also g.findNodeAnywhere(c, '@data ftp')

Cheers -Terry

Ville M. Vainio

unread,
Dec 11, 2010, 2:12:10 AM12/11/10
to leo-e...@googlegroups.com
On Fri, Dec 10, 2010 at 8:55 PM, Ivanov Dmitriy <usr...@gmail.com> wrote:

> Ville, can you tell me those 2 functions: for searching of the node
> and getting it's value. I am stuck with the development because of

c.find_h
c.find_b

You can find examples in leo source code.

--
Ville M. Vainio @@ Forum Nokia

Ivanov Dmitriy

unread,
Dec 11, 2010, 10:30:33 PM12/11/10
to leo-editor
The script button works fine. But when I tried to create a plugin - it
gave an error:

File "C:\Python26\Lib\site-packages\Leo-4.7.1-final\leo\plugins
\ftp.py", line 55, in __init__
    g.app.gui.makeScriptButton(c,script=script,buttonText='Upload')
File "C:\Python26\Lib\site-packages\Leo-4.7.1-final\leo\plugins
\qtGui.py", line 7053, in makeScriptButton   b.configure(command=executeScriptCallback)

the upload function is not executed. Also it creates 2 upload buttons
instead of 1. Maybe, it's because I switched to Qt from Tk UI?

I created a scheme and and a leo file with code:

http://live-wtr.ru/python.zip

Please, tell me, how to proceed?

Ivanov Dmitriy

unread,
Dec 13, 2010, 3:58:58 PM12/13/10
to leo-editor
Ville, sorry, I can't fix it myself - the code, used as an example for
plugins gives an error:

c.k.registerCommand('upload',shortcut=None,func=self.upload)
script = "c.k.simulateCommand('upload')"
g.app.gui.makeScriptButton(c,script=script,buttonText='Upload') <---
here it breaks, saying
"AttributeError: 'leoIconBarButton' object has no attribute 'configure'"


Can you tell me, how to create a button on the top panel inside a
plugin and attach a code to it?

Ville M. Vainio

unread,
Dec 13, 2010, 4:18:55 PM12/13/10
to leo-e...@googlegroups.com
On Mon, Dec 13, 2010 at 9:58 PM, Ivanov Dmitriy <usr...@gmail.com> wrote:

> Can you tell me, how to create a button on the top panel inside a
> plugin and attach a code to it?

Take a look at makeButtons function in nav_qt.py

If you don't want icons but text, instead of:

QtGui.QAction(icon_l, 'prev', ib_w)

do:

QtGui.QAction('prev', ib_w)

I'm pasting the whole function here for reference

def makeButtons(self):
ib_w = self.c.frame.iconBar.w
if not ib_w: return # EKR: can be None when unit testing.
icon_l = ib_w.style().standardIcon(QtGui.QStyle.SP_ArrowLeft)
icon_r = ib_w.style().standardIcon(QtGui.QStyle.SP_ArrowRight)
act_l = QtGui.QAction(icon_l, 'prev', ib_w)
act_r = QtGui.QAction(icon_r, 'next', ib_w)
act_l.connect(act_l, QtCore.SIGNAL("triggered()"), self.clickPrev)
act_r.connect(act_r, QtCore.SIGNAL("triggered()"), self.clickNext)
self.c.frame.iconBar.add(qaction = act_l, command = self.clickPrev)
self.c.frame.iconBar.add(qaction = act_r, command = self.clickNext)

self.clickPrev and clickNext are the function that are run, e.g:

def clickPrev(self):
c = self.c
p = c.goPrevVisitedNode()

Ivanov Dmitriy

unread,
Dec 14, 2010, 2:50:41 PM12/14/10
to leo-editor
Now it works. But I have 3 minor problems:

1. The first time I load leo 2 buttons are created instead of 1:

http://live-wtr.ru/1.png

2. When I click 1 button upload function is called twice. It means,
that onCreate function is called twice for some reason.

3. When I open another instance of leo with node "@data ftp" another 2
buttons are created.

Here is the code:

def init ():
leoPlugins.registerHandler('after-create-leo-frame',onCreate)
return True


def onCreate (tag, keys):
c = keys.get('c')
if not c: return
@
here I want to check, whether the node @data ftp exists in the file,
that is being opened. If it exists, create a button and register
@c
p = g.findTopLevelNode(c, '@data ftp')
if p != None:
controller = pluginController(c)


class pluginController:
def __init__ (self,c):
self.c = c

ib_w = self.c.frame.iconBar.w
action = QtGui.QAction('Upload', ib_w)
action.connect(action, QtCore.SIGNAL("triggered()"), self.upload)
self.c.frame.iconBar.add(qaction = action, command = self.upload)

Edward K. Ream

unread,
Dec 20, 2010, 10:53:16 AM12/20/10
to leo-e...@googlegroups.com
On Fri, Nov 19, 2010 at 10:11 AM, Terry Brown <terry_...@yahoo.com> wrote:
> On Thu, 18 Nov 2010 23:12:26 -0800 (PST)
> Ivanov Dmitriy <usr...@gmail.com> wrote:
>
>> I decided to create a leo ftp plugin and need help. Please, someone
>> experienced in leo programming, read my pdf with scheme and questions:
>>
>> http://live-wtr.ru/ftp.pdf
>
> So the goal is to make Leo log in to an ftp site and upload the derived files in the outline which have changed to that site?
>
> Personally I use the linux command line tool `sitecopy` for this sort of thing.

Let's see if we can simplify the big picture.

It should be easy to simulate sitecopy in Leo. Just put an upload
date in a uA associated with each @<file> node. The script will
compare upload time to modification time, and upload as needed.

There should be no need to create an @button (or @command) node
programmatically. Just make the node and put the script in it. The
next time you reload the .leo file Leo will create the button for you.

HTH.

Edward

Ivanov Dmitriy

unread,
Dec 21, 2010, 8:30:35 AM12/21/10
to leo-editor
At last I finished the first version of the plugin. But 1 problem is
left: function upload is called twice.

In processed leo file I have a node '@data ftp', that contains the
following info:

[ main array
[array of credentials],
[file_1 to upload],
[file_2 to upload],
...
[file_n to upload]
]

array of credentials is an array of arrays, containing the servers, to
which I will upload the files:

[
['ftp.host1.ru', 'login1', 'pass1'],
['ftp.host2.ru', 'login2', 'pass2'],
...
['ftp.hostn.ru', 'loginn', 'passn'],
]

[file to upload]:

[
"path to file on the local machine",
"path to file on server",
timestamp of last modification (optional)
]

If the time of last modification is missing - the file will be
uploaded at the last time set.

Example:
[[["ftp.host1.ru", "login1", "pass1"]], ["d:/www/dubai/cgi-bin/
file1.pl", "/dubai.host1.ru/cgi-bin/file1.pl", 1292457567.78125], ["d:/
www/dubai/cgi-bin/file2.pl", "/dubai.host1.ru/cgi-bin/file2.pl",
1292457567.78125]]

Ville or Edward, can you look at the code and tell me, what's wrong
with the function - why it's called twice? You can get the files at

http://live-wtr.ru/python.zip

Ivanov Dmitriy

unread,
Jan 5, 2011, 3:37:20 PM1/5/11
to leo-editor
I found the bug myself :D. In __init__ function I had the code:

ib_w = self.c.frame.iconBar.w
action = QtGui.QAction('Upload', ib_w)
action.connect(action, QtCore.SIGNAL("triggered()"),
self.upload) #<--- this is the reason, why function self.upload was
called twice. I removed it.
self.c.frame.iconBar.add(qaction = action, command = self.upload)

So now I have a minimally working plugin and ask Ville to test and,
maybe, add it to leo plugins. test.leo contains my vision, how it
should work: it contains '@data ftp' node with format, described in my
previous post, and 2 files.

Plugin is here:

http://live-wtr.ru/python.zip

Ivanov Dmitriy

unread,
Feb 9, 2011, 3:29:22 PM2/9/11
to leo-editor
I added multitheading to plugin. Now leo doesn't freeze, when
uploading the files. Edward or Ville can you merge my new version into
leo plugins:

bzr branch lp:~usr345/leo-editor/ftp-plugin


thx!

Ville M. Vainio

unread,
Feb 9, 2011, 3:34:45 PM2/9/11
to leo-e...@googlegroups.com

I can't do proper merge for this (I would have to copy paste the
files, which loses your personal version history).

You need to "bzr branch lp:leo-editor", add your changes to that tree,
and push these changes to your own branch.

Ivanov Dmitriy

unread,
Feb 10, 2011, 2:59:36 AM2/10/11
to leo-editor
It's ok if you copy and paste. The history is kept in my branch.

Ivanov Dmitriy

unread,
Feb 19, 2011, 12:36:45 AM2/19/11
to leo-editor
Hello. I want to improve the plugin by allowing the user to use
multiple servers. The input data will be stored in the subnodes of
node '@data ftp' - one subnode for each server. I have a function,
that takes the node JSON content and uploads the file. So I just need
to apply it for each subnode.

The desired structure is shown on the screenshot:

http://www.live-wtr.ru/structure.png

Now I use this code to find the node

p = g.findTopLevelNode(c, '@data ftp')

How can I find all the subnodes of this node?

Ivanov Dmitriy

unread,
Feb 21, 2011, 5:14:21 PM2/21/11
to leo-editor
Ville, can you tell me, how to get the subnodes of a leo node in
python? I thought, that you know.

Terry Brown

unread,
Feb 21, 2011, 5:21:36 PM2/21/11
to leo-e...@googlegroups.com

Try: http://webpages.charter.net/edreamleo/scripting.html#iterators

you probably want p.children()

Cheers -Terry

Edward K. Ream

unread,
Feb 22, 2011, 7:48:36 AM2/22/11
to leo-e...@googlegroups.com
On Mon, Feb 21, 2011 at 4:21 PM, Terry Brown <terry_...@yahoo.com> wrote:

>> Ville, can you tell me, how to get the subnodes of a leo node in
>> python? I thought, that you know.
>
> Try: http://webpages.charter.net/edreamleo/scripting.html#iterators
>
> you probably want p.children()

Or p.subtree() or p.self_and_subtree()

EKR

Ivanov Dmitriy

unread,
Feb 22, 2011, 2:10:15 PM2/22/11
to leo-editor
Terry and Edward thanks for help!

Ivanov Dmitriy

unread,
Feb 22, 2011, 7:55:31 PM2/22/11
to leo-editor
Don't know, why, but sometimes the plugin drops leo on the line:

node.b = json.dumps(files)

I repeted the same sequence and it worked well.

Can somebody tell me, how to effectively program leo plugins, so I
won't need to restart leo after I modify the code, so it would load
the new plugin again? And how to debug?

Ivanov Dmitriy

unread,
Feb 22, 2011, 9:13:35 PM2/22/11
to leo-editor
Hiya. I updated the plugin. Please, someone update it in leo main
branch:

bzr branch lp:~usr345/leo-editor/ftp-plugin

Also I created a video tutorial, describing, how to use the plugin:

http://www.youtube.com/watch?v=bnj0NQuljQo&hd=1

Terry Brown

unread,
Feb 22, 2011, 10:43:36 PM2/22/11
to leo-e...@googlegroups.com
On Tue, 22 Feb 2011 16:55:31 -0800 (PST)
Ivanov Dmitriy <usr...@gmail.com> wrote:

> Can somebody tell me, how to effectively program leo plugins, so I
> won't need to restart leo after I modify the code, so it would load
> the new plugin again?

You can usually get all the functionality sorted out in @button nodes,
the

exec(g.findTestScript(c,'@common my common code'))

pattern can be used to let different buttons use the same core code.
Then, when everything works, you can do the conversion to a plugin.

In some cases using pythons reload command might be helpful, but I
suspect the @button route is simpler.

> And how to debug?

I just stick print statements into the code :-) Of course that assumes
quickly executing the code is easy, but that's the case using the
@button route.

Cheers -Terry

Ivanov Dmitriy

unread,
Feb 22, 2011, 4:03:21 PM2/22/11
to leo-editor
Can anybody tell me, how to easily debug leo plugins? It's painful to
open/close leo file, so leo would reload the plugins code and how can
I use a debugger? Also I couldn't create a script button, cause I use
functions, that could be used only inside a plugin:

def upload (self,event=None):
c = self.c ; p = c.p

g.es("upload started")
p = g.findTopLevelNode(c, '@data ftp')

c wasn't defined and it broke the code in script button.

Ivanov Dmitriy

unread,
Feb 23, 2011, 2:17:53 AM2/23/11
to leo-editor
The previous message was written directly after thanks, but was added
much later.

Edward K. Ream

unread,
Feb 23, 2011, 10:12:55 AM2/23/11
to leo-e...@googlegroups.com
On Tue, Feb 22, 2011 at 6:55 PM, Ivanov Dmitriy <usr...@gmail.com> wrote:
> Don't know, why, but sometimes the plugin drops leo on the line:
>
> node.b = json.dumps(files)
>
> I repeated the same sequence and it worked well.

>
> Can somebody tell me, how to effectively program leo plugins, so I
> won't need to restart leo after I modify the code, so it would load
> the new plugin again?

I test Leo in a separate copy so that I don't have to reload Leo. See the FAQ:

http://webpages.charter.net/edreamleo/FAQ.html#how-can-i-use-leo-to-develop-leo-itself

> And how to debug?

I use two main methods:

1. Insert calls to g.trace to see what is happening. This is
extremely easy to do with Python.

For complex code, I use variations on the following pattern::

trace = False and not g.unitTesting # change False to True to enable
verbose = False # Optional, for complex traces.

if trace: g.trace(<<list of variables to trace, just like print>>)

if trace and verbose: g.trace(<< more complex traces>>)

Leo's source code has many examples of this pattern.

2. Use g.pdb() a wrapper for::

import pdb ; pdb.set_trace()

pdb is your friend for mysterious bugs. They don't happen often with Python.

Edward

Ivanov Dmitriy

unread,
Feb 26, 2011, 5:46:48 AM2/26/11
to leo-editor
After some testing I found, that leo breaks on line:

g.es(files[i][0])

In console I get a message:

QWidget::repaint: Recursive repaint detected

In test module I tried to comment all g.es and the error disappeared.

Maybe, that's because QWidget is not thread safe and I use threads?

How can I output messages to the user?

Ville M. Vainio

unread,
Feb 26, 2011, 6:31:41 AM2/26/11
to leo-e...@googlegroups.com, Ivanov Dmitriy
On Sat, Feb 26, 2011 at 11:46 AM, Ivanov Dmitriy <usr...@gmail.com> wrote:

> Maybe, that's because QWidget is not thread safe and I use threads?

Yeah, using threads risks crashing the whole program randomly. In
general threads are not recommended for GUI programs.

> How can I output messages to the user?

Try to avoid threads in the first place.

If that is not possible, Leo could grow an output function that is
thread safe (g.es_thread?). It would only use the ui when running on
main thread.

Making the default g.es wouldn't make sense because it would be somewhat slower.

Ivanov Dmitriy

unread,
Feb 26, 2011, 7:02:52 AM2/26/11
to leo-editor
The reason for using threads is that leo freezes when I upload big
files.

> If that is not possible, Leo could grow an output function that is
> thread safe (g.es_thread?). It would only use the ui when running on
> main thread.

Yes, it would be great. But would the user see the messages in log
pane when plugin is running in a separate thread? Or they will be
output to console in this case?

Terry Brown

unread,
Feb 26, 2011, 9:26:09 AM2/26/11
to leo-e...@googlegroups.com
On Sat, 26 Feb 2011 12:31:41 +0100
"Ville M. Vainio" <viva...@gmail.com> wrote:

> > Maybe, that's because QWidget is not thread safe and I use threads?
>
> Yeah, using threads risks crashing the whole program randomly. In
> general threads are not recommended for GUI programs.

The geotag plugin which lets you interactively geotag / "geoview" nodes
in a google map browser page uses threads and doesn't crash, although
this may only be because it hasn't been tested that much. But I seem
to remember needing to use some quite specialized thread aware queues to
avoid blocking etc., you might look at that.

Come to think of it it probably doesn't do any drawing in leo outside
the main thread, but maybe its architecture will give you a hint about
how you could also avoid that.

Whoops, now I see it does block when you request the user to locate
something, which is ok for it's workflow, but nonetheless it has some
esoteric (to me) thread queue thing in it which might be interesting.

Cheers -Terry

Ivanov Dmitriy

unread,
Feb 26, 2011, 5:59:31 PM2/26/11
to leo-editor
I googled about the problem and found this question:

http://stackoverflow.com/questions/3842558/pyqt-and-threads

and this:

http://diotavelli.net/PyQtWiki/Threading,_Signals_and_Slots

http://doc.qt.nokia.com/4.6/threads-qobject.html

Now I start the work in new thread, using 1 call:

def run(self):
thread.start_new_thread(self.upload, ())

seems, that I'll have to use QThread module instead. If someone here
knows about the organization of leo.core.leoGlobals es function,
please, tell me, how to send the signal, emulating this function call
from QThread. Plus, if I use QThread, ftp module won't work for Tk :(.

Can we create a thread safe function for all the graphic libs?

Edward K. Ream

unread,
Feb 27, 2011, 1:14:04 PM2/27/11
to leo-e...@googlegroups.com
On Sat, Feb 26, 2011 at 4:59 PM, Ivanov Dmitriy <usr...@gmail.com> wrote:

> Can we create a thread safe function for all the graphic libs?

Don't even think about it.

Edward

Ville M. Vainio

unread,
Feb 27, 2011, 2:40:40 PM2/27/11
to leo-e...@googlegroups.com, Edward K. Ream

It could work. The implementation would just be different for Tk and Qt.

In Qt, you would queue the messages in a list when running in a
thread, and use QTimer.singleShot to dump the message to the UI. This
would make all ui manipulation happen in main loop.

Ivanov Dmitriy

unread,
Feb 27, 2011, 5:51:05 PM2/27/11
to leo-editor
> Don't even think about it.
>
> Edward

I will think, and even try to do.

:P

Dmitriy

--------------------------
In Qt, you would queue the messages in a list when running in a
thread, and use QTimer.singleShot to dump the message to the UI. This
would make all ui manipulation happen in main loop.
--------------------------

Ville, do you know the internal structure of g.es function? And how to
add the messages to Qt queue for log pane? If you have time I am eager
to help. Thought, I don't know the internals as well as you do.

Ivanov Dmitriy

unread,
Mar 16, 2011, 6:43:18 PM3/16/11
to leo-editor
I updated the plugin, using logging module, that is thread safe. Also
added the check for FTP errors while connecting. Please, add the new
version to leo.

bzr branch lp:~usr345/leo-editor/ftp-plugin

Also how to get the path to current opened leo file? As now I create
the log in the same folder with launchLeo.py and want to create it in
the local file folder.

Ivanov Dmitriy

unread,
Mar 17, 2011, 9:20:49 AM3/17/11
to leo-editor
Can someone tell me, how to get the full path to the currently opened
leo file, so I could save the log in the save directory?

zhaohe wang

unread,
Jul 10, 2017, 12:33:04 AM7/10/17
to leo-editor, usr...@gmail.com
ValueError: No JSON object could be decoded
Would you teach me how to use this plugin?

Traceback (most recent call last):
  File "/home/swot/leo-editor/leo/plugins/qt_frame.py", line 2332, in button_callback
    val = command()
  File "/home/swot/leo-editor/leo/plugins/ftp.py", line 62, in upload
    files = json.loads(p.b)
  File "/usr/lib/python2.7/json/__init__.py", line 339, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python2.7/json/decoder.py", line 364, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python2.7/json/decoder.py", line 382, in raw_decode
    raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded


在 2011年3月17日星期四 UTC+8上午6:43:18,Ivanov Dmitriy写道:

Edward K. Ream

unread,
Jul 10, 2017, 10:14:12 AM7/10/17
to leo-editor, Ivanov Dmitriy
On Sun, Jul 9, 2017 at 11:33 PM, zhaohe wang <wangz...@gmail.com> wrote:
ValueError: No JSON object could be decoded
Would you teach me how to use this plugin?

​I'll see what I can do today.  It looks like this plugin needs some work.

Edward

Edward K. Ream

unread,
Jul 10, 2017, 3:31:21 PM7/10/17
to Ivanov Dmitry, leo-editor
On Mon, Jul 10, 2017 at 12:01 PM, Ivanov Dmitry <usr...@gmail.com> wrote:
> ​I'll see what I can do today.  It looks like this plugin needs some work.

As far as I remember it needs ftp credentials written in JSON format.
Maybe, user did something wrong with JSON format? I'll look as well.

​I'm working on clarifying the code, but the real need is a proper docstring, telling especially what the json should contain, with examples.

Edward

Edward K. Ream

unread,
Jul 10, 2017, 4:05:36 PM7/10/17
to leo-editor, usr...@gmail.com
On Monday, July 10, 2017 at 2:31:21 PM UTC-5, Edward K. Ream wrote:

> the real need is a proper docstring

json is not python it's much pickier:

- Comments not allowed!
- Single quotes not allowed!
- Backspaces not allowed!
- Commas at the end of lists not allowed.

json.loads does accept this, in the body text of an @data ftp node:

[
    ["user-name", "host-name", "password"],
    ["C:/leo.repo/leo-editor/README.MD"],
    ["C:/leo.repo/leo-editor/INSTALL.TXT"]
]

As shown, the first sub-list describes the server.  All other sub-lists describe files to be uploaded.  The present code requires items of the form:

    [path, time1]

or

    [path, time1, time2]

Imo, a bare path should also be allowed:

   [path]

The idea is that the file will be written unless its modification time matches time time2, with time2 defaulting to -1.  Perhaps should be changed.

That's what I have so far.

Edward

Edward K. Ream

unread,
Jul 10, 2017, 6:25:59 PM7/10/17
to leo-editor, Ivanov Dmitriy
On Mon, Jul 10, 2017 at 3:05 PM, Edward K. Ream <edre...@gmail.com> wrote:

​I have created ftp2.py by rewriting almost every line of ftp.py. At present, it's only in the ftp branch.

Warning: Imo, neither ftp.py nor ftp2.py is safe to use.

The json provides no way to associate paths on the server with paths in the file system. This limitation exists with the original ftp.py plugin as well. Imo, this makes the plugins almost useless.

In ftp2.py, the expected format of the json is:​

[
​  ​
["user-name", "host-name", "password"],
​  ​
["
​filename1"],
​  ​
["
​filename2"]
]

​This must change somehow so that (at least) a common prefix can be stripped from the file-system paths to yield a server path.

For example, there must be some way to strip away
c:\leo.repo\leo-editor
from the paths on the server.

At present, the ftp2 code uses a hack: it just uses the base name of the original file.  This will work only for top-level files!

Improvements

​ftp2 carefully and clearly reports all errors.  ftp does not.

ftp2 (probably) uses a different, flatter json representation.  This is easier to use, but a bit less flexible.

ftp2 updates the @data ftp node with timestamp data.  ftp also does this, but ftp2 pretty-prints the results.​

Coming:

1. (Essential) Change the json representation as discussed above.

2. (Essential) A real docstring.

3. (Important) The ftp2 plugin will use an ftp-upload command rather than an icon.

Edward

lewis

unread,
Jul 10, 2017, 7:58:25 PM7/10/17
to leo-editor, usr...@gmail.com
There is also the sftp.py plugin written by Jacob Peck. It requires the python module 'paramiko' SSH2 protocol library to be installed.

Edward K. Ream

unread,
Jul 11, 2017, 5:38:57 AM7/11/17
to leo-editor, usr...@gmail.com
On Monday, July 10, 2017 at 5:25:59 PM UTC-5, Edward K. Ream wrote:

> I have created ftp2.py by rewriting almost every line of ftp.py. At present, it's only in the ftp branch.

Dimitriy, I am going to delegate this project to you.  I had not intended to spend a whole day on it. Otoh, I've learned a lot about json and ftp. Please feel free to use any code in ftp2 in ftp.py.  I would prefer that there be only a single version of this plugin.

Imo, it's ok for you to work directly in master.  I used a separate branch only to ensure that my work would not collide with yours.

Edward
Reply all
Reply to author
Forward
0 new messages