Skip to content

Commit

Permalink
Windows, OSX, Python 3.5, less limitations, bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
qchateau committed Sep 30, 2019
1 parent b866c9d commit 685f46e
Show file tree
Hide file tree
Showing 24 changed files with 1,007 additions and 368 deletions.
13 changes: 13 additions & 0 deletions .ci/build-linux-sdist.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash
set -e -x

python -m pip install -U -r dev-requirements.txt
python setup.py sdist

# test wheel, move out of here otherwise pip thinks
# cbitstruct is already installed
mkdir testdir
cd testdir
python -m pip install cbitstruct --no-index -f ../dist/
python -m nose cbitstruct
cd ..
26 changes: 26 additions & 0 deletions .ci/build-manylinux-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash
set -e -x

# Show gcc version
gcc --version

# Compile wheels
for PYVER in $PY_VERSIONS; do
PYNUM=$(echo $PYVER | sed 's/\([0-9]\+\)\.\([0-9]\+\)\.[0-9]\+/\1\2/')
PYBIN=$(echo /opt/python/cp${PYNUM}*/bin)
"${PYBIN}/pip" install -r /io/dev-requirements.txt
"${PYBIN}/pip" wheel /io/ -w dist/
done

# Bundle external shared libraries into the wheels
for whl in dist/*.whl; do
auditwheel repair "$whl" --plat $PLAT -w /io/dist/
done

# Install packages and test
for PYVER in $PY_VERSIONS; do
PYNUM=$(echo $PYVER | sed 's/\([0-9]\+\)\.\([0-9]\+\)\.[0-9]\+/\1\2/')
PYBIN=$(echo /opt/python/cp${PYNUM}*/bin)
"${PYBIN}/pip" install cbitstruct --no-index -f /io/dist
(cd "$HOME"; "${PYBIN}/nosetests" cbitstruct)
done
6 changes: 6 additions & 0 deletions .ci/build-manylinux.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
set -e -x

docker pull $DOCKER_IMAGE
docker run --rm -e PLAT=$PLAT -e PY_VERSIONS="$PY_VERSIONS" -v `pwd`:/io $DOCKER_IMAGE $PRE_CMD /io/.ci/build-manylinux-docker.sh
ls dist/
30 changes: 30 additions & 0 deletions .ci/build-osx.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash
set -e -x

export PATH="/Users/travis/.pyenv/shims:${PATH}"

brew update > /dev/null
brew install openssl readline
brew outdated pyenv || brew upgrade pyenv
brew install pyenv-virtualenv

for PY_VERSION in $PY_VERSIONS; do
pyenv install --skip-existing $PY_VERSION
pyenv global $PY_VERSION

python -m pip install -U pip
python -m pip install -U -r dev-requirements.txt
python --version
python -m pip --version
python setup.py bdist_wheel

# test wheel, move out of here otherwise pip thinks
# cbitstruct is already installed
mkdir -p testdir
cd testdir
python -m pip install cbitstruct --no-index -f ../dist/
python -m nose cbitstruct
cd ..
done

ls dist/
30 changes: 30 additions & 0 deletions .ci/build-windows.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash
set -e -x

BASE_PATH=$PATH

for PY_VERSION in $PY_VERSIONS; do
PYNUM=$(echo $PY_VERSION | sed 's/\([0-9]\+\)\.\([0-9]\+\)\.[0-9]\+/\1\2/')

export PATH=/c/Python$PYNUM:/c/Python$PYNUM/Scripts:$BASE_PATH

choco install python --version $PY_VERSION --allow-downgrade
python -m pip install -U pip
python -m pip install -U -r dev-requirements.txt

python --version
python -m pip --version

python setup.py bdist_wheel

# test the wheel, need to move our of the current dir
# otherwise pip thinks cbitstruct is already installed
mkdir -p testdir
cd testdir
python -m pip install cbitstruct --no-index -f ../dist/
python -m nose cbitstruct
python -m pip uninstall -y cbitstruct
cd ..
done

ls dist/
10 changes: 10 additions & 0 deletions .ci/coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
set -e -x

export COVERAGE=1

python -m pip install -U -r dev-requirements.txt
python -m pip install -U cpp-coveralls
python setup.py install
python -m nose cbitstruct
coveralls --exclude clinic --gcov-options '\-lp'
5 changes: 5 additions & 0 deletions .ci/deploy-osx.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
set -e -x

python -m pip install -U pip twine wheel setuptools
python -m twine upload --repository-url "$PYPI_REPOSITORY_URL" -u "$PYPI_USERNAME" -p "$PYPI_PASSWORD" dist/*
5 changes: 5 additions & 0 deletions .ci/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
set -e -x

python -m pip install -U pip twine wheel setuptools
python -m twine upload --repository-url "$PYPI_REPOSITORY_URL" -u "$PYPI_USERNAME" -p "$PYPI_PASSWORD" dist/*
6 changes: 6 additions & 0 deletions .ci/performance.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
set -e -x

python -m pip install -U -r dev-requirements.txt
python setup.py install
python cbitstruct/tests/test_perf.py
6 changes: 6 additions & 0 deletions .ci/test-linux.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
set -e -x

python -m pip install -U -r dev-requirements.txt
python setup.py install
python -m nose cbitstruct
112 changes: 65 additions & 47 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,75 +1,93 @@
language: python
python: "3.7"

python: 3.7

env:
global:
- PY_VERSIONS="3.7.4 3.6.8 3.5.4"
- PYPI_REPOSITORY_URL=https://upload.pypi.org/legacy/
- PYPI_USERNAME=__token__
- secure: "jfAnPdxjTDw/mFbM2rmqjHaBtGpA+Rr+goCB5/Jp6RZ5N2M60nJliw8B5v7RwZDy//nSIdbTBU7cF/7WjEPwzrHUn5LQsS13ThzoS6KE7NUPa77TOxEpHXr4MJsyeSQMindEbAMeYP+lffI8/ISQ85evIbbSn6XzZSN/YBpQCTUGHWViqKIlpT70GYfRTeEJ0YvXxcJHKXoMuG7N80n8DW88FlfuI2age29cAe80pNn2yCZww2ZH0kG62HGQhzVv4v/uvxLLTaDP/JndYQyBnw6y5gesT2XCrgmCPgCTyVrtJsR96LjOukG2/T5etgpK0sgRwfgRk2DjKmlUqQOUm57lO+52ECaqqbaERFYe/Ub4JZ4DD05lKqm6wtQMD+fYl+TLKzGvCWayuLvxs7HqI+cz0BQQ7iynXuPUqX3xpkUDdAUzg9remmEJ1n8WG/NMHsv3Embh6zHS9RW7OUBXTELjrgJHVwjm8N0KtY063kMDArlSLNcf1gUPOx8MTOXBQWcauxmLfXMyaWrjHQ++1dWRFWjNVkUWfv+l17/ZHJHHnaJ6VpzhrZ5uku4LfEH8hgOCL6EYbO1PVXuEvFXDQX6wIFdbXaXSkx78ZgfiJf3xDqxcU1jl7+ipoCe2xkXaRkka0wg1gwCsfF53suXGIEIMRBWqrGJI9vETu5mVQDY="


deploy:
provider: script
skip_cleanup: true
script: bash travis/deploy.sh
script: bash .ci/deploy.sh
on:
tags: true
condition: $DEPLOY = 1


jobs:
include:
- stage: build
name: "Manylinux x86_64"
env: DOCKER_IMAGE=quay.io/pypa/manylinux2010_x86_64
PLAT=manylinux2010_x86_64
DEPLOY=1
services:
- docker
install:
- docker pull $DOCKER_IMAGE
script:
- docker run --rm -e PLAT=$PLAT -v `pwd`:/io $DOCKER_IMAGE $PRE_CMD /io/travis/build-wheels.sh
- ls wheelhouse/
name: OSX
os: osx
osx_image: xcode8.3 # macOS 10.12, oldest supported version
language: generic
env:
- DEPLOY=1
script: bash .ci/build-osx.sh
before_deploy:
- export PATH="/Users/travis/.pyenv/shims:${PATH}"
- pyenv global 3.7.4
before_cache:
- brew cleanup
- find /usr/local/Homebrew \! -regex ".+\.git.+" -delete
- find $HOME/.pyenv/ -name '*.pyc' -delete
cache:
directories:
- $HOME/.pyenv/
- $HOME/Library/Caches/Homebrew
- /usr/local/Homebrew

- stage: build
name: Windows
os: windows
language: c
env:
- DEPLOY=1
script: bash .ci/build-windows.sh
before_deploy:
- export PATH=/c/Python37:/c/Python37/Scripts:$PATH
- bash .ci/deploy.sh

- stage: build
name: "Manylinux i686"
services:
- docker
env: DOCKER_IMAGE=quay.io/pypa/manylinux1_i686
PRE_CMD=linux32
PLAT=manylinux1_i686
DEPLOY=1
install:
- docker pull $DOCKER_IMAGE
script:
- docker run --rm -e PLAT=$PLAT -v `pwd`:/io $DOCKER_IMAGE $PRE_CMD /io/travis/build-wheels.sh
- ls wheelhouse/
name: Manylinux x86_64
services: docker
env:
- DOCKER_IMAGE=quay.io/pypa/manylinux1_x86_64
- PLAT=manylinux1_x86_64
- DEPLOY=1
script: bash .ci/build-manylinux.sh

- stage: build
name: Manylinux i686
services: docker
env:
- DOCKER_IMAGE=quay.io/pypa/manylinux1_i686
- PRE_CMD=linux32
- PLAT=manylinux1_i686
- DEPLOY=1
script: bash .ci/build-manylinux.sh

- stage: build
name: Coverage
env: COVERAGE=1
install:
- pip install cpp-coveralls bitstruct
script:
- python setup.py install
- nosetests cbitstruct
- coveralls --exclude clinic --gcov-options '\-lp'
os: linux
script: bash .ci/coverage.sh

- stage: build
name: Performance
install:
- pip install bitstruct
script:
- python setup.py install
- python cbitstruct/tests/test_perf.py
os: linux
script: bash .ci/performance.sh

- stage: build
name: Linux 3.8-dev
os: linux
python: 3.8-dev
script: bash .ci/test-linux.sh

- stage: build
name: "Forward support"
python: "3.8-dev"
install:
- pip install bitstruct
script:
- python setup.py install
- nosetests cbitstruct
name: Source distribution
os: linux
env:
- DEPLOY=1
script: bash .ci/build-linux-sdist.sh
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include cbitstruct/clinic/*.h
52 changes: 25 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ Obvious increased performance comes with limitations described below.

# Installation

Coming soon
```bash
pip3 install cbitstruct
```

# Documentation

Expand All @@ -22,19 +24,15 @@ If you are not used to `bitstruct`, you should seriously consider using it first

| Limitation | Will it be lifted ? |
|------------|---------------------|
| Only tested on linux | Probably |
| All types except padding are limited to 64 bits | Maybe for 'raw' and 'text' types |
| May not work on big-endian architectures | Maybe |
| Exceptions differ from `bitstruct` | Probably not |
| Python >= 3.4 | Probably not |
| CPython only | Probably not |
| May not work on big-endian architectures | Probably not |
| Out-of-range numbers in packing operations are not detected | Probably not |
| Error messages are unclear | Will never be as clear as `bitstruct` |
| Python >= 3.5 | No |

Some limitations are there because I did not get the time or motivation to lift them up. Some other are deeply rooted into this library and may never be lifted.

Note that since this library is performance oriented, I will refuse changes that degrade performance (except bugfixes).

# Performance

## Comparing to `bitstruct`
Expand All @@ -43,29 +41,29 @@ The script available in `tests/test_perf.py` measures performance comparing to t

Here are the result "on my machine" (Ubuntu in Virtualbox on a laptop):
```
byteswap list of int | x 7.774 ( 8.634us -> 1.111us)
byteswap str | x 8.828 ( 12.992us -> 1.472us)
calcsize | x150.863 ( 60.566us -> 0.401us)
compiled pack | x 46.257 ( 35.994us -> 0.778us)
compiled pack_dict | x 26.440 ( 34.221us -> 1.294us)
compiled pack_into | x 35.690 ( 39.932us -> 1.119us)
compiled pack_into_dict | x 25.579 ( 38.404us -> 1.501us)
compiled unpack | x 35.285 ( 32.051us -> 0.908us)
compiled unpack_dict | x 21.984 ( 32.342us -> 1.471us)
compiled unpack_from | x 32.752 ( 31.353us -> 0.957us)
compiled unpack_from_dict | x 21.354 ( 32.016us -> 1.499us)
pack | x 84.085 ( 103.511us -> 1.231us)
pack_dict | x 47.052 ( 92.523us -> 1.966us)
pack_into | x 76.317 ( 105.994us -> 1.389us)
pack_into_dict | x 41.617 ( 96.210us -> 2.312us)
unpack | x 85.326 ( 94.035us -> 1.102us)
unpack_dict | x 41.138 ( 90.074us -> 2.190us)
unpack_from | x 82.550 ( 94.934us -> 1.150us)
unpack_from_dict | x 40.659 ( 89.523us -> 2.202us)
byteswap list of int | x 8.204 ( 9.208us -> 1.122us)
byteswap str | x 6.433 ( 9.689us -> 1.506us)
calcsize | x149.423 ( 61.967us -> 0.415us)
compiled pack | x 43.227 ( 34.758us -> 0.804us)
compiled pack_dict | x 26.490 ( 34.951us -> 1.319us)
compiled pack_into | x 32.017 ( 39.522us -> 1.234us)
compiled pack_into_dict | x 26.817 ( 38.984us -> 1.454us)
compiled unpack | x 34.454 ( 31.814us -> 0.923us)
compiled unpack_dict | x 23.534 ( 34.071us -> 1.448us)
compiled unpack_from | x 27.170 ( 31.884us -> 1.174us)
compiled unpack_from_dict | x 22.600 ( 33.927us -> 1.501us)
pack | x 78.314 ( 105.593us -> 1.348us)
pack_dict | x 52.916 ( 106.748us -> 2.017us)
pack_into | x 82.233 ( 119.950us -> 1.459us)
pack_into_dict | x 45.214 ( 111.338us -> 2.462us)
unpack | x 82.712 ( 93.686us -> 1.133us)
unpack_dict | x 41.064 ( 91.473us -> 2.228us)
unpack_from | x 81.678 ( 95.729us -> 1.172us)
unpack_from_dict | x 40.379 ( 90.430us -> 2.240us)
```

*Disclaimer:* these results may and will vary largely depending on the number of elements and types you pack/unpack. This script is provided as-is, and I will gladly accept an improved script providing more reliable results.


## The dict API
The `dict` API is marginally slower than the traditional one. As the packing/unpacking performance is quite high, the overhead of performing dictionary lookups and hashing significantly increas pack and unpacking duration.
The `dict` API is marginally slower than the traditional one. As the packing/unpacking performance is quite high, the overhead of performing dictionary lookups and hashing significantly increas pack and unpacking duration.
Loading

0 comments on commit 685f46e

Please sign in to comment.