Skip to content

Commit e7041ba

Browse files
Add jiff into/from python convertions (#4823)
* Add jiff into/from python convertions * Remove `IntoPyObject<'py> for Span` * Update guide/src/features.md * fix docs ci --------- Co-authored-by: Icxolu <[email protected]>
1 parent ad5f6d4 commit e7041ba

File tree

11 files changed

+1455
-81
lines changed

11 files changed

+1455
-81
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ jobs:
439439
- uses: dtolnay/rust-toolchain@nightly
440440
with:
441441
components: rust-src
442-
- run: cargo rustdoc --lib --no-default-features --features full -Zunstable-options --config "build.rustdocflags=[\"--cfg\", \"docsrs\"]"
442+
- run: cargo rustdoc --lib --no-default-features --features full,jiff-01 -Zunstable-options --config "build.rustdocflags=[\"--cfg\", \"docsrs\"]"
443443

444444
coverage:
445445
if: ${{ github.event_name != 'merge_group' }}

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ either = { version = "1.9", optional = true }
3939
eyre = { version = ">= 0.6.8, < 0.7", optional = true }
4040
hashbrown = { version = ">= 0.14.5, < 0.16", optional = true }
4141
indexmap = { version = ">= 2.5.0, < 3", optional = true }
42+
jiff-01 = { package = "jiff", version = "0.1.18", optional = true }
4243
num-bigint = { version = "0.4.2", optional = true }
4344
num-complex = { version = ">= 0.4.6, < 0.5", optional = true }
4445
num-rational = {version = "0.4.1", optional = true }

guide/src/features.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,18 @@ Adds a dependency on [hashbrown](https://docs.rs/hashbrown) and enables conversi
151151

152152
Adds a dependency on [indexmap](https://docs.rs/indexmap) and enables conversions into its [`IndexMap`](https://docs.rs/indexmap/latest/indexmap/map/struct.IndexMap.html) type.
153153

154+
### `jiff-01`
155+
156+
Adds a dependency on [[email protected]](https://docs.rs/jiff/0.1) and requires MSRV 1.70. Enables a conversion from [jiff](https://docs.rs/jiff)'s types to python:
157+
- [SignedDuration](https://docs.rs/jiff/0.1/jiff/struct.SignedDuration.html) -> [`PyDelta`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyDelta.html)
158+
- [TimeZone](https://docs.rs/jiff/0.1/jiff/tz/struct.TimeZone.html) -> [`PyTzInfo`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyTzInfo.html)
159+
- [Offset](https://docs.rs/jiff/0.1/jiff/tz/struct.Offset.html) -> [`PyTzInfo`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyTzInfo.html)
160+
- [Date](https://docs.rs/jiff/0.1/jiff/civil/struct.Date.html) -> [`PyDate`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyDate.html)
161+
- [Time](https://docs.rs/jiff/0.1/jiff/civil/struct.Time.html) -> [`PyTime`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyTime.html)
162+
- [DateTime](https://docs.rs/jiff/0.1/jiff/civil/struct.DateTime.html) -> [`PyDateTime`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyDateTime.html)
163+
- [Zoned](https://docs.rs/jiff/0.1/jiff/struct.Zoned.html) -> [`PyDateTime`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyDateTime.html)
164+
- [Timestamp](https://docs.rs/jiff/0.1/jiff/struct.Timestamp.html) -> [`PyDateTime`]({{#PYO3_DOCS_URL}}/pyo3/types/struct.PyDateTime.html)
165+
154166
### `num-bigint`
155167

156168
Adds a dependency on [num-bigint](https://docs.rs/num-bigint) and enables conversions into its [`BigInt`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigInt.html) and [`BigUint`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigUint.html) types.

newsfragments/4823.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add jiff to/from python conversions.

noxfile.py

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,17 @@
1010
from functools import lru_cache
1111
from glob import glob
1212
from pathlib import Path
13-
from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple
13+
from typing import (
14+
Any,
15+
Callable,
16+
Dict,
17+
Iterable,
18+
Iterator,
19+
List,
20+
Optional,
21+
Tuple,
22+
Generator,
23+
)
1424

1525
import nox
1626
import nox.command
@@ -55,9 +65,9 @@ def test_rust(session: nox.Session):
5565
if not FREE_THREADED_BUILD:
5666
_run_cargo_test(session, features="abi3")
5767
if "skip-full" not in session.posargs:
58-
_run_cargo_test(session, features="full")
68+
_run_cargo_test(session, features="full jiff-01")
5969
if not FREE_THREADED_BUILD:
60-
_run_cargo_test(session, features="abi3 full")
70+
_run_cargo_test(session, features="abi3 full jiff-01")
6171

6272

6373
@nox.session(name="test-py", venv_backend="none")
@@ -381,14 +391,20 @@ def docs(session: nox.Session) -> None:
381391
rustdoc_flags.append(session.env.get("RUSTDOCFLAGS", ""))
382392
session.env["RUSTDOCFLAGS"] = " ".join(rustdoc_flags)
383393

394+
features = "full"
395+
396+
if get_rust_version()[:2] >= (1, 70):
397+
# jiff needs MSRC 1.70+
398+
features += ",jiff-01"
399+
384400
shutil.rmtree(PYO3_DOCS_TARGET, ignore_errors=True)
385401
_run_cargo(
386402
session,
387403
*toolchain_flags,
388404
"doc",
389405
"--lib",
390406
"--no-default-features",
391-
"--features=full",
407+
f"--features={features}",
392408
"--no-deps",
393409
"--workspace",
394410
*cargo_flags,
@@ -761,8 +777,8 @@ def update_ui_tests(session: nox.Session):
761777
env["TRYBUILD"] = "overwrite"
762778
command = ["test", "--test", "test_compile_error"]
763779
_run_cargo(session, *command, env=env)
764-
_run_cargo(session, *command, "--features=full", env=env)
765-
_run_cargo(session, *command, "--features=abi3,full", env=env)
780+
_run_cargo(session, *command, "--features=full,jiff-01", env=env)
781+
_run_cargo(session, *command, "--features=abi3,full,jiff-01", env=env)
766782

767783

768784
def _build_docs_for_ffi_check(session: nox.Session) -> None:
@@ -779,7 +795,7 @@ def _get_rust_info() -> Tuple[str, ...]:
779795
return tuple(output.splitlines())
780796

781797

782-
def _get_rust_version() -> Tuple[int, int, int, List[str]]:
798+
def get_rust_version() -> Tuple[int, int, int, List[str]]:
783799
for line in _get_rust_info():
784800
if line.startswith(_RELEASE_LINE_START):
785801
version = line[len(_RELEASE_LINE_START) :].strip()
@@ -795,30 +811,30 @@ def _get_rust_default_target() -> str:
795811

796812

797813
@lru_cache()
798-
def _get_feature_sets() -> Tuple[Tuple[str, ...], ...]:
814+
def _get_feature_sets() -> Generator[Tuple[str, ...], None, None]:
799815
"""Returns feature sets to use for clippy job"""
800816
cargo_target = os.getenv("CARGO_BUILD_TARGET", "")
817+
818+
yield from (
819+
("--no-default-features",),
820+
(
821+
"--no-default-features",
822+
"--features=abi3",
823+
),
824+
)
825+
826+
features = "full"
827+
801828
if "wasm32-wasip1" not in cargo_target:
802829
# multiple-pymethods not supported on wasm
803-
return (
804-
("--no-default-features",),
805-
(
806-
"--no-default-features",
807-
"--features=abi3",
808-
),
809-
("--features=full multiple-pymethods",),
810-
("--features=abi3 full multiple-pymethods",),
811-
)
812-
else:
813-
return (
814-
("--no-default-features",),
815-
(
816-
"--no-default-features",
817-
"--features=abi3",
818-
),
819-
("--features=full",),
820-
("--features=abi3 full",),
821-
)
830+
features += ",multiple-pymethods"
831+
832+
if get_rust_version()[:2] >= (1, 70):
833+
# jiff needs MSRC 1.70+
834+
features += ",jiff-01"
835+
836+
yield (f"--features={features}",)
837+
yield (f"--features=abi3,{features}",)
822838

823839

824840
_RELEASE_LINE_START = "release: "

src/conversions/chrono.rs

Lines changed: 3 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,13 @@
4444
use crate::conversion::IntoPyObject;
4545
use crate::exceptions::{PyTypeError, PyUserWarning, PyValueError};
4646
#[cfg(Py_LIMITED_API)]
47-
use crate::sync::GILOnceCell;
47+
use crate::intern;
4848
use crate::types::any::PyAnyMethods;
4949
#[cfg(not(Py_LIMITED_API))]
5050
use crate::types::datetime::timezone_from_offset;
5151
#[cfg(Py_LIMITED_API)]
52+
use crate::types::datetime_abi3::{check_type, timezone_utc, DatetimeTypes};
53+
#[cfg(Py_LIMITED_API)]
5254
use crate::types::IntoPyDict;
5355
use crate::types::PyNone;
5456
#[cfg(not(Py_LIMITED_API))]
@@ -57,8 +59,6 @@ use crate::types::{
5759
PyTzInfo, PyTzInfoAccess,
5860
};
5961
use crate::{ffi, Bound, FromPyObject, IntoPyObjectExt, PyAny, PyErr, PyObject, PyResult, Python};
60-
#[cfg(Py_LIMITED_API)]
61-
use crate::{intern, DowncastError};
6262
#[allow(deprecated)]
6363
use crate::{IntoPy, ToPyObject};
6464
use chrono::offset::{FixedOffset, Utc};
@@ -811,54 +811,6 @@ fn py_time_to_naive_time(py_time: &Bound<'_, PyAny>) -> PyResult<NaiveTime> {
811811
.ok_or_else(|| PyValueError::new_err("invalid or out-of-range time"))
812812
}
813813

814-
#[cfg(Py_LIMITED_API)]
815-
fn check_type(value: &Bound<'_, PyAny>, t: &PyObject, type_name: &'static str) -> PyResult<()> {
816-
if !value.is_instance(t.bind(value.py()))? {
817-
return Err(DowncastError::new(value, type_name).into());
818-
}
819-
Ok(())
820-
}
821-
822-
#[cfg(Py_LIMITED_API)]
823-
struct DatetimeTypes {
824-
date: PyObject,
825-
datetime: PyObject,
826-
time: PyObject,
827-
timedelta: PyObject,
828-
timezone: PyObject,
829-
timezone_utc: PyObject,
830-
tzinfo: PyObject,
831-
}
832-
833-
#[cfg(Py_LIMITED_API)]
834-
impl DatetimeTypes {
835-
fn get(py: Python<'_>) -> &Self {
836-
Self::try_get(py).expect("failed to load datetime module")
837-
}
838-
839-
fn try_get(py: Python<'_>) -> PyResult<&Self> {
840-
static TYPES: GILOnceCell<DatetimeTypes> = GILOnceCell::new();
841-
TYPES.get_or_try_init(py, || {
842-
let datetime = py.import("datetime")?;
843-
let timezone = datetime.getattr("timezone")?;
844-
Ok::<_, PyErr>(Self {
845-
date: datetime.getattr("date")?.into(),
846-
datetime: datetime.getattr("datetime")?.into(),
847-
time: datetime.getattr("time")?.into(),
848-
timedelta: datetime.getattr("timedelta")?.into(),
849-
timezone_utc: timezone.getattr("utc")?.into(),
850-
timezone: timezone.into(),
851-
tzinfo: datetime.getattr("tzinfo")?.into(),
852-
})
853-
})
854-
}
855-
}
856-
857-
#[cfg(Py_LIMITED_API)]
858-
fn timezone_utc(py: Python<'_>) -> Bound<'_, PyAny> {
859-
DatetimeTypes::get(py).timezone_utc.bind(py).clone()
860-
}
861-
862814
#[cfg(test)]
863815
mod tests {
864816
use super::*;

0 commit comments

Comments
 (0)