Add Python 3.3 support

Support for unicode literals was reintroduced in Python 3.3 which makes
supporting both Python 2 and Python 3 much easier, so this will be the
minimum supported Python 3 version.

Closes #8.
This commit is contained in:
Kim Silkebækken 2013-01-21 20:42:57 +01:00
parent 77f66cbbc9
commit bee427eb42
15 changed files with 91 additions and 58 deletions

View File

@ -4,7 +4,7 @@ Overview
Requirements Requirements
------------ ------------
Powerline requires Python 2.7 to work. Powerline requires Python 3.3 or Python 2.7 to work.
Powerline uses several special glyphs to get the arrow effect and some Powerline uses several special glyphs to get the arrow effect and some
custom symbols for developers. This requires that you either have the symbol custom symbols for developers. This requires that you either have the symbol
@ -14,13 +14,14 @@ details.
Vim plugin requirements Vim plugin requirements
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
The vim plugin requires a vim version with Python 2.7 support compiled in. The vim plugin requires a vim version with Python support compiled in. You
You can check if your vim supports Python 2 by running ``vim --version can check if your vim supports Python by running ``vim --version | grep
| grep +python``. +python``.
If your vim version doesn't have support for Python 2, you'll have to If your vim version doesn't have support for Python, you'll have to compile
compile it with the ``--enable-pythoninterp`` flag (this also requires the it with the ``--enable-python3interp`` flag (``--enable-pythoninterp`` if
Python headers to be installed on your system). Please consult your you want Python 2 support instead). Note that this also requires the related
Python headers to be installed on your system. Please consult your
distribution's documentation for details on how to compile and install distribution's documentation for details on how to compile and install
packages. packages.
@ -48,9 +49,6 @@ To install Powerline system-wide, run the following command as root::
If you don't have root access or don't want to install Powerline If you don't have root access or don't want to install Powerline
system-wide, install with ``pip install --user`` instead. system-wide, install with ``pip install --user`` instead.
.. note:: Make sure that you install the package for Python 2. For distros
like Arch Linux you'll have to run ``pip2`` instead of ``pip``.
.. note:: This project is currently unavailable on the PyPI due to a naming .. note:: This project is currently unavailable on the PyPI due to a naming
conflict with an unrelated project. conflict with an unrelated project.

View File

@ -1,11 +1,13 @@
if ! has('python') if ! has('python') && ! has('python3')
echohl ErrorMsg echohl ErrorMsg
echomsg 'You need vim compiled with Python 2 support for Powerline to work. Please consult the documentation for more details.' echomsg 'You need vim compiled with Python 3.3 or Python 2.7 support for Powerline to work. Please consult the documentation for more details.'
echohl None echohl None
finish finish
endif endif
python import sys, vim let s:pycmd = has('python3') ? 'python3' : 'python'
python sys.path.append(vim.eval('expand("<sfile>:h:h:h:h:h")'))
exec s:pycmd ' import sys, vim'
exec s:pycmd ' sys.path.append(vim.eval(''expand("<sfile>:h:h:h:h:h")''))'
source <sfile>:h:h/powerline.vim source <sfile>:h:h/powerline.vim

View File

@ -1,13 +1,17 @@
python import uuid let s:pycmd = has('python3') ? 'python3' : 'python'
python from powerline.core import Powerline
python powerline = Powerline('vim')
if exists('*pyeval') exec s:pycmd ' import uuid'
exec s:pycmd ' from powerline.core import Powerline'
exec s:pycmd ' powerline = Powerline("vim")'
if exists('*py3eval')
let s:pyeval = function('py3eval')
elseif exists('*pyeval')
let s:pyeval = function('pyeval') let s:pyeval = function('pyeval')
else else
python import json, vim exec s:pycmd ' import json, vim'
function! s:pyeval(e) function! s:pyeval(e)
python vim.command('return ' + json.dumps(eval(vim.eval('a:e')))) exec s:pycmd ' vim.command("return " + json.dumps(eval(vim.eval("a:e"))))'
endfunction endfunction
endif endif

View File

@ -5,8 +5,8 @@ import json
import os import os
import sys import sys
from colorscheme import Colorscheme from powerline.colorscheme import Colorscheme
from matcher import Matcher from powerline.matcher import Matcher
from powerline.lib import underscore_to_camelcase from powerline.lib import underscore_to_camelcase
@ -37,7 +37,7 @@ class Powerline(object):
'common_config': self.config, 'common_config': self.config,
} }
local_themes = {} local_themes = {}
for key, local_theme_name in self.config_ext.get('local_themes', {}).iteritems(): for key, local_theme_name in self.config_ext.get('local_themes', {}).items():
key = self.get_matcher(key) key = self.get_matcher(key)
local_themes[key] = {'config': self._load_theme_config(ext, local_theme_name)} local_themes[key] = {'config': self._load_theme_config(ext, local_theme_name)}
@ -74,6 +74,6 @@ class Powerline(object):
for path in self.search_paths: for path in self.search_paths:
config_file_path = os.path.join(path, config_file) config_file_path = os.path.join(path, config_file)
if os.path.isfile(config_file_path): if os.path.isfile(config_file_path):
with open(config_file_path, 'rb') as config_file_fp: with open(config_file_path, 'r') as config_file_fp:
return json.load(config_file_fp) return json.load(config_file_fp)
raise IOError('Config file not found in search path: {0}'.format(config_file)) raise IOError('Config file not found in search path: {0}'.format(config_file))

View File

@ -1,5 +1,5 @@
from memoize import memoize # NOQA from powerline.lib.memoize import memoize # NOQA
from humanize_bytes import humanize_bytes # NOQA from powerline.lib.humanize_bytes import humanize_bytes # NOQA
def underscore_to_camelcase(string): def underscore_to_camelcase(string):

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from math import log from math import log
unit_list = zip(['', 'k', 'M', 'G', 'T', 'P'], [0, 0, 1, 2, 2, 2]) unit_list = tuple(zip(['', 'k', 'M', 'G', 'T', 'P'], [0, 0, 1, 2, 2, 2]))
def humanize_bytes(num, suffix='B', binary_prefix=False): def humanize_bytes(num, suffix='B', binary_prefix=False):

View File

@ -1,6 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import cPickle as pickle try:
import cPickle as pickle
except ImportError:
import pickle
import functools import functools
import os import os
import tempfile import tempfile

View File

@ -93,7 +93,7 @@ except ImportError:
p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, cwd=cwd) p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, cwd=cwd)
p.stderr.close() p.stderr.close()
for line in p.stdout: for line in p.stdout:
yield line[:-1] yield line[:-1].decode('utf-8')
class Repository(object): class Repository(object):
__slots__ = ('directory',) __slots__ = ('directory',)
@ -107,10 +107,10 @@ except ImportError:
def status(self, path=None): def status(self, path=None):
if path: if path:
try: try:
return self._gitcmd('status', '--porcelain', '--', path).next()[:2] return next(self._gitcmd('status', '--porcelain', '--', path))[:2]
except StopIteration: except StopIteration:
try: try:
self._gitcmd('ls-files', '--ignored', '--exclude-standard', '--others', '--', path).next() next(self._gitcmd('ls-files', '--ignored', '--exclude-standard', '--others', '--', path))
return '!!' return '!!'
except StopIteration: except StopIteration:
return None return None

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from colorscheme import Colorscheme from powerline.colorscheme import Colorscheme
from theme import Theme from powerline.theme import Theme
class Renderer(object): class Renderer(object):
@ -20,7 +20,7 @@ class Renderer(object):
self.local_themes[matcher] = theme self.local_themes[matcher] = theme
def get_theme(self): def get_theme(self):
for matcher in self.local_themes.iterkeys(): for matcher in self.local_themes.keys():
if matcher(): if matcher():
match = self.local_themes[matcher] match = self.local_themes[matcher]
if 'config' in match: if 'config' in match:

View File

@ -18,6 +18,31 @@ weather_conditions_codes = {
} }
def _urllib_read(url):
try:
import urllib.error
import urllib.request
try:
return urllib.request.urlopen(url).read().decode('utf-8')
except urllib.error.HTTPError:
return
except ImportError:
import urllib2
try:
return urllib2.urlopen(url).read()
except urllib2.HTTPError:
return
def _urllib_urlencode(string):
try:
import urllib.parse
return urllib.parse.urlencode(string)
except ImportError:
import urllib
return urllib.urlencode(string)
def hostname(): def hostname():
import socket import socket
if not os.environ.get('SSH_CLIENT'): if not os.environ.get('SSH_CLIENT'):
@ -44,7 +69,10 @@ def branch():
def cwd(dir_shorten_len=None, dir_limit_depth=None): def cwd(dir_shorten_len=None, dir_limit_depth=None):
import re import re
cwd = os.getcwdu() try:
cwd = os.getcwdu()
except AttributeError:
cwd = os.getcwd()
home = os.environ.get('HOME') home = os.environ.get('HOME')
if home: if home:
cwd = re.sub('^' + re.escape(home), '~', cwd, 1) cwd = re.sub('^' + re.escape(home), '~', cwd, 1)
@ -65,11 +93,7 @@ def date(format='%Y-%m-%d'):
@memoize(600, persistent=True) @memoize(600, persistent=True)
def external_ip(query_url='http://ipv4.icanhazip.com/'): def external_ip(query_url='http://ipv4.icanhazip.com/'):
import urllib2 return _urllib_read(query_url).strip()
try:
return urllib2.urlopen(query_url).read().strip()
except urllib2.HTTPError:
return
def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'): def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'):
@ -92,12 +116,10 @@ def uptime(format='{days:02d}d {hours:02d}h {minutes:02d}m'):
@memoize(1800, persistent=True) @memoize(1800, persistent=True)
def weather(unit='c', location_query=None): def weather(unit='c', location_query=None):
import json import json
import urllib
import urllib2
if not location_query: if not location_query:
try: try:
location = json.loads(urllib2.urlopen('http://freegeoip.net/json/' + external_ip()).read()) location = json.loads(_urllib_read('http://freegeoip.net/json/' + external_ip()))
location_query = ','.join([location['city'], location['region_name'], location['country_name']]) location_query = ','.join([location['city'], location['region_name'], location['country_name']])
except ValueError: except ValueError:
return None return None
@ -107,8 +129,8 @@ def weather(unit='c', location_query=None):
'select * from we where location="{0}" and unit="{1}"'.format(location_query, unit), 'select * from we where location="{0}" and unit="{1}"'.format(location_query, unit),
'format': 'json' 'format': 'json'
} }
url = 'http://query.yahooapis.com/v1/public/yql?' + urllib.urlencode(query_data) url = 'http://query.yahooapis.com/v1/public/yql?' + _urllib_urlencode(query_data)
response = json.loads(urllib2.urlopen(url).read()) response = json.loads(_urllib_read(url))
condition = response['query']['results']['weather']['rss']['channel']['item']['condition'] condition = response['query']['results']['weather']['rss']['channel']['item']['condition']
condition_code = int(condition['code']) condition_code = int(condition['code'])
icon = u'' icon = u''

View File

@ -39,8 +39,8 @@ vim_modes = {
} }
mode_translations = { mode_translations = {
chr(ord('V')-0x40): '^V', chr(ord('V') - 0x40): '^V',
chr(ord('S')-0x40): '^S', chr(ord('S') - 0x40): '^S',
} }
@ -53,7 +53,7 @@ def mode(override=None):
mode = mode({ 'n': 'NORM' }) mode = mode({ 'n': 'NORM' })
''' '''
mode = vim_funcs['mode']() mode = vim_funcs['mode']().decode('utf-8')
mode = mode_translations.get(mode, mode) mode = mode_translations.get(mode, mode)
if not override: if not override:
return vim_modes[mode] return vim_modes[mode]
@ -80,7 +80,7 @@ def readonly_indicator(text=u''):
def file_directory(): def file_directory():
'''Return file directory (head component of the file path).''' '''Return file directory (head component of the file path).'''
file_directory = vim_funcs['expand']('%:~:.:h') file_directory = vim_funcs['expand']('%:~:.:h').decode('utf-8')
return file_directory + os.sep if file_directory else None return file_directory + os.sep if file_directory else None

View File

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from collections import defaultdict
import copy import copy
from collections import defaultdict from .segment import Segment
from segment import Segment
class Theme(object): class Theme(object):
def __init__(self, ext, colorscheme, theme_config, common_config): def __init__(self, ext, colorscheme, theme_config, common_config):
@ -42,7 +42,11 @@ class Theme(object):
pass pass
else: else:
continue continue
segment['contents'] = unicode(segment['before'] + unicode(segment['contents']) + segment['after'])\ try:
contents = unicode(segment['contents'])
except NameError:
contents = str(segment['contents'])
segment['contents'] = (segment['before'] + contents + segment['after'])\
.ljust(segment['ljust'])\ .ljust(segment['ljust'])\
.rjust(segment['rjust']) .rjust(segment['rjust'])
# We need to yield a copy of the segment, or else mode-dependent # We need to yield a copy of the segment, or else mode-dependent

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python2 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
'''Powerline prompt script.''' '''Powerline prompt script.'''
import argparse import argparse
@ -22,4 +22,4 @@ if __name__ == '__main__':
segments = pl.renderer.get_theme().get_segments() segments = pl.renderer.get_theme().get_segments()
if args.side != 'all': if args.side != 'all':
segments = [s for s in segments if s['side'] == args.side] segments = [s for s in segments if s['side'] == args.side]
print(pl.renderer.render(None, segments=segments).encode('utf-8')) print(pl.renderer.render(None, segments=segments))

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python2 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
'''Powerline tmux statusline.''' '''Powerline tmux statusline.'''
import argparse import argparse
@ -9,7 +9,7 @@ except ImportError:
import os import os
import sys import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from powerline.core import Powerline from powerline.core import Powerline # NOQA
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('side', nargs='?', default='all', choices=('all', 'left', 'right')) parser.add_argument('side', nargs='?', default='all', choices=('all', 'left', 'right'))
@ -21,4 +21,4 @@ if __name__ == '__main__':
segments = pl.renderer.get_theme().get_segments() segments = pl.renderer.get_theme().get_segments()
if args.side != 'all': if args.side != 'all':
segments = [s for s in segments if s['side'] == args.side] segments = [s for s in segments if s['side'] == args.side]
print(pl.renderer.render('n', segments=segments).encode('utf-8')) print(pl.renderer.render(None, segments=segments))

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python2 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os