Conversation
* added minor type hinting * apply linter
* Cleanup some typing * Add custom CLI commands * Add unit tests * Align checks and versions * Add SIGKILL type * squash
* Add REPL context * fix(type): add missing return type * chore: rename method and add docstring * fix: handle null docstring * chore: add test case * Add direct attr setting --------- Co-authored-by: goodki-d <[email protected]>
Co-authored-by: Adam Hopkins <[email protected]>
* Cleanup some typing * Add custom CLI commands * Add unit tests * Fix body check in websockets handshake
* Cleanup some typing * Add custom CLI commands * Add unit tests * Add missing test to workflow
* Cleanup some typing * Add custom CLI commands * Add unit tests * Add default to response cookies
Signed-off-by: Zhiwei Liang <[email protected]>
Replaced random payload generation with secrets module for better security. [B311:blacklist] was failing in security tests.
* Add some typing and fix some tests * Pulled in security fixes
Fixes #3055 During worker restart, a race condition occurs between WorkerProcess.restart() and WorkerManager._sync_states() that causes the restart to fail with: "Cannot spawn a worker process until it is idle." The race condition happens when: 1. WorkerProcess.restart() sets state to RESTARTING and terminates the process 2. WorkerManager._sync_states() runs concurrently, sees process is not alive 3. _sync_states() changes state from RESTARTING to COMPLETED 4. WorkerProcess.restart() tries to spawn(), but state is now COMPLETED 5. spawn() raises exception because state is not IDLE or RESTARTING The fix prevents _sync_states() from changing the state of processes that are in the RESTARTING state, allowing the restart flow to complete without interference. Changes: - Added check in _sync_states() to skip processes with state RESTARTING - This prevents the race condition by ensuring restart flow is not interrupted - Safe change: RESTARTING is temporary, managed entirely by restart() method - No performance impact: just one additional state check per process Co-authored-by: Adam Hopkins <[email protected]>
…3085) Fixes #3081 During server shutdown, the _cleanup() function in runners.py calls close_if_idle() on all connection objects. However, if a connection was created but connection_task() hasn't called _setup_connection() yet, the _http attribute doesn't exist, causing an AttributeError. The HttpProtocolMixin already provides an 'http' property (lines 73-77) that safely handles this case using hasattr(). This commit updates close_if_idle() to use the property instead of direct attribute access. Changes: - Changed self._http to self.http in close_if_idle() method - This leverages the existing property that returns None when _http doesn't exist yet - Maintains exact same behavior for initialized connections - Prevents AttributeError for uninitialized connections during shutdown Co-authored-by: Adam Hopkins <[email protected]>
* Add typing for parameters of constructor of `WorkerManager` Signed-off-by: Zhiwei Liang <[email protected]> * Change to Dict * Add future annotations * Cleanup --------- Signed-off-by: Zhiwei Liang <[email protected]> Co-authored-by: Adam Hopkins <[email protected]>
…3080) * Update str_to_bool function to include 'nope' as a valid false value * Run make pretty --------- Co-authored-by: Adam Hopkins <[email protected]>
…3079) Co-authored-by: Adam Hopkins <[email protected]>
* Windows has no os.killpg * Actually kill processes on Windows --------- Co-authored-by: Adam Hopkins <[email protected]>
similar to python-websockets/websockets#1072 Co-authored-by: Adam Hopkins <[email protected]>
Change `dicussed` to `discussed` Co-authored-by: Adam Hopkins <[email protected]>
* Change the log type to debug * Fixed incorrect links throughout the documentation * Update compat.py --------- Co-authored-by: Adam Hopkins <[email protected]>
Co-authored-by: Adam Hopkins <[email protected]>
Co-authored-by: Adam Hopkins <[email protected]>
Co-authored-by: Adam Hopkins <[email protected]>
* Update required Python to >=3.9 * Remove more Python 3.8 reference
* feat: Add automatic charset=UTF-8 to text content types Refactor content type handling to automatically append charset=UTF-8 to text/* MIME types when serving static files and file responses. - Add new guess_content_type() utility function that wraps mimetypes.guess_type() - Automatically append '; charset=UTF-8' to text content types - Replace direct mimetypes.guess_type() usage with new utility - Update static file serving and file() response functions to use new utility * Tests... * Change UTF-8 to utf-8 as is the convention. Fix the new tests. * Moved from overly long test_request to test_static, even for the file() interface as it is related. Removed duplicate tests, clarified naming. * Fix some broken tests --------- Co-authored-by: Adam Hopkins <[email protected]>
* Upgrade tracerite to latest * Update sanic/__main__.py Co-authored-by: Copilot <[email protected]> * Update tests to match new tracerite patterns --------- Co-authored-by: Copilot <[email protected]>
* Upgrade tracerite to latest * Update sanic/__main__.py Co-authored-by: Copilot <[email protected]> * Add better error catching for common startup issues * Use errno.EADDRINUSE constant instead of hardcoded platform-specific value (#3106) * Initial plan * Replace hardcoded errno 98 with errno.EADDRINUSE constant Co-authored-by: ahopkins <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: ahopkins <[email protected]> * Make pretty * Add CODECOV token --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: ahopkins <[email protected]>
* Add support for running tests with xdist * Get coverage and tests running successfully with xdist * Get coverage and tests running successfully with xdist * Run base tests (not coverages) with xdist * Cleanup test file ordering
* Move to 2-stage coverage check * Update .github/workflows/coverage-upload.yml Co-authored-by: Copilot <[email protected]> * Update .github/workflows/coverage-upload.yml Co-authored-by: Copilot <[email protected]> * Update .github/workflows/coverage.yml Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
* fixing bad term cleanup at exit * Move logic to method * Change to 2-stage coverage check * Update upload workflow --------- Co-authored-by: Adam Hopkins <[email protected]>
…ws (#3109) * Bump dawidd6/action-download-artifact from 3 to 6 in /.github/workflows Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 3 to 6. - [Release notes](https://github.com/dawidd6/action-download-artifact/releases) - [Commits](dawidd6/action-download-artifact@v3...v6) --- updated-dependencies: - dependency-name: dawidd6/action-download-artifact dependency-version: '6' dependency-type: direct:production ... Signed-off-by: dependabot[bot] <[email protected]> * Clear caplog before assertions and filter for sanic-specific errors to avoid capturing stray asyncio warnings from other tests. --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Adam Hopkins <[email protected]>
* Add daemon mode to Sanic CLI * Simplify help docs in CLI * Make pretty * Update sanic/worker/daemon.py Co-authored-by: Copilot <[email protected]> * Update sanic/worker/daemon.py Co-authored-by: Copilot <[email protected]> * Fix daemon lockfile race condition during daemonization (#3111) * Initial plan * Fix race condition by acquiring lockfile before forking Co-authored-by: ahopkins <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: ahopkins <[email protected]> * Add explanatory comments to exception handlers in daemon lockfile cleanup (#3113) * Initial plan * Add explanatory comments to except OSError blocks in _release_lockfile Co-authored-by: ahopkins <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: ahopkins <[email protected]> * Improve test coverage * Polish help displays --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: ahopkins <[email protected]>
* Remove v3.9 and add v3.14 * Update test with new version of tracerite header checks * squash * Use explicit loop on server shutdown * Correct py3.14 issues
* Explicit symlink params for static files/dirs * Fix symlink type detection for external symlink access control (#3118) * Initial plan * Fix symlink type detection to correctly distinguish file vs directory symlinks Co-authored-by: ahopkins <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: ahopkins <[email protected]> * Cleanup linting --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: ahopkins <[email protected]>
* Respect LOG_EXTRA in all cases * disable xdist on python 3.10 that seems to have a deadlock with uvloop
* Prepare docs for v25.12 release * Update tests for new worker ready log line
There was a problem hiding this comment.
Pull request overview
This PR deploys the v25.12 documentation release for Sanic, featuring a major version update from 24.12.0 to 25.12.0. The changes include dropping Python 3.8/3.9 support, adding Python 3.14 support, introducing daemon mode functionality, modernizing type annotations to use PEP 604 syntax (Union[X, Y] → X | Y), improving security for static file serving with symlink handling, and numerous bug fixes and improvements across the codebase.
Key changes:
- Drop Python 3.8/3.9 support, add Python 3.14 support
- Introduce daemon mode with full CLI support
- Modernize type annotations throughout codebase
- Add comprehensive symlink security for static file serving
- Improve startup error handling and logging
- Add new test coverage for tasks, daemon, and startup errors
Reviewed changes
Copilot reviewed 159 out of 160 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| sanic/version.py | Version bump to 25.12.0 |
| setup.py | Updated Python version requirements (3.10+), added pytest-xdist/pytest-cov, updated tracerite dependency |
| tox.ini | Removed py39 support, added py314, updated test configuration with parallel execution |
| sanic/worker/daemon.py | New daemon mode implementation with PID/lock file management |
| sanic/startup/errors.py | New startup error handling module |
| sanic/mixins/static.py | Added symlink security controls for static file serving |
| sanic/handlers/directory.py | Enhanced directory handler with symlink filtering |
| sanic/response/convenience.py | Added guess_content_type function with charset handling |
| tests/test_daemon.py | Comprehensive daemon mode test suite |
| tests/test_static_directory.py | Extensive symlink security tests |
| Multiple files | Type annotation modernization (Union → |
| Multiple test files | Added xdist_group markers for parallel test execution |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| @classmethod | ||
| def generate_id(*_) -> Union[uuid.UUID, str, int]: | ||
| def generate_id(*_) -> uuid.UUID | str | int: |
There was a problem hiding this comment.
Class methods or methods of a type deriving from type should have 'cls' as their first parameter.
| """ | ||
|
|
||
|
|
||
| class Config(dict, metaclass=DescriptorMeta): |
There was a problem hiding this comment.
The class 'Config' does not override 'eq', but adds the new attribute defaults.
The class 'Config' does not override 'eq', but adds the new attribute _converters.
The class 'Config' does not override 'eq', but adds the new attribute KEEP_ALIVE.
The class 'Config' does not override 'eq', but adds the new attribute _init.
The class 'Config' does not override 'eq', but adds the new attribute LOCAL_CERT_CREATOR.
| return | ||
| except asyncio.CancelledError: | ||
| ... | ||
| except BaseException: |
There was a problem hiding this comment.
Except block directly handles BaseException.
| if lockfile.exists(): | ||
| try: | ||
| lockfile.unlink() | ||
| except OSError: |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| except OSError as e: | ||
| try: | ||
| os.close(fd) | ||
| except OSError: |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| self.set_state(ProcessState.TERMINATED, force=True) | ||
| try: | ||
| self.set_state(ProcessState.TERMINATED, force=True) | ||
| except (BrokenPipeError, ConnectionResetError, EOFError): |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| for _signal in [SIGINT, SIGTERM]: | ||
| try: | ||
| loop.remove_signal_handler(_signal) | ||
| except (NotImplementedError, OSError): |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| app.set_serving(False) | ||
| try: | ||
| app.set_serving(False) | ||
| except (BrokenPipeError, ConnectionResetError, EOFError): |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
No description provided.