Skip to content

Commit b9fb05f

Browse files
committed
PyPI packaging
1 parent 1673ff6 commit b9fb05f

10 files changed

Lines changed: 185 additions & 20 deletions

File tree

.github/workflows/build.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@ jobs:
4040
run: docker build -t genomicsqlite . && docker run -v $(pwd):/mnt --rm genomicsqlite cp build/libgenomicsqlite.so /mnt/build/
4141
- name: test portable .so
4242
run: env -C build ctest -V
43+
- name: prepare artifacts
44+
run: cp build/libgenomicsqlite.so include/genomicsqlite.h .
4345
- uses: actions/upload-artifact@v2
4446
with:
4547
name: GenomicSQLite-Linux-x86_64-so-${{ env.GIT_REVISION }}
4648
path: |
47-
build/libgenomicsqlite.so
48-
include/genomicsqlite.h
49+
libgenomicsqlite.so
50+
genomicsqlite.h
4951
5052
lint:
5153
runs-on: ubuntu-20.04

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ repos:
3030
files: \.py$
3131
verbose: true
3232
entry: flake8
33-
args: [--max-line-length, "100", "--ignore=E501,W503"]
33+
args: [--max-line-length, "100", "--ignore=E501,W503,E722"]
3434
- id: pylint
3535
name: pylint
3636
language: system
3737
files: \.py$
3838
verbose: true
3939
entry: env PYTHONPATH=bindings/python pylint
40-
args: [-d, "bad-continuation,global-statement,missing-docstring,missing-module-docstring,line-too-long,too-many-arguments,duplicate-code,redefined-outer-name,too-many-locals"]
40+
args: [-d, "bad-continuation,global-statement,missing-docstring,missing-module-docstring,line-too-long,too-many-arguments,duplicate-code,redefined-outer-name,too-many-locals,bare-except"]

bindings/python/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
build/
22
dist/
33
*.egg-info/
4+
genomicsqlite/libgenomicsqlite.so
5+
RELEASE-VERSION

bindings/python/MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include RELEASE-VERSION version.py genomicsqlite/libgenomicsqlite.so

bindings/python/Makefile

Lines changed: 0 additions & 9 deletions
This file was deleted.

bindings/python/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Genomics Extension for SQLite
2+
3+
**("GenomicSQLite")**
4+
5+
This [SQLite3 loadable extension](https://www.sqlite.org/loadext.html) adds features supporting applications in genome bioinformatics:
6+
7+
* genomic range indexing for overlap queries & joins
8+
* streaming storage compression (also available [standalone](https://github.com/mlin/sqlite_zstd_vfs))
9+
* pre-tuned settings for "big data"
10+
11+
Notice: this project is not associated with the SQLite developers.
12+
13+
https://github.com/mlin/GenomicSQLite
Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,23 @@
44
"""
55
import os
66
import sys
7+
import platform
78
import json
89
import sqlite3
910
from typing import Optional, NamedTuple, Dict, Any
1011
from ctypes.util import find_library
1112

12-
# One-time global initialization -- load the extension shared-library
13-
_DLL = find_library("genomicsqlite")
13+
HERE = os.path.dirname(__file__)
14+
15+
# One-time global initialization -- locate shared library file; preferably the copy installed with
16+
# this package, otherwise look in the usual places.
17+
_DLL = None
18+
if platform.system() == "Linux" and os.path.isfile(os.path.join(HERE, "libgenomicsqlite.so")):
19+
_DLL = os.path.join(HERE, "libgenomicsqlite.so")
20+
if not _DLL:
21+
_DLL = find_library("genomicsqlite")
1422
assert _DLL, "couldn't locate genomicsqlite shared-library file"
15-
# open a dummy connection to :memory: just for setting up the extension.
23+
# open a dummy connection to :memory: just for loading the extension.
1624
_MEMCONN = sqlite3.connect(":memory:")
1725
_MEMCONN.enable_load_extension(True)
1826
_MEMCONN.load_extension(_DLL)
@@ -27,6 +35,8 @@ def _execute1(conn, sql, params=None):
2735
return next(conn.execute(sql, params) if params else conn.execute(sql))[0]
2836

2937

38+
__version__ = _execute1(_MEMCONN, "SELECT genomicsqlite_version()")
39+
3040
# load default configuration
3141
_DEFAULT_CONFIG = json.loads(_execute1(_MEMCONN, "SELECT genomicsqlite_default_config_json()"))
3242

bindings/python/release.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/bin/bash
2+
3+
set -euo pipefail
4+
5+
HERE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
6+
cd "$HERE"
7+
8+
if grep dirty <(git describe --always --dirty); then
9+
>&2 echo "Cannot release dirty working tree"
10+
exit 1
11+
fi
12+
if [[ ! -f genomicsqlite/libgenomicsqlite.so ]]; then
13+
>&2 echo "Download the portable libgenomicsqlite.so to ${HERE}/genomicsqlite/"
14+
exit 1
15+
fi
16+
function cleanup {
17+
# force us to put it there each time; ensures a stale version can't be left behind
18+
rm -f genomicsqlite/libgenomicsqlite.so
19+
}
20+
trap cleanup EXIT
21+
22+
rm -rf build dist *.egg-info
23+
python3 setup.py sdist
24+
echo -e "\033[0;31;5m -- Pushing $(basename `ls -1 dist/*.tar.gz` .tar.gz) to PyPI! -- \033[0m"
25+
twine upload dist/*.tar.gz

bindings/python/setup.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
#!/usr/bin/env python3
22
from os import path
3-
from setuptools import setup
3+
from setuptools import setup, find_packages
4+
from version import get_version
45

56
HERE = path.abspath(path.dirname(__file__))
67
with open(path.join(HERE, "README.md")) as f:
78
README = f.read()
89

910
setup(
11+
python_requires=">=3.6",
1012
name="genomicsqlite",
11-
version="0.0.1",
13+
version=get_version(),
1214
url="https://github.com/mlin/GenomicSQLite",
1315
description="Genomics Extension for SQLite",
1416
long_description=README,
1517
long_description_content_type="text/markdown",
1618
author="Mike Lin",
1719
author_email="dna@mlin.net",
18-
py_modules=["genomicsqlite"],
19-
python_requires=">=3.6",
20+
packages=find_packages(),
21+
package_data={"": ["genomicsqlite/libgenomicsqlite.so"]},
22+
include_package_data=True,
2023
entry_points={"console_scripts": ["genomicsqlite = genomicsqlite:_cli"]},
2124
)

bindings/python/version.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""Calculates the current version number.
4+
5+
If possible, uses output of “git describe” modified to conform to the
6+
visioning scheme that setuptools uses (see PEP 386). Releases must be
7+
labelled with annotated tags (signed tags are annotated) of the following
8+
format:
9+
10+
v<num>(.<num>)+ [ {a|b|c|rc} <num> (.<num>)* ]
11+
12+
If “git describe” returns an error (likely because we're in an unpacked copy
13+
of a release tarball, rather than a git working copy), or returns a tag that
14+
does not match the above format, version is read from RELEASE-VERSION file.
15+
16+
To use this script, simply import it your setup.py file, and use the results
17+
of get_version() as your package version:
18+
19+
import version
20+
setup(
21+
version=version.get_version(),
22+
.
23+
.
24+
.
25+
)
26+
27+
This will automatically update the RELEASE-VERSION file. The RELEASE-VERSION
28+
file should *not* be checked into git but it *should* be included in sdist
29+
tarballs (as should version.py file). To do this, run:
30+
31+
echo include RELEASE-VERSION version.py >>MANIFEST.in
32+
echo RELEASE-VERSION >>.gitignore
33+
34+
With that setup, a new release can be labelled by simply invoking:
35+
36+
git tag -s v1.0
37+
"""
38+
39+
__author__ = ("Douglas Creager <dcreager@dcreager.net>", "Michal Nazarewicz <mina86@mina86.com>")
40+
__license__ = "This file is placed into the public domain."
41+
__maintainer__ = "Michal Nazarewicz"
42+
__email__ = "mina86@mina86.com"
43+
44+
__all__ = "get_version"
45+
46+
47+
import re
48+
import subprocess
49+
import sys
50+
51+
52+
RELEASE_VERSION_FILE = "RELEASE-VERSION"
53+
54+
# http://www.python.org/dev/peps/pep-0386/
55+
_PEP386_SHORT_VERSION_RE = r"\d+(?:\.\d+)+(?:(?:[abc]|rc)\d+(?:\.\d+)*)?"
56+
_PEP386_VERSION_RE = r"^%s(?:\.post\d+)?(?:\.dev\d+)?$" % (_PEP386_SHORT_VERSION_RE)
57+
_GIT_DESCRIPTION_RE = r"^v(?P<ver>%s)-(?P<commits>\d+)-g(?P<sha>[\da-f]+)$" % (
58+
_PEP386_SHORT_VERSION_RE
59+
)
60+
61+
62+
def read_git_version():
63+
try:
64+
proc = subprocess.Popen(
65+
("git", "describe", "--long", "--tags", "--match", "v[0-9]*.*"),
66+
stdout=subprocess.PIPE,
67+
stderr=subprocess.PIPE,
68+
)
69+
data, _ = proc.communicate()
70+
if proc.returncode:
71+
return None
72+
ver = data.decode().splitlines()[0].strip()
73+
except:
74+
return None
75+
76+
if not ver:
77+
return None
78+
match = re.search(_GIT_DESCRIPTION_RE, ver)
79+
if not match:
80+
sys.stderr.write("version: git description (%s) is invalid, " "ignoring\n" % ver)
81+
return None
82+
83+
commits = int(match.group("commits"))
84+
if not commits:
85+
return match.group("ver")
86+
return "%s.post%d.dev%d" % (match.group("ver"), commits, int(match.group("sha"), 16))
87+
88+
89+
def read_release_version():
90+
try:
91+
with open(RELEASE_VERSION_FILE) as infile:
92+
ver = infile.readline().strip()
93+
if not re.search(_PEP386_VERSION_RE, ver):
94+
sys.stderr.write(
95+
"version: release version (%s) is invalid, " "will use it anyway\n" % ver
96+
)
97+
return ver
98+
except:
99+
return None
100+
101+
102+
def write_release_version(version):
103+
with open(RELEASE_VERSION_FILE, "w") as outfile:
104+
outfile.write("%s\n" % version)
105+
106+
107+
def get_version():
108+
release_version = read_release_version()
109+
version = read_git_version() or release_version
110+
if not version:
111+
raise ValueError("Cannot find the version number")
112+
if version != release_version:
113+
write_release_version(version)
114+
return version
115+
116+
117+
if __name__ == "__main__":
118+
print(get_version())

0 commit comments

Comments
 (0)