Skip to content

Commit

Permalink
Merge pull request #200 from pganssle/datetime
Browse files Browse the repository at this point in the history
Initial datetime bindings
  • Loading branch information
konstin committed Aug 22, 2018
2 parents 0b6d47d + 265a680 commit 3e7d528
Show file tree
Hide file tree
Showing 76 changed files with 1,708 additions and 198 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ before_install:
- source ./ci/travis/setup.sh

install:
- pip install setuptools-rust pytest pytest-benchmark
- pip install setuptools-rust pytest pytest-benchmark tox

script:
- ./ci/travis/test.sh
Expand Down
8 changes: 6 additions & 2 deletions ci/travis/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ cargo test --features $FEATURES

for example in examples/*; do
cd $example
python setup.py install
pytest -v tests
if [ -f tox.ini ]; then
tox -e py
else
pip install -e .
pytest -v tests
fi
cd $TRAVIS_BUILD_DIR
done
6 changes: 6 additions & 0 deletions examples/rustapi_module/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.pytest_cache
.mypy_cache
.hypothesis
.tox

*.py[co]
15 changes: 15 additions & 0 deletions examples/rustapi_module/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
authors = ["PyO3 Authors"]
name = "rustapi-module"
version = "0.1.0"
description = "A Python wrapper for the Rust API for purposes of testing"

[dependencies]

[dependencies.pyo3]
path = "../../"
features = ["extension-module"]

[lib]
name = "rustapi_module"
crate-type = ["cdylib"]
2 changes: 2 additions & 0 deletions examples/rustapi_module/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build-system]
requires = ["setuptools", "wheel", "setuptools_rust>=0.10.2"]
3 changes: 3 additions & 0 deletions examples/rustapi_module/requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
hypothesis>=3.55
pytest>=3.5.0
setuptools-rust>=0.10.2
Empty file.
56 changes: 56 additions & 0 deletions examples/rustapi_module/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import sys

from setuptools import setup
from setuptools.command.test import test as TestCommand
from setuptools_rust import RustExtension


class PyTest(TestCommand):
user_options = []

def run(self):
self.run_command("test_rust")

import subprocess
errno = subprocess.call(['pytest', 'tests'])
raise SystemExit(errno)


def get_py_version_cfgs():
# For now each Cfg Py_3_X flag is interpreted as "at least 3.X"
version = sys.version_info[0:2]

if version[0] == 2:
return ['--cfg=Py_2']

py3_min = 5
out_cfg = []
for minor in range(py3_min, version[1]+1):
out_cfg.append('--cfg=Py_3_%d' % minor)

return out_cfg

install_requires = []
tests_require = install_requires + ['pytest', 'pytest-benchmark']

setup(
name='rustapi-module',
version='0.1.0',
classifiers=[
'License :: OSI Approved :: MIT License',
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'Programming Language :: Python',
'Programming Language :: Rust',
'Operating System :: POSIX',
'Operating System :: MacOS :: MacOS X',
],
packages=['rustapi_module'],
rust_extensions=[RustExtension('rustapi_module.datetime', 'Cargo.toml',
rustc_flags=get_py_version_cfgs())],
install_requires=install_requires,
tests_require=tests_require,
include_package_data=True,
zip_safe=False,
cmdclass=dict(test=PyTest)
)
217 changes: 217 additions & 0 deletions examples/rustapi_module/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
#![feature(specialization)]

#[macro_use]
extern crate pyo3;

use pyo3::prelude::PyDeltaAccess;
use pyo3::prelude::PyModule;
use pyo3::prelude::PyObject;
use pyo3::prelude::{pyfunction, pymodinit};
use pyo3::prelude::{PyDate, PyDateTime, PyDelta, PyTime, PyTzInfo};
use pyo3::prelude::{PyDateAccess, PyTimeAccess};
use pyo3::prelude::{PyDict, PyTuple};
use pyo3::{ObjectProtocol, ToPyObject};
use pyo3::{Py, PyResult, Python};

#[pyfunction]
fn make_date(py: Python, year: i32, month: u8, day: u8) -> PyResult<Py<PyDate>> {
PyDate::new(py, year, month, day)
}

#[pyfunction]
fn get_date_tuple(py: Python, d: &PyDate) -> Py<PyTuple> {
PyTuple::new(
py,
&[d.get_year(), d.get_month() as i32, d.get_day() as i32],
)
}

#[pyfunction]
fn date_from_timestamp(py: Python, ts: i64) -> PyResult<Py<PyDate>> {
let timestamp = ts.to_object(py);
let args = PyTuple::new(py, &[timestamp]);
PyDate::from_timestamp(py, &args.to_object(py))
}

#[pyfunction]
fn make_time(
py: Python,
hour: u8,
minute: u8,
second: u8,
microsecond: u32,
tzinfo: Option<&PyTzInfo>,
) -> PyResult<Py<PyTime>> {
PyTime::new(
py,
hour,
minute,
second,
microsecond,
tzinfo.map(|o| o.to_object(py)).as_ref(),
)
}

#[cfg(Py_3_6)]
#[pyfunction]
fn time_with_fold(
py: Python,
hour: u8,
minute: u8,
second: u8,
microsecond: u32,
tzinfo: Option<&PyTzInfo>,
fold: bool,
) -> PyResult<Py<PyTime>> {
PyTime::new_with_fold(
py,
hour,
minute,
second,
microsecond,
tzinfo.map(|o| o.to_object(py)).as_ref(),
fold,
)
}

#[pyfunction]
fn get_time_tuple(py: Python, dt: &PyTime) -> Py<PyTuple> {
PyTuple::new(
py,
&[
dt.get_hour() as u32,
dt.get_minute() as u32,
dt.get_second() as u32,
dt.get_microsecond(),
],
)
}

#[cfg(Py_3_6)]
#[pyfunction]
fn get_time_tuple_fold(py: Python, dt: &PyTime) -> Py<PyTuple> {
PyTuple::new(
py,
&[
dt.get_hour() as u32,
dt.get_minute() as u32,
dt.get_second() as u32,
dt.get_microsecond(),
dt.get_fold() as u32,
],
)
}

#[pyfunction]
fn make_delta(py: Python, days: i32, seconds: i32, microseconds: i32) -> PyResult<Py<PyDelta>> {
PyDelta::new(py, days, seconds, microseconds, true)
}

#[pyfunction]
fn get_delta_tuple(py: Python, delta: &PyDelta) -> Py<PyTuple> {
PyTuple::new(
py,
&[
delta.get_days(),
delta.get_seconds(),
delta.get_microseconds(),
],
)
}

#[pyfunction]
fn make_datetime(
py: Python,
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
microsecond: u32,
tzinfo: Option<&PyTzInfo>,
) -> PyResult<Py<PyDateTime>> {
PyDateTime::new(
py,
year,
month,
day,
hour,
minute,
second,
microsecond,
tzinfo.map(|o| (o.to_object(py))).as_ref(),
)
}

#[pyfunction]
fn get_datetime_tuple(py: Python, dt: &PyDateTime) -> Py<PyTuple> {
PyTuple::new(
py,
&[
dt.get_year(),
dt.get_month() as i32,
dt.get_day() as i32,
dt.get_hour() as i32,
dt.get_minute() as i32,
dt.get_second() as i32,
dt.get_microsecond() as i32,
],
)
}

#[cfg(Py_3_6)]
#[pyfunction]
fn get_datetime_tuple_fold(py: Python, dt: &PyDateTime) -> Py<PyTuple> {
PyTuple::new(
py,
&[
dt.get_year(),
dt.get_month() as i32,
dt.get_day() as i32,
dt.get_hour() as i32,
dt.get_minute() as i32,
dt.get_second() as i32,
dt.get_microsecond() as i32,
dt.get_fold() as i32,
],
)
}

#[pyfunction]
fn datetime_from_timestamp(py: Python, ts: f64, tz: Option<&PyTzInfo>) -> PyResult<Py<PyDateTime>> {
let timestamp: PyObject = ts.to_object(py);
let tzi: PyObject = match tz {
Some(t) => t.to_object(py),
None => py.None(),
};

let args = PyTuple::new(py, &[timestamp, tzi]);
let kwargs = PyDict::new(py);

PyDateTime::from_timestamp(py, &args.to_object(py), &kwargs.to_object(py))
}

#[pymodinit]
fn datetime(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_function!(make_date))?;
m.add_function(wrap_function!(get_date_tuple))?;
m.add_function(wrap_function!(date_from_timestamp))?;
m.add_function(wrap_function!(make_time))?;
m.add_function(wrap_function!(get_time_tuple))?;
m.add_function(wrap_function!(make_delta))?;
m.add_function(wrap_function!(get_delta_tuple))?;
m.add_function(wrap_function!(make_datetime))?;
m.add_function(wrap_function!(get_datetime_tuple))?;
m.add_function(wrap_function!(datetime_from_timestamp))?;

// Python 3.6+ functions
#[cfg(Py_3_6)]
{
m.add_function(wrap_function!(time_with_fold));
m.add_function(wrap_function!(get_time_tuple_fold));
m.add_function(wrap_function!(get_datetime_tuple_fold));
}

Ok(())
}
Loading

0 comments on commit 3e7d528

Please sign in to comment.