Skip to content

Commit 15c5b44

Browse files
committed
Use Cython for popcount
1 parent c5bab1d commit 15c5b44

13 files changed

+2925
-31
lines changed

.dockerignore

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
**/*
1+
**
22

33
!src
4-
!**/*.py
4+
!build.py
55
!pyproject.toml
66
!poetry.lock
7+
!README.rst
8+
9+
**/*.egg-info
10+
**/.DS_Store
11+
**/Thumbs.db
12+
**/__pycache__

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src/audiomatch/popcount/_popcount.c linguist-detectable=false

.github/workflows/deploy.yml

+28-4
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,38 @@ jobs:
2323
with:
2424
python-version: "3.8"
2525

26-
- name: Publish Package to PyPI
26+
- name: Install poetry
27+
run: |
28+
python -m pip install --upgrade pip
29+
pip install poetry
30+
31+
- name: Build Package
32+
run: |
33+
poetry build --format sdist
34+
35+
- name: Unpack sdist to build wheels
36+
env:
37+
TAG: ${{ steps.tag_name.outputs.TAG }}
38+
run: |
39+
tar -xf dist/audiomatch-"${TAG}".tar.gz -C dist/
40+
mv dist/audiomatch-"${TAG}" dist/audiomatch
41+
42+
- name: Build manylinux wheels
43+
uses: RalfG/[email protected]
44+
with:
45+
python-versions: "cp38-cp38"
46+
package-path: 'dist/audiomatch'
47+
48+
- name: Copy wheel to dist
49+
run: |
50+
cp wheelhouse/*-manylinux*.whl dist/
51+
rm -rf dist/audiomatch
52+
53+
- name: Upload Package to PyPI
2754
env:
2855
PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }}
2956
PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
3057
run: |
31-
python -m pip install --upgrade pip
32-
pip install poetry
33-
poetry build
3458
poetry publish -u $PYPI_USERNAME -p $PYPI_PASSWORD
3559
3660
- name: Wait for PyPI to update indexes

.github/workflows/lint-and-test.yml

+4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ jobs:
5454
run: |
5555
tox -e py
5656
57+
- name: Test with no extensions at all
58+
run: |
59+
tox -e py-noextensions
60+
5761
- name: Generate coverage report
5862
run: |
5963
tox -e coverage

Dockerfile

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
FROM python:3.8-alpine
22

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

87
ARG package_version
98
ENV PACKAGE_VERSION=$package_version
109

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

1314
ENTRYPOINT ["audiomatch"]

README.rst

+7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ Then you can install this library:
3434
3535
pip install audiomatch
3636
37+
To do things fast *audiomatch* requires C compiler and Python headers to be installed.
38+
You can skip compilation by setting ``AUDIOMATCH_NO_EXTENSIONS`` environment variable:
39+
40+
.. code-block:: bash
41+
42+
AUDIOMATCH_NO_EXTENSIONS=1 pip install audiomatch
43+
3744
You can avoid installing all this libraries on your computer and run everything in
3845
docker:
3946

build.py

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import os
2+
from distutils.extension import Extension
3+
4+
5+
def _get_bool(key: str, default: bool = False) -> bool:
6+
value = os.getenv(key)
7+
if value is not None:
8+
return value.lower() in ["true", "1", "t"]
9+
return default
10+
11+
12+
def _get_extensions():
13+
return [
14+
Extension(
15+
"audiomatch.popcount._popcount",
16+
sources=["src/audiomatch/popcount/_popcount.c"],
17+
)
18+
]
19+
20+
21+
def build(setup_kwargs):
22+
"""This function is mandatory in order to build the extensions."""
23+
use_extensions = not _get_bool("AUDIOMATCH_NO_EXTENSIONS", default=False)
24+
25+
if use_extensions:
26+
setup_kwargs.update({"ext_modules": _get_extensions()})

pyproject.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "audiomatch"
3-
version = "0.1.8"
3+
version = "0.2.0"
44
description = "A small command-line tool to find similar audio files"
55
keywords = ["duplicate", "detection", "audio", "fingerprinting", "command-line"]
66
readme = "README.rst"
@@ -18,6 +18,8 @@ classifiers = [
1818
"Typing :: Typed",
1919
]
2020

21+
build = "build.py"
22+
2123
[tool.poetry.scripts]
2224
audiomatch = "audiomatch.cli:invoke"
2325

src/audiomatch/popcount.py

-18
This file was deleted.

src/audiomatch/popcount/__init__.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
try:
2+
from audiomatch.popcount._popcount import popcount
3+
except ImportError:
4+
# Source:
5+
# http://www.valuedlessons.com/2009/01/popcount-in-python-with-benchmarks.html
6+
#
7+
# This popcount version works slightly faster than 'bin(x).count("1")'
8+
9+
def _popcount_table(size):
10+
table = [0] * 2 ** size
11+
for i in range(len(table)):
12+
table[i] = (i & 1) + table[i >> 1]
13+
return table
14+
15+
_POPCOUNT_TABLE16 = _popcount_table(16)
16+
17+
def popcount(x):
18+
return _POPCOUNT_TABLE16[x & 0xFFFF] + _POPCOUNT_TABLE16[(x >> 16) & 0xFFFF]

0 commit comments

Comments
 (0)