-
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from messense/python-binding
Add Python binding
- Loading branch information
Showing
6 changed files
with
314 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
name: Python | ||
|
||
on: | ||
push: | ||
pull_request: | ||
|
||
jobs: | ||
macos: | ||
runs-on: macos-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-python@v2 | ||
with: | ||
python-version: 3.6 | ||
architecture: x64 | ||
- name: Install Rust toolchain | ||
uses: actions-rs/toolchain@v1 | ||
with: | ||
toolchain: stable | ||
target: aarch64-apple-darwin | ||
profile: minimal | ||
default: true | ||
- name: Install maturin | ||
run: pip install maturin | ||
- name: Build wheels - x86_64 | ||
run: | | ||
maturin build -i python --target x86_64-apple-darwin --release --out dist -m python/Cargo.toml | ||
pip install crfs --no-index --find-links dist --force-reinstall | ||
python -c "import crfs" | ||
- name: Build wheels - universal2 | ||
env: | ||
DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer | ||
MACOSX_DEPLOYMENT_TARGET: '10.9' | ||
PYO3_CROSS_LIB_DIR: /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib | ||
run: | | ||
# Build wheels | ||
maturin build -i python --release --universal2 --out dist --no-sdist -m python/Cargo.toml | ||
pip install crfs --no-index --find-links dist --force-reinstall | ||
python -c "import crfs" | ||
- name: Upload wheels | ||
uses: actions/upload-artifact@v2 | ||
with: | ||
name: wheels | ||
path: dist | ||
|
||
windows: | ||
runs-on: windows-latest | ||
strategy: | ||
matrix: | ||
platform: [ | ||
{ python-architecture: "x64", target: "x86_64-pc-windows-msvc" }, | ||
{ python-architecture: "x86", target: "i686-pc-windows-msvc" }, | ||
] | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-python@v2 | ||
with: | ||
python-version: 3.6 | ||
architecture: ${{ matrix.platform.python-architecture }} | ||
- name: Install Rust toolchain | ||
uses: actions-rs/toolchain@v1 | ||
with: | ||
toolchain: stable | ||
target: ${{ matrix.platform.target }} | ||
profile: minimal | ||
default: true | ||
- name: Install maturin | ||
run: pip install maturin | ||
- name: Build wheels | ||
run: | | ||
maturin build -i python --release --out dist --no-sdist -m python/Cargo.toml --target ${{ matrix.platform.target }} | ||
pip install crfs --no-index --find-links dist --force-reinstall | ||
python -c "import crfs" | ||
- name: Upload wheels | ||
uses: actions/upload-artifact@v2 | ||
with: | ||
name: wheels | ||
path: dist | ||
|
||
linux: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
platform: [ | ||
{ manylinux: '2010', target: "x86_64-unknown-linux-gnu", arch: "x86_64" }, | ||
{ manylinux: '2010', target: "i686-unknown-linux-gnu", arch: "i686" }, | ||
] | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-python@v2 | ||
with: | ||
python-version: 3.6 | ||
architecture: x64 | ||
- name: Build Wheels | ||
run: | | ||
echo 'curl --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable | ||
source ~/.cargo/env | ||
export PATH=/opt/python/cp38-cp38/bin:$PATH | ||
pip install maturin | ||
maturin build -i python --release --out dist --no-sdist -m python/Cargo.toml --target ${{ matrix.platform.target }} --manylinux ${{ matrix.platform.manylinux }} | ||
pip install crfs --no-index --find-links dist --force-reinstall | ||
python -c "import crfs" | ||
' > build-wheel.sh | ||
docker run --rm -v "$PWD":/io -w /io quay.io/pypa/manylinux${{ matrix.platform.manylinux }}_${{ matrix.platform.arch }} bash build-wheel.sh | ||
- name: Auditwheel Symbols | ||
run: | | ||
pip install auditwheel-symbols | ||
auditwheel-symbols dist/*.whl | ||
- name: Upload wheels | ||
uses: actions/upload-artifact@v2 | ||
with: | ||
name: wheels | ||
path: dist | ||
|
||
linux-cross: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
platform: [ | ||
{ manylinux: "2014", target: "aarch64-unknown-linux-gnu", arch: "aarch64" }, | ||
{ manylinux: "2014", target: "armv7-unknown-linux-gnueabihf", arch: "armv7" }, | ||
] | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-python@v2 | ||
with: | ||
python-version: 3.6 | ||
- name: Build Wheels | ||
run: | | ||
echo 'curl -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable | ||
source ~/.cargo/env | ||
rustup target add ${{ matrix.platform.target }} | ||
maturin build -i python --release --out dist --no-sdist -m python/Cargo.toml --target ${{ matrix.platform.target }} --manylinux ${{ matrix.platform.manylinux }} | ||
' > build-wheel.sh | ||
docker run --rm -v "$PWD":/io -w /io messense/manylinux2014-cross:${{ matrix.platform.arch }} bash build-wheel.sh | ||
- uses: uraimo/[email protected] | ||
name: Install built wheel | ||
with: | ||
arch: ${{ matrix.platform.arch }} | ||
distro: ubuntu18.04 | ||
githubToken: ${{ github.token }} | ||
# Mount the dist directory as /artifacts in the container | ||
dockerRunArgs: | | ||
--volume "${PWD}/dist:/artifacts" | ||
install: | | ||
apt-get update | ||
apt-get install -y --no-install-recommends python3 python3-pip | ||
pip3 install -U pip | ||
run: | | ||
ls -lrth /artifacts | ||
pip3 install crfs --no-index --find-links /artifacts --force-reinstall | ||
python3 -c "import crfs" | ||
- name: Auditwheel Symbols | ||
run: | | ||
pip install auditwheel-symbols | ||
auditwheel-symbols dist/*.whl | ||
- name: Upload wheels | ||
uses: actions/upload-artifact@v2 | ||
with: | ||
name: wheels | ||
path: dist | ||
|
||
|
||
release: | ||
name: Release | ||
runs-on: ubuntu-latest | ||
if: "startsWith(github.ref, 'refs/tags/')" | ||
needs: [ macos, windows, linux, linux-cross ] | ||
steps: | ||
- uses: actions/download-artifact@v2 | ||
with: | ||
name: wheels | ||
- uses: actions/setup-python@v2 | ||
with: | ||
python-version: 3.9 | ||
- name: Publish to PyPi | ||
env: | ||
TWINE_USERNAME: __token__ | ||
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} | ||
run: | | ||
pip install --upgrade wheel pip setuptools twine | ||
twine upload --skip-existing * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
/target | ||
/python/target | ||
Cargo.lock | ||
tests/output/*.cqdb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[target.x86_64-apple-darwin] | ||
rustflags = [ | ||
"-C", "link-arg=-undefined", | ||
"-C", "link-arg=dynamic_lookup", | ||
] | ||
|
||
[target.aarch64-apple-darwin] | ||
rustflags = [ | ||
"-C", "link-arg=-undefined", | ||
"-C", "link-arg=dynamic_lookup", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
[package] | ||
name = "py-crfs" | ||
version = "0.1.0" | ||
authors = ["messense <[email protected]>"] | ||
edition = "2018" | ||
license = "MIT" | ||
keywords = ["crf", "crfsuite"] | ||
readme = "README.md" | ||
homepage = "https://github.com/messense/crfs-rs" | ||
repository = "https://github.com/messense/crfs-rs.git" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[lib] | ||
name = "crfs" | ||
crate-type = ["cdylib"] | ||
|
||
[dependencies] | ||
crfs-rs = { package = "crfs", version = "0.1.0" } | ||
ouroboros = "0.8.2" | ||
pyo3 = { version = "0.13.2", features = ["abi3-py36", "extension-module"] } | ||
|
||
[package.metadata.maturin] | ||
name = "crfs" |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
use std::fs; | ||
|
||
use crfs_rs::{Attribute, Model}; | ||
use ouroboros::self_referencing; | ||
use pyo3::prelude::*; | ||
|
||
#[pyclass(module = "crfs", name = "Attribute")] | ||
#[derive(FromPyObject)] | ||
struct PyAttribute { | ||
/// Attribute name | ||
#[pyo3(get, set)] | ||
name: String, | ||
/// Value of the attribute | ||
#[pyo3(get, set)] | ||
value: f64, | ||
} | ||
|
||
#[pymethods] | ||
impl PyAttribute { | ||
#[new] | ||
fn new(name: String, value: f64) -> Self { | ||
Self { name, value } | ||
} | ||
} | ||
|
||
#[derive(FromPyObject)] | ||
enum PyAttributeInput { | ||
#[pyo3(transparent)] | ||
Attr(PyAttribute), | ||
Dict { | ||
/// Attribute name | ||
#[pyo3(item("name"))] | ||
name: String, | ||
/// Value of the attribute | ||
#[pyo3(item("value"))] | ||
value: f64, | ||
}, | ||
Tuple(String, f64), | ||
} | ||
|
||
impl From<PyAttributeInput> for Attribute { | ||
fn from(attr: PyAttributeInput) -> Self { | ||
match attr { | ||
PyAttributeInput::Attr(PyAttribute { name, value }) => Attribute::new(name, value), | ||
PyAttributeInput::Dict { name, value } => Attribute::new(name, value), | ||
PyAttributeInput::Tuple(name, value) => Attribute::new(name, value), | ||
} | ||
} | ||
} | ||
|
||
#[pyclass(module = "crfs", name = "Model")] | ||
#[self_referencing] | ||
struct PyModel { | ||
data: Vec<u8>, | ||
#[borrows(data)] | ||
#[not_covariant] | ||
model: Model<'this>, | ||
} | ||
|
||
#[pymethods] | ||
impl PyModel { | ||
#[new] | ||
fn new_py(data: Vec<u8>) -> PyResult<Self> { | ||
let model = PyModelTryBuilder { | ||
data, | ||
model_builder: |model_data| Model::new(model_data), | ||
} | ||
.try_build()?; | ||
Ok(model) | ||
} | ||
|
||
#[staticmethod] | ||
fn open(path: &str) -> PyResult<Self> { | ||
let data = fs::read(path)?; | ||
Self::new_py(data) | ||
} | ||
|
||
pub fn tag(&self, xseq: Vec<Vec<PyAttributeInput>>) -> PyResult<Vec<String>> { | ||
let mut tagger = self.with_model(|model| model.tagger())?; | ||
let xseq: Vec<Vec<Attribute>> = xseq | ||
.into_iter() | ||
.map(|xs| xs.into_iter().map(Into::into).collect()) | ||
.collect(); | ||
let labels = tagger.tag(&xseq)?; | ||
Ok(labels.iter().map(|l| l.to_string()).collect()) | ||
} | ||
} | ||
|
||
#[pymodule] | ||
fn crfs(_py: Python, m: &PyModule) -> PyResult<()> { | ||
m.add_class::<PyAttribute>()?; | ||
m.add_class::<PyModel>()?; | ||
Ok(()) | ||
} |