> I do not know if this helps you in this issue - or - distracts you from your current work on improving the key handling of Leo. - I'm sending you this traceback in the hope that it helps ...
> File "D:\Branches\leo-editor\leo\core\leoRst.py", line 1749, in
> write_files
> f.write(s)
> File "C:\Python32\lib\encodings\cp1252.py", line 19, in encode
> return codecs.charmap_encode(input,self.errors,encoding_table)[0]
> UnicodeEncodeError: 'charmap' codec can't encode character '\u0142' in
> position
> 19816: character maps to <undefined>
Thanks for this report. This is a "garden variety" unicode error,
having nothing to do with Leo's key handling.
Edward
At last, some real unit tests are complete. I'll be pushing to the trunk soon.
This should fix bug 879331: Redefining a key binding breaks menu items
with same binding
I have spent the last hour or so trying to understand if
k.registerCommand might cause problems when overriding a shortcut. I
can't say for sure either way. This question will be much easier to
answer with better key-related classes. In any event, any bug in this
area is much less serious than 879331.
> The new test may have uncovered another key-related buglet.
I don't think there is a problem here, at present. This kind of
hash-related bug should disappear when better key-related classes are
in place.
> Next, I'll ensure that the key bug Terry recently mentioned has been stamped out...
This bug relates to settings, but not 879331.
Edward
> Btw, a simple change now shows *all* bindings for command in each menu
> item. I think this is always a good idea, but it can be easily
> reverted if people have objections...
Is there a way, which has perhaps existed for years ;-), to see the
binding for a command in the mini-buffer?
Oops, I see auto complete in the mini-buffer fills that role, e.g. to
see binding for fill-paragraph just enter 'Alt-X fill-par<tab>'
Cheers -Terry
>> Next, I'll ensure that the key bug Terry recently mentioned has been stamped out...
>
> This bug relates to settings, but not 879331.
I think I see what is causing problems with settings that disappear
depending on exactly how the .leo files are described.
Some of the configuration code uses c.hash() to associate settings
with commanders. It's bad code, but we are stuck with it But c.hash
probably doesn't work for all descriptions.
Terry, you might try messing with c.hash to see if changing it makes a
difference. In particular, you might trace the result returned in the
various scenarios that have been giving you troubles.
The new key code still doesn't work as I would like. The problem is
that settings are read *twice* for local files, and it's not so easy
to associate values read in the first pass with the values read in the
second pass. In other words, c.config is different in the two passes
because c is different.
These kinds of bizarre considerations lead me to c.hash...
Extreme patience is required for this work. I have added some
important comments to methods like get and getShortcutHelper that
indicate what the various (badly-named) dictionaries and lists come
from and how they are used. So now I understand the code about as well
as it *can* be understood :-)
Looking back on the code, I can see that it arose as the result of 30+
years of procedure-oriented C coding. Theoretically, refactoring into
new classes would help, but that's way too risky to do in one bite.
So I'm stuck with making smallish changes for now...
Edward
> Terry, you might try messing with c.hash to see if changing it makes a
> difference. In particular, you might trace the result returned in the
> various scenarios that have been giving you troubles.
cd /home/tbrown/Package/leo/bzr/leo.repo/trunk
python launchLeo.py ~/.leo/del.leo
@settings in del.leo ignored, c.hash() = "/home/tbrown/.leo/del.leo"
cd /home/tbrown/.leo
python /home/tbrown/Package/leo/bzr/leo.repo/trunk/launchLeo.py del.leo
@settings in del.leo honored, c.hash() = "/mnt/usr1/usr1/home/tbrown/.leo/del.leo"
So, I can see why Leo uses the path to the Leo file to identify its
settings, but at the same time @setting in a particular file should be
honored.
The problem is that os.getcwd() and friends use the POSIX C getcwd()
function (whatever it's called), and POSIX says return the absolute
path.
os.path.abspath(path) === os.path.normpath(join(os.getcwd(), path))
Where to fix it? I'd prefer it use /home/tbrown so settings follow me
around more, at home /home/tbrown may be /mnt/usr1/usr1/home/tbrown,
whereas at work /home/tbrown may be /media/raid2TB/home/tbrown.
So, c.hash() could operate on os.path.abspath(path), simplest, and
probably fixes the ignored @settings problem.
Or c.hash() could be calculated from
os.path.normpath(join(os.getenv('PWD'), path))
At least it explains what's going on.
Cheers -Terry
>> Terry, you might try messing with c.hash to see if changing it makes a
>> difference. In particular, you might trace the result returned in the
>> various scenarios that have been giving you troubles.
>
> cd /home/tbrown/Package/leo/bzr/leo.repo/trunk
> python launchLeo.py ~/.leo/del.leo
>
> @settings in del.leo ignored, c.hash() = "/home/tbrown/.leo/del.leo"
>
> cd /home/tbrown/.leo
> python /home/tbrown/Package/leo/bzr/leo.repo/trunk/launchLeo.py del.leo
>
> @settings in del.leo honored, c.hash() = "/mnt/usr1/usr1/home/tbrown/.leo/del.leo"
Thanks for this, and the rest of your post. Now that you have
confirmed the source of the problem, I'll see if there might not be a
way of associating settings with files that can avoid these
path-related issues entirely.
Edward
> Thanks for this, and the rest of your post. Now that you have
> confirmed the source of the problem, I'll see if there might not be a
> way of associating settings with files that can avoid these
> path-related issues entirely.
That would be good. id(c) might work? Docs. below. Or maybe, to
avoid the (probably miniscule) risk of a c with the same id as a
previously existing c, self.id = id(self)+int(time.time())
c.db uses a hash of the path, but that seems more unavoidable, given
that it's persistent, anyway that's not related to @settings.
Cheers -Terry
id(object)
Return the “identity” of an object. This is an integer (or long
integer) which is guaranteed to be unique and constant for this
object during its lifetime. Two objects with non-overlapping
lifetimes may have the same id() value.
CPython implementation detail: This is the address of the object in
memory.
> At last, some real unit tests are complete. I'll be pushing to the trunk soon.
Work on this project has entered a high-energy phase. There has been
an important collapse in complexity, and other opportunities for
simplification and improvement have appeared.
The ShortcutInfo class has been a spectacular success. Canonicalizing
settings in that class's ctor ensures some important code invariants.
Canonicalizing settings in the ctor appears to have created a buglet
or two. That's actually *good* news: it suggests an important new
invariant: namely that canonicalizing an already-canonicalized
shortcut should be a no-op. That is, for any valid setting S, we
should have:
shortcutFromSetting(S) == shortcutFromSetting(shortcutFromSetting(S))
This constraint probably fails at present for some small number of
settings S. Ensuring the constraint is valid for all S will not only
fix the buglet, but will make Leo's core much more robust. A unit
test enforcing this invariant will be straightforward, and may uncover
some bugs in what is in fact extremely picky code.
Today or tomorrow I *will* push what I have to the trunk. Slight
problems may arise, but that can't be helped: all the present unit
tests work.
After the first push, I'll create a ShortcutsDict class to encapsulate
the "raw" dicts that describe shortcuts. This will eliminate the
wretched _hash hack. Also, the ShortcutsDict and ShortcutInfo classes
will be the natural home to many of the key-related methods that now
clutter leoKeys.py. For example, I deliberately did not specify what
class will ultimately contain shortcutFromSetting. There are some
tricky issues involved, but there is no need to go into details here.
Edward
> Canonicalizing settings in the ctor appears to have created a buglet or two.
The fix was to properly init a new dict.
> it suggests an important new invariant: namely that canonicalizing an already-canonicalized shortcut should be a no-op.
Nice try, Edward. This would be a nice invariant, but it's not going
to happen any time soon. Canonicalized settings were not designed
with this invariant in mind. For example, shortcutFromSetting
translates Shift-a to A, and A to a. Oops.
Yes, it would be possible to change this *convention*, but that would
instantly have ripple effects everywhere. It probably will never
happen. In the meantime, the code must take care to canonicalize
settings in exactly the right places.
With the buglet fixed and the with no hope of making
shortcutFromSetting safer, it looks like time to push the code. I
plan to do this later tonight, after using the code myself for a bit
longer.
Edward
>> > it suggests an important new invariant: namely that canonicalizing an already-canonicalized shortcut should be a no-op.
> On second thought, this should actually be fairly easy to do safely.
This invariant is important because it is the doorway towards another
collapse in complexity. The important thing is that internally almost
everything uses the strokes created by shortcutFromSetting. It
doesn't much matter what convention is used--what matters is that all
the code knows that strokes *are* strokes.
This convention will have several important consequences:
1. I have just discovered that the very strange k.tkbindingFromStroke
is not needed. That is, everything works if k.tkbindingFromStroke is
a do-nothing. That's not too surprising, given that Tk is long gone,
but it's good to know. I'll remove all traces of this beast after
some more testing...
2. Having strokes, and *only* strokes, be the result of the Qt
key-input methods should clarify the code considerably. That's good,
because at present the code is a horror show.
Edward
> I'm not sure whether adding time.time() to a hash will make things
> better or worse. Let me think about what problem the hash is *really*
> attempting to solve. That may suggest a way forward.
I have spent several pleasant hours researching the situation, mostly
using the clone-find-all command.
As expected, c.hash() is used *only* to create a "weak" link between a
commander C created during a the prepass of a local file and *another*
commander C2 created *later* during the second and final read of the
local file.
Happily, there is no need for a weak link: it is possible to create a
"strong" link to the existing commander (or c.config). The code is
kludgy: g.app.config.updateSettings can set
self.copied_local_options_dict, and the c.config ctor called later can
copy g.app.config.copied_local_options_dict into its own ivar.
Actually, things are a bit more complicated than that, because all the
methods are really g.app.config methods rather than c.config methods,
but the idea is the same.
In short, there is no need for c.hash: it is possible, though not
pretty, to remember the dicts created during the pre-load of a local
file until such time as the permanent c.config object is created.
I'll be committing the code tomorrow, after I clean it up and test it further.
Edward
On Sun, Jan 29, 2012 at 1:44 PM, Terry Brown <terry_...@yahoo.com> wrote:
> On Sun, 29 Jan 2012 04:57:25 -0600
> cd /home/tbrown/Package/leo/bzr/leo.repo/trunk
> python launchLeo.py ~/.leo/del.leo
>
> @settings in del.leo ignored, c.hash() = "/home/tbrown/.leo/del.leo"
>
> cd /home/tbrown/.leo
> python /home/tbrown/Package/leo/bzr/leo.repo/trunk/launchLeo.py del.leo
>
> @settings in del.leo honored, c.hash() = "/mnt/usr1/usr1/home/tbrown/.leo/del.leo"
>
> So, I can see why Leo uses the path to the Leo file to identify its
> settings, but at the same time @setting in a particular file should be
> honored.
>
> The problem is that os.getcwd() and friends use the POSIX C getcwd()
> function (whatever it's called), and POSIX says return the absolute
> path.
> os.path.abspath(path) === os.path.normpath(join(os.getcwd(), path))
>
> Where to fix it? I'd prefer it use /home/tbrown so settings follow me
> around more, at home /home/tbrown may be /mnt/usr1/usr1/home/tbrown,
> whereas at work /home/tbrown may be /media/raid2TB/home/tbrown.
>
> So, c.hash() could operate on os.path.abspath(path), simplest, and
> probably fixes the ignored @settings problem.
>
> Or c.hash() could be calculated from
> os.path.normpath(join(os.getenv('PWD'), path))
>
> http://stackoverflow.com/questions/1542803/is-there-a-version-of-os-getcwd-that-doesnt-dereference-symlinks
>
> At least it explains what's going on.
Thanks for sharing these details. I encountered the same issue. I
hadn't realized I was using a path with a symlink in it until your
email. Now I know to use the workaround of specifying a non-symlinked
path on the leo command-line. Before I had found I could just revert
the file and get the settings to take effect. This new workaround
saves me a step of having to revert as soon as I open the file.
Brian
I encountered the same issue. I hadn't realized I was using a path with a symlink in it until your email.