1 | """This module focuses on improvements to the Python libtcod API. |
||
2 | """ |
||
3 | from __future__ import absolute_import as _ |
||
4 | |||
5 | import sys as _sys |
||
6 | |||
7 | import warnings |
||
8 | |||
9 | from tcod.libtcod import lib, ffi, BKGND_DEFAULT, BKGND_SET |
||
10 | |||
11 | |||
12 | def _unpack_char_p(char_p): |
||
13 | if char_p == ffi.NULL: |
||
14 | return '' |
||
15 | return ffi.string(char_p).decode() |
||
16 | |||
17 | |||
18 | def _int(int_or_str): |
||
19 | 'return an integer where a single character string may be expected' |
||
20 | if isinstance(int_or_str, str): |
||
21 | return ord(int_or_str) |
||
22 | if isinstance(int_or_str, bytes): |
||
23 | return int_or_str[0] |
||
24 | return int(int_or_str) # check for __count__ |
||
25 | |||
26 | |||
27 | if _sys.version_info[0] == 2: # Python 2 |
||
28 | def _bytes(string): |
||
29 | if isinstance(string, unicode): |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
![]() |
|||
30 | return string.encode() |
||
31 | return string |
||
32 | |||
33 | def _unicode(string): |
||
34 | if not isinstance(string, unicode): |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||
35 | return string.decode('latin-1') |
||
36 | return string |
||
37 | |||
38 | else: # Python 3 |
||
39 | def _bytes(string): |
||
40 | if isinstance(string, str): |
||
41 | return string.encode() |
||
42 | return string |
||
43 | |||
44 | def _unicode(string): |
||
45 | if isinstance(string, bytes): |
||
46 | return string.decode('latin-1') |
||
47 | return string |
||
48 | |||
49 | |||
50 | def _fmt_bytes(string): |
||
51 | return _bytes(string).replace(b'%', b'%%') |
||
52 | |||
53 | def _fmt_unicode(string): |
||
54 | return _unicode(string).replace(u'%', u'%%') |
||
55 | |||
56 | |||
57 | class _PropagateException(): |
||
58 | """ context manager designed to propagate exceptions outside of a cffi |
||
59 | callback context. normally cffi suppresses the exception |
||
60 | |||
61 | when propagate is called this class will hold onto the error until the |
||
62 | control flow leaves the context, then the error will be raised |
||
63 | |||
64 | with _PropagateException as propagate: |
||
65 | # give propagate as onerror parameter for ffi.def_extern |
||
66 | """ |
||
67 | |||
68 | def __init__(self): |
||
69 | self.exc_info = None # (exception, exc_value, traceback) |
||
70 | |||
71 | def propagate(self, *exc_info): |
||
72 | """ set an exception to be raised once this context exits |
||
73 | |||
74 | if multiple errors are caught, only keep the first exception raised |
||
75 | """ |
||
76 | if not self.exc_info: |
||
77 | self.exc_info = exc_info |
||
78 | |||
79 | def __enter__(self): |
||
80 | """ once in context, only the propagate call is needed to use this |
||
81 | class effectively |
||
82 | """ |
||
83 | return self.propagate |
||
84 | |||
85 | def __exit__(self, type, value, traceback): |
||
86 | """ if we're holding on to an exception, raise it now |
||
87 | |||
88 | prefers our held exception over any current raising error |
||
89 | |||
90 | self.exc_info is reset now in case of nested manager shenanigans |
||
91 | """ |
||
92 | if self.exc_info: |
||
93 | type, value, traceback = self.exc_info |
||
94 | self.exc_info = None |
||
95 | if type: |
||
96 | # Python 2/3 compatible throw |
||
97 | exception = type(value) |
||
98 | exception.__traceback__ = traceback |
||
99 | raise exception |
||
100 | |||
101 | |||
102 | class _CDataWrapper(object): |
||
103 | |||
104 | def __init__(self, *args, **kargs): |
||
105 | self.cdata = self._get_cdata_from_args(*args, **kargs) |
||
106 | if self.cdata == None: |
||
107 | self.cdata = ffi.NULL |
||
108 | super(_CDataWrapper, self).__init__() |
||
109 | |||
110 | @staticmethod |
||
111 | def _get_cdata_from_args(*args, **kargs): |
||
112 | if len(args) == 1 and isinstance(args[0], ffi.CData) and not kargs: |
||
113 | return args[0] |
||
114 | else: |
||
115 | return None |
||
116 | |||
117 | |||
118 | def __hash__(self): |
||
119 | return hash(self.cdata) |
||
120 | |||
121 | def __eq__(self, other): |
||
122 | try: |
||
123 | return self.cdata == other.cdata |
||
124 | except AttributeError: |
||
125 | return NotImplemented |
||
126 | |||
127 | def __getattr__(self, attr): |
||
128 | if 'cdata' in self.__dict__: |
||
129 | return getattr(self.__dict__['cdata'], attr) |
||
130 | raise AttributeError(attr) |
||
131 | |||
132 | def __setattr__(self, attr, value): |
||
133 | if hasattr(self, 'cdata') and hasattr(self.cdata, attr): |
||
134 | setattr(self.cdata, attr, value) |
||
135 | else: |
||
136 | super(_CDataWrapper, self).__setattr__(attr, value) |
||
137 | |||
138 | |||
139 | def _console(console): |
||
140 | """Return a cffi console.""" |
||
141 | try: |
||
142 | return console.console_c |
||
143 | except AttributeError: |
||
144 | warnings.warn( |
||
145 | ("Falsy console parameters are deprecated, " |
||
146 | "always use a console instance."), |
||
147 | DeprecationWarning, |
||
148 | stacklevel=2, |
||
149 | ) |
||
150 | return ffi.NULL |
||
151 |