From 329cb1a9d9122ec113173429a7e758146d93ee8b Mon Sep 17 00:00:00 2001 From: Shunichiro Nomura Date: Sun, 30 Jun 2024 01:50:47 +0900 Subject: [PATCH 1/6] Ignore S311 rule in examples --- examples/calculate_pi_cli.py | 4 ++-- examples/enc_context_manager.py | 4 ++-- examples/low_level.py | 4 ++-- pyproject.toml | 1 + 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/calculate_pi_cli.py b/examples/calculate_pi_cli.py index bbd92965..dd48cd44 100644 --- a/examples/calculate_pi_cli.py +++ b/examples/calculate_pi_cli.py @@ -33,8 +33,8 @@ def main(n: int, seed: int | None = None) -> None: logger.info(f"Calculating pi with {n} samples.") logger.debug(f"Seed: {seed}") random.seed(seed) - xs = (random.random() for _ in range(n)) # noqa: S311 - ys = (random.random() for _ in range(n)) # noqa: S311 + xs = (random.random() for _ in range(n)) + ys = (random.random() for _ in range(n)) inside = sum(x * x + y * y <= 1.0 for x, y in zip(xs, ys)) pi_estimate = (4.0 * inside) / n diff --git a/examples/enc_context_manager.py b/examples/enc_context_manager.py index e2e62520..358e2469 100644 --- a/examples/enc_context_manager.py +++ b/examples/enc_context_manager.py @@ -14,8 +14,8 @@ def calc_pi(n_samples: int, seed: int) -> float: random.seed(seed) - xs = (random.random() for _ in range(n_samples)) # noqa: S311 - ys = (random.random() for _ in range(n_samples)) # noqa: S311 + xs = (random.random() for _ in range(n_samples)) + ys = (random.random() for _ in range(n_samples)) inside = sum(x * x + y * y <= 1.0 for x, y in zip(xs, ys)) capsula.record("inside", inside) diff --git a/examples/low_level.py b/examples/low_level.py index 733ae485..7ca759d0 100644 --- a/examples/low_level.py +++ b/examples/low_level.py @@ -87,8 +87,8 @@ logger.info(f"Calculating pi with {N_SAMPLES} samples.") logger.debug(f"Seed: {SEED}") random.seed(SEED) - xs = (random.random() for _ in range(N_SAMPLES)) # noqa: S311 - ys = (random.random() for _ in range(N_SAMPLES)) # noqa: S311 + xs = (random.random() for _ in range(N_SAMPLES)) + ys = (random.random() for _ in range(N_SAMPLES)) inside = sum(x * x + y * y <= 1.0 for x, y in zip(xs, ys)) in_run_enc.record("inside", inside) diff --git a/pyproject.toml b/pyproject.toml index 7dfce0e2..d5f22a86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -111,6 +111,7 @@ ignore = [ "examples/*" = [ "INP001", # implicit-namespace-package "T201", # print statement + "S311", # suspicious-non-cryptographic-random-usage ] "capsula/_backport.py" = [ "ANN202", # Missing return type annotation From 7386b4414fa37b27217265597bbe1d02ab64453e Mon Sep 17 00:00:00 2001 From: Shunichiro Nomura Date: Sun, 30 Jun 2024 01:54:50 +0900 Subject: [PATCH 2/6] Recreate simple_decorator.py example and revert decorator example --- examples/decorator.py | 17 ++++++++++++++--- examples/simple_decorator.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 examples/simple_decorator.py diff --git a/examples/decorator.py b/examples/decorator.py index 96e97994..dc4bcc60 100644 --- a/examples/decorator.py +++ b/examples/decorator.py @@ -9,17 +9,28 @@ logger = logging.getLogger(__name__) -@capsula.run() +@capsula.run(ignore_config=True) @capsula.context(capsula.EnvVarContext("HOME"), mode="pre") @capsula.context(capsula.EnvVarContext("PATH"), mode="pre") +@capsula.context(capsula.CwdContext(), mode="pre") +@capsula.context(capsula.CpuContext(), mode="pre") +@capsula.context(capsula.GitRepositoryContext.default("capsula"), mode="pre") +@capsula.context(capsula.CommandContext("poetry check --lock"), mode="pre") +@capsula.context(capsula.FileContext.default(Path(__file__).parents[1] / "pyproject.toml", copy=True), mode="pre") +@capsula.context(capsula.FileContext.default(Path(__file__).parents[1] / "poetry.lock", copy=True), mode="pre") +@capsula.context(capsula.CommandContext("pip freeze --exclude-editable > requirements.txt"), mode="pre") +@capsula.context(capsula.FileContext.default(Path(__file__).parents[1] / "requirements.txt", move=True), mode="pre") +@capsula.watcher(capsula.UncaughtExceptionWatcher("Exception")) +@capsula.watcher(capsula.TimeWatcher("calculation_time")) @capsula.context(capsula.FileContext.default(Path(__file__).parent / "pi.txt", move=True), mode="post") +@capsula.reporter(capsula.JsonDumpReporter.default(), mode="all") @capsula.pass_pre_run_capsule def calculate_pi(pre_run_capsule: capsula.Capsule, *, n_samples: int = 1_000, seed: int = 42) -> None: logger.info(f"Calculating pi with {n_samples} samples.") logger.debug(f"Seed: {seed}") random.seed(seed) - xs = (random.random() for _ in range(n_samples)) # noqa: S311 - ys = (random.random() for _ in range(n_samples)) # noqa: S311 + xs = (random.random() for _ in range(n_samples)) + ys = (random.random() for _ in range(n_samples)) inside = sum(x * x + y * y <= 1.0 for x, y in zip(xs, ys)) capsula.record("inside", inside) diff --git a/examples/simple_decorator.py b/examples/simple_decorator.py new file mode 100644 index 00000000..5e2b4025 --- /dev/null +++ b/examples/simple_decorator.py @@ -0,0 +1,28 @@ +import random +from pathlib import Path + +import capsula + + +@capsula.run() +@capsula.context(capsula.FileContext.default(Path(__file__).parent / "pi.txt", move=True), mode="post") +def calculate_pi(n_samples: int = 1_000, seed: int = 42) -> None: + random.seed(seed) + xs = (random.random() for _ in range(n_samples)) + ys = (random.random() for _ in range(n_samples)) + inside = sum(x * x + y * y <= 1.0 for x, y in zip(xs, ys)) + + # You can record values to the capsule using the `record` method. + capsula.record("inside", inside) + + pi_estimate = (4.0 * inside) / n_samples + print(f"Pi estimate: {pi_estimate}") + capsula.record("pi_estimate", pi_estimate) + print(f"Run name: {capsula.current_run_name()}") + + with (Path(__file__).parent / "pi.txt").open("w") as output_file: + output_file.write(f"Pi estimate: {pi_estimate}.") + + +if __name__ == "__main__": + calculate_pi(n_samples=1_000) From 4c80c6191425d765151cfb3b6e1d3fdb8288c051 Mon Sep 17 00:00:00 2001 From: Shunichiro Nomura Date: Sun, 30 Jun 2024 01:57:58 +0900 Subject: [PATCH 3/6] Update example in README --- README.md | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 7d994215..266e825a 100644 --- a/README.md +++ b/README.md @@ -24,41 +24,37 @@ Prepare a `capsula.toml` file in the root directory of your project. An example ```toml [pre-run] -# Contexts to be captured before the execution of the decorated function/CLI command. contexts = [ + { type = "CwdContext" }, + { type = "CpuContext" }, + { type = "GitRepositoryContext", name = "capsula", path = "." }, { type = "CommandContext", command = "poetry check --lock" }, - { type = "CommandContext", command = "pip freeze --exclude-editable > requirements.txt" }, { type = "FileContext", path = "pyproject.toml", copy = true }, + { type = "FileContext", path = "poetry.lock", copy = true }, + { type = "CommandContext", command = "pip freeze --exclude-editable > requirements.txt" }, { type = "FileContext", path = "requirements.txt", move = true }, - { type = "GitRepositoryContext", name = "capsula" }, - { type = "CwdContext" }, - { type = "CpuContext" }, ] -# Reporter to be used to report the captured contexts. reporters = [{ type = "JsonDumpReporter" }] [in-run] -# Watchers to be used during the execution of the decorated function/CLI command. watchers = [{ type = "UncaughtExceptionWatcher" }, { type = "TimeWatcher" }] -# Reporter to be used to report the execution status. reporters = [{ type = "JsonDumpReporter" }] [post-run] -# Contexts to be captured after the execution of the decorated function/CLI command. -contexts = [{ type = "FileContext", path = "examples/pi.txt", move = true }] -# Reporter to be used to report the captured contexts. reporters = [{ type = "JsonDumpReporter" }] + ``` -Then, all you need to do is decorate your Python function with the `@capsula.run` decorator and specify the `load_from_config` argument as `True`. The following is an example of a Python script that estimates the value of π using the Monte Carlo method: +Then, all you need to do is decorate your Python function with the `@capsula.run()` decorator. You can also use the `@capsula.context()` decorator to add a context specific to the function. + +The following is an example of a Python script that estimates the value of π using the Monte Carlo method: ```python import random -from pathlib import Path - import capsula -@capsula.run(load_from_config=True) +@capsula.run() +@capsula.context(capsula.FileContext.default("pi.txt", move=True), mode="post") def calculate_pi(n_samples: int = 1_000, seed: int = 42) -> None: random.seed(seed) xs = (random.random() for _ in range(n_samples)) @@ -73,7 +69,7 @@ def calculate_pi(n_samples: int = 1_000, seed: int = 42) -> None: capsula.record("pi_estimate", pi_estimate) print(f"Run name: {capsula.current_run_name()}") - with (Path(__file__).parent / "pi.txt").open("w") as output_file: + with open("pi.txt", "w") as output_file: output_file.write(f"Pi estimate: {pi_estimate}.") if __name__ == "__main__": From 4730ee33d733941aae185c1ec86b37e63eb843ab Mon Sep 17 00:00:00 2001 From: Shunichiro Nomura Date: Sun, 30 Jun 2024 01:58:07 +0900 Subject: [PATCH 4/6] add per-file-ignore --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index d5f22a86..afccd4a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -112,6 +112,7 @@ ignore = [ "INP001", # implicit-namespace-package "T201", # print statement "S311", # suspicious-non-cryptographic-random-usage + "PTH123", # builtin-open ] "capsula/_backport.py" = [ "ANN202", # Missing return type annotation From db7b86d3ed95e178521cd140505f1c8b25f4f30e Mon Sep 17 00:00:00 2001 From: Shunichiro Nomura Date: Sun, 30 Jun 2024 01:58:15 +0900 Subject: [PATCH 5/6] Simplify example --- examples/simple_decorator.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/simple_decorator.py b/examples/simple_decorator.py index 5e2b4025..1b074d0e 100644 --- a/examples/simple_decorator.py +++ b/examples/simple_decorator.py @@ -1,11 +1,10 @@ import random -from pathlib import Path import capsula @capsula.run() -@capsula.context(capsula.FileContext.default(Path(__file__).parent / "pi.txt", move=True), mode="post") +@capsula.context(capsula.FileContext.default("pi.txt", move=True), mode="post") def calculate_pi(n_samples: int = 1_000, seed: int = 42) -> None: random.seed(seed) xs = (random.random() for _ in range(n_samples)) @@ -20,7 +19,7 @@ def calculate_pi(n_samples: int = 1_000, seed: int = 42) -> None: capsula.record("pi_estimate", pi_estimate) print(f"Run name: {capsula.current_run_name()}") - with (Path(__file__).parent / "pi.txt").open("w") as output_file: + with open("pi.txt", "w") as output_file: output_file.write(f"Pi estimate: {pi_estimate}.") From dc853fd730db770a7a8a306d8a745c64168d92f6 Mon Sep 17 00:00:00 2001 From: Shunichiro Nomura Date: Sun, 30 Jun 2024 02:00:16 +0900 Subject: [PATCH 6/6] update example output --- README.md | 119 +++++++++++++++++++++++++++++------------------------- 1 file changed, 65 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 266e825a..6477b51c 100644 --- a/README.md +++ b/README.md @@ -79,53 +79,6 @@ if __name__ == "__main__":
Example of output pre-run-report.json:
{
-  "command": {
-    "poetry check --lock": {
-      "command": "poetry check --lock",
-      "cwd": null,
-      "returncode": 0,
-      "stdout": "All set!\n",
-      "stderr": ""
-    },
-    "pip freeze --exclude-editable > requirements.txt": {
-      "command": "pip freeze --exclude-editable > requirements.txt",
-      "cwd": null,
-      "returncode": 0,
-      "stdout": "",
-      "stderr": ""
-    }
-  },
-  "file": {
-    "pyproject.toml": {
-      "copied_to": [
-        "vault/calculate_pi_20240225_221901_M7b3/pyproject.toml"
-      ],
-      "moved_to": null,
-      "hash": {
-        "algorithm": "sha256",
-        "digest": "6c59362587bf43411461b69675980ea338d83a468acddbc8f6cac4f2c17f7605"
-      }
-    },
-    "requirements.txt": {
-      "copied_to": [],
-      "moved_to": "vault/calculate_pi_20240225_221901_M7b3",
-      "hash": {
-        "algorithm": "sha256",
-        "digest": "99d0dbddd7f27aa25bd2d7ce3e2f4a555cdb48b039d73a6cf01fc5fa33f527e1"
-      }
-    }
-  },
-  "git": {
-    "capsula": {
-      "working_dir": "/home/nomura/ghq/github.com/shunichironomura/capsula",
-      "sha": "ff51cb6245e43253d036fcaa0b2af09c0089b783",
-      "remotes": {
-        "origin": "ssh://git@github.com/shunichironomura/capsula.git"
-      },
-      "branch": "improve-example",
-      "is_dirty": true
-    }
-  },
   "cwd": "/home/nomura/ghq/github.com/shunichironomura/capsula",
   "cpu": {
     "python_version": "3.8.17.final.0 (64 bit)",
@@ -148,7 +101,7 @@ if __name__ == "__main__":
       0
     ],
     "hz_actual": [
-      2904010000,
+      2904008000,
       0
     ],
     "stepping": 5,
@@ -194,6 +147,7 @@ if __name__ == "__main__":
       "lm",
       "mca",
       "mce",
+      "md_clear",
       "mmx",
       "movbe",
       "msr",
@@ -249,6 +203,63 @@ if __name__ == "__main__":
     "l1_instruction_cache_size": 196608,
     "l2_cache_line_size": 256,
     "l2_cache_associativity": 6
+  },
+  "git": {
+    "capsula": {
+      "working_dir": "/home/nomura/ghq/github.com/shunichironomura/capsula",
+      "sha": "db7b86d3ed95e178521cd140505f1c8b25f4f30e",
+      "remotes": {
+        "origin": "ssh://git@github.com/shunichironomura/capsula.git"
+      },
+      "branch": "update-readme",
+      "is_dirty": false
+    }
+  },
+  "command": {
+    "poetry check --lock": {
+      "command": "poetry check --lock",
+      "cwd": null,
+      "returncode": 0,
+      "stdout": "All set!\n",
+      "stderr": ""
+    },
+    "pip freeze --exclude-editable > requirements.txt": {
+      "command": "pip freeze --exclude-editable > requirements.txt",
+      "cwd": null,
+      "returncode": 0,
+      "stdout": "",
+      "stderr": ""
+    }
+  },
+  "file": {
+    "pyproject.toml": {
+      "copied_to": [
+        "vault/calculate_pi_20240630_015823_S3vb/pyproject.toml"
+      ],
+      "moved_to": null,
+      "hash": {
+        "algorithm": "sha256",
+        "digest": "9b2ccc978e950a3a4d2b5f3d29eadab593e1ffe8cd48e7606389e214cb82c8a6"
+      }
+    },
+    "poetry.lock": {
+      "copied_to": [
+        "vault/calculate_pi_20240630_015823_S3vb/poetry.lock"
+      ],
+      "moved_to": null,
+      "hash": {
+        "algorithm": "sha256",
+        "digest": "8d89f9943c8e515340a5c8c16b17a30a749d935ffe765024acaaa81fc1ed5587"
+      }
+    },
+    "requirements.txt": {
+      "copied_to": [],
+      "moved_to": "vault/calculate_pi_20240630_015823_S3vb",
+      "hash": {
+        "algorithm": "sha256",
+        "digest": "b7a36d48fda3efc9374d7d8b0fd4d910234497e2cf229001a1c2c76fce35810c"
+      }
+    }
   }
 }
@@ -259,7 +270,7 @@ if __name__ == "__main__": "function": { "calculate_pi": { "file_path": "examples/simple_decorator.py", - "first_line_no": 10, + "first_line_no": 6, "args": [], "kwargs": { "n_samples": 1000 @@ -268,15 +279,15 @@ if __name__ == "__main__": }, "inside": 782, "pi_estimate": 3.128, + "time": { + "execution_time": "0:00:00.000568" + }, "exception": { "exception": { "exc_type": null, "exc_value": null, "traceback": null } - }, - "time": { - "execution_time": "0:00:00.000658" } } @@ -285,9 +296,9 @@ if __name__ == "__main__": Example of output post-run-report.json:
{
   "file": {
-    "examples/pi.txt": {
+    "pi.txt": {
       "copied_to": [],
-      "moved_to": "vault/calculate_pi_20240225_221901_M7b3",
+      "moved_to": "vault/calculate_pi_20240630_015823_S3vb",
       "hash": {
         "algorithm": "sha256",
         "digest": "a64c761cb6b6f9ef1bc1f6afa6ba44d796c5c51d14df0bdc9d3ab9ced7982a74"