Skip to content

Commit 7d2a7a0

Browse files
authored
Merge pull request RustPython#5731 from arihant2math/pprint-313
2 parents 494918d + 92e72aa commit 7d2a7a0

File tree

11 files changed

+750
-228
lines changed

11 files changed

+750
-228
lines changed

Lib/colorsys.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
__all__ = ["rgb_to_yiq","yiq_to_rgb","rgb_to_hls","hls_to_rgb",
2525
"rgb_to_hsv","hsv_to_rgb"]
2626

27-
# Some floating point constants
27+
# Some floating-point constants
2828

2929
ONE_THIRD = 1.0/3.0
3030
ONE_SIXTH = 1.0/6.0

Lib/graphlib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def done(self, *nodes):
154154
This method unblocks any successor of each node in *nodes* for being returned
155155
in the future by a call to "get_ready".
156156
157-
Raises :exec:`ValueError` if any node in *nodes* has already been marked as
157+
Raises ValueError if any node in *nodes* has already been marked as
158158
processed by a previous call to this method, if a node was not added to the
159159
graph by using "add" or if called without calling "prepare" previously or if
160160
node has not yet been returned by "get_ready".

Lib/linecache.py

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,13 @@
55
that name.
66
"""
77

8-
import functools
9-
import sys
10-
import os
11-
import tokenize
12-
138
__all__ = ["getline", "clearcache", "checkcache", "lazycache"]
149

1510

1611
# The cache. Maps filenames to either a thunk which will provide source code,
1712
# or a tuple (size, mtime, lines, fullname) once loaded.
1813
cache = {}
14+
_interactive_cache = {}
1915

2016

2117
def clearcache():
@@ -49,28 +45,54 @@ def getlines(filename, module_globals=None):
4945
return []
5046

5147

48+
def _getline_from_code(filename, lineno):
49+
lines = _getlines_from_code(filename)
50+
if 1 <= lineno <= len(lines):
51+
return lines[lineno - 1]
52+
return ''
53+
54+
def _make_key(code):
55+
return (code.co_filename, code.co_qualname, code.co_firstlineno)
56+
57+
def _getlines_from_code(code):
58+
code_id = _make_key(code)
59+
if code_id in _interactive_cache:
60+
entry = _interactive_cache[code_id]
61+
if len(entry) != 1:
62+
return _interactive_cache[code_id][2]
63+
return []
64+
65+
5266
def checkcache(filename=None):
5367
"""Discard cache entries that are out of date.
5468
(This is not checked upon each call!)"""
5569

5670
if filename is None:
57-
filenames = list(cache.keys())
58-
elif filename in cache:
59-
filenames = [filename]
71+
# get keys atomically
72+
filenames = cache.copy().keys()
6073
else:
61-
return
74+
filenames = [filename]
6275

6376
for filename in filenames:
64-
entry = cache[filename]
77+
try:
78+
entry = cache[filename]
79+
except KeyError:
80+
continue
81+
6582
if len(entry) == 1:
6683
# lazy cache entry, leave it lazy.
6784
continue
6885
size, mtime, lines, fullname = entry
6986
if mtime is None:
7087
continue # no-op for files loaded via a __loader__
88+
try:
89+
# This import can fail if the interpreter is shutting down
90+
import os
91+
except ImportError:
92+
return
7193
try:
7294
stat = os.stat(fullname)
73-
except OSError:
95+
except (OSError, ValueError):
7496
cache.pop(filename, None)
7597
continue
7698
if size != stat.st_size or mtime != stat.st_mtime:
@@ -82,6 +104,17 @@ def updatecache(filename, module_globals=None):
82104
If something's wrong, print a message, discard the cache entry,
83105
and return an empty list."""
84106

107+
# These imports are not at top level because linecache is in the critical
108+
# path of the interpreter startup and importing os and sys take a lot of time
109+
# and slows down the startup sequence.
110+
try:
111+
import os
112+
import sys
113+
import tokenize
114+
except ImportError:
115+
# These import can fail if the interpreter is shutting down
116+
return []
117+
85118
if filename in cache:
86119
if len(cache[filename]) != 1:
87120
cache.pop(filename, None)
@@ -128,16 +161,20 @@ def updatecache(filename, module_globals=None):
128161
try:
129162
stat = os.stat(fullname)
130163
break
131-
except OSError:
164+
except (OSError, ValueError):
132165
pass
133166
else:
134167
return []
168+
except ValueError: # may be raised by os.stat()
169+
return []
135170
try:
136171
with tokenize.open(fullname) as fp:
137172
lines = fp.readlines()
138173
except (OSError, UnicodeDecodeError, SyntaxError):
139174
return []
140-
if lines and not lines[-1].endswith('\n'):
175+
if not lines:
176+
lines = ['\n']
177+
elif not lines[-1].endswith('\n'):
141178
lines[-1] += '\n'
142179
size, mtime = stat.st_size, stat.st_mtime
143180
cache[filename] = size, mtime, lines, fullname
@@ -166,17 +203,29 @@ def lazycache(filename, module_globals):
166203
return False
167204
# Try for a __loader__, if available
168205
if module_globals and '__name__' in module_globals:
169-
name = module_globals['__name__']
170-
if (loader := module_globals.get('__loader__')) is None:
171-
if spec := module_globals.get('__spec__'):
172-
try:
173-
loader = spec.loader
174-
except AttributeError:
175-
pass
206+
spec = module_globals.get('__spec__')
207+
name = getattr(spec, 'name', None) or module_globals['__name__']
208+
loader = getattr(spec, 'loader', None)
209+
if loader is None:
210+
loader = module_globals.get('__loader__')
176211
get_source = getattr(loader, 'get_source', None)
177212

178213
if name and get_source:
179-
get_lines = functools.partial(get_source, name)
214+
def get_lines(name=name, *args, **kwargs):
215+
return get_source(name, *args, **kwargs)
180216
cache[filename] = (get_lines,)
181217
return True
182218
return False
219+
220+
def _register_code(code, string, name):
221+
entry = (len(string),
222+
None,
223+
[line + '\n' for line in string.splitlines()],
224+
name)
225+
stack = [code]
226+
while stack:
227+
code = stack.pop()
228+
for const in code.co_consts:
229+
if isinstance(const, type(code)):
230+
stack.append(const)
231+
_interactive_cache[_make_key(code)] = entry

Lib/pprint.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ def __init__(self, indent=1, width=80, depth=None, stream=None, *,
128128
sort_dicts
129129
If true, dict keys are sorted.
130130
131+
underscore_numbers
132+
If true, digit groups are separated with underscores.
133+
131134
"""
132135
indent = int(indent)
133136
width = int(width)

Lib/queue.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,15 @@
1010
except ImportError:
1111
SimpleQueue = None
1212

13-
__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue', 'SimpleQueue']
13+
__all__ = [
14+
'Empty',
15+
'Full',
16+
'ShutDown',
17+
'Queue',
18+
'PriorityQueue',
19+
'LifoQueue',
20+
'SimpleQueue',
21+
]
1422

1523

1624
try:
@@ -25,6 +33,10 @@ class Full(Exception):
2533
pass
2634

2735

36+
class ShutDown(Exception):
37+
'''Raised when put/get with shut-down queue.'''
38+
39+
2840
class Queue:
2941
'''Create a queue object with a given maximum size.
3042
@@ -54,6 +66,9 @@ def __init__(self, maxsize=0):
5466
self.all_tasks_done = threading.Condition(self.mutex)
5567
self.unfinished_tasks = 0
5668

69+
# Queue shutdown state
70+
self.is_shutdown = False
71+
5772
def task_done(self):
5873
'''Indicate that a formerly enqueued task is complete.
5974
@@ -65,6 +80,9 @@ def task_done(self):
6580
have been processed (meaning that a task_done() call was received
6681
for every item that had been put() into the queue).
6782
83+
shutdown(immediate=True) calls task_done() for each remaining item in
84+
the queue.
85+
6886
Raises a ValueError if called more times than there were items
6987
placed in the queue.
7088
'''
@@ -129,15 +147,21 @@ def put(self, item, block=True, timeout=None):
129147
Otherwise ('block' is false), put an item on the queue if a free slot
130148
is immediately available, else raise the Full exception ('timeout'
131149
is ignored in that case).
150+
151+
Raises ShutDown if the queue has been shut down.
132152
'''
133153
with self.not_full:
154+
if self.is_shutdown:
155+
raise ShutDown
134156
if self.maxsize > 0:
135157
if not block:
136158
if self._qsize() >= self.maxsize:
137159
raise Full
138160
elif timeout is None:
139161
while self._qsize() >= self.maxsize:
140162
self.not_full.wait()
163+
if self.is_shutdown:
164+
raise ShutDown
141165
elif timeout < 0:
142166
raise ValueError("'timeout' must be a non-negative number")
143167
else:
@@ -147,6 +171,8 @@ def put(self, item, block=True, timeout=None):
147171
if remaining <= 0.0:
148172
raise Full
149173
self.not_full.wait(remaining)
174+
if self.is_shutdown:
175+
raise ShutDown
150176
self._put(item)
151177
self.unfinished_tasks += 1
152178
self.not_empty.notify()
@@ -161,14 +187,21 @@ def get(self, block=True, timeout=None):
161187
Otherwise ('block' is false), return an item if one is immediately
162188
available, else raise the Empty exception ('timeout' is ignored
163189
in that case).
190+
191+
Raises ShutDown if the queue has been shut down and is empty,
192+
or if the queue has been shut down immediately.
164193
'''
165194
with self.not_empty:
195+
if self.is_shutdown and not self._qsize():
196+
raise ShutDown
166197
if not block:
167198
if not self._qsize():
168199
raise Empty
169200
elif timeout is None:
170201
while not self._qsize():
171202
self.not_empty.wait()
203+
if self.is_shutdown and not self._qsize():
204+
raise ShutDown
172205
elif timeout < 0:
173206
raise ValueError("'timeout' must be a non-negative number")
174207
else:
@@ -178,6 +211,8 @@ def get(self, block=True, timeout=None):
178211
if remaining <= 0.0:
179212
raise Empty
180213
self.not_empty.wait(remaining)
214+
if self.is_shutdown and not self._qsize():
215+
raise ShutDown
181216
item = self._get()
182217
self.not_full.notify()
183218
return item
@@ -198,6 +233,29 @@ def get_nowait(self):
198233
'''
199234
return self.get(block=False)
200235

236+
def shutdown(self, immediate=False):
237+
'''Shut-down the queue, making queue gets and puts raise ShutDown.
238+
239+
By default, gets will only raise once the queue is empty. Set
240+
'immediate' to True to make gets raise immediately instead.
241+
242+
All blocked callers of put() and get() will be unblocked. If
243+
'immediate', a task is marked as done for each item remaining in
244+
the queue, which may unblock callers of join().
245+
'''
246+
with self.mutex:
247+
self.is_shutdown = True
248+
if immediate:
249+
while self._qsize():
250+
self._get()
251+
if self.unfinished_tasks > 0:
252+
self.unfinished_tasks -= 1
253+
# release all blocked threads in `join()`
254+
self.all_tasks_done.notify_all()
255+
# All getters need to re-check queue-empty to raise ShutDown
256+
self.not_empty.notify_all()
257+
self.not_full.notify_all()
258+
201259
# Override these methods to implement other queue organizations
202260
# (e.g. stack or priority queue).
203261
# These will only be called with appropriate locks held

Lib/sched.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
implement simulated time by writing your own functions. This can
1212
also be used to integrate scheduling with STDWIN events; the delay
1313
function is allowed to modify the queue. Time can be expressed as
14-
integers or floating point numbers, as long as it is consistent.
14+
integers or floating-point numbers, as long as it is consistent.
1515
1616
Events are specified by tuples (time, priority, action, argument, kwargs).
1717
As in UNIX, lower priority numbers mean higher priority; in this

Lib/test/test_heapq.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import unittest
55
import doctest
66

7-
from test import support
87
from test.support import import_helper
98
from unittest import TestCase, skipUnless
109
from operator import itemgetter

0 commit comments

Comments
 (0)