diff --git a/README.md b/README.md index 7d994215..6477b51c 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__": @@ -83,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)",
@@ -152,7 +101,7 @@ if __name__ == "__main__":
       0
     ],
     "hz_actual": [
-      2904010000,
+      2904008000,
       0
     ],
     "stepping": 5,
@@ -198,6 +147,7 @@ if __name__ == "__main__":
       "lm",
       "mca",
       "mce",
+      "md_clear",
       "mmx",
       "movbe",
       "msr",
@@ -253,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"
+      }
+    }
   }
 }
@@ -263,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 @@ -272,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" } } @@ -289,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"
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/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/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/examples/simple_decorator.py b/examples/simple_decorator.py
new file mode 100644
index 00000000..1b074d0e
--- /dev/null
+++ b/examples/simple_decorator.py
@@ -0,0 +1,27 @@
+import random
+
+import capsula
+
+
+@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))
+    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 open("pi.txt", "w") as output_file:
+        output_file.write(f"Pi estimate: {pi_estimate}.")
+
+
+if __name__ == "__main__":
+    calculate_pi(n_samples=1_000)
diff --git a/pyproject.toml b/pyproject.toml
index 7dfce0e2..afccd4a7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -111,6 +111,8 @@ ignore = [
 "examples/*" = [
     "INP001", # implicit-namespace-package
     "T201",   # print statement
+    "S311",   # suspicious-non-cryptographic-random-usage
+    "PTH123", # builtin-open
 ]
 "capsula/_backport.py" = [
     "ANN202", # Missing return type annotation