|
6 | 6 | needs not to be installed in remote environments.
|
7 | 7 | """
|
8 | 8 |
|
| 9 | +import contextlib |
9 | 10 | import sys
|
10 | 11 | import os
|
11 | 12 | import time
|
@@ -56,14 +57,21 @@ def worker_title(title):
|
56 | 57 |
|
57 | 58 |
|
58 | 59 | class WorkerInteractor:
|
| 60 | + SHUTDOWN_MARK = object() |
| 61 | + |
59 | 62 | def __init__(self, config, channel):
|
60 | 63 | self.config = config
|
61 | 64 | self.workerid = config.workerinput.get("workerid", "?")
|
62 | 65 | self.testrunuid = config.workerinput["testrunuid"]
|
63 | 66 | self.log = Producer(f"worker-{self.workerid}", enabled=config.option.debug)
|
64 | 67 | self.channel = channel
|
| 68 | + self.torun = self._make_queue() |
| 69 | + self.nextitem_index = None |
65 | 70 | config.pluginmanager.register(self)
|
66 | 71 |
|
| 72 | + def _make_queue(self): |
| 73 | + return self.channel.gateway.execmodel.queue.Queue() |
| 74 | + |
67 | 75 | def sendevent(self, name, **kwargs):
|
68 | 76 | self.log("sending", name, kwargs)
|
69 | 77 | self.channel.send((name, kwargs))
|
@@ -92,38 +100,60 @@ def pytest_sessionfinish(self, exitstatus):
|
92 | 100 | def pytest_collection(self, session):
|
93 | 101 | self.sendevent("collectionstart")
|
94 | 102 |
|
| 103 | + def handle_command(self, command): |
| 104 | + if command is self.SHUTDOWN_MARK: |
| 105 | + self.torun.put(self.SHUTDOWN_MARK) |
| 106 | + return |
| 107 | + |
| 108 | + name, kwargs = command |
| 109 | + |
| 110 | + self.log("received command", name, kwargs) |
| 111 | + if name == "runtests": |
| 112 | + for i in kwargs["indices"]: |
| 113 | + self.torun.put(i) |
| 114 | + elif name == "runtests_all": |
| 115 | + for i in range(len(self.session.items)): |
| 116 | + self.torun.put(i) |
| 117 | + elif name == "shutdown": |
| 118 | + self.torun.put(self.SHUTDOWN_MARK) |
| 119 | + elif name == "steal": |
| 120 | + self.steal(kwargs["indices"]) |
| 121 | + |
| 122 | + def steal(self, indices): |
| 123 | + indices = set(indices) |
| 124 | + stolen = [] |
| 125 | + |
| 126 | + old_queue, self.torun = self.torun, self._make_queue() |
| 127 | + |
| 128 | + def old_queue_get_nowait_noraise(): |
| 129 | + with contextlib.suppress(self.channel.gateway.execmodel.queue.Empty): |
| 130 | + return old_queue.get_nowait() |
| 131 | + |
| 132 | + for i in iter(old_queue_get_nowait_noraise, None): |
| 133 | + if i in indices: |
| 134 | + stolen.append(i) |
| 135 | + else: |
| 136 | + self.torun.put(i) |
| 137 | + |
| 138 | + self.sendevent("unscheduled", indices=stolen) |
| 139 | + |
95 | 140 | @pytest.hookimpl
|
96 | 141 | def pytest_runtestloop(self, session):
|
97 | 142 | self.log("entering main loop")
|
98 |
| - torun = [] |
99 |
| - while 1: |
100 |
| - try: |
101 |
| - name, kwargs = self.channel.receive() |
102 |
| - except EOFError: |
103 |
| - return True |
104 |
| - self.log("received command", name, kwargs) |
105 |
| - if name == "runtests": |
106 |
| - torun.extend(kwargs["indices"]) |
107 |
| - elif name == "runtests_all": |
108 |
| - torun.extend(range(len(session.items))) |
109 |
| - self.log("items to run:", torun) |
110 |
| - # only run if we have an item and a next item |
111 |
| - while len(torun) >= 2: |
112 |
| - self.run_one_test(torun) |
113 |
| - if name == "shutdown": |
114 |
| - if torun: |
115 |
| - self.run_one_test(torun) |
116 |
| - break |
| 143 | + self.channel.setcallback(self.handle_command, endmarker=self.SHUTDOWN_MARK) |
| 144 | + self.nextitem_index = self.torun.get() |
| 145 | + while self.nextitem_index is not self.SHUTDOWN_MARK: |
| 146 | + self.run_one_test() |
117 | 147 | return True
|
118 | 148 |
|
119 |
| - def run_one_test(self, torun): |
| 149 | + def run_one_test(self): |
120 | 150 | items = self.session.items
|
121 |
| - self.item_index = torun.pop(0) |
| 151 | + self.item_index, self.nextitem_index = self.nextitem_index, self.torun.get() |
122 | 152 | item = items[self.item_index]
|
123 |
| - if torun: |
124 |
| - nextitem = items[torun[0]] |
125 |
| - else: |
| 153 | + if self.nextitem_index is self.SHUTDOWN_MARK: |
126 | 154 | nextitem = None
|
| 155 | + else: |
| 156 | + nextitem = items[self.nextitem_index] |
127 | 157 |
|
128 | 158 | worker_title("[pytest-xdist running] %s" % item.nodeid)
|
129 | 159 |
|
|
0 commit comments