├── .dockerignore ├── .github ├── dependabot.yml └── workflows │ ├── image-build.yml │ └── versionator.yml ├── LICENSE ├── README.md ├── assets ├── main.py └── start.sh ├── dockerfile └── templates ├── description.md ├── documentation.md └── quick_start.md /.dockerignore: -------------------------------------------------------------------------------- 1 | # Ignore everything 2 | ** 3 | 4 | # Allow files and directories 5 | !/assets/* 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | -------------------------------------------------------------------------------- /.github/workflows/image-build.yml: -------------------------------------------------------------------------------- 1 | name: Uvicorn Image Builder 2 | 3 | # Publish on new pushed, and build on Monday Morning (UTC) regardless. 4 | on: 5 | push: 6 | branches: 7 | - "main" 8 | paths-ignore: 9 | - "**/README.md" 10 | - "templates/**" 11 | schedule: 12 | - cron: "4 0 * * MON" 13 | 14 | jobs: 15 | Uvicorn-Builder: 16 | runs-on: ubuntu-latest 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | python_versions: ["3.9", "3.10", "3.11", "3.12", "3.13"] 21 | package_versions: ["0.31.1", "0.32.0", "0.32.1", "0.33.0", "0.34.0"] 22 | target_base: ["full", "slim"] 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@v4 26 | 27 | - name: "Create and Push Image" 28 | uses: multi-py/action-python-image-builder@v1 29 | timeout-minutes: 45 30 | with: 31 | package: "uvicorn" 32 | package_latest_version: "0.34.0" 33 | maintainer: "Robert Hafner " 34 | python_version: ${{ matrix.python_versions }} 35 | package_version: ${{ matrix.package_versions }} 36 | target_base: ${{ matrix.target_base }} 37 | registry_password: ${{ secrets.GITHUB_TOKEN }} 38 | dockerfile: "${{ github.workspace }}/dockerfile" 39 | docker_build_path: "${{ github.workspace }}/" 40 | -------------------------------------------------------------------------------- /.github/workflows/versionator.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Uvicorn Version Updater 3 | 4 | # Every 30 minutes check for a new version of the package. 5 | on: 6 | push: 7 | branches: 8 | - 'main' 9 | schedule: 10 | - cron: '0,30 * * * *' 11 | 12 | jobs: 13 | Version-Updater: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v4 18 | with: 19 | token: ${{ secrets.WORKFLOW_GITHUB_TOKEN }} 20 | 21 | - name: "Update Image Build Workflow" 22 | uses: multi-py/action-python-versionator@v1 23 | with: 24 | package: "uvicorn" 25 | git_name: "Robert Hafner" 26 | git_email: "tedivm@tedivm.com" 27 | action_path: ${{ github.workspace }}/.github/workflows/image-build.yml 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Robert Hafner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python-uvicorn 2 | 3 | 4 | A multiarchitecture container image for running Python with Uvicorn. 5 | 6 | Looking for the containers? [Head over to the Github Container Registry](https://github.com/multi-py/python-uvicorn/pkgs/container/python-uvicorn)! 7 | 8 | 9 | * [python-uvicorn](#python-uvicorn) 10 | * [Benefits](#benefits) 11 | * [Multi Architecture Builds](#multi-architecture-builds) 12 | * [Small Images via Multi Stage Builds](#small-images-via-multi-stage-builds) 13 | * [No Rate Limits](#no-rate-limits) 14 | * [Rapid Building of New Versions](#rapid-building-of-new-versions) 15 | * [Regular Updates](#regular-updates) 16 | * [How To](#how-to) 17 | * [Using the Full Image](#using-the-full-image) 18 | * [Using the Slim Image](#using-the-slim-image) 19 | * [Copy Just the Packages](#copy-just-the-packages) 20 | * [Add Your App](#add-your-app) 21 | * [PreStart Script](#prestart-script) 22 | * [Environmental Variables](#environmental-variables) 23 | * [PORT](#port) 24 | * [LOG_LEVEL](#log_level) 25 | * [MODULE_NAME](#module_name) 26 | * [VARIABLE_NAME](#variable_name) 27 | * [APP_MODULE](#app_module) 28 | * [PRE_START_PATH](#pre_start_path) 29 | * [RELOAD](#reload) 30 | * [UVICORN_EXTRA_FLAGS](#uvicorn_extra_flags) 31 | * [Python Versions](#python-versions) 32 | * [Image Variants](#image-variants) 33 | * [Full](#full) 34 | * [Slim](#slim) 35 | * [Architectures](#architectures) 36 | * [Sponsorship](#sponsorship) 37 | * [Tags](#tags) 38 | * [Older Tags](#older-tags) 39 | 40 | 41 | ## Benefits 42 | 43 | ### Multi Architecture Builds 44 | 45 | Every tag in this repository supports these architectures: 46 | 47 | * linux/amd64 48 | * linux/arm64 49 | * linux/arm/v7 50 | 51 | 52 | ### Small Images via Multi Stage Builds 53 | 54 | All libraries are compiled in one image before being moved into the final published image. This keeps all of the build tools out of the published container layers. 55 | 56 | ### No Rate Limits 57 | 58 | This project uses the Github Container Registry to store images, which have no rate limiting on pulls (unlike Docker Hub). 59 | 60 | ### Rapid Building of New Versions 61 | 62 | Within 30 minutes of a new release to uvicorn on PyPI builds will kick off for new containers. This means new versions can be used in hours, not days. 63 | 64 | ### Regular Updates 65 | 66 | Containers are rebuilt weekly in order to take on the security patches from upstream containers. 67 | 68 | ## How To 69 | 70 | ### Using the Full Image 71 | The Full Images use the base Python Docker images as their parent. These images are based off of Ubuntu and contain a variety of build tools. 72 | 73 | To pull the latest full version: 74 | 75 | ```bash 76 | docker pull ghcr.io/multi-py/python-uvicorn:py3.12-LATEST 77 | ``` 78 | 79 | To include it in the dockerfile instead: 80 | 81 | ```dockerfile 82 | FROM ghcr.io/multi-py/python-uvicorn:py3.12-LATEST 83 | ``` 84 | 85 | ### Using the Slim Image 86 | 87 | The Slim Images use the base Python Slim Docker images as their parent. These images are very similar to the Full images, but without the build tools. These images are much smaller than their counter parts but are more difficult to compile wheels on. 88 | 89 | To pull the latest slim version: 90 | 91 | ```bash 92 | docker pull ghcr.io/multi-py/python-uvicorn:py3.12-slim-LATEST 93 | ``` 94 | 95 | To include it in the dockerfile instead: 96 | 97 | ```dockerfile 98 | FROM ghcr.io/multi-py/python-uvicorn:py3.12-slim-LATEST 99 | ``` 100 | 101 | 102 | 103 | 104 | 105 | ### Copy Just the Packages 106 | It's also possible to copy just the Python packages themselves. This is particularly useful when you want to use the precompiled libraries from multiple containers. 107 | 108 | ```dockerfile 109 | FROM python:3.12 110 | 111 | COPY --from=ghcr.io/multi-py/python-uvicorn:py3.12-slim-LATEST /usr/local/lib/python3.12/site-packages/* /usr/local/lib/python3.12/site-packages/ 112 | ``` 113 | 114 | ### Add Your App 115 | 116 | By default the startup script checks for the following packages and uses the first one it can find- 117 | 118 | * `/app/app/main.py` 119 | * `/app/main.py` 120 | 121 | If you are using pip to install dependencies your dockerfile could look like this- 122 | 123 | ```dockerfile 124 | FROM ghcr.io/multi-py/python-uvicorn:py3.12-0.34.0 125 | 126 | COPY requirements /requirements 127 | RUN pip install --no-cache-dir -r /requirements 128 | COPY ./app app 129 | ``` 130 | 131 | ### PreStart Script 132 | 133 | When the container is launched it will run the script at `/app/prestart.sh` before starting the uvicorn service. This is an ideal place to put things like database migrations. 134 | 135 | 136 | ## Environmental Variables 137 | 138 | ### `PORT` 139 | 140 | The port that the application inside of the container will listen on. This is different from the host port that gets mapped to the container. 141 | 142 | 143 | ### `LOG_LEVEL` 144 | 145 | The uvicorn log level. Must be one of the following: 146 | 147 | * `critical` 148 | * `error` 149 | * `warning` 150 | * `info` 151 | * `debug` 152 | * `trace` 153 | 154 | 155 | ### `MODULE_NAME` 156 | 157 | The python module that uvicorn will import. This value is used to generate the APP_MODULE value. 158 | 159 | 160 | ### `VARIABLE_NAME` 161 | 162 | The python variable containing the ASGI application inside of the module that uvicorn imports. This value is used to generate the APP_MODULE value. 163 | 164 | 165 | ### `APP_MODULE` 166 | 167 | The python module and variable that is passed to uvicorn. When used the `VARIABLE_NAME` and `MODULE_NAME` environmental variables are ignored. 168 | 169 | 170 | ### `PRE_START_PATH` 171 | 172 | Where to find the prestart script. 173 | 174 | 175 | ### `RELOAD` 176 | 177 | When this is set to the string `true` uvicorn is launched in reload mode. If any files change uvicorn will reload the modules again, allowing for quick debugging. This comes at a performance cost, however, and should not be enabled on production machines. 178 | 179 | 180 | ### `UVICORN_EXTRA_FLAGS` 181 | 182 | This variable can be used to pass extra flags to the `uvicorn` command on launch. It's value is added directly to the command that is called, and has to be formatted appropriately for the command line. 183 | 184 | ## Python Versions 185 | 186 | This project actively supports these Python versions: 187 | 188 | * 3.12 189 | * 3.11 190 | * 3.10 191 | * 3.9 192 | * 3.8 193 | 194 | 195 | ## Image Variants 196 | 197 | Like the upstream Python containers themselves a variety of image variants are supported. 198 | 199 | 200 | ### Full 201 | 202 | The default container type, and if you're not sure what container to use start here. It has a variety of libraries and build tools installed, making it easy to extend. 203 | 204 | 205 | 206 | ### Slim 207 | 208 | This container is similar to Full but with far less libraries and tools installed by default. If yo're looking for the tiniest possible image with the most stability this is your best bet. 209 | 210 | 211 | 212 | 213 | 214 | ## Architectures 215 | 216 | Every tag in this repository supports these architectures: 217 | 218 | * linux/amd64 219 | * linux/arm64 220 | * linux/arm/v7 221 | 222 | 223 | ## Sponsorship 224 | 225 | If you get use out of these containers please consider sponsoring me using Github! 226 |
227 | 228 | [![Github Sponsorship](https://raw.githubusercontent.com/mechPenSketch/mechPenSketch/master/img/github_sponsor_btn.svg)](https://github.com/sponsors/tedivm) 229 | 230 |
231 | 232 | ## Tags 233 | * Recommended Image: `ghcr.io/multi-py/python-uvicorn:py3.12-0.34.0` 234 | * Slim Image: `ghcr.io/multi-py/python-uvicorn:py3.12-slim-0.34.0` 235 | 236 | Tags are based on the package version, python version, and the upstream container the container is based on. 237 | 238 | | uvicorn Version | Python Version | Full Container | Slim Container | 239 | |-----------------------|----------------|----------------|----------------| 240 | | latest | 3.12 | py3.12-latest | py3.12-slim-latest | 241 | | latest | 3.11 | py3.11-latest | py3.11-slim-latest | 242 | | latest | 3.10 | py3.10-latest | py3.10-slim-latest | 243 | | latest | 3.9 | py3.9-latest | py3.9-slim-latest | 244 | | latest | 3.8 | py3.8-latest | py3.8-slim-latest | 245 | | 0.34.0 | 3.12 | py3.12-0.34.0 | py3.12-slim-0.34.0 | 246 | | 0.34.0 | 3.11 | py3.11-0.34.0 | py3.11-slim-0.34.0 | 247 | | 0.34.0 | 3.10 | py3.10-0.34.0 | py3.10-slim-0.34.0 | 248 | | 0.34.0 | 3.9 | py3.9-0.34.0 | py3.9-slim-0.34.0 | 249 | | 0.34.0 | 3.8 | py3.8-0.34.0 | py3.8-slim-0.34.0 | 250 | | 0.33.0 | 3.12 | py3.12-0.33.0 | py3.12-slim-0.33.0 | 251 | | 0.33.0 | 3.11 | py3.11-0.33.0 | py3.11-slim-0.33.0 | 252 | | 0.33.0 | 3.10 | py3.10-0.33.0 | py3.10-slim-0.33.0 | 253 | | 0.33.0 | 3.9 | py3.9-0.33.0 | py3.9-slim-0.33.0 | 254 | | 0.33.0 | 3.8 | py3.8-0.33.0 | py3.8-slim-0.33.0 | 255 | | 0.32.1 | 3.12 | py3.12-0.32.1 | py3.12-slim-0.32.1 | 256 | | 0.32.1 | 3.11 | py3.11-0.32.1 | py3.11-slim-0.32.1 | 257 | | 0.32.1 | 3.10 | py3.10-0.32.1 | py3.10-slim-0.32.1 | 258 | | 0.32.1 | 3.9 | py3.9-0.32.1 | py3.9-slim-0.32.1 | 259 | | 0.32.1 | 3.8 | py3.8-0.32.1 | py3.8-slim-0.32.1 | 260 | | 0.32.0 | 3.12 | py3.12-0.32.0 | py3.12-slim-0.32.0 | 261 | | 0.32.0 | 3.11 | py3.11-0.32.0 | py3.11-slim-0.32.0 | 262 | | 0.32.0 | 3.10 | py3.10-0.32.0 | py3.10-slim-0.32.0 | 263 | | 0.32.0 | 3.9 | py3.9-0.32.0 | py3.9-slim-0.32.0 | 264 | | 0.32.0 | 3.8 | py3.8-0.32.0 | py3.8-slim-0.32.0 | 265 | | 0.31.1 | 3.12 | py3.12-0.31.1 | py3.12-slim-0.31.1 | 266 | | 0.31.1 | 3.11 | py3.11-0.31.1 | py3.11-slim-0.31.1 | 267 | | 0.31.1 | 3.10 | py3.10-0.31.1 | py3.10-slim-0.31.1 | 268 | | 0.31.1 | 3.9 | py3.9-0.31.1 | py3.9-slim-0.31.1 | 269 | | 0.31.1 | 3.8 | py3.8-0.31.1 | py3.8-slim-0.31.1 | 270 | 271 | 272 | ### Older Tags 273 | 274 | Older tags are left for historic purposes but do not receive updates. A full list of tags can be found on the package's [registry page](https://github.com/multi-py/python-uvicorn/pkgs/container/python-uvicorn). 275 | 276 | -------------------------------------------------------------------------------- /assets/main.py: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # This is an example "pure python" uvicorn app. 4 | # 5 | 6 | async def app(scope, receive, send): 7 | assert scope['type'] == 'http' 8 | 9 | await send({ 10 | 'type': 'http.response.start', 11 | 'status': 200, 12 | 'headers': [ 13 | [b'content-type', b'text/plain'], 14 | ], 15 | }) 16 | await send({ 17 | 'type': 'http.response.body', 18 | 'body': b'Hello, world!', 19 | }) 20 | -------------------------------------------------------------------------------- /assets/start.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | set -e 3 | 4 | # 5 | # The follow block comes from tiangolo/uvicorn-gunicorn-docker 6 | # MIT License: https://github.com/tiangolo/uvicorn-gunicorn-docker/blob/master/LICENSE 7 | # 8 | 9 | if [ -f /app/app/main.py ]; then 10 | DEFAULT_MODULE_NAME=app.main 11 | elif [ -f /app/main.py ]; then 12 | DEFAULT_MODULE_NAME=main 13 | fi 14 | MODULE_NAME=${MODULE_NAME:-$DEFAULT_MODULE_NAME} 15 | VARIABLE_NAME=${VARIABLE_NAME:-app} 16 | export APP_MODULE=${APP_MODULE:-"$MODULE_NAME:$VARIABLE_NAME"} 17 | 18 | # If there's a prestart.sh script in the /app directory or other path specified, run it before starting 19 | PRE_START_PATH=${PRE_START_PATH:-/app/prestart.sh} 20 | echo "Checking for script in $PRE_START_PATH" 21 | if [ -f $PRE_START_PATH ]; then 22 | echo "Running script $PRE_START_PATH" 23 | . "$PRE_START_PATH" 24 | else 25 | echo "There is no script $PRE_START_PATH" 26 | fi 27 | 28 | # 29 | # End of tiangolo/uvicorn-gunicorn-docker block 30 | # 31 | 32 | if [[ $RELOAD == "true" ]]; then 33 | exec python -m uvicorn "$APP_MODULE" --host 0.0.0.0 --port ${PORT:-80} --log-level "${LOG_LEVEL:-info}" $UVICORN_EXTRA_FLAGS --reload 34 | else 35 | exec python -m uvicorn "$APP_MODULE" --host 0.0.0.0 --port ${PORT:-80} --log-level "${LOG_LEVEL:-info}" $UVICORN_EXTRA_FLAGS 36 | fi 37 | -------------------------------------------------------------------------------- /dockerfile: -------------------------------------------------------------------------------- 1 | ARG python_version=3.9 2 | ARG build_target=$python_version 3 | ARG publish_target=$python_version 4 | 5 | FROM python:$build_target as Builder 6 | 7 | # Add arguments to container scope 8 | ARG build_target 9 | ARG package 10 | ARG package_version 11 | 12 | # Only add build tools for alpine image. The ubuntu based images have build tools already. 13 | # Only runs if `apk` is on the system. 14 | RUN if which apk ; then apk add python3-dev libffi-dev libevent-dev build-base bash; fi 15 | 16 | # Install rust on alpine if not using linux/arm/v7 17 | RUN bash -c 'if which apk && [[ "$TARGETPLATFORM" != "linux/arm/v7" ]] ; then apk add cargo rust gcc musl-dev maturin; fi' 18 | 19 | 20 | # Install packaer and build all dependencies. 21 | RUN pip install $package==$package_version 22 | 23 | 24 | # Install limited packages on linux/arm/v7- exclude anything relying on Rust. 25 | RUN bash -c 'if [[ "$TARGETPLATFORM" == "linux/arm/v7" ]] ; then pip install uvicorn==$package_version websockets>=10.0 httptools>=0.4.0 uvloop>=0.14.0,!=0.15.0,!=0.15.1 python-dotenv>=0.13 PyYAML>=5.1 ; fi' 26 | RUN bash -c 'if [[ "$TARGETPLATFORM" != "linux/arm/v7" ]] ; then pip install uvicorn[standard]==$package_version ; fi' 27 | 28 | 29 | 30 | 31 | # Build our actual container now. 32 | FROM python:$publish_target 33 | 34 | # Add args to container scope. 35 | ARG publish_target 36 | ARG python_version 37 | ARG package 38 | ARG maintainer="" 39 | ARG TARGETPLATFORM="" 40 | LABEL python=$python_version 41 | LABEL package=$package 42 | LABEL maintainer=$maintainer 43 | LABEL org.opencontainers.image.description="python:$publish_target $package:$package_version $TARGETPLATFORM" 44 | 45 | 46 | # Copy all of the python files built in the Builder container into this smaller container. 47 | COPY --from=Builder /usr/local/lib/python$python_version /usr/local/lib/python$python_version 48 | 49 | # Startup Script 50 | COPY ./assets/start.sh /start.sh 51 | RUN chmod +x /start.sh 52 | 53 | # Example application so container "works" when run directly. 54 | COPY ./assets/main.py /app/main.py 55 | WORKDIR /app/ 56 | 57 | ENV PYTHONPATH=/app 58 | 59 | CMD ["/start.sh"] 60 | -------------------------------------------------------------------------------- /templates/description.md: -------------------------------------------------------------------------------- 1 | 2 | A multiarchitecture container image for running Python with Uvicorn. 3 | 4 | Looking for the containers? [Head over to the Github Container Registry](https://github.com/multi-py/python-uvicorn/pkgs/container/python-uvicorn)! 5 | -------------------------------------------------------------------------------- /templates/documentation.md: -------------------------------------------------------------------------------- 1 | ## Environmental Variables 2 | 3 | ### `PORT` 4 | 5 | The port that the application inside of the container will listen on. This is different from the host port that gets mapped to the container. 6 | 7 | 8 | ### `LOG_LEVEL` 9 | 10 | The uvicorn log level. Must be one of the following: 11 | 12 | * `critical` 13 | * `error` 14 | * `warning` 15 | * `info` 16 | * `debug` 17 | * `trace` 18 | 19 | 20 | ### `MODULE_NAME` 21 | 22 | The python module that uvicorn will import. This value is used to generate the APP_MODULE value. 23 | 24 | 25 | ### `VARIABLE_NAME` 26 | 27 | The python variable containing the ASGI application inside of the module that uvicorn imports. This value is used to generate the APP_MODULE value. 28 | 29 | 30 | ### `APP_MODULE` 31 | 32 | The python module and variable that is passed to uvicorn. When used the `VARIABLE_NAME` and `MODULE_NAME` environmental variables are ignored. 33 | 34 | 35 | ### `PRE_START_PATH` 36 | 37 | Where to find the prestart script. 38 | 39 | 40 | ### `RELOAD` 41 | 42 | When this is set to the string `true` uvicorn is launched in reload mode. If any files change uvicorn will reload the modules again, allowing for quick debugging. This comes at a performance cost, however, and should not be enabled on production machines. 43 | 44 | 45 | ### `UVICORN_EXTRA_FLAGS` 46 | 47 | This variable can be used to pass extra flags to the `uvicorn` command on launch. It's value is added directly to the command that is called, and has to be formatted appropriately for the command line. 48 | -------------------------------------------------------------------------------- /templates/quick_start.md: -------------------------------------------------------------------------------- 1 | {% set short_repository = repository.split("/")[1] -%} 2 | 3 | ### Add Your App 4 | 5 | By default the startup script checks for the following packages and uses the first one it can find- 6 | 7 | * `/app/app/main.py` 8 | * `/app/main.py` 9 | 10 | If you are using pip to install dependencies your dockerfile could look like this- 11 | 12 | ```dockerfile 13 | FROM ghcr.io/{{ organization }}/{{ short_repository }}:py{{ python_versions|last }}-{{ package_versions|last }} 14 | 15 | COPY requirements /requirements 16 | RUN pip install --no-cache-dir -r /requirements 17 | COPY ./app app 18 | ``` 19 | 20 | ### PreStart Script 21 | 22 | When the container is launched it will run the script at `/app/prestart.sh` before starting the uvicorn service. This is an ideal place to put things like database migrations. 23 | 24 | --------------------------------------------------------------------------------