Stop crawler process
(self)
| 151 | return False |
| 152 | |
| 153 | async def stop(self) -> bool: |
| 154 | """Stop crawler process""" |
| 155 | async with self._lock: |
| 156 | if not self.process or self.process.poll() is not None: |
| 157 | return False |
| 158 | |
| 159 | self.status = "stopping" |
| 160 | entry = self._create_log_entry("Sending SIGTERM to crawler process...", "warning") |
| 161 | await self._push_log(entry) |
| 162 | |
| 163 | try: |
| 164 | self.process.send_signal(signal.SIGTERM) |
| 165 | |
| 166 | # Wait for graceful exit (up to 15 seconds) |
| 167 | for _ in range(30): |
| 168 | if self.process.poll() is not None: |
| 169 | break |
| 170 | await asyncio.sleep(0.5) |
| 171 | |
| 172 | # If still not exited, force kill |
| 173 | if self.process.poll() is None: |
| 174 | entry = self._create_log_entry("Process not responding, sending SIGKILL...", "warning") |
| 175 | await self._push_log(entry) |
| 176 | self.process.kill() |
| 177 | |
| 178 | entry = self._create_log_entry("Crawler process terminated", "info") |
| 179 | await self._push_log(entry) |
| 180 | |
| 181 | except Exception as e: |
| 182 | entry = self._create_log_entry(f"Error stopping crawler: {str(e)}", "error") |
| 183 | await self._push_log(entry) |
| 184 | |
| 185 | self.status = "idle" |
| 186 | self.current_config = None |
| 187 | |
| 188 | # Cancel log reading task |
| 189 | if self._read_task: |
| 190 | self._read_task.cancel() |
| 191 | self._read_task = None |
| 192 | |
| 193 | return True |
| 194 | |
| 195 | def get_status(self) -> dict: |
| 196 | """Get current status""" |
no test coverage detected