Sublime Forum

Unicode & sys.path

#1

Read this: sublimetext.com/blog/article … ith-python
and then: viewtopic.php?f=6&t=4979&start=0#p22442

0 Likes

#2

http://gmh.akalias.net/letter-f.gif

[code]

inspect.getfile

def getfile(object):
“”“Work out which source or compiled file an object was defined in.”""
if ismodule(object):
if hasattr(object, ‘file’):
return object.file
raise TypeError(’{!r} is a built-in module’.format(object))
if isclass(object):
object = sys.modules.get(object.module)
if hasattr(object, ‘file’):
return object.file
raise TypeError(’{!r} is a built-in class’.format(object))
if ismethod(object):
object = object.im_func
if isfunction(object):
object = object.func_code
if istraceback(object):
object = object.tb_frame
if isframe(object):
object = object.f_code
if iscode(object):
return object.co_filename
raise TypeError(’{!r} is not a module, class, method, ’
‘function, traceback, frame, or code object’.format(object))[/code]

sys.excepthook(type, value, traceback)

    This function prints out a given traceback and exception to sys.stderr.

    When an exception is raised and uncaught, the interpreter calls sys.excepthook with three arguments, the exception class, exception instance, and a traceback object. In an interactive session this happens just before control is returned to the prompt; in a Python program this happens just before the program exits. The handling of such top-level exceptions can be customized by assigning another three-argument function to sys.excepthook.
0 Likes

#3

0 Likes

#4

0 Likes

#5

I don’t… I don’t know what’s going on here…

0 Likes

#6

castles is having a moment…

@castles, you might want to start explaining what the hell you’re doing/talking about…

Also, you might want to get your head checked out :stuck_out_tongue:

0 Likes

#7

http://www.forumsextreme.com/images/Funny_Pictures_General_Thanks_Captain_Obvious.jpg

0 Likes

#8

I’m so confused

0 Likes

#9

0 Likes

#10

J.C. Redish’s law of useability performance states:

What the hell does that even mean?

People are lazy. Imagine you are working your arse off and in the middle of
something you think, Jeez, I wish I had a plugin, or could tweak an existing
plugin, to help me with this task.

Do you

  1. Reach for you handy command source teleportation plugin, make some tweaks
    to a plugin, smash out the work, quicker than it would have taken you
    otherwise and have an enhanced arsenal for the next time you face that
    situation?

  2. Just tough it out and do shit manually to Get Shit Done By the time
    you have navigated to the source (Which file was it in again? FFFF) you
    going to get distracted anyway right. Stop fucking around and nail it up
    and get the money.

How the hell does any of this relate to unicode in sys.path I imagine?
Everything is connected. A tiny detail can have big implications.

What is sys.path? It’s basically an array of directories which Python will
search, in turn, for modules. It can take relative (to the working directory) or
absolute paths.

If you’d read the ‘not all beer and sunshine’ post you’ll know that Sublime
employs some import chicanery to preemptively work around possible unicode
characters in sys.path which aren’t supported on windows. Before loading each
plugin, sublime makes sure to change the directory to the folder containing the
plugin module.

What are the implications of this? Normally it’s possible to take a Python
class/module and use introspection to determine the file it was declared in.
However, when you use relative paths on sys.path, namely ‘.’ as Sublime does,
what’s determined as a file for an object is a relative path.

If you been paying attention, you’ll recall that Sublime changes directory
before loading each and every plugin. Therefore, the rug has been swept out from
underneath all the relative paths and they float without anchor.

http://gmh.akalias.net/letter-f.gif

Let’s try and make sense of the above:

“todays (psychotic) episode was brought to you by the letter f (file_)”

You can see it’s a QuickPanel containing a bunch of event handlers, commands
grouped by command/event type (window|application|text|on_.*)

Upon quickpanel selection it jumps to the source of PackageControls
InstallPackageCommand

How does it do that? You guessed it. It uses the introspection capabilities
exposed in the std lib inspect module.

Suddenly the following code makes sense:

# `inspect.getfile`
def getfile(object):
    """Work out which source or compiled file an object was defined in."""
    if ismodule(object):
        if hasattr(object, '__file__'):
            return object.__file__
        raise TypeError('{!r} is a built-in module'.format(object))
    if isclass(object):
        object = sys.modules.get(object.__module__)
        if hasattr(object, '__file__'):
            return object.__file__
        raise TypeError('{!r} is a built-in class'.format(object))
    if ismethod(object):
        object = object.im_func
    if isfunction(object):
        object = object.func_code
    if istraceback(object):
        object = object.tb_frame
    if isframe(object):
        object = object.f_code
    if iscode(object):
        return object.co_filename
    raise TypeError('{!r} is not a module, class, method, '
                    'function, traceback, frame, or code object'.format(object))

Can you see the object.__file__ mentioned above? When a module is imported,
rooted from a relative path on sys.path, at least on windows, you’ll get a
relative path like say .\Package Control.py

That’s not really enough information to navigate to the source of a plugin is
it?

For the navigation to work in the image above a quick patch was applied to
sublime_plugin.py to monkeypatch imported modules with the __file__ attribute.

Ok, so if that’s all you need to do, why not just do that then?

Recall J.C. Redish’s, “Something is only useful if it’s readily useable”. Now
imagine you were working again, and you’d just successfully teleported to the
command to edit it and after some tinkering you got a half useful exception
message.

Traceback (most recent call last):
  File ".\sublime_plugin.py", line 345, in run_
    
  File ".\sublimezen.py", line 118, in wrapper
  File ".\sublimezenplugin.py", line 208, in run
  File ".\zencoding\__init__.py", line 75, in run_action
  File ".\zencoding\actions\basic.py", line 96, in match_pair
ZeroDivisionError: integer division or modulo by zero

You thought that monkeypatch would have taken care of it yeah?

>>> import sublimezenplugin
>>> sublimezenplugin.__file__
u'C:\\Users\\nick\\AppData\\Roaming\\Sublime Text 2\\Packages\\ZenCoding\\sublimezenplugin.py'

Hrmm, the file is being set but something is amiss. Remember
object.co_filename from above? Wonder if he’s the culprit?

Your coworker is getting ancy. Fuck.

Now if the paths on sys.path were absolute paths that traceback would contain
a lot more useful information. You could just copy paste the full paths to the
files. But, I mean, fuck that.

Newsflash, it’s 2012, and Python has had sys.excepthook for ages

sys.excepthook

excepthook(type, value, traceback)

This function prints out a given traceback and exception to sys.stderr.

When an exception is raised and uncaught, the interpreter calls sys.excepthook with three arguments, the exception class, exception instance, and a traceback object.

Now recall a relevant section in inspect.getfile and pay attention to traceback

    if istraceback(object):
        object = object.tb_frame
    if isframe(object):
        object = object.f_code
    if iscode(object):
        return object.co_filename

So what if you had an sys.excepthook handler that opened an output panel (think
Tools -> Build ) and allowed you to navigate the traceback with f4/shift+f4
bindings? What if it automatically navigated to the most recent call last?

So what the fuck is the Count trying to say? And where does he pull these
numbers from anyway? I’d guess he values numbers over intuition. Hes’ called the
Count after all

Bert is saying in the remote case that someone actually IS on windows (roughly
half of sublime users are on OSX) and does have unicode in their sys.path and
DOES have short path names disabled they can always use the portable
installation.

He let it go without saying that if you can manage JSON for configuration then
you’ll be likely be comfortable using a portable installation.

Now, that’s something only tangentially related. If you look at @wbonds
community package index you’ll see that there’s currently at least 188
packages available.

http://www.forumsextreme.com/images/Funny_Pictures_General_Thanks_Captain_Obvious.jpg

Captain obvious would probably say “It’s only early days.” How many packages
will there be at the end of the year?

You could point out that namespacing was invented for a reason.

>>> import this
The Zen of Python, by Tim Peters

...

Namespaces are one honking great idea -- let's do more of those!
0 Likes

#11

@castles:
I could see why you decided not to explain all that in the beginning. It makes so much sense from just looking at sesame street characters. :stuck_out_tongue:

0 Likes

#12

haha But seriously, shit’s so much easier when it’s easier :smile:

0 Likes

#13

0 Likes

#14

From what I can tell you added code to zencoding that reports statistics about users and their sublime.packages_path() to see if it is feasible for Jon to change the package system to use python packages instead of importing files from the current directory.

0 Likes

#15

Yep, inspired by Package Control, I wonder what it itself would have to say.

[code]import sublime

WINDOWS = sublime.platform() == ‘windows’
if WINDOWS: from ctypes import windll, create_unicode_buffer

def importable_path(unicode_file_name):
try:
if WINDOWS: unicode_file_name.encode(‘ascii’)
return unicode_file_name
except UnicodeEncodeError:
buf = create_unicode_buffer(512)
return( buf.value if (
windll.kernel32
.GetShortPathNameW(unicode_file_name, buf, len(buf)) )
else False )
[/code]

0 Likes