From 3f32df0bb66220e2952779af7899be63ec84698b Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Mon, 16 Dec 2024 07:52:03 +0000 Subject: [PATCH 01/18] [2024-12-16] Update .gitignore to ignore venv files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index cf6ca89..29ab927 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ dist/ eggs/ lib/ lib64/ +lib64 parts/ sdist/ var/ @@ -28,6 +29,7 @@ venv/ *.egg-info/ .installed.cfg *.egg +pyvenv.cfg # Installer logs pip-log.txt From 6a4e81814458a5ffd0d3d18cc4ba40b467461512 Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Mon, 16 Dec 2024 10:34:41 +0000 Subject: [PATCH 02/18] [2024-12-16] Bumping maturin and PyO3 versions for `mod` syntax --- Cargo.lock | 174 ++++++++++------------------------------------- Cargo.toml | 2 +- requirements.txt | 2 +- 3 files changed, 38 insertions(+), 140 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f67e630..a3a00a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,12 +8,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "cfg-if" version = "1.0.0" @@ -21,10 +15,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "indoc" -version = "1.0.9" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "libc" @@ -32,16 +32,6 @@ version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "memoffset" version = "0.9.0" @@ -58,48 +48,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] -name = "parking_lot" -version = "0.12.1" +name = "portable-atomic" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "pyo3" -version = "0.19.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e681a6cfdc4adcc93b4d3cf993749a4552018ee0a9b65fc0ccfad74352c72a38" +checksum = "e484fd2c8b4cb67ab05a318f1fd6fa8f199fcc30819f08f07d200809dba26c15" dependencies = [ "cfg-if", "indoc", "libc", "memoffset", - "parking_lot", + "once_cell", + "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", @@ -108,9 +82,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.19.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076c73d0bc438f7a4ef6fdd0c3bb4732149136abd952b110ac93e4edb13a6ba5" +checksum = "dc0e0469a84f208e20044b98965e1561028180219e35352a2afaf2b942beff3b" dependencies = [ "once_cell", "target-lexicon", @@ -118,9 +92,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.19.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53cee42e77ebe256066ba8aa77eff722b3bb91f3419177cf4cd0f304d3284d9" +checksum = "eb1547a7f9966f6f1a0f0227564a9945fe36b90da5a93b3933fc3dc03fae372d" dependencies = [ "libc", "pyo3-build-config", @@ -128,9 +102,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.19.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfeb4c99597e136528c6dd7d5e3de5434d1ceaf487436a3f03b2d56b6fc9efd1" +checksum = "fdb6da8ec6fa5cedd1626c886fc8749bdcbb09424a86461eb8cdf096b7c33257" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -140,50 +114,31 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.19.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "947dc12175c254889edc0c02e399476c2f652b4b9ebd123aa655c224de259536" +checksum = "38a385202ff5a92791168b1136afae5059d3ac118457bb7bc304c197c2d33e7d" dependencies = [ + "heck", "proc-macro2", + "pyo3-build-config", "quote", "syn", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "smallvec" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" - [[package]] name = "syn" -version = "1.0.109" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -192,9 +147,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.12" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tictoc" @@ -211,63 +166,6 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unindent" -version = "0.1.11" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" diff --git a/Cargo.toml b/Cargo.toml index 553dc4a..d03a33d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,4 @@ name = "tictoc" crate-type = ["cdylib"] [dependencies] -pyo3 = "0.19.0" +pyo3 = "0.23.3" diff --git a/requirements.txt b/requirements.txt index db9c234..a22da45 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ charset-normalizer==3.3.2 coverage==7.3.2 idna==3.4 iniconfig==2.0.0 -maturin==1.3.1 +maturin==1.7.8 packaging==23.2 pluggy==1.3.0 pytest==7.4.3 From c47dd577f510b5c5feef2f6d58e937a87b5f07e4 Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Mon, 16 Dec 2024 11:09:34 +0000 Subject: [PATCH 03/18] [2024-12-16] Changing to `mod` syntax in Rust --- src/lib.rs | 174 ++++++++++++++++++++++++++--------------------------- 1 file changed, 86 insertions(+), 88 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4974904..a802ae3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,92 +1,90 @@ use pyo3::prelude::*; -use std::time::Instant; -use pyo3::exceptions::PyException; - -#[pyclass] -#[derive(Clone)] -struct Results { - #[pyo3(get)] - nanos: u128, - #[pyo3(get)] - micros: u128, - #[pyo3(get)] - millis: u128, - #[pyo3(get)] - seconds: f64, -} - -#[pyclass(module = "tictoc", name = "init")] -struct Init { - time: Instant, - #[pyo3(get)] - results: Results, - status: bool, -} - -#[pymethods] -impl Init { - #[new] - fn new() -> Self { - let res = Results { - nanos: 0, - micros: 0, - millis: 0, - seconds: 0.0, - }; - Init { - time: Instant::now(), - results: res, - status: false, - } - } - - fn tic(&mut self) { - self.time = Instant::now(); - self.status = true; - } - - fn toc(&mut self) -> PyResult<()> { - if self.status == false { - Err(PyException::new_err("tic() must be called before toc()")) - } else { - let elapsed_time = self.time.elapsed(); - self.results = Results { - nanos: elapsed_time.as_nanos(), - micros: elapsed_time.as_micros(), - millis: elapsed_time.as_millis(), - seconds: elapsed_time.as_secs_f64(), - }; - Ok(()) - } - } -} #[pymodule] -fn tictoc(_py: Python<'_>, m: &PyModule) -> PyResult<()> { - m.add_class::()?; - Ok(()) -} - -#[test] -fn test_new() { - let init = Init::new(); - assert_eq!(init.results.nanos,0); -} - -#[test] -fn test_tic() { - let mut init = Init::new(); - let time1 = init.time; - init.tic(); - let time2 = init.time; - assert!(time2 > time1) -} - -#[test] -fn test_toc() { - let mut init = Init::new(); - init.tic(); - println!("{}","test"); - let _ = init.toc(); - assert!(init.results.nanos > 0) +mod tictoc { + use super::*; + use std::time::Instant; + use pyo3::exceptions::PyException; + + #[pyclass] + #[derive(Clone)] + struct Results { + #[pyo3(get)] + nanos: u128, + #[pyo3(get)] + micros: u128, + #[pyo3(get)] + millis: u128, + #[pyo3(get)] + seconds: f64, + } + + #[pyclass(name = "init")] + pub struct Init { + time: Instant, + #[pyo3(get)] + results: Results, + status: bool, + } + + #[pymethods] + impl Init { + #[new] + fn new() -> Self { + let res = Results { + nanos: 0, + micros: 0, + millis: 0, + seconds: 0.0, + }; + Init { + time: Instant::now(), + results: res, + status: false, + } + } + + fn tic(&mut self) { + self.time = Instant::now(); + self.status = true; + } + + fn toc(&mut self) -> PyResult<()> { + if self.status == false { + Err(PyException::new_err("tic() must be called before toc()")) + } else { + let elapsed_time = self.time.elapsed(); + self.results = Results { + nanos: elapsed_time.as_nanos(), + micros: elapsed_time.as_micros(), + millis: elapsed_time.as_millis(), + seconds: elapsed_time.as_secs_f64(), + }; + Ok(()) + } + } + } + #[test] + fn test_new() { + let init = Init::new(); + assert_eq!(init.results.nanos,0); + } + + #[test] + fn test_tic() { + let mut init = Init::new(); + let time1 = init.time; + init.tic(); + let time2 = init.time; + assert!(time2 > time1) + } + + #[test] + fn test_toc() { + let mut init = Init::new(); + init.tic(); + println!("{}","test"); + let _ = init.toc(); + assert!(init.results.nanos > 0) + } } From 3c4978bef8bdbb27be7bf8b8fed3dbab511b1467 Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Tue, 17 Dec 2024 07:23:01 +0000 Subject: [PATCH 04/18] [2024-12-17] Adding Python boilerplate to improve interface --- tictoc/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tictoc/__init__.py diff --git a/tictoc/__init__.py b/tictoc/__init__.py new file mode 100644 index 0000000..170a70b --- /dev/null +++ b/tictoc/__init__.py @@ -0,0 +1,9 @@ +from .tictoc import * + +__doc__ = tictoc.__doc__ +if hasattr(tictoc, "__all__"): + __all__ = tictoc.__all__ + +results = tictoc.init(); +tic = results.tic; +toc = results.toc; From a5475a460bec45e3fe368a8c31ffc6d56d3e607b Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Tue, 17 Dec 2024 10:11:31 +0000 Subject: [PATCH 05/18] [2024-12-17] Allow for multiple timing operations - Return `Init` object from `tic`, and accept it in `toc`. This overrides the internal class time. - Return results from `toc` to remove need for class instantiation in Python. - Print elapsed time in seconds by default when `toc` is called. --- src/lib.rs | 20 +++++++++++++++----- tictoc/__init__.py | 6 +++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a802ae3..00bbdf3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ mod tictoc { seconds: f64, } + #[derive(Clone)] #[pyclass(name = "init")] pub struct Init { time: Instant, @@ -44,23 +45,32 @@ mod tictoc { } } - fn tic(&mut self) { + fn tic(&mut self) -> PyResult { self.time = Instant::now(); self.status = true; + Ok(Init::new()) } - fn toc(&mut self) -> PyResult<()> { - if self.status == false { + fn toc(&mut self, tic: Option) -> PyResult { + let elapsed_time = match tic { + Some(ref tic) => tic.time.elapsed(), + None => self.time.elapsed(), + }; + let status = match tic { + Some(ref _tic) => true, + None => self.status, + }; + if status == false { Err(PyException::new_err("tic() must be called before toc()")) } else { - let elapsed_time = self.time.elapsed(); self.results = Results { nanos: elapsed_time.as_nanos(), micros: elapsed_time.as_micros(), millis: elapsed_time.as_millis(), seconds: elapsed_time.as_secs_f64(), }; - Ok(()) + println!("The elapsed time was {} seconds.",self.results.seconds); + Ok(self.results.clone()) } } } diff --git a/tictoc/__init__.py b/tictoc/__init__.py index 170a70b..9e15bc7 100644 --- a/tictoc/__init__.py +++ b/tictoc/__init__.py @@ -4,6 +4,6 @@ __doc__ = tictoc.__doc__ if hasattr(tictoc, "__all__"): __all__ = tictoc.__all__ -results = tictoc.init(); -tic = results.tic; -toc = results.toc; +t = tictoc.init(); +tic = t.tic; +toc = t.toc; From bec55635f9d9dbc70c29be595d4c9726b64cb3c1 Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Wed, 18 Dec 2024 12:28:00 +0000 Subject: [PATCH 06/18] [2024-12-18] Adding tests for new syntax - Moving old tests into `testInitSyntax.py` - This syntax is still supported - `testInitSyntax.py` also tests calling tic() before toc(), which is not possible with the setup of `test.py`. This should be moved to its own file soon. --- tests/test.py | 107 +++++++++++++++++++++------------------- tests/testInitSyntax.py | 82 ++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 52 deletions(-) create mode 100644 tests/testInitSyntax.py diff --git a/tests/test.py b/tests/test.py index b019fd2..7fbfdd4 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1,43 +1,41 @@ import pytest -import tictoc +from tictoc import tic,toc import time import math - -@pytest.fixture -def t(): - t = tictoc.init() - return t - - class testFunctionality: - def testBasic(self, t): - t.tic() + def testBasic(self): + tic() print("test") - t.toc() - assert t.results.seconds > 0 + results = toc() + assert results.seconds > 0 - def testOverwrite(self, t): - t.tic() + def testMultipleGlobalCalls(self): + tic() print("test") - t.toc() - firstResult = t.results.seconds + results = toc() print("test2") - t.toc() - secondResult = t.results.seconds + results2 = toc() - assert firstResult < secondResult + assert results.seconds < results2.seconds + + def testMultipleCalls(self): + first = tic() + print("test") + second = tic() + print("test2") + secondResult = toc(second).seconds + firstResult = toc(first).seconds + + assert firstResult > secondResult class testInvalid: - def testNoInit(self): + def testNonTicInputForToc(self): with pytest.raises(Exception): - t.tic() - - def testTocBeforeTic(self, t): - with pytest.raises(Exception): - t.toc() - + tic() + print("test") + toc(1) @pytest.mark.parametrize("sleepTime", [0.05, 0.5, 1]) class testAccuracy: @@ -45,42 +43,47 @@ class testAccuracy: def tol(self): return 0.0006 - def testSingleCall(self, t, sleepTime, tol): - t.tic() + def testSingleCall(self, sleepTime, tol): + tic() time.sleep(sleepTime) - t.toc() - assert (t.results.seconds > sleepTime) & ( - t.results.seconds < (t.results.seconds + tol) - ) + results = toc() + assert (results.seconds < sleepTime+tol) - def testMultipleCalls(self, t, sleepTime, tol): - t.tic() + def testMultipleGlobalCalls(self, sleepTime, tol): + tic() time.sleep(sleepTime) - t.toc() + toc() time.sleep(sleepTime) - t.toc() - assert (t.results.seconds > sleepTime * 2) & ( - t.results.seconds < (t.results.seconds + tol) - ) + results = toc() + assert (results.seconds < (sleepTime * 2)+tol) + def testMultipleCalls(self, sleepTime, tol): + first = tic() + time.sleep(sleepTime) + second = tic() + time.sleep(sleepTime) + results2 = toc(second) + results = toc(first) + assert (results.seconds < (sleepTime * 2)+tol) + assert (results2.seconds < sleepTime+tol) class testConsistency: - def testMicros(self, t): - t.tic() + def testMicros(self): + tic() print("test") - t.toc() - assert t.results.micros == (math.floor(t.results.nanos * pow(10, -3))) + results = toc() + assert results.micros == (math.floor(results.nanos * pow(10, -3))) - def testMillis(self, t): - t.tic() + def testMillis(self): + tic() print("test") - t.toc() - assert t.results.millis == (math.floor(t.results.nanos * pow(10, -6))) + results = toc() + assert results.millis == (math.floor(results.nanos * pow(10, -6))) - def testSeconds(self, t): - t.tic() + def testSeconds(self): + tic() print("test") - t.toc() - assert t.results.seconds == round( - (t.results.nanos * pow(10, -9)), 9 + results = toc() + assert results.seconds == round( + (results.nanos * pow(10, -9)), 9 ) # f64 vs u128, hence the round diff --git a/tests/testInitSyntax.py b/tests/testInitSyntax.py new file mode 100644 index 0000000..97b7557 --- /dev/null +++ b/tests/testInitSyntax.py @@ -0,0 +1,82 @@ +import pytest +import tictoc +import time +import math + + +@pytest.fixture +def t(): + t = tictoc.init() + return t + + +class testFunctionality: + def testBasic(self, t): + t.tic() + print("test") + t.toc() + assert t.results.seconds > 0 + + def testOverwrite(self, t): + t.tic() + print("test") + t.toc() + firstResult = t.results.seconds + print("test2") + t.toc() + secondResult = t.results.seconds + + assert firstResult < secondResult + + +class testInvalid: + def testNoInit(self): + with pytest.raises(Exception): + t.tic() + + def testTocBeforeTic(self, t): + with pytest.raises(Exception): + t.toc() + + +@pytest.mark.parametrize("sleepTime", [0.05, 0.5, 1]) +class testAccuracy: + @pytest.fixture(scope="class") + def tol(self): + return 0.0006 + + def testSingleCall(self, t, sleepTime, tol): + t.tic() + time.sleep(sleepTime) + t.toc() + assert (t.results.seconds < sleepTime+tol) + + def testMultipleCalls(self, t, sleepTime, tol): + t.tic() + time.sleep(sleepTime) + t.toc() + time.sleep(sleepTime) + t.toc() + assert (t.results.seconds < (sleepTime * 2)+tol) + + +class testConsistency: + def testMicros(self, t): + t.tic() + print("test") + t.toc() + assert t.results.micros == (math.floor(t.results.nanos * pow(10, -3))) + + def testMillis(self, t): + t.tic() + print("test") + t.toc() + assert t.results.millis == (math.floor(t.results.nanos * pow(10, -6))) + + def testSeconds(self, t): + t.tic() + print("test") + t.toc() + assert t.results.seconds == round( + (t.results.nanos * pow(10, -9)), 9 + ) # f64 vs u128, hence the round From 2a3087ad355cdeaa8602743cd6fc707c28b0bbaf Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Wed, 18 Dec 2024 13:55:35 +0000 Subject: [PATCH 07/18] [2024-12-18] Update Rust tests, add relevant derives to Results --- src/lib.rs | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 00bbdf3..66a1b23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ mod tictoc { use pyo3::exceptions::PyException; #[pyclass] - #[derive(Clone)] + #[derive(Clone, Debug, PartialEq)] struct Results { #[pyo3(get)] nanos: u128, @@ -74,6 +74,7 @@ mod tictoc { } } } + #[test] fn test_new() { let init = Init::new(); @@ -84,17 +85,48 @@ mod tictoc { fn test_tic() { let mut init = Init::new(); let time1 = init.time; - init.tic(); + let _ = init.tic(); let time2 = init.time; assert!(time2 > time1) } - + #[test] fn test_toc() { let mut init = Init::new(); - init.tic(); + let _ = init.tic(); println!("{}","test"); - let _ = init.toc(); - assert!(init.results.nanos > 0) + let _ = init.toc(None).unwrap(); + assert!(init.results.nanos > 0); + } + + #[test] + fn test_passing_tic_to_toc() { + let mut init = Init::new(); + let tic_obj = init.tic().unwrap(); + println!("{}","test"); + let results = init.toc(Some(tic_obj)).unwrap(); + assert!(init.results.nanos > 0); + assert_eq!(init.results,results) + } + + #[test] + fn test_multiple_calls() { + let mut init = Init::new(); + let first_tic = init.tic().unwrap(); + println!("{}","test"); + let second_tic = init.tic().unwrap(); + println!("{}","test"); + let results2 = init.toc(Some(second_tic)).unwrap(); + let results = init.toc(Some(first_tic)).unwrap(); + assert!(results.nanos > results2.nanos); + } + + #[test] + fn test_toc_before_tic() { + let mut init = Init::new(); + //assert!(init.toc(None).is_err()) + pyo3::prepare_freethreaded_python(); + let e = init.toc(None).unwrap_err(); + assert_eq!(e.to_string(),"Exception: tic() must be called before toc()") } } From 5b2a23d06af8943b5e17a325a671fc4058b5c8df Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Wed, 18 Dec 2024 15:41:46 +0000 Subject: [PATCH 08/18] [2024-12-18] Update README and run.py to reflect new syntax Also remove GitHub specific links and logo --- README.md | 79 ++++++++++++++++++++++++++++++++++++++----------------- run.py | 25 +++++++++++++----- 2 files changed, 73 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 2a88882..348d3b9 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,5 @@ -![A logo with the word tictoc followed by a stopwatch emoji](./.docs/logoLightMode.png#gh-light-mode-only) -![A logo with the word tictoc followed by a stopwatch emoji](./.docs/logoDarkMode.png#gh-dark-mode-only) - # Fast, simple and accurate Python timing. Written in Rust. -![badge](https://github.com/andrwcnln/tictoc-py/actions/workflows/python.yml/badge.svg) -![badge](https://github.com/andrwcnln/tictoc-py/actions/workflows/rust.yml/badge.svg) ![badge](https://img.shields.io/pypi/dm/tictoc) ## Installation @@ -14,40 +9,76 @@ $ python -m pip install tictoc ``` ## Usage -Import and initialise. **The module must be initialised to be used!** +Import. ```python -import tictoc -t = tictoc.init() +from tictoc import tic,toc ``` -Begin timing with `tic()`, and stop with `toc()`. +If you only want to time one section of code then use `tic() and `toc()` directly. Begin timing with `tic()`, and stop with `toc()`. ```python -t.tic() +tic() # some code -t.toc() +toc() ``` -When `toc` is called, the results are saved. They can be accessed with the following syntax: +A call to `tic()` can be followed with multiple `toc()` calls. Each will print the time elapsed since the most recent `tic()` call. ```python -t.results.{unit} +tic() +time.sleep(3) +toc() +# >>> The elapsed time was 3.000132333 seconds. +time.sleep(3) +toc() +# >>> The elapsed time was 6.000383124 seconds. +``` +For more complex timing operations, you can assign the output of `tic()` and pass it as an input to `toc()`. +> [!NOTE] +> This syntax cannot be used interchangeably with the default syntax above. Any call to `tic()` resets the global timer. +```python +firstTic = tic() +time.sleep(3) +secondTic = tic() +time.sleep(1) +toc(firstTic) +# >>> The elapsed time was 4.000317251 seconds. +time.sleep(3) +toc(secondTic) +# >>> The elapsed time was 4.000312568 seconds. +``` +Any call to `toc()` will print the elapsed time in seconds. You can save the results with full precision by assigning the output of `toc()`. +```python +tic() +# some code +results = toc() ``` The available units are: ```python -t.results.nanos # u128 -t.results.micros # u128 -t.results.millis # u128 -t.results.seconds # f64 +results.nanos # u128 +results.micros # u128 +results.millis # u128 +results.seconds # f64 ``` ## Full example ```python import time +from tictoc import tic,toc -import tictoc -t = tictoc.init() - -t.tic() # start timing +tic() # start timing time.sleep(3) # sleep for 3 seconds -t.toc() # stop timing +toc() # stop timing +# >>> The elapsed time was 3.000132333 seconds. -print(t.results.seconds) -# >>> 3.000457715 +firstTic = tic() +time.sleep(3) +secondTic = tic() +time.sleep(1) +toc(firstTic) +# >>> The elapsed time was 4.000317251 seconds. +time.sleep(3) +toc(secondTic) +# >>> The elapsed time was 4.000312568 seconds. + +tic() +results = toc() +print(results.nanos) +# >>> 2825 ``` diff --git a/run.py b/run.py index 64b2978..62ee3df 100644 --- a/run.py +++ b/run.py @@ -1,11 +1,22 @@ import time +from tictoc import tic,toc -import tictoc +tic() # start timing +time.sleep(3) # sleep for 3 seconds +toc() # stop timing +# >>> The elapsed time was 3.000132333 seconds. -t = tictoc.init() +firstTic = tic() +time.sleep(3) +secondTic = tic() +time.sleep(1) +toc(firstTic) +# >>> The elapsed time was 4.000317251 seconds. +time.sleep(3) +toc(secondTic) +# >>> The elapsed time was 4.000312568 seconds. -t.tic() # start timing -time.sleep(3) # sleep for 3 seconds -t.toc() # stop timing - -print(t.results.seconds) +tic() +results = toc() +print(results.nanos) +# >>> 2825 From 4a0c0de3cba8922e02066afdf3ef77d4c76c1b4b Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Wed, 18 Dec 2024 15:46:07 +0000 Subject: [PATCH 09/18] [2024-12-18] Fixup README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 348d3b9..ad10b4a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Import. ```python from tictoc import tic,toc ``` -If you only want to time one section of code then use `tic() and `toc()` directly. Begin timing with `tic()`, and stop with `toc()`. +Begin timing with `tic()`, and stop with `toc()`. ```python tic() # some code From 2b03ba598baf215e007dd8119f2a04a0e1c59f4c Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Wed, 18 Dec 2024 16:02:52 +0000 Subject: [PATCH 10/18] [2024-12-18] Add LICENSE, update pyproject.toml for PyPi --- LICENSE.md | 7 +++++++ pyproject.toml | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..2ad3b1d --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,7 @@ +Copyright (c) 2023-2024 Andrew Conlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/pyproject.toml b/pyproject.toml index 4cdf708..54d8428 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,11 +4,18 @@ build-backend = "maturin" [project] name = "tictoc" +authors = [ + {name = "Andrew Conlin", email = "andrew@andrewconl.in"}, + ] +description = "Fast, simple and accurate Python timing. Written in Rust." +readme = "README.md" +license = {file = "LICENSE.md"} requires-python = ">=3.8" classifiers = [ "Programming Language :: Rust", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", + "License :: OSI Approved :: MIT License", ] dynamic = ["version"] From 47f7beb14cfd50842114b33d39b7aad9feedb1ff Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Wed, 18 Dec 2024 16:08:53 +0000 Subject: [PATCH 11/18] [2024-12-18] Rename results class in Python, Results -> results --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 66a1b23..580be2c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ mod tictoc { use std::time::Instant; use pyo3::exceptions::PyException; - #[pyclass] + #[pyclass(name = "results")] #[derive(Clone, Debug, PartialEq)] struct Results { #[pyo3(get)] From a01349bf186f0faa795ae7e2ab0773263d984d7b Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Wed, 18 Dec 2024 16:15:49 +0000 Subject: [PATCH 12/18] [2024-12-18] Move toc before tic test to its own test file --- tests/testInitSyntax.py | 4 ---- tests/testTocBeforeTic.py | 7 +++++++ 2 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 tests/testTocBeforeTic.py diff --git a/tests/testInitSyntax.py b/tests/testInitSyntax.py index 97b7557..15d0a0b 100644 --- a/tests/testInitSyntax.py +++ b/tests/testInitSyntax.py @@ -34,10 +34,6 @@ class testInvalid: with pytest.raises(Exception): t.tic() - def testTocBeforeTic(self, t): - with pytest.raises(Exception): - t.toc() - @pytest.mark.parametrize("sleepTime", [0.05, 0.5, 1]) class testAccuracy: diff --git a/tests/testTocBeforeTic.py b/tests/testTocBeforeTic.py new file mode 100644 index 0000000..8f886ff --- /dev/null +++ b/tests/testTocBeforeTic.py @@ -0,0 +1,7 @@ +import pytest +import tictoc + +def testTocBeforeTic(): + with pytest.raises(Exception): + t = tictoc.init() + t.toc() From c54e001cd79348385ae537063abddf583f58a309 Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Wed, 18 Dec 2024 22:14:26 +0000 Subject: [PATCH 13/18] [2024-12-18] Add PyO3 features to support Windows cross-compilation --- Cargo.lock | 27 ++++++++++++++++++++++++++- Cargo.toml | 2 +- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3a00a4..247c571 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "autocfg" @@ -8,6 +8,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "cc" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -87,6 +96,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc0e0469a84f208e20044b98965e1561028180219e35352a2afaf2b942beff3b" dependencies = [ "once_cell", + "python3-dll-a", "target-lexicon", ] @@ -125,6 +135,15 @@ dependencies = [ "syn", ] +[[package]] +name = "python3-dll-a" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b9e268ee1be609e93a13eb06839f68f67e5fe0fb4049834d261c2d5091c1b6d" +dependencies = [ + "cc", +] + [[package]] name = "quote" version = "1.0.37" @@ -134,6 +153,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "syn" version = "2.0.90" diff --git a/Cargo.toml b/Cargo.toml index d03a33d..f1bbd93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,4 @@ name = "tictoc" crate-type = ["cdylib"] [dependencies] -pyo3 = "0.23.3" +pyo3 = { version = "0.23.3", features = ["extension-module", "generate-import-lib"] } From 932247bb7c9c68119cd0f38f32e0ba9292dc23e3 Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Thu, 19 Dec 2024 08:50:09 +0000 Subject: [PATCH 14/18] [2024-12-19] Add CHANGELOG.md --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..bf9bf18 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# `v0.2.0` +- Bumped maturin version to `1.7.8` +- Bumped PyO3 version to `0.23.3` +- Changed from `init()` syntax to direct `tic()` and `toc()` calls. `init()` syntax still supported. +- Multiple timing operations now possible by assigning output of `tic()` and passing it to `toc()` +- Added PyO3 features to support Windows cross-compilation + +# `v0.1.0` +- Initial release From 2219a148fd1945c1b1788937ce67e266c54fe4ec Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Thu, 19 Dec 2024 09:43:22 +0000 Subject: [PATCH 15/18] [2024-12-19] Suppress warning about implicit default --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 580be2c..1bd593c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,6 +51,7 @@ mod tictoc { Ok(Init::new()) } + #[pyo3(signature = (tic=None))] fn toc(&mut self, tic: Option) -> PyResult { let elapsed_time = match tic { Some(ref tic) => tic.time.elapsed(), From 08d918675dbf48d7f0f9734b76d9f645544f0900 Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Thu, 19 Dec 2024 09:50:03 +0000 Subject: [PATCH 16/18] [2024-12-19] Add repo and changelog links to pyproject.toml q --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 54d8428..3de2dd6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,5 +19,9 @@ classifiers = [ ] dynamic = ["version"] +[project.urls] +Repository = "https://git.andrewconl.in/andrew/tictoc.git" +Changelog = "https://git.andrewconl.in/andrew/tictoc/src/branch/main/CHANGELOG.md" + [tool.maturin] features = ["pyo3/extension-module"] From a3b819ded5b9252d09ef8070b1fb9c204fe32c9c Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Thu, 19 Dec 2024 09:50:14 +0000 Subject: [PATCH 17/18] [2024-12-19] Add build script --- build.sh | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100755 build.sh diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..2cf2116 --- /dev/null +++ b/build.sh @@ -0,0 +1,35 @@ +# All OSes build for CPython 3.8, CPython 3.9, CPython 3.10, CPython 3.11, CPython 3.12, CPython 3.13, CPython 3.13t, PyPy 3.9, PyPy 3.10 + +### Source distribution +maturin sdist + +### Linux +# - aarch64-unknown-linux-gnu +# - i686-unknown-linux-gnu +# - x86_64-unknown-linux-gnu +# Add Rust targets +rustup target add aarch64-unknown-linux-gnu i686-unknown-linux-gnu x86_64-unknown-linux-gnu +# Build wheels +maturin build --release --target aarch64-unknown-linux-gnu --compatibility manylinux2014 --auditwheel repair --find-interpreter --zig --quiet +maturin build --release --target i686-unknown-linux-gnu --compatibility manylinux2014 --auditwheel repair --find-interpreter --zig --quiet +maturin build --release --target x86_64-unknown-linux-gnu --compatibility manylinux2014 --auditwheel repair --find-interpreter --zig --quiet + +### macOS +# - aarch64-apple-darwin +# - x86_64-apple-darwin +# Add Rust targets +rustup target add aarch64-apple-darwin x86_64-apple-darwin +# Build wheels +maturin build --release --target aarch64-apple-darwin --compatibility manylinux2014 --auditwheel repair --find-interpreter --zig --quiet +maturin build --release --target x86_64-apple-darwin --compatibility manylinux2014 --auditwheel repair --find-interpreter --zig --quiet + +### Windows +# - x86_64-pc-windows-msvc +# Add Rust target +rustup target add x86_64-pc-windows-msvc +# Install LLVM for llvm-dlltool +sudo apt install llvm -y +# Force use of cargo-xwin for building +export MATURIN_USE_XWIN=1 +# Build wheels +maturin build --release --target x86_64-pc-windows-msvc --compatibility manylinux2014 --auditwheel repair --find-interpreter --quiet From 982ef8b81362e65b9f67db8cfdf4554d8e01bbab Mon Sep 17 00:00:00 2001 From: Andrew Conlin Date: Thu, 19 Dec 2024 09:57:20 +0000 Subject: [PATCH 18/18] [2024-12-19] Bumping version number in Cargo.toml --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 247c571..1995b68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,7 +178,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tictoc" -version = "0.1.0" +version = "0.2.0" dependencies = [ "pyo3", ] diff --git a/Cargo.toml b/Cargo.toml index f1bbd93..79907b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tictoc" -version = "0.1.0" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html