Skip to content

Commit

Permalink
feat(template): Added maturin as an option for build-system (#152)
Browse files Browse the repository at this point in the history
* Activate option in TUI to maturin
* Adding documentation about maturin
* Adding maturin as a build-system:
* Creating a maturin-pyproject.toml
* Editing post_gen_project.py
* Creating a smoke test (build-system.sh)
* Editing cookicutter.json
* Added maturin in Readme.md
  • Loading branch information
ayeankit authored Jul 27, 2023
1 parent 03377b0 commit 4a6bfbd
Show file tree
Hide file tree
Showing 13 changed files with 230 additions and 6 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ for a Python package.
[Setuptools](https://setuptools.pypa.io/en/latest/),
[PDM](https://pdm.fming.dev/) or
[Hatch](https://hatch.pypa.io)
[Maturin](https://pypi.org/project/maturin/0.8.2/)
based on your preference.
- The structure of the project can use the *src layout* or *flat
layout*. The “src layout” moving the code that is intended to be
Expand Down
10 changes: 9 additions & 1 deletion docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,15 @@ packages. SciCookie support the following:
can be confident that they will always produce the same results. It also helps
you manage your Python environments, so you can be sure that your projects have
the correct dependencies.

- [**Maturin**](https://pypi.org/project/maturin/0.8.2/):It's build system designed
to create Python bindings from Rust projects. It allows Rust code to be seamlessly
integrated into Python applications, providing efficient builds and cross-platform
support for various Python versions. Maturin automates the generation of Python
modules that directly access Rust functions, harnessing Rust's high performance
and low-level capabilities within Python. Its user-friendly interface and
compatibility with setuptools and Cargo make it an easy-to-use tool, offering
developers a simple solution to combine the strengths of Python and Rust within
a unified project.
The idea behind the options in SciCookie is that you can choose from some of the
most popular system compilers to suit your needs and preferences for developing
Python packages. If you think we should add more options, you can submit your
Expand Down
3 changes: 2 additions & 1 deletion src/scicookie/cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
"mesonpy",
"setuptools",
"pdm",
"hatch"
"hatch",
"maturin"
],
"use_bandit": "yes",
"use_black": "no",
Expand Down
27 changes: 25 additions & 2 deletions src/scicookie/hooks/post_gen_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
BUILD_SYSTEM = "pdm"
{% elif cookiecutter.build_system == "hatch" -%}
BUILD_SYSTEM = "hatch"
{% elif cookiecutter.build_system == "maturin" -%}
BUILD_SYSTEM = "maturin"
{%- else %}
BUILD_SYSTEM = None
{%- endif %}
Expand Down Expand Up @@ -186,7 +188,16 @@ def clean_up_build_system():
shutil.move(
build_system_dir / "hatch-pyproject.toml",
PROJECT_DIRECTORY / 'pyproject.toml'
)
)
elif BUILD_SYSTEM == "maturin":
shutil.move(
build_system_dir / "maturin-pyproject.toml",
PROJECT_DIRECTORY / 'pyproject.toml'
)
shutil.move(
build_system_dir / "Cargo.toml",
PROJECT_DIRECTORY / 'Cargo.toml'
)
else:
shutil.move(
build_system_dir / "base-pyproject.toml",
Expand Down Expand Up @@ -241,12 +252,24 @@ def prepare_git():
print("=" * 80)


def add_binding_source_files():
if BUILD_SYSTEM == "maturin":
build_system_dir = PROJECT_DIRECTORY / "build-system"
src_system_dir = PROJECT_DIRECTORY/ "src"
if USE_SRC_LAYOUT :
shutil.move(build_system_dir / "lib.rs", "src")
else:
os.makedir(src_system_dir)
shutil.move(build_system_dir / "lib.rs", src_system_dir)
else:
pass

def post_gen():
validation()

# keep this one first, because it changes the package folder
clean_up_project_layout()

add_binding_source_files()
clean_up_cli()
clean_up_code_of_conduct()
clean_up_conda()
Expand Down
1 change: 1 addition & 0 deletions src/scicookie/profiles/base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ build_system:
- setuptools
- pdm
- hatch
- maturin
enabled: false

command_line_interface:
Expand Down
2 changes: 2 additions & 0 deletions src/scicookie/{{cookiecutter.project_slug}}/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ build:
pdm build
{%- elif cookiecutter.build_system == "hatch" %}
hatch build
{%- elif cookiecutter.build_system == "maturin" %}
maturin build
{%- endif %}

.PHONY:release-ci
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "{{ cookiecutter.project_slug }}"
version = "0.1.0"
authors = ["{{ cookiecutter.author_full_name }} <{{ cookiecutter.author_email }}>"]
edition = "2021"

[lib]
name = "_core"
# "cdylib" is necessary to produce a shared library for Python to import from.
crate-type = ["cdylib"]

[dependencies]
rand = "0.8.4"

[dependencies.pyo3]
version = "0.19.1"
# "extension-module" tells pyo3 we want to build an extension module (skips linking against libpython.so)
# "abi3-py38" tells pyo3 (and maturin) to build using the stable ABI with minimum Python version 3.8
features = ["extension-module", "abi3-py38"]
24 changes: 24 additions & 0 deletions src/scicookie/{{cookiecutter.project_slug}}/build-system/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;

#[pyfunction]
fn add(x: i64, y: i64) -> i64 {
x + y
}

#[pyfunction]
fn subtract(x: i64, y: i64) -> i64 {
x - y
}

/// A Python module implemented in Rust. The name of this function must match
/// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to
/// import the module.
#[pymodule]
fn _core(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(add, m)?)?;
m.add_function(wrap_pyfunction!(subtract, m)?)?;
m.add("__version__", env!("CARGO_PKG_VERSION"))?;

Ok(())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
[build-system]
requires = ["maturin>=1.0,<2.0"]
build-backend = "maturin"


[project]
name = "{{ cookiecutter.project_slug }}"
description = "{{ cookiecutter.project_short_description }}"
authors = [
{ name = "{{ cookiecutter.author_full_name }}", email = "{{ cookiecutter.author_email }}" },
]
{% if cookiecutter.project_layout == "src" -%}
packages = [
{include = "{{ cookiecutter.package_slug }}", from="src"},
]
{% else -%}
packages = [
{include = "{{ cookiecutter.package_slug }}"},
]
{% endif -%}
readme = "README.md"
classifiers = [
"Development Status :: 1 - Planning",
"Intended Audience :: Science/Research",
"Intended Audience :: Developers",
{%- if cookiecutter.project_license == "MIT" %}
"License :: OSI Approved :: MIT License",
{%- elif cookiecutter.project_license == "BSD 3 Clause" %}
"License :: OSI Approved :: BSD License",
{%- elif cookiecutter.project_license == "Apache Software License 2.0" %}
"License :: OSI Approved :: Apache Software License",
{%- elif cookiecutter.project_license == "GNU General Public License v3" %}
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
{%- endif %}
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Scientific/Engineering",
"Typing :: Typed",
]
dynamic = ["version"]
requires-python = ">=3.8"
dependencies = [
{# keep this line here #}
{%- if cookiecutter.use_pytest == "yes" -%}
"pytest>=7.3.2",
{% if cookiecutter.use_coverage == "yes" -%}
"pytest-cov>=4.1.0",
{% endif %}
{%- endif -%}{#- end of use_pytest -#}
{%- if cookiecutter.use_hypothesis == "yes" -%}
"hypothesis>=6.0",
{% endif %}
{%- if cookiecutter.use_coverage == "yes" -%}
"coverage>=7.2.7",
{% endif %}
{%- if cookiecutter.use_blue == "yes" -%}
"blue>=0.9.1",
{% endif %}
{%- if cookiecutter.use_black == "yes" -%}
"black>=23.3.0",
{% endif %}
{%- if cookiecutter.use_isort == "yes" -%}
"isort>=5.12.0",
{% endif %}
{%- if cookiecutter.use_pre_commit -%}
"pre-commit>=3.3.2",
{% endif %}
{%- if cookiecutter.use_flake8 == "yes" -%}
"flake8>=4.0.1, <7",
{% endif %}
{%- if cookiecutter.use_ruff == "yes" -%}
"ruff>=0.0.272",
{% endif %}
{%- if cookiecutter.use_mypy == "yes" -%}
"mypy>=1.3.0",
{% endif %}
{%- if cookiecutter.use_bandit == "yes" -%}
"bandit>=1.7.5",
{% endif %}
{%- if cookiecutter.use_pydocstyle == "yes" -%}
"pydocstyle>=6.3.0",
{% endif %}
{%- if cookiecutter.use_vulture == "yes" -%}
"vulture>=2.7",
{% endif %}
{%- if cookiecutter.use_mccabe == "yes" -%}
"mccabe>=0.6.1",
{% endif %}
{%- if cookiecutter.use_containers in ['Docker', 'Podman'] -%}
# if you want to use docker-compose from your system, remove compose-go here
"compose-go>=2.18.1",
{% endif %}
"ipython<8",
"ipykernel>=6.0.0",
{%- if cookiecutter.documentation_engine == 'mkdocs' -%}
"Jinja2>=3.1.2",
"mkdocs>=1.4.3",
"mkdocs-exclude>=1.0.2",
"mkdocs-jupyter>=0.24.1",
"mkdocs-literate-nav>=0.6.0",
"mkdocs-macros-plugin>=0.7.0, <1",
"mkdocs-material>=9.1.15",
"mkdocstrings>=0.21.2",
"mkdocstrings-python>=1.1.2",
{% elif cookiecutter.documentation_engine == 'sphinx' -%}
"Sphinx>=6.2.1",
"sphinx-rtd-theme>=1.2.2",
"importlib-metadata>=6.5.1",
"myst-parser>=0.19.2",
"nbsphinx>=0.9.2",
"pandoc>=2.3",
{% elif cookiecutter.documentation_engine == 'jupyter-book' -%}
"jupyter-book>=0.15.1",
"myst-parser>=0.18.1",
{% endif %}
]

[project.urls]
Homepage = "{{ cookiecutter.project_url }}"
"Bug Tracker" = "{{ cookiecutter.project_url }}/issues"
Discussions = "{{ cookiecutter.project_url }}/discussions"
Changelog = "{{ cookiecutter.project_url }}/releases"

[tool.maturin]
manifest-path = "Cargo.toml"


{% include "build-system/base-pyproject.toml" %}
{#- keep this line at the end of the file -#}
3 changes: 3 additions & 0 deletions src/scicookie/{{cookiecutter.project_slug}}/conda/dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ dependencies:
- pdm
{%- elif cookiecutter.build_system == "hatch" %}
- hatch
{%- elif cookiecutter.build_system == "maturin" %}
- maturin
- rust
{%- endif %}
- nodejs # used by semantic-release
- shellcheck
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,12 @@ It provides an efficient and fast way to manage project dependencies, as
well as build and distribute code. It is fast to install, has built-in
virtualenv, offers support for different package sources, and provides
an easy way to distribute code.
{%- elif cookiecutter.build_system == "hatch" -%}
In addition, you should know that to build our package we use
[Hatch](https://hatch.pypa.io): It's a Python Package that is compatible build backend used by Hatch, a modern, extensible Python project manager. It provides a standardized build system with reproducible builds by default, robust environment management with support for custom scripts, easy publishing to PyPI or other indexes, version management, and configurable project generation with sane defaults. Hatchling might support multiple programming languages and offer language-specific options for building projects in different languages. It could also provide customization and extensibility options, allowing you to incorporate plugins or scripts for tailored build processes.

{%- elif cookiecutter.build_system == "maturin" -%}
In addition, you should know that to build our package we use
[Maturin](https://pypi.org/project/maturin/0.8.2/):It's a Python packaging tool and build system for creating Python bindings from Rust projects. It enables seamless integration of Rust code into Python applications, offering efficient builds, cross-platform support, and compatibility with different Python versions. Maturin automates the process of generating Python modules that directly call Rust functions, leveraging Rust's performance and low-level capabilities in Python. With its easy-to-use interface and integration with setuptools and Cargo, Maturin provides a straightforward solution for developers seeking to combine the strengths of Python and Rust in a single project.
{%- endif %}

Contributions are welcome, and they are greatly appreciated! Every little bit
Expand Down
3 changes: 2 additions & 1 deletion tests/smoke/base.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ elif command -v pdm &> /dev/null; then
pdm install
elif command -v hatch &> /dev/null; then
COMMAND_PREFIX="hatch run"

elif command -v maturin &> /dev/null; then
pip install .
else
# use setuptools
pip install --editable .
Expand Down
1 change: 1 addition & 0 deletions tests/smoke/build-system.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ SMOKE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
. ${SMOKE_DIR}/base.sh "build_system=setuptools"
. ${SMOKE_DIR}/base.sh "build_system=pdm"
. ${SMOKE_DIR}/base.sh "build_system=hatch"
. ${SMOKE_DIR}/base.sh "build_system=maturin"

0 comments on commit 4a6bfbd

Please sign in to comment.