Skip to content

Commit cd940e2

Browse files
Pull in main
2 parents cf66105 + e6f96cf commit cd940e2

7 files changed

+89
-30
lines changed

Lib/inspect.py

+46-11
Original file line numberDiff line numberDiff line change
@@ -1034,9 +1034,13 @@ class ClassFoundException(Exception):
10341034

10351035
class _ClassFinder(ast.NodeVisitor):
10361036

1037-
def __init__(self, qualname):
1037+
def __init__(self, cls, tree, lines, qualname):
10381038
self.stack = []
1039+
self.cls = cls
1040+
self.tree = tree
1041+
self.lines = lines
10391042
self.qualname = qualname
1043+
self.lineno_found = []
10401044

10411045
def visit_FunctionDef(self, node):
10421046
self.stack.append(node.name)
@@ -1057,11 +1061,48 @@ def visit_ClassDef(self, node):
10571061
line_number = node.lineno
10581062

10591063
# decrement by one since lines starts with indexing by zero
1060-
line_number -= 1
1061-
raise ClassFoundException(line_number)
1064+
self.lineno_found.append((line_number - 1, node.end_lineno))
10621065
self.generic_visit(node)
10631066
self.stack.pop()
10641067

1068+
def get_lineno(self):
1069+
self.visit(self.tree)
1070+
lineno_found_number = len(self.lineno_found)
1071+
if lineno_found_number == 0:
1072+
raise OSError('could not find class definition')
1073+
elif lineno_found_number == 1:
1074+
return self.lineno_found[0][0]
1075+
else:
1076+
# We have multiple candidates for the class definition.
1077+
# Now we have to guess.
1078+
1079+
# First, let's see if there are any method definitions
1080+
for member in self.cls.__dict__.values():
1081+
if isinstance(member, types.FunctionType):
1082+
for lineno, end_lineno in self.lineno_found:
1083+
if lineno <= member.__code__.co_firstlineno <= end_lineno:
1084+
return lineno
1085+
1086+
class_strings = [(''.join(self.lines[lineno: end_lineno]), lineno)
1087+
for lineno, end_lineno in self.lineno_found]
1088+
1089+
# Maybe the class has a docstring and it's unique?
1090+
if self.cls.__doc__:
1091+
ret = None
1092+
for candidate, lineno in class_strings:
1093+
if self.cls.__doc__.strip() in candidate:
1094+
if ret is None:
1095+
ret = lineno
1096+
else:
1097+
break
1098+
else:
1099+
if ret is not None:
1100+
return ret
1101+
1102+
# We are out of ideas, just return the last one found, which is
1103+
# slightly better than previous ones
1104+
return self.lineno_found[-1][0]
1105+
10651106

10661107
def findsource(object):
10671108
"""Return the entire source file and starting line number for an object.
@@ -1098,14 +1139,8 @@ def findsource(object):
10981139
qualname = object.__qualname__
10991140
source = ''.join(lines)
11001141
tree = ast.parse(source)
1101-
class_finder = _ClassFinder(qualname)
1102-
try:
1103-
class_finder.visit(tree)
1104-
except ClassFoundException as e:
1105-
line_number = e.args[0]
1106-
return lines, line_number
1107-
else:
1108-
raise OSError('could not find class definition')
1142+
class_finder = _ClassFinder(object, tree, lines, qualname)
1143+
return lines, class_finder.get_lineno()
11091144

11101145
if ismethod(object):
11111146
object = object.__func__

Lib/selectors.py

+14-18
Original file line numberDiff line numberDiff line change
@@ -314,17 +314,15 @@ def select(self, timeout=None):
314314
r, w, _ = self._select(self._readers, self._writers, [], timeout)
315315
except InterruptedError:
316316
return ready
317-
r = set(r)
318-
w = set(w)
319-
for fd in r | w:
320-
events = 0
321-
if fd in r:
322-
events |= EVENT_READ
323-
if fd in w:
324-
events |= EVENT_WRITE
325-
326-
key = self._fd_to_key.get(fd)
317+
r = frozenset(r)
318+
w = frozenset(w)
319+
rw = r | w
320+
fd_to_key_get = self._fd_to_key.get
321+
for fd in rw:
322+
key = fd_to_key_get(fd)
327323
if key:
324+
events = ((fd in r and EVENT_READ)
325+
| (fd in w and EVENT_WRITE))
328326
ready.append((key, events & key.events))
329327
return ready
330328

@@ -547,23 +545,21 @@ def select(self, timeout=None):
547545
# If max_ev is 0, kqueue will ignore the timeout. For consistent
548546
# behavior with the other selector classes, we prevent that here
549547
# (using max). See https://bugs.python.org/issue29255
550-
max_ev = max(len(self._fd_to_key), 1)
548+
max_ev = len(self._fd_to_key) or 1
551549
ready = []
552550
try:
553551
kev_list = self._selector.control(None, max_ev, timeout)
554552
except InterruptedError:
555553
return ready
554+
555+
fd_to_key_get = self._fd_to_key.get
556556
for kev in kev_list:
557557
fd = kev.ident
558558
flag = kev.filter
559-
events = 0
560-
if flag == select.KQ_FILTER_READ:
561-
events |= EVENT_READ
562-
if flag == select.KQ_FILTER_WRITE:
563-
events |= EVENT_WRITE
564-
565-
key = self._fd_to_key.get(fd)
559+
key = fd_to_key_get(fd)
566560
if key:
561+
events = ((flag == select.KQ_FILTER_READ and EVENT_READ)
562+
| (flag == select.KQ_FILTER_WRITE and EVENT_WRITE))
567563
ready.append((key, events & key.events))
568564
return ready
569565

Lib/test/inspect_fodder2.py

+20
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,23 @@ def complex_decorated(foo=0, bar=lambda: 0):
290290
nested_lambda = (
291291
lambda right: [].map(
292292
lambda length: ()))
293+
294+
# line 294
295+
if True:
296+
class cls296:
297+
def f():
298+
pass
299+
else:
300+
class cls296:
301+
def g():
302+
pass
303+
304+
# line 304
305+
if False:
306+
class cls310:
307+
def f():
308+
pass
309+
else:
310+
class cls310:
311+
def g():
312+
pass

Lib/test/test_inspect.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -949,7 +949,6 @@ def test_class_decorator(self):
949949
self.assertSourceEqual(mod2.cls196.cls200, 198, 201)
950950

951951
def test_class_inside_conditional(self):
952-
self.assertSourceEqual(mod2.cls238, 238, 240)
953952
self.assertSourceEqual(mod2.cls238.cls239, 239, 240)
954953

955954
def test_multiple_children_classes(self):
@@ -975,6 +974,10 @@ def test_nested_class_definition_inside_async_function(self):
975974
self.assertSourceEqual(mod2.cls226, 231, 235)
976975
self.assertSourceEqual(asyncio.run(mod2.cls226().func232()), 233, 234)
977976

977+
def test_class_definition_same_name_diff_methods(self):
978+
self.assertSourceEqual(mod2.cls296, 296, 298)
979+
self.assertSourceEqual(mod2.cls310, 310, 312)
980+
978981
class TestNoEOL(GetSourceBase):
979982
def setUp(self):
980983
self.tempdir = TESTFN + '_dir'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make :func:`inspect.getsource` smarter for class for same name definitions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Optimize :meth:`KqueueSelector.select` for many iteration case. Patch By
2+
Dong-hee Na.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Optimize :meth:`SelectSelector.select` for many iteration case. Patch By
2+
Dong-hee Na.

0 commit comments

Comments
 (0)