Because it's unlikely that anything other than the `year` parameter will
change in the C Python API, the rest can be restricted to their logical
ranges, which improves the compile-time error checking.
While the valid ranges for the constructor parameters is the same when
expressed as either u32 or i32, since the Python API uses i32 in their
public interface, we won't have to make any changes to the signatures if
the Python behavior changes (e.g. supporting negative years) without
their API changing.
The PEP 518 way to do this is with pyproject.toml. tox doesn't support
PEP 518 yet, but we get around that by using pip install -e . as part of
the tox build until PEP 518 support arrives in tox.
This is really a test module, but the Rust convention is to put
something like this under examples/, and when it lands, we can take
advantage of "Project-based Examples for Cargo Projects" - RFC link
at https://github.com/rust-lang/rfcs/pull/2517
This more closely mimics the CPython API, since the import logic
populates the global, it should also populate the cache.
This also allows users to eagerly initialize the Python C API if
preferred (for example, doing so before populating a bunch of threads,
or before making performance measurements that will be thrown off by a
lazy import).
For whatever reason I cannot build rustapi_module without this, and
rather than figure out the core problem, I figured that, for symmetry,
it makes sense to just implement Debug for PyObject.
These were basically cargo culted from lazy_static's implementation, but
they are not idiomatic or necessary to indicate that these are private
variables.
Because the PyDateTime_CAPI is always accessed via a lazy-initialized
static reference, it is not necessary to handle the case where the
functions on the C struct have not been initialized.
It *should* be safe to cast u32 to i32 in the case of the Py{Date}{Time}
constructors, because any unsigned values that would overflow would
raise an error anyway.
This adds tests to ensure that this is the case - they currently fail on
Python <3.6 because of a known issue with the Python C API.
As noted in the comments, using call_once to initialize the
PyDateTimeAPI singleton is causing deadlocks with the GIL; these
deadlocks can be avoided by falling back on CPython's own locking
behavior.
Most of the datetime related changes were made before pyo3 switched to
using rustfmt, so I ran rustfmt only on the final commit to make it
easier to rewrite history as necessary (for fixups and whatnot).
The bounds checking tests are xfail because the datetime "fast path"
constructor didn't do bounds checking until bpo-29100 was resolved in
CPython commit b67f0967386a9c90411, which was merged for Python 3.6.
This functionality should be simple to backport to earlier versions in a
future commit.
BPO issue:
https://bugs.python.org/issue29100
CPython commit:
b67f096738