Skip to content

Commit

Permalink
Use Cython for popcount
Browse files Browse the repository at this point in the history
  • Loading branch information
unmade committed Apr 6, 2020
1 parent c5bab1d commit 15c5b44
Show file tree
Hide file tree
Showing 13 changed files with 2,925 additions and 31 deletions.
10 changes: 8 additions & 2 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
**/*
**

!src
!**/*.py
!build.py
!pyproject.toml
!poetry.lock
!README.rst

**/*.egg-info
**/.DS_Store
**/Thumbs.db
**/__pycache__
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/audiomatch/popcount/_popcount.c linguist-detectable=false
32 changes: 28 additions & 4 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,38 @@ jobs:
with:
python-version: "3.8"

- name: Publish Package to PyPI
- name: Install poetry
run: |
python -m pip install --upgrade pip
pip install poetry
- name: Build Package
run: |
poetry build --format sdist
- name: Unpack sdist to build wheels
env:
TAG: ${{ steps.tag_name.outputs.TAG }}
run: |
tar -xf dist/audiomatch-"${TAG}".tar.gz -C dist/
mv dist/audiomatch-"${TAG}" dist/audiomatch
- name: Build manylinux wheels
uses: RalfG/[email protected]
with:
python-versions: "cp38-cp38"
package-path: 'dist/audiomatch'

- name: Copy wheel to dist
run: |
cp wheelhouse/*-manylinux*.whl dist/
rm -rf dist/audiomatch
- name: Upload Package to PyPI
env:
PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }}
PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python -m pip install --upgrade pip
pip install poetry
poetry build
poetry publish -u $PYPI_USERNAME -p $PYPI_PASSWORD
- name: Wait for PyPI to update indexes
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/lint-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ jobs:
run: |
tox -e py
- name: Test with no extensions at all
run: |
tox -e py-noextensions
- name: Generate coverage report
run: |
tox -e coverage
Expand Down
7 changes: 4 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
FROM python:3.8-alpine

RUN apk update \
&& apk add --no-cache ffmpeg ffmpeg-libs \
RUN apk add --no-cache ffmpeg ffmpeg-libs \
&& echo "http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories \
&& apk add --no-cache chromaprint-dev

ARG package_version
ENV PACKAGE_VERSION=$package_version

RUN pip3 install "audiomatch==${PACKAGE_VERSION}"
RUN apk add --virtual .build-deps gcc libc-dev libffi-dev openssl-dev \
&& pip3 install "audiomatch==${PACKAGE_VERSION}" \
&& apk del .build-deps gcc libc-dev libffi-dev openssl-dev

ENTRYPOINT ["audiomatch"]
7 changes: 7 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ Then you can install this library:
pip install audiomatch
To do things fast *audiomatch* requires C compiler and Python headers to be installed.
You can skip compilation by setting ``AUDIOMATCH_NO_EXTENSIONS`` environment variable:

.. code-block:: bash
AUDIOMATCH_NO_EXTENSIONS=1 pip install audiomatch
You can avoid installing all this libraries on your computer and run everything in
docker:

Expand Down
26 changes: 26 additions & 0 deletions build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import os
from distutils.extension import Extension


def _get_bool(key: str, default: bool = False) -> bool:
value = os.getenv(key)
if value is not None:
return value.lower() in ["true", "1", "t"]
return default


def _get_extensions():
return [
Extension(
"audiomatch.popcount._popcount",
sources=["src/audiomatch/popcount/_popcount.c"],
)
]


def build(setup_kwargs):
"""This function is mandatory in order to build the extensions."""
use_extensions = not _get_bool("AUDIOMATCH_NO_EXTENSIONS", default=False)

if use_extensions:
setup_kwargs.update({"ext_modules": _get_extensions()})
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "audiomatch"
version = "0.1.8"
version = "0.2.0"
description = "A small command-line tool to find similar audio files"
keywords = ["duplicate", "detection", "audio", "fingerprinting", "command-line"]
readme = "README.rst"
Expand All @@ -18,6 +18,8 @@ classifiers = [
"Typing :: Typed",
]

build = "build.py"

[tool.poetry.scripts]
audiomatch = "audiomatch.cli:invoke"

Expand Down
18 changes: 0 additions & 18 deletions src/audiomatch/popcount.py

This file was deleted.

18 changes: 18 additions & 0 deletions src/audiomatch/popcount/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
try:
from audiomatch.popcount._popcount import popcount
except ImportError:
# Source:
# http://www.valuedlessons.com/2009/01/popcount-in-python-with-benchmarks.html
#
# This popcount version works slightly faster than 'bin(x).count("1")'

def _popcount_table(size):
table = [0] * 2 ** size
for i in range(len(table)):
table[i] = (i & 1) + table[i >> 1]
return table

_POPCOUNT_TABLE16 = _popcount_table(16)

def popcount(x):
return _POPCOUNT_TABLE16[x & 0xFFFF] + _POPCOUNT_TABLE16[(x >> 16) & 0xFFFF]
Loading

0 comments on commit 15c5b44

Please sign in to comment.