From 3fba0ecdcb4987cba6be6569996da77a0ddb78aa Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Sun, 7 Mar 2021 11:12:56 +0000 Subject: [PATCH] docs: add sphinx / readthedocs configuration --- .gitignore | 1 + .readthedocs.yaml | 7 ++ README.md | 76 ++----------------- docs/Makefile | 20 +++++ docs/README.rst | 1 + docs/conf.py | 90 ++++++++++++++++++++++ docs/index.rst | 8 ++ docs/make.bat | 35 +++++++++ docs/reference.rst | 8 ++ docs/requirements.txt | 4 + setuptools_rust/__init__.py | 9 ++- setuptools_rust/build.py | 6 +- setuptools_rust/extension.py | 142 +++++++++++++++++++++-------------- setuptools_rust/utils.py | 27 +------ 14 files changed, 274 insertions(+), 160 deletions(-) create mode 100644 .readthedocs.yaml create mode 100644 docs/Makefile create mode 100644 docs/README.rst create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/make.bat create mode 100644 docs/reference.rst create mode 100644 docs/requirements.txt diff --git a/.gitignore b/.gitignore index 62a696e4..759f3be9 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ target *.egg-info setuptools_rust/version.py pyo3 +docs/_build diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..d95f8721 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,7 @@ +version: 2 + +python: + install: + - requirements: docs/requirements.txt + - method: pip + path: . diff --git a/README.md b/README.md index 116516c4..ea28d4e7 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # Setuptools plugin for Rust extensions -[![Build Status](https://travis-ci.org/PyO3/setuptools-rust.svg?branch=master)](https://travis-ci.org/PyO3/setuptools-rust) +![example workflow](https://github.com/PyO3/setuptools-rust/actions/workflows/ci.yml/badge.svg) [![pypi package](https://badge.fury.io/py/setuptools-rust.svg)](https://badge.fury.io/py/setuptools-rust) +[![readthedocs](https://readthedocs.org/projects/pip/badge/)](https://setuptools-rust.readthedocs.io/en/latest/) [![code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) -Setuptools helpers for rust Python extensions implemented with [PyO3](https://github.com/PyO3/pyo3) and [rust-cpython](https://github.com/dgrunwald/rust-cpython). +Setuptools helpers for Rust Python extensions implemented with [PyO3](https://github.com/PyO3/pyo3) and [rust-cpython](https://github.com/dgrunwald/rust-cpython). -Compile and distribute Python extensions written in rust as easily as if +Compile and distribute Python extensions written in Rust as easily as if they were written in C. ## Setup @@ -32,6 +33,9 @@ setup( ) ``` +For a complete reference of the options supported by the `RustExtension` class, see the +[API reference](https://setuptools-rust.readthedocs.io/en/latest/reference.html). + ### MANIFEST.in This file is required for building source distributions @@ -129,70 +133,6 @@ You can then upload the `manylinux2014` wheels to pypi using [twine](https://git It is possible to use any of the `manylinux` docker images: `manylinux1`, `manylinux2010` or `manylinux2014`. (Just replace `manylinux2014` in the above instructions with the alternative version you wish to use.) -## RustExtension - -You can define rust extension with RustExtension class: - -RustExtension(name, path, args=None, features=None, -rust\_version=None, quiet=False, debug=False) - -The class for creating rust extensions. - - - param str name - the full name of the extension, including any packages -- ie. - *not* a filename or pathname, but Python dotted name. It is - possible to specify multiple binaries, if extension uses - Binsing.Exec binding mode. In that case first argument has to be - dictionary. Keys of the dictionary corresponds to compiled rust - binaries and values are full name of the executable inside python - package. - - - param str path - path to the Cargo.toml manifest file - - - param \[str\] args - a list of extra argumenents to be passed to cargo. - - - param \[str\] features - a list of features to also build - - - param \[str\] rustc\_flags - A list of arguments to pass to rustc, e.g. cargo rustc --features - \ \ -- \ - - - param str rust\_version - sematic version of rust compiler version -- for example - *\>1.14,\<1.16*, default is None - - - param bool quiet - Does not echo cargo's output. default is False - - - param bool debug - Controls whether --debug or --release is passed to cargo. If set - to None then build type is auto-detect. Inplace build is debug - build otherwise release. Default: None - - - param int binding - Controls which python binding is in use. Binding.PyO3 uses PyO3 - Binding.RustCPython uses rust-cpython Binding.NoBinding uses no - binding. Binding.Exec build executable. - - - param int strip - Strip symbols from final file. Does nothing for debug build. - Strip.No - do not strip symbols (default) Strip.Debug - strip - debug symbols Strip.All - strip all symbols - - - param bool script - Generate console script for executable if Binding.Exec is used. - - - param bool native - Build extension or executable with "-C target-cpu=native" - - - param bool optional - if it is true, a build failure in the extension will not abort the - build process, but instead simply not install the failing - extension. - ## Commands - build - Standard build command builds all rust extensions. @@ -203,5 +143,5 @@ The class for creating rust extensions. extensions. - tomlgen\_rust - Automatically generate a Cargo.toml manifest based on Python package metadata. See the [example - project](https://github.com/PyO3/setuptools-rust/tree/master/example_tomlgen) + project](https://github.com/PyO3/setuptools-rust/tree/master/examples/tomlgen) on GitHub for more information about this command. diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d4bb2cbb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/README.rst b/docs/README.rst new file mode 100644 index 00000000..97d49585 --- /dev/null +++ b/docs/README.rst @@ -0,0 +1 @@ +.. mdinclude:: ../README.md diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..d434be5b --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,90 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. + +# -- Project information ----------------------------------------------------- + +project = 'setuptools-rust' +copyright = '2021, The PyO3 Contributors' +author = 'The PyO3 Contributors' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx_autodoc_typehints", + "sphinx_rtd_theme", + "m2r2", +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +html_theme_options = { + 'prev_next_buttons_location': None, +} + +# -- Custom HTML link transformation to make documentation links relative -- + +# This is necessary because the README.md (for example) has links to the latest +# documentation, but we want them to be relative to the specific docs version. + +from sphinx.transforms import SphinxTransform + +DOCS_URL = 'https://setuptools-rust.readthedocs.io/en/latest/' + +class RelativeDocLinks(SphinxTransform): + + default_priority = 750 + + def apply(self): + from docutils.nodes import reference, Text + baseref = lambda o: ( + isinstance(o, reference) and + o.get('refuri', '').startswith(DOCS_URL)) + basetext = lambda o: ( + isinstance(o, Text) and o.startswith(DOCS_URL)) + for node in self.document.traverse(baseref): + target = node['refuri'].replace(DOCS_URL, "", 1) + node.replace_attr('refuri', target) + for t in node.traverse(basetext): + t1 = Text(t.replace(DOCS_URL, "", 1), t.rawsource) + t.parent.replace(t, t1) + return + +# end of class + +def setup(app): + app.add_transform(RelativeDocLinks) + return diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..406116ba --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,8 @@ +.. toctree:: + :maxdepth: 2 + :hidden: + + README + reference + +.. include:: README.rst diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..2119f510 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/reference.rst b/docs/reference.rst new file mode 100644 index 00000000..56aead80 --- /dev/null +++ b/docs/reference.rst @@ -0,0 +1,8 @@ +API Reference +============= + +.. py:module:: setuptools_rust + +.. autoclass:: RustExtension +.. autoclass:: Binding +.. autoclass:: Strip diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..3429d446 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,4 @@ +m2r2==0.2.7 +Sphinx==3.5.2 +sphinx-autodoc-typehints==1.11.1 +sphinx-rtd-theme==0.5.1 diff --git a/setuptools_rust/__init__.py b/setuptools_rust/__init__.py index a3d62ada..ac98edeb 100644 --- a/setuptools_rust/__init__.py +++ b/setuptools_rust/__init__.py @@ -1,19 +1,20 @@ from .build import build_rust from .check import check_rust from .clean import clean_rust -from .extension import RustExtension +from .extension import Binding, RustExtension, Strip from .test import test_rust from .tomlgen import tomlgen_rust, find_rust_extensions -from .utils import Binding, Strip from .version import version as __version__ __all__ = ( - "RustExtension", "Binding", + "RustExtension", "Strip", + "build_rust", "check_rust", "clean_rust", - "build_rust", + "find_rust_extensions", "test_rust", + "tomlgen_rust", ) diff --git a/setuptools_rust/build.py b/setuptools_rust/build.py index 9f46cc15..8e756330 100644 --- a/setuptools_rust/build.py +++ b/setuptools_rust/build.py @@ -12,10 +12,8 @@ from subprocess import check_output from .command import RustCommand -from .extension import RustExtension -from .utils import ( - Binding, Strip, rust_features, get_rust_target_info -) +from .extension import Binding, RustExtension, Strip +from .utils import rust_features, get_rust_target_info class build_rust(RustCommand): diff --git a/setuptools_rust/extension.py b/setuptools_rust/extension.py index 41866afb..6db72cf3 100644 --- a/setuptools_rust/extension.py +++ b/setuptools_rust/extension.py @@ -1,73 +1,99 @@ import os import re from distutils.errors import DistutilsSetupError -from .utils import Binding, Strip +from enum import IntEnum, auto +from typing import Dict, List, Optional, Union import semantic_version +class Binding(IntEnum): + """ + Enumeration of possible Rust binding types supported by `setuptools-rust`. + + Attributes: + PyO3: This is an extension built using + `PyO3 `_. + RustCPython: This is an extension built using + `rust-cpython `_. + NoBinding: Bring your own bindings for the extension. + Exec: Build an executable instead of an extension. + """ + PyO3 = auto() + RustCPython = auto() + NoBinding = auto() + Exec = auto() + + def __repr__(self): + return f"{self.__class__.__name__}.{self.name}" + + +class Strip(IntEnum): + """ + Enumeration of modes for stripping symbols from the built extension. + + Attributes: + No: Do not strip symbols. + Debug: Strip debug symbols. + All: Strip all symbols. + """ + No = auto() + Debug = auto() + All = auto() + + def __repr__(self): + return f"{self.__class__.__name__}.{self.name}" + + class RustExtension: - """Just a collection of attributes that describes an rust extension - module and everything needed to build it - - Instance attributes: - target : string - the full name of the extension, including any packages -- ie. - *not* a filename or pathname, but Python dotted name - path : string - path to the cargo.toml manifest file - args : [string] - a list of extra argumenents to be passed to cargo. - features : [string] - a list of features to also build - rust_version : string - rust compiler version - quiet : bool - If True, doesn't echo cargo's output. - debug : bool - Controls whether --debug or --release is passed to cargo. If set to - None then build type is auto-detect. Inplace build is debug build - otherwise release. Default: None - binding : setuptools_rust.Binding - Controls which python binding is in use. - Binding.PyO3 uses PyO3 - Binding.RustCPython uses Rust CPython. - Binding.NoBinding uses no binding. - Binding.Exec build executable. - strip : setuptools_rust.Strip - Strip symbols from final file. Does nothing for debug build. - * Strip.No - do not strip symbols - * Strip.Debug - strip debug symbols - * Strip.All - strip all symbols - script : bool - Generate console script for executable if `Binding.Exec` is used. - native : bool - Build extension or executable with "target-cpu=native" - optional : bool - if it is true, a build failure in the extension will not abort the - build process, but instead simply not install the failing extension. - py_limited_api : bool - Same as `py_limited_api` on `setuptools.Extension`. Note that if you - set this to True, your extension must pass the appropriate feature - flags to pyo3 (ensuring that `abi3` feature is enabled). + """Used to define a rust extension module and its build configuration. + + Args: + target: The full name of the extension, including any packages i.e. + *not* a filename or pathname, but Python dotted name. It is possible to + specify multiple binaries, if extension uses ``Binding.Exec`` binding mode. + In that case first argument has to be dictionary. Keys of the + dictionary corresponds to compiled rust binaries and values are full + name of the executable inside python package. + path: Path to the ``Cargo.toml`` manifest file + args: A list of extra argumenents to be passed to cargo. + features: A list of features to also build. + rust_version: Minimum Rust compiler version required for this + extension. + quiet: Suppress Cargo's output. + debug: Controls whether ``--debug`` or ``--release`` is passed to + cargo. If set to `None` (the default) then build type is auto-detect. + Inplace build is debug build otherwise release. + binding: Controls which python binding is in use. + strip: Strip symbols from final file. Does nothing for debug build. + script: Generate console script for executable if ``Binding.Exec`` is + used. + native: Build extension or executable with ``--target-cpu=native``. + optional: if it is true, a build failure in the extension will not + abort the build process, but instead simply not install the failing + extension. + py_limited_api: Same as `py_limited_api` on + `setuptools.Extension`. Note that if you set this to True, your extension + must pass the appropriate feature flags to pyo3 (ensuring that `abi3` + feature is enabled). """ def __init__( self, - target, - path="Cargo.toml", - args=None, - features=None, - rustc_flags=None, - rust_version=None, - quiet=False, - debug=None, - binding=Binding.PyO3, - strip=Strip.No, - script=False, - native=False, - optional=False, - py_limited_api=False, + target: Union[str, Dict[str, str]], + path: str = "Cargo.toml", + args: Optional[List[str]] = None, + features: Optional[List[str]] = None, + rustc_flags: Optional[List[str]] = None, + rust_version: Optional[str] = None, + quiet: bool = False, + debug: Optional[bool] = None, + binding: Binding = Binding.PyO3, + strip: Strip = Strip.No, + script: bool = False, + native: bool = False, + optional: bool = False, + py_limited_api: bool = False, ): if isinstance(target, dict): name = "; ".join("%s=%s" % (key, val) for key, val in target.items()) diff --git a/setuptools_rust/utils.py b/setuptools_rust/utils.py index ecb95d65..07142ddb 100644 --- a/setuptools_rust/utils.py +++ b/setuptools_rust/utils.py @@ -1,35 +1,10 @@ import sys import subprocess -from enum import IntEnum from distutils.errors import DistutilsPlatformError import semantic_version - -class Binding(IntEnum): - """ - Binding Options - """ - # https://github.com/PyO3/PyO3 - PyO3 = 0 - # https://github.com/dgrunwald/rust-cpython - RustCPython = 1 - # Bring your own binding - NoBinding = 2 - # Build executable - Exec = 3 - - -class Strip(IntEnum): - """ - Strip Options - """ - # do not strip symbols - No = 0 - # strip debug symbols - Debug = 1 - # strip all symbos - All = 2 +from .extension import Binding def rust_features(ext=True, binding=Binding.PyO3):