leo import issue

3 views
Skip to first unread message

zpcspm

unread,
Jun 2, 2010, 8:01:24 AM6/2/10
to leo-editor
I'm trying to import a python source code file into leo via "Main menu-
>File->Import->Import To @file".

For a minimal example consider that the file contains this code:

--- cut here ---
class MyClass:

def method1(self):

pass

m1 = method1()

def method2(self):

pass

m2 = method2()

--- cut here ---

Please try importing this and see what happens.
The "class MyClass" node has the following body:
--- cut here ---
class MyClass:
@others
m2 = method2()

--- cut here ---

and two child nodes (one for each method).
They look like this:

--- cut here ---
head:
method1

body:

def method1(self):

pass

--- cut here ---
head:
method2

body:
m1 = method1()

def method2(self):

pass

--- cut here ---

As you can see, the line "m1 = method1()" landed into the second
child.
I want it to land into the first one, because it's related to it.
The line "m2 = method2()" landed into the body of the class node.
I want it to land into the body of the second method, because it's
related to it.

Now, after importing the python code, I change the @file directive to
@shadow.
I can make the needed changes manually to achieve my goal.
The changes will stay persistent (to leo restarts) as long as there
will be "shadows" in .leo_shadow directories.
As soon as these directories disappear (for any reason: they get
deleted, the source code tree is replicated in another place without
them), the @shadow tree gets refreshed automatically, and I loose the
correct layout again.
I would be happy to change the things manually after import, assuming
there's a way to achieve persistency without depending on the content
of .leo_shadow directories (if that is possible somehow), so I would
need to do the manual changes only once.

I'm confident that I can't use @thin, because the source code files
need to be modified outside leo by other people, thus they must not
contain sentinels.

Terry Brown

unread,
Jun 2, 2010, 10:31:18 AM6/2/10
to leo-e...@googlegroups.com
On Wed, 2 Jun 2010 05:01:24 -0700 (PDT)
zpcspm <zpc...@gmail.com> wrote:

> As you can see, the line "m1 = method1()" landed into the second
> child.

I think that leo really only sees the def and class blocks, and just punts on where to put the stuff that comes between them. A @decorator should obviously go with the thing that follows it. But for comments and statements there's no way for leo to know what makes sense.

class t(object):

def m1(self):
pass

m = m1

def m2(self, func=m):
print func

I know that's a weird piece of code, but I think you could argue the m = m1 belongs with the m2 definition in that case.

Cheers -Terry

zpcspm

unread,
Jun 2, 2010, 11:09:16 AM6/2/10
to leo-editor
On Jun 2, 5:31 pm, Terry Brown <terry_n_br...@yahoo.com> wrote:
> class t(object):
>
>     def m1(self):
>         pass
>
>     m = m1
>
>     def m2(self, func=m):
>         print func
>
> I know that's a weird piece of code, but I think you could argue the m = m1 belongs with the m2 definition in that case.

Your example is more complicated than mine, but it's a good addition
to the topic. It shows a particular use case when python-specific
stuff provides specific meaning for the code.

After looking at your example, I'm making an attempt to simplify mine.

Let's consider the following code (not necessary pythonic, but it's
important that the language shall allow variable assignements inside
the class body):

--- cut here ---
class MyClass:

def method1(self):
pass

foo = bar

def method1(self):
pass
--- cut here ---

Here are the the possible options for the leo parser that I see:

- stick to the default assumptions that it uses now (I'm not happy
with them, and I don't like the idea of modifying some code base just
for the sake of fitting into the leo import algorithms)
- put the code blocks that have the same indentation as class methods
(but are not class methods themselves) in their own subnodes of the
class node
- use an option set by the user to decide if it needs to include the
"code" part in the "method-code-method" scenario into the first node
body or into the second one, this shall also include the special case
"method-code" at the end of the class body

Your comments are welcome.

Terry Brown

unread,
Jun 2, 2010, 4:31:59 PM6/2/10
to leo-e...@googlegroups.com
On Wed, 2 Jun 2010 08:09:16 -0700 (PDT)
zpcspm <zpc...@gmail.com> wrote:

> A) stick to the default assumptions that it uses now (I'm not happy


> with them, and I don't like the idea of modifying some code base just
> for the sake of fitting into the leo import algorithms)

> B) put the code blocks that have the same indentation as class methods


> (but are not class methods themselves) in their own subnodes of the
> class node

> C) use an option set by the user to decide if it needs to include the


> "code" part in the "method-code-method" scenario into the first node
> body or into the second one, this shall also include the special case
> "method-code" at the end of the class body
>
> Your comments are welcome.

Not sure how (C) would work, seems like code could belong to the preceding of following def randomly throughout a class def. The same issue comes up at the module level of course.

I like (B), it seems to me that putting code into def nodes (with the exception of @decorators) can lead to a lot of confusion, as the code may have some adverse impact and you might have trouble finding it. Comments are harmless of course, but you still might miss an important comment for function X if the comment gets shoved into function Y's node.

So,

class foo(object):

"""class to do some stuff"""

stream_base = sys.stdout

def m_int(self, x):
pass

mdef = m_int

# minty doesn't do anything

def minty(self):
pass

raise SystemExit

would be cut like this

class foo(object):

"""class to do some stuff"""
--------------------------------------
stream_base = sys.stdout
--------------------------------------
def m_int(self, x):
pass
--------------------------------------
mdef = m_int

# minty doesn't do anything
--------------------------------------
def minty(self):
pass
--------------------------------------
raise SystemExit

producing nodes like this

foo
foo code
m_int
foo code
minty
foo code

the two bad cuts in this case are the node containing only 'stream_base = sys.stdout', which might have been better left in the foo node (containing the class name line and docstring), and the separation of the comment about minty from the def of minty. For the stream_base case... perhaps the class node should contain everything up to the first def (or nested class)? For the minty case, dumb place to put the comment, no way to tell whether it applies to the preceding or following def.

Cheers -Terry


zpcspm

unread,
Jun 2, 2010, 5:01:07 PM6/2/10
to leo-editor
On Jun 2, 11:31 pm, Terry Brown <terry_n_br...@yahoo.com> wrote:
>Comments are harmless of course, but you still might miss an important comment for function X if the comment gets shoved into function Y's node.

>For the minty case, dumb place to put the comment, no way to tell whether it applies to the preceding or following def.

Fair criticism towards minty, but I think that chances are reasonably
low for such situation to happen in practice. The weirdness of minty
comment is emphasized by the lack of docstring in the minty() method.
Knowing that pylint is being a pest about missing docstrings, I tend
to think that this is a highly motivating factor for the programmer to
remove that comment and stick it into a method docstring :-)

Now that you stated that you like (B), with your moral support I dare
to make an assumption why it looks good and makes sense. I didn't see
that when writing my previous post.

There are differences between (B) and (C):
- (B) is lower level than (C) - it would provide more smaller pieces
and let me organize them as I wish. After all, I could "join" two
neighbor nodes by simply using a container (not sure this is the
standard term for a node that has empty body and is used to store a
set of subnodes).
- (B) can be used as base for (C), or for any other variation that
implies some strategy of organizing neighbor nodes. In fact (B) can be
used as a base for (A) too

Edward K. Ream

unread,
Jul 29, 2010, 12:21:38 PM7/29/10
to leo-e...@googlegroups.com
On Wed, Jun 2, 2010 at 7:01 AM, zpcspm <zpc...@gmail.com> wrote:
> I'm trying to import a python source code file into leo via "Main menu-
>>File->Import->Import To @file".

[snip]

> As you can see, the line "m1 = method1()" landed into the second
> child.
> I want it to land into the first one, because it's related to it.

I've wanted the same thing for a long time. For example, here is a
common pattern:

def foo():
pass

Foo = Foo

> I can make the needed changes manually to achieve my goal.
> The changes will stay persistent (to leo restarts) as long as there
> will be "shadows" in .leo_shadow directories.
> As soon as these directories disappear (for any reason: they get
> deleted, the source code tree is replicated in another place without
> them), the @shadow tree gets refreshed automatically, and I loose the
> correct layout again.

Right. This is an import problem, and will reappear any time an import happens.

Please create a wish-list bug for this. There almost certainly will
be some complications, but we must start somewhere.

Edward

zpcspm

unread,
Jul 29, 2010, 2:22:51 PM7/29/10
to leo-editor
On Jul 29, 7:21 pm, "Edward K. Ream" <edream...@gmail.com> wrote:
> Please create a wish-list bug for this.  There almost certainly will
> be some complications, but we must start somewhere.

Done

https://bugs.launchpad.net/leo-editor/+bug/611413

I made the bug report extremely laconic and neutral, feel free to
improve the content.
The discussion in this thread happened some time ago, yet I remember
that there was another thread aiming at the same issue. I added
references to both threads in the bug report, so the ideas that
appeared there wouldn't get lost.
Reply all
Reply to author
Forward
0 new messages