summaryrefslogtreecommitdiffstats
path: root/powerline/bindings/pdb/__init__.py
blob: 4033e61125fa6701008786003ce18d630f1af0ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)

import sys
import pdb

from powerline.pdb import PDBPowerline
from powerline.lib.encoding import get_preferred_output_encoding
from powerline.lib.unicode import unicode


if sys.version_info < (3,):
	# XXX The below classes make code compatible with PDBpp which uses pyrepl
	#     which does not expect unicode or something above ASCII. They are 
	#     completely not needed if pdbpp is not used, but that’s not always the 
	#     case.
	class PowerlineRenderBytesResult(bytes):
		def __new__(cls, s, encoding=None):
			encoding = encoding or s.encoding
			if isinstance(s, PowerlineRenderResult):
				return s.encode(encoding)
			self = bytes.__new__(cls, s.encode(encoding) if isinstance(s, unicode) else s)
			self.encoding = encoding
			return self

		for meth in (
			'__contains__',
			'partition', 'rpartition',
			'split', 'rsplit',
			'count', 'join',
		):
			exec((
				'def {0}(self, *args):\n'
				'	if any((isinstance(arg, unicode) for arg in args)):\n'
				'		return self.__unicode__().{0}(*args)\n'
				'	else:\n'
				'		return bytes.{0}(self, *args)'
			).format(meth))

		for meth in (
			'find', 'rfind',
			'index', 'rindex',
		):
			exec((
				'def {0}(self, *args):\n'
				'	if any((isinstance(arg, unicode) for arg in args)):\n'
				'		args = [arg.encode(self.encoding) if isinstance(arg, unicode) else arg for arg in args]\n'
				'	return bytes.{0}(self, *args)'
			).format(meth))

		def __len__(self):
			return len(self.decode(self.encoding))

		def __getitem__(self, *args):
			return PowerlineRenderBytesResult(bytes.__getitem__(self, *args), encoding=self.encoding)

		def __getslice__(self, *args):
			return PowerlineRenderBytesResult(bytes.__getslice__(self, *args), encoding=self.encoding)

		@staticmethod
		def add(encoding, *args):
			if any((isinstance(arg, unicode) for arg in args)):
				return PowerlineRenderResult(''.join((
					arg
					if isinstance(arg, unicode)
					else arg.decode(encoding)
					for arg in args
				)), encoding)
			else:
				return PowerlineRenderBytesResult(b''.join(args), encoding=encoding)

		def __add__(self, other):
			return self.add(self.encoding, self, other)

		def __radd__(self, other):
			return self.add(self.encoding, other, self)

		def __unicode__(self):
			return PowerlineRenderResult(self)

	class PowerlineRenderResult(unicode):
		def __new__(cls, s, encoding=None):
			encoding = (
				encoding
				or getattr(s, 'encoding', None)
				or get_preferred_output_encoding()
			)
			if isinstance(s, unicode):
				self = unicode.__new__(cls, s)
			else:
				self = unicode.__new__(cls, s, encoding, 'replace')
			self.encoding = encoding
			return self

		def __str__(self):
			return PowerlineRenderBytesResult(self)

		def __getitem__(self, *args):
			return PowerlineRenderResult(unicode.__getitem__(self, *args))

		def __getslice__(self, *args):
			return PowerlineRenderResult(unicode.__getslice__(self, *args))

		@staticmethod
		def add(encoding, *args):
			return PowerlineRenderResult(''.join((
				arg
				if isinstance(arg, unicode)
				else arg.decode(encoding)
				for arg in args
			)), encoding)

		def __add__(self, other):
			return self.add(self.encoding, self, other)

		def __radd__(self, other):
			return self.add(self.encoding, other, self)

		def encode(self, *args, **kwargs):
			return PowerlineRenderBytesResult(unicode.encode(self, *args, **kwargs), args[0])
else:
	PowerlineRenderResult = str


def use_powerline_prompt(cls):
	'''Decorator that installs powerline prompt to the class

	:param pdb.Pdb cls:
		Class that should be decorated.

	:return:
		``cls`` argument or a class derived from it. Latter is used to turn 
		old-style classes into new-style classes.
	'''
	@property
	def prompt(self):
		try:
			powerline = self.powerline
		except AttributeError:
			powerline = PDBPowerline()
			powerline.setup(self)
			self.powerline = powerline
		return PowerlineRenderResult(powerline.render(side='left'))

	@prompt.setter
	def prompt(self, _):
		pass

	@prompt.deleter
	def prompt(self):
		pass

	if not hasattr(cls, '__class__'):
		# Old-style class: make it new-style or @property will not work.
		old_cls = cls

		class cls(cls, object):
			__module__ = cls.__module__
			__doc__ = cls.__doc__

		cls.__name__ = old_cls.__name__

	cls.prompt = prompt

	return cls


def main():
	'''Run module as a script

	Uses :py:func:`pdb.main` function directly, but prior to that it mocks 
	:py:class:`pdb.Pdb` class with powerline-specific class instance.
	'''
	orig_pdb = pdb.Pdb

	@use_powerline_prompt
	class Pdb(pdb.Pdb, object):
		def __init__(self):
			orig_pdb.__init__(self)

	pdb.Pdb = Pdb

	return pdb.main()