Compare commits
1 Commits
bind_excep
...
smoother-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0839fe130 |
28
.github/workflows/coverage.yml
vendored
28
.github/workflows/coverage.yml
vendored
@@ -12,23 +12,29 @@ on:
|
|||||||
- main
|
- main
|
||||||
- current-release
|
- current-release
|
||||||
- "*LTS"
|
- "*LTS"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
coverage:
|
test:
|
||||||
name: Check coverage
|
runs-on: ${{ matrix.os }}
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: [3.9]
|
||||||
|
os: [ubuntu-latest]
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Run coverage
|
- uses: actions/checkout@v2
|
||||||
uses: sanic-org/simple-tox-action@v1
|
- uses: actions/setup-python@v1
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: ${{ matrix.python-version }}
|
||||||
tox-env: coverage
|
|
||||||
ignore-errors: true
|
- name: Install dependencies 🔨
|
||||||
- name: Run Codecov
|
run: |
|
||||||
uses: codecov/codecov-action@v3
|
python -m pip install --upgrade pip
|
||||||
|
pip install tox
|
||||||
|
- name: Run coverage
|
||||||
|
run: tox -e coverage
|
||||||
|
continue-on-error: true
|
||||||
|
- uses: codecov/codecov-action@v2
|
||||||
with:
|
with:
|
||||||
files: ./coverage.xml
|
files: ./coverage.xml
|
||||||
fail_ci_if_error: false
|
fail_ci_if_error: false
|
||||||
|
|||||||
39
.github/workflows/on-demand.yml
vendored
Normal file
39
.github/workflows/on-demand.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
name: On Demand Task
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
python-version:
|
||||||
|
description: 'Version of Python to use for running Test'
|
||||||
|
required: false
|
||||||
|
default: "3.8"
|
||||||
|
tox-env:
|
||||||
|
description: 'Test Environment to Run'
|
||||||
|
required: true
|
||||||
|
default: ''
|
||||||
|
os:
|
||||||
|
description: 'Operating System to Run Test on'
|
||||||
|
required: false
|
||||||
|
default: ubuntu-latest
|
||||||
|
jobs:
|
||||||
|
onDemand:
|
||||||
|
name: tox-${{ matrix.config.tox-env }}-on-${{ matrix.os }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: ["${{ github.event.inputs.os}}"]
|
||||||
|
config:
|
||||||
|
- { tox-env: "${{ github.event.inputs.tox-env }}", py-version: "${{ github.event.inputs.python-version }}"}
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
uses: harshanarayana/custom-actions@main
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.config.py-version }}
|
||||||
|
test-infra-tool: tox
|
||||||
|
test-infra-version: latest
|
||||||
|
action: tests
|
||||||
|
test-additional-args: "-e=${{ matrix.config.tox-env }}"
|
||||||
|
experimental-ignore-error: "yes"
|
||||||
37
.github/workflows/pr-bandit.yml
vendored
Normal file
37
.github/workflows/pr-bandit.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
name: Security Analysis
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- current-release
|
||||||
|
- "*LTS"
|
||||||
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
bandit:
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
name: type-check-${{ matrix.config.python-version }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
config:
|
||||||
|
- { python-version: 3.8, tox-env: security}
|
||||||
|
- { python-version: 3.9, tox-env: security}
|
||||||
|
- { python-version: "3.10", tox-env: security}
|
||||||
|
- { python-version: "3.11", tox-env: security}
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
id: checkout-branch
|
||||||
|
|
||||||
|
- name: Run Linter Checks
|
||||||
|
id: linter-check
|
||||||
|
uses: harshanarayana/custom-actions@main
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.config.python-version }}
|
||||||
|
test-infra-tool: tox
|
||||||
|
test-infra-version: latest
|
||||||
|
action: tests
|
||||||
|
test-additional-args: "-e=${{ matrix.config.tox-env }}"
|
||||||
33
.github/workflows/pr-docs.yml
vendored
Normal file
33
.github/workflows/pr-docs.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
name: Document Linter
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- current-release
|
||||||
|
- "*LTS"
|
||||||
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docsLinter:
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
name: Lint Documentation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
config:
|
||||||
|
- {python-version: "3.10", tox-env: "docs"}
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Run Document Linter
|
||||||
|
uses: harshanarayana/custom-actions@main
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.config.python-version }}
|
||||||
|
test-infra-tool: tox
|
||||||
|
test-infra-version: latest
|
||||||
|
action: tests
|
||||||
|
test-additional-args: "-e=${{ matrix.config.tox-env }}"
|
||||||
34
.github/workflows/pr-linter.yml
vendored
Normal file
34
.github/workflows/pr-linter.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
name: Linter Checks
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- current-release
|
||||||
|
- "*LTS"
|
||||||
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linter:
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
name: lint
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
config:
|
||||||
|
- { python-version: "3.10", tox-env: lint}
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
id: checkout-branch
|
||||||
|
|
||||||
|
- name: Run Linter Checks
|
||||||
|
id: linter-check
|
||||||
|
uses: harshanarayana/custom-actions@main
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.config.python-version }}
|
||||||
|
test-infra-tool: tox
|
||||||
|
test-infra-version: latest
|
||||||
|
action: tests
|
||||||
|
test-additional-args: "-e=${{ matrix.config.tox-env }}"
|
||||||
41
.github/workflows/pr-python-pypy.yml
vendored
Normal file
41
.github/workflows/pr-python-pypy.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
name: Python PyPy Tests
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tox-env:
|
||||||
|
description: "Tox Env to run on the PyPy Infra"
|
||||||
|
required: false
|
||||||
|
default: "pypy310"
|
||||||
|
pypy-version:
|
||||||
|
description: "Version of PyPy to use"
|
||||||
|
required: false
|
||||||
|
default: "pypy-3.10"
|
||||||
|
jobs:
|
||||||
|
testPyPy:
|
||||||
|
name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# os: [ubuntu-latest, macos-latest]
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
config:
|
||||||
|
- {
|
||||||
|
python-version: "${{ github.event.inputs.pypy-version }}",
|
||||||
|
tox-env: "${{ github.event.inputs.tox-env }}",
|
||||||
|
}
|
||||||
|
steps:
|
||||||
|
- name: Checkout the Repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
id: checkout-branch
|
||||||
|
|
||||||
|
- name: Run Unit Tests
|
||||||
|
uses: harshanarayana/custom-actions@main
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.config.python-version }}
|
||||||
|
test-infra-tool: tox
|
||||||
|
test-infra-version: latest
|
||||||
|
action: tests
|
||||||
|
test-additional-args: "-e=${{ matrix.config.tox-env }}"
|
||||||
|
experimental-ignore-error: "true"
|
||||||
|
command-timeout: "600000"
|
||||||
48
.github/workflows/pr-python310.yml
vendored
Normal file
48
.github/workflows/pr-python310.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
name: Python 3.10 Tests
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- current-release
|
||||||
|
- "*LTS"
|
||||||
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
testPy310:
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# os: [ubuntu-latest, macos-latest]
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
config:
|
||||||
|
- {
|
||||||
|
python-version: "3.10",
|
||||||
|
tox-env: py310,
|
||||||
|
ignore-error-flake: "false",
|
||||||
|
command-timeout: "0",
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
python-version: "3.10",
|
||||||
|
tox-env: py310-no-ext,
|
||||||
|
ignore-error-flake: "true",
|
||||||
|
command-timeout: "600000",
|
||||||
|
}
|
||||||
|
steps:
|
||||||
|
- name: Checkout the Repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
id: checkout-branch
|
||||||
|
|
||||||
|
- name: Run Unit Tests
|
||||||
|
uses: harshanarayana/custom-actions@main
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.config.python-version }}
|
||||||
|
test-infra-tool: tox
|
||||||
|
test-infra-version: latest
|
||||||
|
action: tests
|
||||||
|
test-additional-args: "-e=${{ matrix.config.tox-env }},-vv=''"
|
||||||
|
experimental-ignore-error: "${{ matrix.config.ignore-error-flake }}"
|
||||||
|
command-timeout: "${{ matrix.config.command-timeout }}"
|
||||||
|
test-failure-retry: "3"
|
||||||
48
.github/workflows/pr-python311.yml
vendored
Normal file
48
.github/workflows/pr-python311.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
name: Python 3.11 Tests
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- current-release
|
||||||
|
- "*LTS"
|
||||||
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
testPy311:
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# os: [ubuntu-latest, macos-latest]
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
config:
|
||||||
|
- {
|
||||||
|
python-version: "3.11",
|
||||||
|
tox-env: py311,
|
||||||
|
ignore-error-flake: "false",
|
||||||
|
command-timeout: "0",
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
python-version: "3.11",
|
||||||
|
tox-env: py311-no-ext,
|
||||||
|
ignore-error-flake: "true",
|
||||||
|
command-timeout: "600000",
|
||||||
|
}
|
||||||
|
steps:
|
||||||
|
- name: Checkout the Repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
id: checkout-branch
|
||||||
|
|
||||||
|
- name: Run Unit Tests
|
||||||
|
uses: harshanarayana/custom-actions@main
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.config.python-version }}
|
||||||
|
test-infra-tool: tox
|
||||||
|
test-infra-version: latest
|
||||||
|
action: tests
|
||||||
|
test-additional-args: "-e=${{ matrix.config.tox-env }},-vv=''"
|
||||||
|
experimental-ignore-error: "${{ matrix.config.ignore-error-flake }}"
|
||||||
|
command-timeout: "${{ matrix.config.command-timeout }}"
|
||||||
|
test-failure-retry: "3"
|
||||||
36
.github/workflows/pr-python38.yml
vendored
Normal file
36
.github/workflows/pr-python38.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
name: Python 3.8 Tests
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- current-release
|
||||||
|
- "*LTS"
|
||||||
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
testPy38:
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
# os: [ubuntu-latest, macos-latest]
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
config:
|
||||||
|
- { python-version: 3.8, tox-env: py38 }
|
||||||
|
- { python-version: 3.8, tox-env: py38-no-ext }
|
||||||
|
steps:
|
||||||
|
- name: Checkout the Repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
id: checkout-branch
|
||||||
|
|
||||||
|
- name: Run Unit Tests
|
||||||
|
uses: harshanarayana/custom-actions@main
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.config.python-version }}
|
||||||
|
test-infra-tool: tox
|
||||||
|
test-infra-version: latest
|
||||||
|
action: tests
|
||||||
|
test-additional-args: "-e=${{ matrix.config.tox-env }}"
|
||||||
|
test-failure-retry: "3"
|
||||||
48
.github/workflows/pr-python39.yml
vendored
Normal file
48
.github/workflows/pr-python39.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
name: Python 3.9 Tests
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- current-release
|
||||||
|
- "*LTS"
|
||||||
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
testPy39:
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
# os: [ubuntu-latest, macos-latest]
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
config:
|
||||||
|
- {
|
||||||
|
python-version: 3.9,
|
||||||
|
tox-env: py39,
|
||||||
|
ignore-error-flake: "false",
|
||||||
|
command-timeout: "0",
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
python-version: 3.9,
|
||||||
|
tox-env: py39-no-ext,
|
||||||
|
ignore-error-flake: "true",
|
||||||
|
command-timeout: "600000",
|
||||||
|
}
|
||||||
|
steps:
|
||||||
|
- name: Checkout the Repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
id: checkout-branch
|
||||||
|
|
||||||
|
- name: Run Unit Tests
|
||||||
|
uses: harshanarayana/custom-actions@main
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.config.python-version }}
|
||||||
|
test-infra-tool: tox
|
||||||
|
test-infra-version: latest
|
||||||
|
action: tests
|
||||||
|
test-additional-args: "-e=${{ matrix.config.tox-env }},-vv=''"
|
||||||
|
experimental-ignore-error: "${{ matrix.config.ignore-error-flake }}"
|
||||||
|
command-timeout: "${{ matrix.config.command-timeout }}"
|
||||||
|
test-failure-retry: "3"
|
||||||
37
.github/workflows/pr-type-check.yml
vendored
Normal file
37
.github/workflows/pr-type-check.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
name: Typing Checks
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- current-release
|
||||||
|
- "*LTS"
|
||||||
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
typeChecking:
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
name: type-check-${{ matrix.config.python-version }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
config:
|
||||||
|
- { python-version: 3.8, tox-env: type-checking}
|
||||||
|
- { python-version: 3.9, tox-env: type-checking}
|
||||||
|
- { python-version: "3.10", tox-env: type-checking}
|
||||||
|
- { python-version: "3.11", tox-env: type-checking}
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
id: checkout-branch
|
||||||
|
|
||||||
|
- name: Run Linter Checks
|
||||||
|
id: linter-check
|
||||||
|
uses: harshanarayana/custom-actions@main
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.config.python-version }}
|
||||||
|
test-infra-tool: tox
|
||||||
|
test-infra-version: latest
|
||||||
|
action: tests
|
||||||
|
test-additional-args: "-e=${{ matrix.config.tox-env }}"
|
||||||
38
.github/workflows/pr-windows.yml
vendored
Normal file
38
.github/workflows/pr-windows.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
name: Run Unit Tests on Windows
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- current-release
|
||||||
|
- "*LTS"
|
||||||
|
types: [opened, synchronize, reopened, ready_for_review]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
testsOnWindows:
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
name: ut-${{ matrix.config.tox-env }}
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
config:
|
||||||
|
- { python-version: 3.8, tox-env: py38-no-ext }
|
||||||
|
- { python-version: 3.9, tox-env: py39-no-ext }
|
||||||
|
- { python-version: "3.10", tox-env: py310-no-ext }
|
||||||
|
- { python-version: "3.11", tox-env: py310-no-ext }
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Run Unit Tests
|
||||||
|
uses: ahopkins/custom-actions@pip-extra-args
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.config.python-version }}
|
||||||
|
test-infra-tool: tox
|
||||||
|
test-infra-version: latest
|
||||||
|
action: tests
|
||||||
|
test-additional-args: "-e=${{ matrix.config.tox-env }}"
|
||||||
|
experimental-ignore-error: "true"
|
||||||
|
command-timeout: "600000"
|
||||||
|
pip-extra-args: "--user"
|
||||||
48
.github/workflows/publish-images.yml
vendored
Normal file
48
.github/workflows/publish-images.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
name: Publish Docker Images
|
||||||
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows:
|
||||||
|
- 'Publish Artifacts'
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publishDockerImages:
|
||||||
|
name: Docker Image Build [${{ matrix.python-version }}]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
python-version: ["3.8", "3.9", "3.10", "3.11"]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Build Latest Base images for ${{ matrix.python-version }}
|
||||||
|
uses: harshanarayana/custom-actions@main
|
||||||
|
with:
|
||||||
|
docker-image-base-name: sanicframework/sanic-build
|
||||||
|
ignore-python-setup: 'true'
|
||||||
|
dockerfile-base-dir: './docker'
|
||||||
|
action: 'image-publish'
|
||||||
|
docker-image-tag: "${{ matrix.python-version }}"
|
||||||
|
docker-file-suffix: "base"
|
||||||
|
docker-build-args: "PYTHON_VERSION=${{ matrix.python-version }}"
|
||||||
|
registry-auth-user: ${{ secrets.DOCKER_ACCESS_USER }}
|
||||||
|
registry-auth-password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||||
|
push-images: 'true'
|
||||||
|
|
||||||
|
- name: Publish Sanic Docker Image for ${{ matrix.python-version }}
|
||||||
|
uses: harshanarayana/custom-actions@main
|
||||||
|
with:
|
||||||
|
docker-image-base-name: sanicframework/sanic
|
||||||
|
ignore-python-setup: 'true'
|
||||||
|
dockerfile-base-dir: './docker'
|
||||||
|
action: 'image-publish'
|
||||||
|
docker-build-args: "BASE_IMAGE_TAG=${{ matrix.python-version }}"
|
||||||
|
docker-image-prefix: "${{ matrix.python-version }}"
|
||||||
|
registry-auth-user: ${{ secrets.DOCKER_ACCESS_USER }}
|
||||||
|
registry-auth-password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||||
|
push-images: 'true'
|
||||||
39
.github/workflows/publish-package.yml
vendored
Normal file
39
.github/workflows/publish-package.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
name: Upload Python Package
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [created]
|
||||||
|
workflow_dispatch:
|
||||||
|
jobs:
|
||||||
|
build-n-publish:
|
||||||
|
name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: "3.x"
|
||||||
|
- name: Install pypa/build
|
||||||
|
run: >-
|
||||||
|
python3 -m
|
||||||
|
pip install
|
||||||
|
build
|
||||||
|
--user
|
||||||
|
- name: Build a binary wheel and a source tarball
|
||||||
|
run: >-
|
||||||
|
python3 -m
|
||||||
|
build
|
||||||
|
--sdist
|
||||||
|
--wheel
|
||||||
|
--outdir dist/
|
||||||
|
.
|
||||||
|
# - name: Publish distribution 📦 to Test PyPI
|
||||||
|
# uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
|
# with:
|
||||||
|
# password: ${{ secrets.SANIC_TEST_PYPI_API_TOKEN }}
|
||||||
|
# repository-url: https://test.pypi.org/legacy/
|
||||||
|
- name: Publish distribution 📦 to PyPI
|
||||||
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
|
with:
|
||||||
|
password: ${{ secrets.SANIC_PYPI_API_TOKEN }}
|
||||||
174
.github/workflows/publish-release.yml
vendored
174
.github/workflows/publish-release.yml
vendored
@@ -1,174 +0,0 @@
|
|||||||
name: Publish release
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
env:
|
|
||||||
IS_TEST: false
|
|
||||||
DOCKER_ORG_NAME: sanicframework
|
|
||||||
DOCKER_IMAGE_NAME: sanic
|
|
||||||
DOCKER_BASE_IMAGE_NAME: sanic-build
|
|
||||||
DOCKER_IMAGE_DOCKERFILE: ./docker/Dockerfile
|
|
||||||
DOCKER_BASE_IMAGE_DOCKERFILE: ./docker/Dockerfile-base
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
generate_info:
|
|
||||||
name: Generate info
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
docker-tags: ${{ steps.generate_docker_info.outputs.tags }}
|
|
||||||
pypi-version: ${{ steps.parse_version_tag.outputs.pypi-version }}
|
|
||||||
steps:
|
|
||||||
- name: Parse version tag
|
|
||||||
id: parse_version_tag
|
|
||||||
env:
|
|
||||||
TAG_NAME: ${{ github.event.release.tag_name }}
|
|
||||||
run: |
|
|
||||||
tag_name="${{ env.TAG_NAME }}"
|
|
||||||
|
|
||||||
if [[ ! "${tag_name}" =~ ^v([0-9]{2})\.([0-9]{1,2})\.([0-9]+)$ ]]; then
|
|
||||||
echo "::error::Tag name must be in the format vYY.MM.MICRO"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
year_output="year=${BASH_REMATCH[1]}"
|
|
||||||
month_output="month=${BASH_REMATCH[2]}"
|
|
||||||
pypi_output="pypi-version=${tag_name#v}"
|
|
||||||
|
|
||||||
echo "${year_output}"
|
|
||||||
echo "${month_output}"
|
|
||||||
echo "${pypi_output}"
|
|
||||||
|
|
||||||
echo "${year_output}" >> $GITHUB_OUTPUT
|
|
||||||
echo "${month_output}" >> $GITHUB_OUTPUT
|
|
||||||
echo "${pypi_output}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Get latest release
|
|
||||||
id: get_latest_release
|
|
||||||
run: |
|
|
||||||
latest_tag=$(
|
|
||||||
curl -L \
|
|
||||||
-H "Accept: application/vnd.github+json" \
|
|
||||||
-H "Authorization: Bearer ${{ github.token }}" \
|
|
||||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
||||||
https://api.github.com/repos/${{ github.repository }}/releases/latest \
|
|
||||||
| jq -r '.tag_name'
|
|
||||||
)
|
|
||||||
echo "latest_tag=$latest_tag" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Generate Docker info
|
|
||||||
id: generate_docker_info
|
|
||||||
run: |
|
|
||||||
tag_year="${{ steps.parse_version_tag.outputs.year }}"
|
|
||||||
tag_month="${{ steps.parse_version_tag.outputs.month }}"
|
|
||||||
latest_tag="${{ steps.get_latest_release.outputs.latest_tag }}"
|
|
||||||
tag="${{ github.event.release.tag_name }}"
|
|
||||||
|
|
||||||
tags="${tag_year}.${tag_month}"
|
|
||||||
|
|
||||||
if [[ "${tag_month}" == "12" ]]; then
|
|
||||||
tags+=",LTS"
|
|
||||||
echo "::notice::Tag ${tag} is LTS version"
|
|
||||||
else
|
|
||||||
echo "::notice::Tag ${tag} is not LTS version"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${latest_tag}" == "${{ github.event.release.tag_name }}" ]]; then
|
|
||||||
tags+=",latest"
|
|
||||||
echo "::notice::Tag ${tag} is marked as latest"
|
|
||||||
else
|
|
||||||
echo "::notice::Tag ${tag} is not marked as latest"
|
|
||||||
fi
|
|
||||||
|
|
||||||
tags_output="tags=${tags}"
|
|
||||||
|
|
||||||
echo "${tags_output}"
|
|
||||||
echo "${tags_output}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
publish_package:
|
|
||||||
name: Build and publish package
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: generate_info
|
|
||||||
steps:
|
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: "3.11"
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pip install build twine
|
|
||||||
|
|
||||||
- name: Update package version
|
|
||||||
run: |
|
|
||||||
echo "__version__ = \"${{ needs.generate_info.outputs.pypi-version }}\"" > sanic/__version__.py
|
|
||||||
|
|
||||||
- name: Build a binary wheel and a source tarball
|
|
||||||
run: python -m build --sdist --wheel --outdir dist/ .
|
|
||||||
|
|
||||||
- name: Publish to PyPi 🚀
|
|
||||||
run: twine upload --non-interactive --disable-progress-bar dist/*
|
|
||||||
env:
|
|
||||||
TWINE_USERNAME: __token__
|
|
||||||
TWINE_PASSWORD: ${{ env.IS_TEST == 'true' && secrets.SANIC_TEST_PYPI_API_TOKEN || secrets.SANIC_PYPI_API_TOKEN }}
|
|
||||||
TWINE_REPOSITORY: ${{ env.IS_TEST == 'true' && 'testpypi' || 'pypi' }}
|
|
||||||
|
|
||||||
publish_docker:
|
|
||||||
name: Publish Docker / Python ${{ matrix.python-version }}
|
|
||||||
needs: [generate_info, publish_package]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: true
|
|
||||||
matrix:
|
|
||||||
python-version: ["3.10", "3.11"]
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_ACCESS_USER }}
|
|
||||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
- name: Build and push base image
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
push: ${{ env.IS_TEST == 'false' }}
|
|
||||||
file: ${{ env.DOCKER_BASE_IMAGE_DOCKERFILE }}
|
|
||||||
tags: ${{ env.DOCKER_ORG_NAME }}/${{ env.DOCKER_BASE_IMAGE_NAME }}:${{ matrix.python-version }}
|
|
||||||
build-args: |
|
|
||||||
PYTHON_VERSION=${{ matrix.python-version }}
|
|
||||||
|
|
||||||
- name: Parse tags for this Python version
|
|
||||||
id: parse_tags
|
|
||||||
run: |
|
|
||||||
IFS=',' read -ra tags <<< "${{ needs.generate_info.outputs.docker-tags }}"
|
|
||||||
tag_args=""
|
|
||||||
|
|
||||||
for tag in "${tags[@]}"; do
|
|
||||||
tag_args+=",${{ env.DOCKER_ORG_NAME }}/${{ env.DOCKER_IMAGE_NAME }}:${tag}-py${{ matrix.python-version }}"
|
|
||||||
done
|
|
||||||
|
|
||||||
tag_args_output="tag_args=${tag_args:1}"
|
|
||||||
|
|
||||||
echo "${tag_args_output}"
|
|
||||||
echo "${tag_args_output}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Build and push Sanic image
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
push: ${{ env.IS_TEST == 'false' }}
|
|
||||||
file: ${{ env.DOCKER_IMAGE_DOCKERFILE }}
|
|
||||||
tags: ${{ steps.parse_tags.outputs.tag_args }}
|
|
||||||
build-args: |
|
|
||||||
BASE_IMAGE_ORG=${{ env.DOCKER_ORG_NAME }}
|
|
||||||
BASE_IMAGE_NAME=${{ env.DOCKER_BASE_IMAGE_NAME }}
|
|
||||||
BASE_IMAGE_TAG=${{ matrix.python-version }}
|
|
||||||
SANIC_PYPI_VERSION=${{ needs.generate_info.outputs.pypi-version }}
|
|
||||||
56
.github/workflows/tests.yml
vendored
56
.github/workflows/tests.yml
vendored
@@ -1,56 +0,0 @@
|
|||||||
name: Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- current-release
|
|
||||||
- "*LTS"
|
|
||||||
tags:
|
|
||||||
- "!*"
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- current-release
|
|
||||||
- "*LTS"
|
|
||||||
types: [opened, synchronize, reopened, ready_for_review]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
run_tests:
|
|
||||||
name: "${{ matrix.config.platform == 'windows-latest' && 'Windows' || 'Linux' }} / Python ${{ matrix.config.python-version }} / tox -e ${{ matrix.config.tox-env }}"
|
|
||||||
if: github.event.pull_request.draft == false
|
|
||||||
runs-on: ${{ matrix.config.platform || 'ubuntu-latest' }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: true
|
|
||||||
matrix:
|
|
||||||
config:
|
|
||||||
- { python-version: "3.8", tox-env: security }
|
|
||||||
- { python-version: "3.9", tox-env: security }
|
|
||||||
- { python-version: "3.10", tox-env: security }
|
|
||||||
- { python-version: "3.11", tox-env: security }
|
|
||||||
- { python-version: "3.10", tox-env: lint }
|
|
||||||
- { python-version: "3.10", tox-env: docs }
|
|
||||||
- { python-version: "3.8", tox-env: type-checking }
|
|
||||||
- { python-version: "3.9", tox-env: type-checking }
|
|
||||||
- { python-version: "3.10", tox-env: type-checking }
|
|
||||||
- { python-version: "3.11", tox-env: type-checking }
|
|
||||||
- { python-version: "3.8", tox-env: py38, max-attempts: 3 }
|
|
||||||
- { python-version: "3.8", tox-env: py38-no-ext, max-attempts: 3 }
|
|
||||||
- { python-version: "3.9", tox-env: py39, max-attempts: 3 }
|
|
||||||
- { python-version: "3.9", tox-env: py39-no-ext, max-attempts: 3 }
|
|
||||||
- { python-version: "3.10", tox-env: py310, max-attempts: 3 }
|
|
||||||
- { python-version: "3.10", tox-env: py310-no-ext, max-attempts: 3 }
|
|
||||||
- { python-version: "3.11", tox-env: py311, max-attempts: 3 }
|
|
||||||
- { python-version: "3.11", tox-env: py311-no-ext, max-attempts: 3 }
|
|
||||||
- { python-version: "3.8", tox-env: py38-no-ext, platform: windows-latest, ignore-errors: true }
|
|
||||||
- { python-version: "3.9", tox-env: py39-no-ext, platform: windows-latest, ignore-errors: true }
|
|
||||||
- { python-version: "3.10", tox-env: py310-no-ext, platform: windows-latest, ignore-errors: true }
|
|
||||||
- { python-version: "3.11", tox-env: py310-no-ext, platform: windows-latest, ignore-errors: true }
|
|
||||||
steps:
|
|
||||||
- name: Run tests
|
|
||||||
uses: sanic-org/simple-tox-action@v1
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.config.python-version }}
|
|
||||||
tox-env: ${{ matrix.config.tox-env }}
|
|
||||||
max-attempts: ${{ matrix.config.max-attempts || 1 }}
|
|
||||||
ignore-errors: ${{ matrix.config.ignore-errors || false }}
|
|
||||||
10
README.rst
10
README.rst
@@ -11,7 +11,7 @@ Sanic | Build fast. Run fast.
|
|||||||
:stub-columns: 1
|
:stub-columns: 1
|
||||||
|
|
||||||
* - Build
|
* - Build
|
||||||
- | |Tests|
|
- | |Py310Test| |Py39Test| |Py38Test|
|
||||||
* - Docs
|
* - Docs
|
||||||
- | |UserGuide| |Documentation|
|
- | |UserGuide| |Documentation|
|
||||||
* - Package
|
* - Package
|
||||||
@@ -27,8 +27,12 @@ Sanic | Build fast. Run fast.
|
|||||||
:target: https://community.sanicframework.org/
|
:target: https://community.sanicframework.org/
|
||||||
.. |Discord| image:: https://img.shields.io/discord/812221182594121728?logo=discord
|
.. |Discord| image:: https://img.shields.io/discord/812221182594121728?logo=discord
|
||||||
:target: https://discord.gg/FARQzAEMAA
|
:target: https://discord.gg/FARQzAEMAA
|
||||||
.. |Tests| image:: https://github.com/sanic-org/sanic/actions/workflows/tests.yml/badge.svg?branch=main
|
.. |Py310Test| image:: https://github.com/sanic-org/sanic/actions/workflows/pr-python310.yml/badge.svg?branch=main
|
||||||
:target: https://github.com/sanic-org/sanic/actions/workflows/tests.yml
|
:target: https://github.com/sanic-org/sanic/actions/workflows/pr-python310.yml
|
||||||
|
.. |Py39Test| image:: https://github.com/sanic-org/sanic/actions/workflows/pr-python39.yml/badge.svg?branch=main
|
||||||
|
:target: https://github.com/sanic-org/sanic/actions/workflows/pr-python39.yml
|
||||||
|
.. |Py38Test| image:: https://github.com/sanic-org/sanic/actions/workflows/pr-python38.yml/badge.svg?branch=main
|
||||||
|
:target: https://github.com/sanic-org/sanic/actions/workflows/pr-python38.yml
|
||||||
.. |Documentation| image:: https://readthedocs.org/projects/sanic/badge/?version=latest
|
.. |Documentation| image:: https://readthedocs.org/projects/sanic/badge/?version=latest
|
||||||
:target: http://sanic.readthedocs.io/en/latest/?badge=latest
|
:target: http://sanic.readthedocs.io/en/latest/?badge=latest
|
||||||
.. |PyPI| image:: https://img.shields.io/pypi/v/sanic.svg
|
.. |PyPI| image:: https://img.shields.io/pypi/v/sanic.svg
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
ARG BASE_IMAGE_ORG
|
|
||||||
ARG BASE_IMAGE_NAME
|
|
||||||
ARG BASE_IMAGE_TAG
|
ARG BASE_IMAGE_TAG
|
||||||
|
|
||||||
FROM ${BASE_IMAGE_ORG}/${BASE_IMAGE_NAME}:${BASE_IMAGE_TAG}
|
FROM sanicframework/sanic-build:${BASE_IMAGE_TAG}
|
||||||
|
|
||||||
RUN apk update
|
RUN apk update
|
||||||
RUN update-ca-certificates
|
RUN update-ca-certificates
|
||||||
|
|
||||||
ARG SANIC_PYPI_VERSION
|
RUN pip install sanic
|
||||||
|
|
||||||
RUN pip install -U pip && pip install sanic==${SANIC_PYPI_VERSION}
|
|
||||||
RUN apk del build-base
|
RUN apk del build-base
|
||||||
|
|||||||
16
sanic/app.py
16
sanic/app.py
@@ -5,7 +5,6 @@ import logging
|
|||||||
import logging.config
|
import logging.config
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from asyncio import (
|
from asyncio import (
|
||||||
AbstractEventLoop,
|
AbstractEventLoop,
|
||||||
CancelledError,
|
CancelledError,
|
||||||
@@ -94,7 +93,6 @@ from sanic.worker.inspector import Inspector
|
|||||||
from sanic.worker.loader import CertLoader
|
from sanic.worker.loader import CertLoader
|
||||||
from sanic.worker.manager import WorkerManager
|
from sanic.worker.manager import WorkerManager
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
try:
|
try:
|
||||||
from sanic_ext import Extend # type: ignore
|
from sanic_ext import Extend # type: ignore
|
||||||
@@ -1743,6 +1741,20 @@ class Sanic(
|
|||||||
if hasattr(self, "multiplexer"):
|
if hasattr(self, "multiplexer"):
|
||||||
self.multiplexer.ack()
|
self.multiplexer.ack()
|
||||||
|
|
||||||
|
def set_serving(self, serving: bool) -> None:
|
||||||
|
"""Set the serving state of the application.
|
||||||
|
|
||||||
|
This method is used to set the serving state of the application.
|
||||||
|
It is used internally by Sanic and should not typically be called
|
||||||
|
manually.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
serving (bool): Whether the application is serving.
|
||||||
|
"""
|
||||||
|
self.state.is_running = serving
|
||||||
|
if hasattr(self, "multiplexer"):
|
||||||
|
self.multiplexer.set_serving(serving)
|
||||||
|
|
||||||
async def _server_event(
|
async def _server_event(
|
||||||
self,
|
self,
|
||||||
concern: str,
|
concern: str,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from asyncio import (
|
from asyncio import (
|
||||||
AbstractEventLoop,
|
AbstractEventLoop,
|
||||||
CancelledError,
|
CancelledError,
|
||||||
@@ -49,7 +48,7 @@ from sanic.application.motd import MOTD
|
|||||||
from sanic.application.state import ApplicationServerInfo, Mode, ServerStage
|
from sanic.application.state import ApplicationServerInfo, Mode, ServerStage
|
||||||
from sanic.base.meta import SanicMeta
|
from sanic.base.meta import SanicMeta
|
||||||
from sanic.compat import OS_IS_WINDOWS, StartMethod
|
from sanic.compat import OS_IS_WINDOWS, StartMethod
|
||||||
from sanic.exceptions import SanicException, ServerKilled
|
from sanic.exceptions import ServerKilled
|
||||||
from sanic.helpers import Default, _default, is_atty
|
from sanic.helpers import Default, _default, is_atty
|
||||||
from sanic.http.constants import HTTP
|
from sanic.http.constants import HTTP
|
||||||
from sanic.http.tls import get_ssl_context, process_to_context
|
from sanic.http.tls import get_ssl_context, process_to_context
|
||||||
@@ -65,13 +64,13 @@ from sanic.server.protocols.http_protocol import HttpProtocol
|
|||||||
from sanic.server.protocols.websocket_protocol import WebSocketProtocol
|
from sanic.server.protocols.websocket_protocol import WebSocketProtocol
|
||||||
from sanic.server.runners import serve
|
from sanic.server.runners import serve
|
||||||
from sanic.server.socket import configure_socket, remove_unix_socket
|
from sanic.server.socket import configure_socket, remove_unix_socket
|
||||||
|
from sanic.worker.constants import ProcessState
|
||||||
from sanic.worker.loader import AppLoader
|
from sanic.worker.loader import AppLoader
|
||||||
from sanic.worker.manager import WorkerManager
|
from sanic.worker.manager import WorkerManager
|
||||||
from sanic.worker.multiplexer import WorkerMultiplexer
|
from sanic.worker.multiplexer import WorkerMultiplexer
|
||||||
from sanic.worker.reloader import Reloader
|
from sanic.worker.reloader import Reloader
|
||||||
from sanic.worker.serve import worker_serve
|
from sanic.worker.serve import worker_serve
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.application.state import ApplicationState
|
from sanic.application.state import ApplicationState
|
||||||
@@ -880,18 +879,12 @@ class StartupMixin(metaclass=SanicMeta):
|
|||||||
manager.run()
|
manager.run()
|
||||||
except ServerKilled:
|
except ServerKilled:
|
||||||
exit_code = 1
|
exit_code = 1
|
||||||
except SanicException as e:
|
|
||||||
exit_code = 1
|
|
||||||
kwargs = primary_server_info.settings
|
|
||||||
if e.quiet:
|
|
||||||
error_logger.error(str(e))
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
except BaseException:
|
except BaseException:
|
||||||
kwargs = primary_server_info.settings
|
kwargs = primary_server_info.settings
|
||||||
error_logger.exception(
|
error_logger.exception(
|
||||||
"Experienced exception while trying to serve"
|
"Experienced exception while trying to serve"
|
||||||
)
|
)
|
||||||
|
raise
|
||||||
finally:
|
finally:
|
||||||
logger.info("Server Stopped")
|
logger.info("Server Stopped")
|
||||||
for app in apps:
|
for app in apps:
|
||||||
@@ -899,7 +892,6 @@ class StartupMixin(metaclass=SanicMeta):
|
|||||||
app.router.reset()
|
app.router.reset()
|
||||||
app.signal_router.reset()
|
app.signal_router.reset()
|
||||||
|
|
||||||
sync_manager.shutdown()
|
|
||||||
for sock in socks:
|
for sock in socks:
|
||||||
try:
|
try:
|
||||||
sock.shutdown(SHUT_RDWR)
|
sock.shutdown(SHUT_RDWR)
|
||||||
@@ -911,12 +903,33 @@ class StartupMixin(metaclass=SanicMeta):
|
|||||||
loop.close()
|
loop.close()
|
||||||
cls._cleanup_env_vars()
|
cls._cleanup_env_vars()
|
||||||
cls._cleanup_apps()
|
cls._cleanup_apps()
|
||||||
|
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
limit = 100
|
||||||
|
while cls._get_process_states(worker_state):
|
||||||
|
sleep(0.1)
|
||||||
|
limit -= 1
|
||||||
|
if limit <= 0:
|
||||||
|
error_logger.warning(
|
||||||
|
"Worker shutdown timed out. "
|
||||||
|
"Some processes may still be running."
|
||||||
|
)
|
||||||
|
break
|
||||||
|
sync_manager.shutdown()
|
||||||
unix = kwargs.get("unix")
|
unix = kwargs.get("unix")
|
||||||
if unix:
|
if unix:
|
||||||
remove_unix_socket(unix)
|
remove_unix_socket(unix)
|
||||||
|
logger.info("Goodbye.")
|
||||||
if exit_code:
|
if exit_code:
|
||||||
os._exit(exit_code)
|
os._exit(exit_code)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_process_states(worker_state) -> List[str]:
|
||||||
|
return [
|
||||||
|
state for s in worker_state.values() if (state := s.get("state"))
|
||||||
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def serve_single(cls, primary: Optional[Sanic] = None) -> None:
|
def serve_single(cls, primary: Optional[Sanic] = None) -> None:
|
||||||
os.environ["SANIC_MOTD_OUTPUT"] = "true"
|
os.environ["SANIC_MOTD_OUTPUT"] = "true"
|
||||||
|
|||||||
@@ -122,17 +122,15 @@ def _setup_system_signals(
|
|||||||
register_sys_signals: bool,
|
register_sys_signals: bool,
|
||||||
loop: asyncio.AbstractEventLoop,
|
loop: asyncio.AbstractEventLoop,
|
||||||
) -> None: # no cov
|
) -> None: # no cov
|
||||||
# Ignore SIGINT when run_multiple
|
|
||||||
if run_multiple:
|
|
||||||
signal_func(SIGINT, SIG_IGN)
|
signal_func(SIGINT, SIG_IGN)
|
||||||
|
signal_func(SIGTERM, SIG_IGN)
|
||||||
os.environ["SANIC_WORKER_PROCESS"] = "true"
|
os.environ["SANIC_WORKER_PROCESS"] = "true"
|
||||||
|
|
||||||
# Register signals for graceful termination
|
# Register signals for graceful termination
|
||||||
if register_sys_signals:
|
if register_sys_signals:
|
||||||
if OS_IS_WINDOWS:
|
if OS_IS_WINDOWS:
|
||||||
ctrlc_workaround_for_windows(app)
|
ctrlc_workaround_for_windows(app)
|
||||||
else:
|
else:
|
||||||
for _signal in [SIGTERM] if run_multiple else [SIGINT, SIGTERM]:
|
for _signal in [SIGINT, SIGTERM]:
|
||||||
loop.add_signal_handler(
|
loop.add_signal_handler(
|
||||||
_signal, partial(app.stop, terminate=False)
|
_signal, partial(app.stop, terminate=False)
|
||||||
)
|
)
|
||||||
@@ -143,8 +141,6 @@ def _run_server_forever(loop, before_stop, after_stop, cleanup, unix):
|
|||||||
try:
|
try:
|
||||||
server_logger.info("Starting worker [%s]", pid)
|
server_logger.info("Starting worker [%s]", pid)
|
||||||
loop.run_forever()
|
loop.run_forever()
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
finally:
|
finally:
|
||||||
server_logger.info("Stopping worker [%s]", pid)
|
server_logger.info("Stopping worker [%s]", pid)
|
||||||
|
|
||||||
@@ -156,6 +152,7 @@ def _run_server_forever(loop, before_stop, after_stop, cleanup, unix):
|
|||||||
loop.run_until_complete(after_stop())
|
loop.run_until_complete(after_stop())
|
||||||
remove_unix_socket(unix)
|
remove_unix_socket(unix)
|
||||||
loop.close()
|
loop.close()
|
||||||
|
server_logger.info("Worker complete [%s]", pid)
|
||||||
|
|
||||||
|
|
||||||
def _serve_http_1(
|
def _serve_http_1(
|
||||||
@@ -259,8 +256,11 @@ def _serve_http_1(
|
|||||||
else:
|
else:
|
||||||
conn.abort()
|
conn.abort()
|
||||||
|
|
||||||
|
app.set_serving(False)
|
||||||
|
|
||||||
_setup_system_signals(app, run_multiple, register_sys_signals, loop)
|
_setup_system_signals(app, run_multiple, register_sys_signals, loop)
|
||||||
loop.run_until_complete(app._server_event("init", "after"))
|
loop.run_until_complete(app._server_event("init", "after"))
|
||||||
|
app.set_serving(True)
|
||||||
_run_server_forever(
|
_run_server_forever(
|
||||||
loop,
|
loop,
|
||||||
partial(app._server_event, "shutdown", "before"),
|
partial(app._server_event, "shutdown", "before"),
|
||||||
|
|||||||
@@ -47,10 +47,10 @@ def bind_unix_socket(path: str, *, mode=0o666, backlog=100) -> socket.socket:
|
|||||||
path = os.path.abspath(path)
|
path = os.path.abspath(path)
|
||||||
folder = os.path.dirname(path)
|
folder = os.path.dirname(path)
|
||||||
if not os.path.isdir(folder):
|
if not os.path.isdir(folder):
|
||||||
raise FileNotFoundError("Socket folder does not exist")
|
raise FileNotFoundError(f"Socket folder does not exist: {folder}")
|
||||||
try:
|
try:
|
||||||
if not stat.S_ISSOCK(os.stat(path, follow_symlinks=False).st_mode):
|
if not stat.S_ISSOCK(os.stat(path, follow_symlinks=False).st_mode):
|
||||||
raise FileExistsError("Existing file is not a socket")
|
raise FileExistsError(f"Existing file is not a socket: {path}")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
# Create new socket with a random temporary name
|
# Create new socket with a random temporary name
|
||||||
@@ -103,10 +103,7 @@ def configure_socket(
|
|||||||
unix = server_settings["unix"]
|
unix = server_settings["unix"]
|
||||||
backlog = server_settings["backlog"]
|
backlog = server_settings["backlog"]
|
||||||
if unix:
|
if unix:
|
||||||
try:
|
|
||||||
sock = bind_unix_socket(unix, backlog=backlog)
|
sock = bind_unix_socket(unix, backlog=backlog)
|
||||||
except OSError as e:
|
|
||||||
raise ServerError(f"Error binding {unix}: {e}", quiet=True)
|
|
||||||
server_settings["unix"] = unix
|
server_settings["unix"] = unix
|
||||||
if sock is None:
|
if sock is None:
|
||||||
try:
|
try:
|
||||||
@@ -115,17 +112,6 @@ def configure_socket(
|
|||||||
server_settings["port"],
|
server_settings["port"],
|
||||||
backlog=backlog,
|
backlog=backlog,
|
||||||
)
|
)
|
||||||
except PermissionError:
|
|
||||||
p = server_settings["port"]
|
|
||||||
if not p or p >= 1024:
|
|
||||||
raise
|
|
||||||
addr = f"{server_settings['host']}:{p}"
|
|
||||||
error = ServerError(
|
|
||||||
f"Permission denied binding to {addr}.\n\n"
|
|
||||||
"Use `sudo sanic` to run on a privileged port.\n"
|
|
||||||
)
|
|
||||||
error.quiet = True
|
|
||||||
raise error
|
|
||||||
except OSError as e: # no cov
|
except OSError as e: # no cov
|
||||||
error = ServerError(
|
error = ServerError(
|
||||||
f"Sanic server could not start: {e}.\n\n"
|
f"Sanic server could not start: {e}.\n\n"
|
||||||
|
|||||||
@@ -532,11 +532,12 @@ class WebsocketImplProtocol:
|
|||||||
raise WebsocketClosed(
|
raise WebsocketClosed(
|
||||||
"Cannot receive from websocket interface after it is closed."
|
"Cannot receive from websocket interface after it is closed."
|
||||||
)
|
)
|
||||||
assembler_get: Optional[asyncio.Task] = None
|
|
||||||
try:
|
try:
|
||||||
self.recv_cancel = asyncio.Future()
|
self.recv_cancel = asyncio.Future()
|
||||||
assembler_get = asyncio.create_task(self.assembler.get(timeout))
|
tasks = (
|
||||||
tasks = (self.recv_cancel, assembler_get)
|
self.recv_cancel,
|
||||||
|
asyncio.ensure_future(self.assembler.get(timeout)),
|
||||||
|
)
|
||||||
done, pending = await asyncio.wait(
|
done, pending = await asyncio.wait(
|
||||||
tasks,
|
tasks,
|
||||||
return_when=asyncio.FIRST_COMPLETED,
|
return_when=asyncio.FIRST_COMPLETED,
|
||||||
@@ -550,11 +551,6 @@ class WebsocketImplProtocol:
|
|||||||
else:
|
else:
|
||||||
self.recv_cancel.cancel()
|
self.recv_cancel.cancel()
|
||||||
return done_task.result()
|
return done_task.result()
|
||||||
except asyncio.CancelledError:
|
|
||||||
# recv was cancelled
|
|
||||||
if assembler_get:
|
|
||||||
assembler_get.cancel()
|
|
||||||
raise
|
|
||||||
finally:
|
finally:
|
||||||
self.recv_cancel = None
|
self.recv_cancel = None
|
||||||
self.recv_lock.release()
|
self.recv_lock.release()
|
||||||
@@ -588,15 +584,16 @@ class WebsocketImplProtocol:
|
|||||||
"Cannot receive from websocket interface after it is closed."
|
"Cannot receive from websocket interface after it is closed."
|
||||||
)
|
)
|
||||||
messages = []
|
messages = []
|
||||||
assembler_get: Optional[asyncio.Task] = None
|
|
||||||
try:
|
try:
|
||||||
# Prevent pausing the transport when we're
|
# Prevent pausing the transport when we're
|
||||||
# receiving a burst of messages
|
# receiving a burst of messages
|
||||||
self.can_pause = False
|
self.can_pause = False
|
||||||
self.recv_cancel = asyncio.Future()
|
self.recv_cancel = asyncio.Future()
|
||||||
while True:
|
while True:
|
||||||
assembler_get = asyncio.create_task(self.assembler.get(0))
|
tasks = (
|
||||||
tasks = (self.recv_cancel, assembler_get)
|
self.recv_cancel,
|
||||||
|
asyncio.ensure_future(self.assembler.get(timeout=0)),
|
||||||
|
)
|
||||||
done, pending = await asyncio.wait(
|
done, pending = await asyncio.wait(
|
||||||
tasks,
|
tasks,
|
||||||
return_when=asyncio.FIRST_COMPLETED,
|
return_when=asyncio.FIRST_COMPLETED,
|
||||||
@@ -619,11 +616,6 @@ class WebsocketImplProtocol:
|
|||||||
# next message to pass into the Assembler
|
# next message to pass into the Assembler
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
self.recv_cancel.cancel()
|
self.recv_cancel.cancel()
|
||||||
except asyncio.CancelledError:
|
|
||||||
# recv_burst was cancelled
|
|
||||||
if assembler_get:
|
|
||||||
assembler_get.cancel()
|
|
||||||
raise
|
|
||||||
finally:
|
finally:
|
||||||
self.recv_cancel = None
|
self.recv_cancel = None
|
||||||
self.can_pause = True
|
self.can_pause = True
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ class WorkerManager:
|
|||||||
self.monitor()
|
self.monitor()
|
||||||
self.join()
|
self.join()
|
||||||
self.terminate()
|
self.terminate()
|
||||||
|
self.cleanup()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
for process in self.processes:
|
for process in self.processes:
|
||||||
@@ -147,6 +148,11 @@ class WorkerManager:
|
|||||||
for process in self.processes:
|
for process in self.processes:
|
||||||
process.terminate()
|
process.terminate()
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
"""Cleanup the worker processes."""
|
||||||
|
for process in self.processes:
|
||||||
|
process.exit()
|
||||||
|
|
||||||
def restart(
|
def restart(
|
||||||
self,
|
self,
|
||||||
process_names: Optional[List[str]] = None,
|
process_names: Optional[List[str]] = None,
|
||||||
|
|||||||
@@ -28,6 +28,24 @@ class WorkerMultiplexer:
|
|||||||
"state": ProcessState.ACKED.name,
|
"state": ProcessState.ACKED.name,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def set_serving(self, serving: bool) -> None:
|
||||||
|
"""Set the worker to serving.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
serving (bool): Whether the worker is serving.
|
||||||
|
"""
|
||||||
|
self._state._state[self.name] = {
|
||||||
|
**self._state._state[self.name],
|
||||||
|
"serving": serving,
|
||||||
|
}
|
||||||
|
|
||||||
|
def exit(self):
|
||||||
|
"""Run cleanup at worker exit."""
|
||||||
|
try:
|
||||||
|
del self._state._state[self.name]
|
||||||
|
except ConnectionRefusedError:
|
||||||
|
logger.debug("Monitor process has already exited.")
|
||||||
|
|
||||||
def restart(
|
def restart(
|
||||||
self,
|
self,
|
||||||
name: str = "",
|
name: str = "",
|
||||||
|
|||||||
@@ -65,6 +65,20 @@ class WorkerProcess:
|
|||||||
self.set_state(ProcessState.JOINED)
|
self.set_state(ProcessState.JOINED)
|
||||||
self._current_process.join()
|
self._current_process.join()
|
||||||
|
|
||||||
|
def exit(self):
|
||||||
|
limit = 100
|
||||||
|
while self.is_alive() and limit > 0:
|
||||||
|
sleep(0.1)
|
||||||
|
limit -= 1
|
||||||
|
|
||||||
|
if not self.is_alive():
|
||||||
|
try:
|
||||||
|
del self.worker_state[self.name]
|
||||||
|
except ConnectionRefusedError:
|
||||||
|
logger.debug("Monitor process has already exited.")
|
||||||
|
except KeyError:
|
||||||
|
logger.debug("Could not find worker state to delete.")
|
||||||
|
|
||||||
def terminate(self):
|
def terminate(self):
|
||||||
if self.state is not ProcessState.TERMINATED:
|
if self.state is not ProcessState.TERMINATED:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
@@ -77,7 +91,6 @@ class WorkerProcess:
|
|||||||
self.set_state(ProcessState.TERMINATED, force=True)
|
self.set_state(ProcessState.TERMINATED, force=True)
|
||||||
try:
|
try:
|
||||||
os.kill(self.pid, SIGINT)
|
os.kill(self.pid, SIGINT)
|
||||||
del self.worker_state[self.name]
|
|
||||||
except (KeyError, AttributeError, ProcessLookupError):
|
except (KeyError, AttributeError, ProcessLookupError):
|
||||||
...
|
...
|
||||||
|
|
||||||
@@ -118,6 +131,16 @@ class WorkerProcess:
|
|||||||
except AssertionError:
|
except AssertionError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# def _run(self, **kwargs):
|
||||||
|
# atexit.register(self._exit)
|
||||||
|
# self.target(**kwargs)
|
||||||
|
|
||||||
|
# def _exit(self):
|
||||||
|
# try:
|
||||||
|
# del self.worker_state[self.name]
|
||||||
|
# except ConnectionRefusedError:
|
||||||
|
# logger.debug("Monitor process has already exited.")
|
||||||
|
|
||||||
def spawn(self):
|
def spawn(self):
|
||||||
if self.state not in (ProcessState.IDLE, ProcessState.RESTARTING):
|
if self.state not in (ProcessState.IDLE, ProcessState.RESTARTING):
|
||||||
raise Exception("Cannot spawn a worker process until it is idle.")
|
raise Exception("Cannot spawn a worker process until it is idle.")
|
||||||
|
|||||||
Reference in New Issue
Block a user