GithubHelp home page GithubHelp logo

pyo3 / tokio Goto Github PK

View Code? Open in Web Editor NEW
244.0 18.0 12.0 589 KB

Asyncio event loop written in Rust language

License: Other

Rust 72.57% Python 27.13% Makefile 0.30%
asyncio python rust tokio-rs aiohttp

tokio's Introduction

PyO3

actions status benchmark codecov crates.io minimum rustc 1.63 discord server contributing notes

Rust bindings for Python, including tools for creating native Python extension modules. Running and interacting with Python code from a Rust binary is also supported.

Usage

PyO3 supports the following software versions:

  • Python 3.7 and up (CPython, PyPy, and GraalPy)
  • Rust 1.63 and up

You can use PyO3 to write a native Python module in Rust, or to embed Python in a Rust binary. The following sections explain each of these in turn.

Using Rust from Python

PyO3 can be used to generate a native Python module. The easiest way to try this out for the first time is to use maturin. maturin is a tool for building and publishing Rust-based Python packages with minimal configuration. The following steps install maturin, use it to generate and build a new Python package, and then launch Python to import and execute a function from the package.

First, follow the commands below to create a new directory containing a new Python virtualenv, and install maturin into the virtualenv using Python's package manager, pip:

# (replace string_sum with the desired package name)
$ mkdir string_sum
$ cd string_sum
$ python -m venv .env
$ source .env/bin/activate
$ pip install maturin

Still inside this string_sum directory, now run maturin init. This will generate the new package source. When given the choice of bindings to use, select pyo3 bindings:

$ maturin init
✔ 🤷 What kind of bindings to use? · pyo3
  ✨ Done! New project created string_sum

The most important files generated by this command are Cargo.toml and lib.rs, which will look roughly like the following:

Cargo.toml

[package]
name = "string_sum"
version = "0.1.0"
edition = "2021"

[lib]
# The name of the native library. This is the name which will be used in Python to import the
# library (i.e. `import string_sum`). If you change this, you must also change the name of the
# `#[pymodule]` in `src/lib.rs`.
name = "string_sum"
# "cdylib" is necessary to produce a shared library for Python to import from.
#
# Downstream Rust code (including code in `bin/`, `examples/`, and `tests/`) will not be able
# to `use string_sum;` unless the "rlib" or "lib" crate type is also included, e.g.:
# crate-type = ["cdylib", "rlib"]
crate-type = ["cdylib"]

[dependencies]
pyo3 = { version = "0.22.2", features = ["extension-module"] }

src/lib.rs

use pyo3::prelude::*;

/// Formats the sum of two numbers as string.
#[pyfunction]
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
    Ok((a + b).to_string())
}

/// 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 string_sum(m: &Bound<'_, PyModule>) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
    Ok(())
}

Finally, run maturin develop. This will build the package and install it into the Python virtualenv previously created and activated. The package is then ready to be used from python:

$ maturin develop
# lots of progress output as maturin runs the compilation...
$ python
>>> import string_sum
>>> string_sum.sum_as_string(5, 20)
'25'

To make changes to the package, just edit the Rust source code and then re-run maturin develop to recompile.

To run this all as a single copy-and-paste, use the bash script below (replace string_sum in the first command with the desired package name):

mkdir string_sum && cd "$_"
python -m venv .env
source .env/bin/activate
pip install maturin
maturin init --bindings pyo3
maturin develop

If you want to be able to run cargo test or use this project in a Cargo workspace and are running into linker issues, there are some workarounds in the FAQ.

As well as with maturin, it is possible to build using setuptools-rust or manually. Both offer more flexibility than maturin but require more configuration to get started.

Using Python from Rust

To embed Python into a Rust binary, you need to ensure that your Python installation contains a shared library. The following steps demonstrate how to ensure this (for Ubuntu), and then give some example code which runs an embedded Python interpreter.

To install the Python shared library on Ubuntu:

sudo apt install python3-dev

To install the Python shared library on RPM based distributions (e.g. Fedora, Red Hat, SuSE), install the python3-devel package.

Start a new project with cargo new and add pyo3 to the Cargo.toml like this:

[dependencies.pyo3]
version = "0.22.2"
features = ["auto-initialize"]

Example program displaying the value of sys.version and the current user name:

use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
use pyo3::ffi::c_str;

fn main() -> PyResult<()> {
    Python::with_gil(|py| {
        let sys = py.import("sys")?;
        let version: String = sys.getattr("version")?.extract()?;

        let locals = [("os", py.import("os")?)].into_py_dict(py);
        let code = c_str!("os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'");
        let user: String = py.eval(code, None, Some(&locals))?.extract()?;

        println!("Hello {}, I'm Python {}", user, version);
        Ok(())
    })
}

The guide has a section with lots of examples about this topic.

Tools and libraries

  • maturin Build and publish crates with pyo3, rust-cpython or cffi bindings as well as rust binaries as python packages
  • setuptools-rust Setuptools plugin for Rust support.
  • pyo3-built Simple macro to expose metadata obtained with the built crate as a PyDict
  • rust-numpy Rust binding of NumPy C-API
  • dict-derive Derive FromPyObject to automatically transform Python dicts into Rust structs
  • pyo3-log Bridge from Rust to Python logging
  • pythonize Serde serializer for converting Rust objects to JSON-compatible Python objects
  • pyo3-asyncio Utilities for working with Python's Asyncio library and async functions
  • rustimport Directly import Rust files or crates from Python, without manual compilation step. Provides pyo3 integration by default and generates pyo3 binding code automatically.
  • pyo3-arrow Lightweight Apache Arrow integration for pyo3.

Examples

  • autopy A simple, cross-platform GUI automation library for Python and Rust.
    • Contains an example of building wheels on TravisCI and appveyor using cibuildwheel
  • ballista-python A Python library that binds to Apache Arrow distributed query engine Ballista.
  • bed-reader Read and write the PLINK BED format, simply and efficiently.
    • Shows Rayon/ndarray::parallel (including capturing errors, controlling thread num), Python types to Rust generics, Github Actions
  • cryptography Python cryptography library with some functionality in Rust.
  • css-inline CSS inlining for Python implemented in Rust.
  • datafusion-python A Python library that binds to Apache Arrow in-memory query engine DataFusion.
  • deltalake-python Native Delta Lake Python binding based on delta-rs with Pandas integration.
  • fastbloom A fast bloom filter | counting bloom filter implemented by Rust for Rust and Python!
  • fastuuid Python bindings to Rust's UUID library.
  • feos Lightning fast thermodynamic modeling in Rust with fully developed Python interface.
  • forust A lightweight gradient boosted decision tree library written in Rust.
  • granian A Rust HTTP server for Python applications.
  • greptimedb Support Python scripting in the database
  • haem A Python library for working on Bioinformatics problems.
  • html2text-rs Python library for converting HTML to markup or plain text.
  • html-py-ever Using html5ever through kuchiki to speed up html parsing and css-selecting.
  • hyperjson A hyper-fast Python module for reading/writing JSON data using Rust's serde-json.
  • inline-python Inline Python code directly in your Rust code.
  • johnnycanencrypt OpenPGP library with Yubikey support.
  • jsonschema-rs Fast JSON Schema validation library.
  • mocpy Astronomical Python library offering data structures for describing any arbitrary coverage regions on the unit sphere.
  • opendal A data access layer that allows users to easily and efficiently retrieve data from various storage services in a unified way.
  • orjson Fast Python JSON library.
  • ormsgpack Fast Python msgpack library.
  • point-process High level API for pointprocesses as a Python library.
  • polaroid Hyper Fast and safe image manipulation library for Python written in Rust.
  • polars Fast multi-threaded DataFrame library in Rust | Python | Node.js.
  • pydantic-core Core validation logic for pydantic written in Rust.
  • pyheck Fast case conversion library, built by wrapping heck.
    • Quite easy to follow as there's not much code.
  • pyre Fast Python HTTP server written in Rust.
  • primp The fastest python HTTP client that can impersonate web browsers by mimicking their headers and TLS/JA3/JA4/HTTP2 fingerprints.
  • ril-py A performant and high-level image processing library for Python written in Rust.
  • river Online machine learning in python, the computationally heavy statistics algorithms are implemented in Rust.
  • robyn A Super Fast Async Python Web Framework with a Rust runtime.
  • rust-python-coverage Example PyO3 project with automated test coverage for Rust and Python.
  • sail Unifying stream, batch, and AI workloads with Apache Spark compatibility.
  • tiktoken A fast BPE tokeniser for use with OpenAI's models.
  • tokenizers Python bindings to the Hugging Face tokenizers (NLP) written in Rust.
  • tzfpy A fast package to convert longitude/latitude to timezone name.
  • utiles Fast Python web-map tile utilities
  • wasmer-python Python library to run WebAssembly binaries.

Articles and other media

Contributing

Everyone is welcomed to contribute to PyO3! There are many ways to support the project, such as:

  • help PyO3 users with issues on GitHub and Discord
  • improve documentation
  • write features and bugfixes
  • publish blogs and examples of how to use PyO3

Our contributing notes and architecture guide have more resources if you wish to volunteer time for PyO3 and are searching where to start.

If you don't have time to contribute yourself but still wish to support the project's future success, some of our maintainers have GitHub sponsorship pages:

License

PyO3 is licensed under the Apache-2.0 license or the MIT license, at your option.

Python is licensed under the Python License.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in PyO3 by you, as defined in the Apache License, shall be dual-licensed as above, without any additional terms or conditions.

Deploys by Netlify

tokio's People

Contributors

fafhrd91 avatar messense avatar popravich avatar pyup-bot avatar wmanley avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tokio's Issues

Add copyright headers to tests

Proposed header:

# Copied from the uvloop project.  If you add a new unittest here,
# please consider contributing it to the uvloop project.
#
# Portions copyright (c) 2015-present MagicStack Inc.  http://magic.io

impl future::Future for PyFuture

what type PyFuture should resolve to?

currently, Future::Item is PyResult<PyObject> and Future::Err is unsync::oneshot::Cancelled

future handling code looks like:

PyFuture::new(...).map(|res: PyResult<PyObject>| {
    match res {
        Ok(obj) => "do something with result (PyObject)"
        Err(err) => "do something with py exception"
    }
}).map_err(|err| "cancelled");

other option is to use PyObject as Future::Item and PyErr as Future::Err:

PyFuture::new(...).map(|res: PyObject| {
    "do something with result (PyObject)"
}).map_err(|err| {
    "do something with py exception"
);

I like second option, but there would be no explicit cancelation state

add native PIPES api

current implementation is based on asyncio.unix_events, we need native rust pipes support

add __weakref__ slot support

what classes should support weakrefs? PyHandle, PyTimerHandler needs to support for sure. what about Future and Task objects?

New python binding implementation

We now use new python binding https://github.com/PyO3/PyO3
it is based on rust-cpython, but whole class generation has been significantly redesigned
now code looks more like rust and not like some kind of python:

#[py:class]
pub struct Socket {
  connected: bool
}

#[py:proto]
impl PyIterProtocol for Socket {
  fn __iter__(&self, py: Python) -> PyResult<PyObject> {
  }
}

#[py:proto]
impl PyAsyncProtocol for Socket {
  fn __await__(&self, py: Python) -> PyResult<PyObject> {
  }
}

most of class functionality of already implemented, everything else should be completed within next week

test_parse_headers_multi is broken

running 51 tests
test test_compression_unknown ... ok
test test_compression_deflate ... ok
test test_compression_gzip ... ok
test test_conn_close ... ok
test test_conn_close_and_upgrade ... ok
test test_conn_close_and_upgrade_order ... ok
test test_conn_close_1_0 ... ok
test test_conn_default_1_0 ... ok
test test_conn_default_1_1 ... ok
test test_conn_keep_alive_1_0 ... ok
test test_conn_other_1_0 ... ok
test test_conn_other_1_1 ... ok
test test_headers_content_length_err_1 ... ok
test test_conn_upgrade ... ok
test test_headers_content_length_err_2 ... ok
test test_headers_multi_feed ... ok
test test_http_payload_parser_length ... ok
test test_http_request_bad_status_line ... ok
test test_http_request_chunked_payload ... ok
test test_http_request_chunked_payload_and_next_message ... ok
test test_http_request_chunked_payload_chunks ... ok
test test_http_request_parser_bad_method ... ok
test test_http_request_parser_bad_version ... ok
test test_http_request_parser_non_utf8 ... ok
test test_http_request_parser_two_slashes ... ok
test test_http_request_parser_utf8 ... ok
test test_http_request_upgrade ... ok
test test_http_request_websocket ... ok
test test_invalid_header ... ok
test test_invalid_name ... ok
test test_parse_body ... ok
test test_parse_chunked_payload_chunk_extension ... ok
test test_parse_chunked_payload_size_error ... ok
test test_parse_delayed ... ok
test test_parse_eof_payload ... ok
test test_parse_headers_max_multi ... ok
test test_parse_length_payload ... ok
test test_parse_headers_multi ... FAILED
test test_parse_length_payload_eof ... ok
test test_parse_no_length_payload ... ok
test test_request_chunked ... ok
test test_request_chunked_partial ... ok
test test_request_simple ... ok
test test_request_simple_10 ... ok
test test_request_simple_with_prefix_cr ... ok
test test_request_simple_with_prefix_crlf ... ok
test test_special_headers_partial ... ok
test test_http_request_max_status_line ... ok
test test_max_header_value_size ... ok
test test_max_header_value_size_continuation ... ok
test test_max_header_name_size ... ok

failures:

---- test_parse_headers_multi stdout ----
	thread 'test_parse_headers_multi' panicked at 'assertion failed: `(left == right)` (left: `"c2=cookie2"`, right: `"c1=cookie1"`)', tests/test_http_decoder.rs:260
note: Run with `RUST_BACKTRACE=1` for a backtrace.


failures:
    test_parse_headers_multi

test result: FAILED. 50 passed; 1 failed; 0 ignored; 0 measured

error: test failed, to rerun pass '--test test_http_decoder'

How to distribute?

should we provide python module with async-tokio? at the moment async-tokio is rust crate, to run event loop aiohttp-tokio is required.

Transport.get_extra_info('socket') should return a socket-like object

While working on uvloop I've iterated on the following three different approaches to how get_extra_info('socket') should be implemented:

  1. Wrap the underlying socket FD into a Python socket object: socket.socket(fileno=FD). The downside of this approach is that Python will try to control the FD. For instance, when the socket object is GCed, Python will try to close the FD, which isn't acceptable for uvloop and asyncio-tokio. Moreover, beginning with Python 3.6 there is a race to who closes the socket first, and Python expects to win, otherwise it will raise an IOError in socket.close().

  2. Duplicate the FD before wrapping (currently implemented in uvloop). This fixes (1), but has its own downsides: FDs are a limited resource and now we need 2x of them; FDs can stay alive longer than necessary (if the transport is closed, but its duplicated sockets are alive).

  3. Return a socket-like object. Essentially, the object should implement all socket.socket methods and have all of its attributes (to be duck-type compatible), but the implementation of socket.close() and socket.shutdown() will be empty (or will raise an error).

The third option is the one I want to implement in uvloop, and I believe the one we need to implement for asyncio-tokio.

Better documentation for rust-python interop

Hey, this is great. It's exactly what I need for a project I'm working on. Is there any documentation or examples on how to use it in a project using Rust code with tokio that calls python with asyncjo/aiohttp? Thanks much.

need to decide how to do logging

exception_handler api is implemented, but rust level logging is not defined.
should we use python logging? or rust "log" or "slog" crates and separately define logging configuration api.

documentation error: TokioLoopPolicy not EventLoopPolicy

I just had a quick try with tokio, I'm currently getting a slightly speed up (~5%) compared to asyncio loop, but significantly less quick than uvloop. Is that to be expected at this stage?

Also your readme refer to tokio.EventLoopPolicy() which is wrong, as of pypi v0.1.0 the policy is called tokio.TokioLoopPolicy()

building issues

(tokio) rocklobster:~/python/tokio $ python setup.py build
running build
running build_py
running egg_info
writing tokio.egg-info/PKG-INFO
writing dependency_links to tokio.egg-info/dependency_links.txt
error[E0308]: mismatched typesio.egg-info/top_level.txt
  --> /home/name/.cargo/git/checkouts/pyo3-a22e69bc62b9f0fd/68937db/src/objects/string.rs:54:21
   |> /home/name/.cargo/git/checkouts/pyo3-a22e69bc62b9f0fd/68937db/src/objects/string.rs:53:21
   = note: expected type `*const u8`tr() as *const i8)) pyo3/python3 pyo3/extension-module --release -- --crate-type cderror: aborting due to 2 previous errors^^^^^^^^^^^^^ expected u8, found i8
error: cargo failed with code: 101^^^^^^^^^^^^^^^^^^^^^ expected u8, found i8
To learn more, run the command again with --verbose.

Python 3.6.3
rustc 1.23.0-nightly (827cb0d61 2017-11-26)
aarch64

looks like a solvable issue, just haven't hit the source yet.

add signal handling

async-tokio already uses tokio-signal crate for ctrl-c handling
should be easy to extend to all signals

Replace loop.current_task() with officially supported API

loop.current_task() was never a part of asyncio API.
Starting from 3.7 third party loops can use dedicated calls for custom task implementation:
asyncio._register_task, ``asyncio._unregister_task, asyncio._enter_task`, `asyncio._leave_task`.

The API calls are started from underscore to emphasize these very low level position (should be used by libraries like tokio only) but they are a part of of official API.

asyncio.Task.current_task() returns None

If I register a signal handler within a task to cancel itself, tokio failes with:
AttributeError: 'NoneType' object has no attribute 'cancel'

async def test_tokio_failes():
    current_task = asyncio.Task.current_task()
    asyncio.get_event_loop().add_signal_handler(signal.SIGINT, current_task.cancel)

def main():
    # ...
    loop.run_until_complete(test_tokio_failes())

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.