Uvicorn: Use optional package installs.

Created on 10 Oct 2018  路  29Comments  路  Source: encode/uvicorn

Instead of the platform detection I鈥檇 like uvicorn to use optional installs.

  • pip install uvicorn - Just the package itself.
  • pip install uvicorn[standard] - uvloop/httptools/websockets
  • pip install uvicorn[pure] - asyncio/h11/wsproto
  • pip install uvicorn[full] - Everything

All 29 comments

I like this, since this would solve an issue I have with a Docker container on which the installation of uvloop fails (not enough RAM).

However, does this mean that just doing pip install uvicorn will not give me a working uvicorn?

What about:

  • pip install uvicorn - deps click/h11/wsproto
  • pip install uvicorn[fast] - deps h11/wsproto/uvloop/httptools/websocket

In this case a plain install should give a fully working server, all in pure Python. The user can then separately install uvloop/httptools/websockets, or use the [fast] option.

I'd be happy with that yup. Tho I'd probably prefer uvicorn and uvicorn[full].

Implementation in #224 resolves this as requested in the opening of this...

uvicorn/uvicorn[pure]/uvicorn[standard]/uvicorn[full]

We could easily make sure that Uvicorn raises loud errors if the cli is used and click isn't installed. Or we could go with @almarklein's suggestion and make the base case be the pure install.

cc @carlwgeorge

Should I go ahead and rework the PR to match the layout in https://github.com/encode/uvicorn/issues/219#issuecomment-430178997? Also, should uvicorn[fast] also install click?

@carltongibson @almarklein Depends which of the two we think makes more sense.

(And yes, we'd include click whatever.)

The alternative tack would be to keep everything as per #219, except using this:

    install_requires=[
        'click',
        'h11',
        'wsproto', 
   ],
    extras_require={
        'full': [
            'uvloop',
            'httptools',
            'websockets>=6.0'
        ]
    },

(Did you mean me?)

@carltongibson Nope. I meant @carlwgeorge

(hits autocomplete a few more times)

I agree that pip install uvicorn should give you a functional base install. I modified the PR to always install click, h11, and wsproto, with a full option to also install uvloop, httptools, and websockets.

A lightweight option here would be...

pip install uvicorn  # Just click, h11.
pip install uvicorn[full]  # +websockets +watchdog +uvloop/httptools (conditional on env markers)

That鈥檚 probably a good balance of:

  1. Keeping it simple.
  2. Allowing for minimal installs if wanted.
  3. Ensuring that the base install is operational.

Right?

@tomchristie Jupp, that would really be helpful. I currently have to use h11 because httptools ships no wheels and I'd need manylinux wheels to be able to use it. What's needed to move this forward?

This just made me realize that https://www.uvicorn.org/settings/#implementation seems to be wrong nowadays. httptools requires compilation on every system since it just ships a .tar.gz :)

@apollo13 - I'm not sure what you mean?

$ python -m venv venv
$ venv/bin/pip install httptools
Collecting httptools
Installing collected packages: httptools
Successfully installed httptools-0.0.13

@tomchristie Looking at PyPI it is just a tar.gz https://pypi.org/project/httptools/#files and the setup.py says: https://github.com/MagicStack/httptools/blob/master/setup.py#L27-L32 so I'd argue it needs a compiler. Your system seems to have one so it just work (tm)

In a docker container:

root@08fb3e67185f:/# rm /usr/bin/gcc*
root@08fb3e67185f:/# pip install httptools
Collecting httptools
  Downloading https://files.pythonhosted.org/packages/1b/03/215969db11abe8741e9c266a4cbe803a372bd86dd35fa0084c4df6d4bd00/httptools-0.0.13.tar.gz (104kB)
     |鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅| 112kB 1.2MB/s 
Building wheels for collected packages: httptools
  Building wheel for httptools (setup.py) ... error
  ERROR: Command errored out with exit status 1:
   command: /usr/local/bin/python -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-1flxic8y/httptools/setup.py'"'"'; __file__='"'"'/tmp/pip-install-1flxic8y/httptools/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' bdist_wheel -d /tmp/pip-wheel-_7dxx7x1 --python-tag cp37
       cwd: /tmp/pip-install-1flxic8y/httptools/
  Complete output (28 lines):
  running bdist_wheel
  running build
  running build_py
  creating build
  creating build/lib.linux-x86_64-3.7
  creating build/lib.linux-x86_64-3.7/httptools
  copying httptools/__init__.py -> build/lib.linux-x86_64-3.7/httptools
  creating build/lib.linux-x86_64-3.7/httptools/parser
  copying httptools/parser/__init__.py -> build/lib.linux-x86_64-3.7/httptools/parser
  copying httptools/parser/errors.py -> build/lib.linux-x86_64-3.7/httptools/parser
  running egg_info
  writing httptools.egg-info/PKG-INFO
  writing dependency_links to httptools.egg-info/dependency_links.txt
  writing top-level names to httptools.egg-info/top_level.txt
  reading manifest file 'httptools.egg-info/SOURCES.txt'
  reading manifest template 'MANIFEST.in'
  writing manifest file 'httptools.egg-info/SOURCES.txt'
  copying httptools/parser/parser.c -> build/lib.linux-x86_64-3.7/httptools/parser
  running build_ext
  building 'httptools.parser.parser' extension
  creating build/temp.linux-x86_64-3.7
  creating build/temp.linux-x86_64-3.7/httptools
  creating build/temp.linux-x86_64-3.7/httptools/parser
  creating build/temp.linux-x86_64-3.7/vendor
  creating build/temp.linux-x86_64-3.7/vendor/http-parser
  gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/usr/local/include/python3.7m -c httptools/parser/parser.c -o build/temp.linux-x86_64-3.7/httptools/parser/parser.o -O2
  unable to execute 'gcc': No such file or directory
  error: command 'gcc' failed with exit status 1
  ----------------------------------------
  ERROR: Failed building wheel for httptools
  Running setup.py clean for httptools
Failed to build httptools
Installing collected packages: httptools
    Running setup.py install for httptools ... error
    ERROR: Command errored out with exit status 1:
     command: /usr/local/bin/python -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-1flxic8y/httptools/setup.py'"'"'; __file__='"'"'/tmp/pip-install-1flxic8y/httptools/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-sgcnentz/install-record.txt --single-version-externally-managed --compile
         cwd: /tmp/pip-install-1flxic8y/httptools/
    Complete output (28 lines):
    running install
    running build
    running build_py
    creating build
    creating build/lib.linux-x86_64-3.7
    creating build/lib.linux-x86_64-3.7/httptools
    copying httptools/__init__.py -> build/lib.linux-x86_64-3.7/httptools
    creating build/lib.linux-x86_64-3.7/httptools/parser
    copying httptools/parser/__init__.py -> build/lib.linux-x86_64-3.7/httptools/parser
    copying httptools/parser/errors.py -> build/lib.linux-x86_64-3.7/httptools/parser
    running egg_info
    writing httptools.egg-info/PKG-INFO
    writing dependency_links to httptools.egg-info/dependency_links.txt
    writing top-level names to httptools.egg-info/top_level.txt
    reading manifest file 'httptools.egg-info/SOURCES.txt'
    reading manifest template 'MANIFEST.in'
    writing manifest file 'httptools.egg-info/SOURCES.txt'
    copying httptools/parser/parser.c -> build/lib.linux-x86_64-3.7/httptools/parser
    running build_ext
    building 'httptools.parser.parser' extension
    creating build/temp.linux-x86_64-3.7
    creating build/temp.linux-x86_64-3.7/httptools
    creating build/temp.linux-x86_64-3.7/httptools/parser
    creating build/temp.linux-x86_64-3.7/vendor
    creating build/temp.linux-x86_64-3.7/vendor/http-parser
    gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/usr/local/include/python3.7m -c httptools/parser/parser.c -o build/temp.linux-x86_64-3.7/httptools/parser/parser.o -O2
    unable to execute 'gcc': No such file or directory
    error: command 'gcc' failed with exit status 1
    ----------------------------------------
ERROR: Command errored out with exit status 1: /usr/local/bin/python -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-1flxic8y/httptools/setup.py'"'"'; __file__='"'"'/tmp/pip-install-1flxic8y/httptools/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-sgcnentz/install-record.txt --single-version-externally-managed --compile Check the logs for full command output.

So is pip install uvicorn failing for you right now then?

My initial interest for this issue was also because in a docker container it would compile the dependencies from source. The problem for me (at the time) was that the build of uvloop requires more RAM than I had available on my cheap VM.

All that aside, for services that have low traffic, I prefer the pure Python libs, because it makes the docker container much lighter, so I can host more services on a VM without needing to upgrade my hardware.

BTW: #298 is ready to go from my end :)

BTW: #298 is ready to go from my end :)

@almarklein - Great yup! It's number one on my list. Just waiting for a proper moment so I can review if there's anything else we'd want in a "Uvicorn 0.11" release.

So is pip install uvicorn failing for you right now then?

Yes, and it would fail for you to if you wouldn't have a compiler installed (pip nicely hides that fact).

if you wouldn't have a compiler installed

Not necessarily, because binary wheels, right? I'd think the binary wheels are compatible with most Linux Docker images, but somehow Pip does not think so. (Not my area of expertise tho, maybe someone else knows better.)

@almarklein httptools does not ship wheels though: https://pypi.org/project/httptools/#files

I'd think the binary wheels are compatible with most Linux Docker images, but somehow Pip does not think so.

depends, most of them are just not compatible with alpine linux afaiks.

Oh: "Alpine uses a different C library, musl, instead of the more common glibc." (source)

I'm slightly surprised we're not seeing more folks chiming in with "installing uvicorn fails on my platform".

Do either of "websockets" (Includes a small C extention but I think has an optional pure python fallback) or "uvloop" also fail to install for you.

I'm wondering if there's an environment marker or some other workaround we could use to make sure that they only installed optionally.

(Tho we're probably okay once we move to pip install uvicorn and pip install uvicorn[standard], and can just clearly document any failure cases)

I'm slightly surprised we're not seeing more folks chiming in with "installing uvicorn fails on my platform".

Because most people can compile stuff; I also can but I try to avoid it because it should run everywhere.

Do either of "websockets" (Includes a small C extention but I think has an optional pure python fallback) or "uvloop" also fail to install for you.

If you look at websockets (https://pypi.org/project/websockets/#files) they include binary wheels, no compilation needed during install, same for https://pypi.org/project/uvloop/#files

I'm wondering if there's an environment marker or some other workaround we could use to make sure that they only installed optionally.

Sadly no, there is nothing that says "I can compile something"

Just to clarify: Binary packages are fine as long as PyPI has .whl files (at least if they are maintained properly). If there are no .whl files but only .tar.gz on PyPI it means that you need a compiler, which generally means the wheel file pip locally builds and caches will work on your platform and your platform only.

Oh: "Alpine uses a different C library, musl, instead of the more common glibc." (source)

Yes, this is one of the reasons why I generally try to use debian-based python docker containers because installing in them is way faster (as you can use the prebuilt wheels). As a result you can also use python:slimin the containers which don't ship GCC. The default python containers ship gcc so you can compile missing stuff.

@tomchristie is there anything I can do to help move this forward?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tomchristie picture tomchristie  路  4Comments

tomchristie picture tomchristie  路  6Comments

unitto1 picture unitto1  路  7Comments

Legionof7 picture Legionof7  路  3Comments

itayperl picture itayperl  路  3Comments