labnotes 0.0.61__tar.gz → 0.0.62__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. {labnotes-0.0.61 → labnotes-0.0.62}/PKG-INFO +1 -1
  2. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/create_app.py +3 -8
  3. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/notebook_kernel.py +18 -27
  4. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/serve_app.py +1 -4
  5. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/server.py +3 -16
  6. labnotes-0.0.61/src/labnotes/stop_idle_kernels.py +0 -13
  7. {labnotes-0.0.61 → labnotes-0.0.62}/.gitignore +0 -0
  8. {labnotes-0.0.61 → labnotes-0.0.62}/CHANGELOG.md +0 -0
  9. {labnotes-0.0.61 → labnotes-0.0.62}/MIGRATIONS.md +0 -0
  10. {labnotes-0.0.61 → labnotes-0.0.62}/README.md +0 -0
  11. {labnotes-0.0.61 → labnotes-0.0.62}/pyproject.toml +0 -0
  12. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/__init__.py +0 -0
  13. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/asset_url.py +0 -0
  14. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/cell_progress.py +0 -0
  15. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/check_counts.py +0 -0
  16. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/cli.py +0 -0
  17. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/context.md +0 -0
  18. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/context_guide.py +0 -0
  19. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/count_runs.py +0 -0
  20. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/create_experiment.py +0 -0
  21. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/create_note.py +0 -0
  22. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/data_html.py +0 -0
  23. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/default_config_text.py +0 -0
  24. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/default_counts.py +0 -0
  25. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/drain_watch.py +0 -0
  26. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/experiment_notes.py +0 -0
  27. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/experiment_progress.py +0 -0
  28. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/experiment_runs.py +0 -0
  29. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/experiments.py +0 -0
  30. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/file_changes.py +0 -0
  31. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/format_duration.py +0 -0
  32. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/format_value.py +0 -0
  33. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/group_runs.py +0 -0
  34. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/init.py +0 -0
  35. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/invocation_started.py +0 -0
  36. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/is_positive_int.py +0 -0
  37. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/list_experiments.py +0 -0
  38. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/list_runs.py +0 -0
  39. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/load_config.py +0 -0
  40. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/load_experiment.py +0 -0
  41. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/models.py +0 -0
  42. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/modes.py +0 -0
  43. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/nests_runs.py +0 -0
  44. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/output_html.py +0 -0
  45. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/parse_run_view.py +0 -0
  46. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/print_rows.py +0 -0
  47. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/python_blocks.py +0 -0
  48. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/read_config.py +0 -0
  49. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/read_path.py +0 -0
  50. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/render_markdown.py +0 -0
  51. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/__init__.py +0 -0
  52. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/describe_event.py +0 -0
  53. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/execute_run.py +0 -0
  54. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/hold_folder.py +0 -0
  55. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/new_param_run_id.py +0 -0
  56. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/new_run_id.py +0 -0
  57. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/new_stamped_run_id.py +0 -0
  58. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/param_args.py +0 -0
  59. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/param_grid.py +0 -0
  60. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/param_run_name.py +0 -0
  61. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/plan_runs.py +0 -0
  62. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/read_experiment_config.py +0 -0
  63. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/run_experiment.py +0 -0
  64. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/run_folder.py +0 -0
  65. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/run_headless.py +0 -0
  66. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/runs_parent.py +0 -0
  67. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/shell_command.py +0 -0
  68. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/start_experiment.py +0 -0
  69. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/stop_then_kill.py +0 -0
  70. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/wait_for_group.py +0 -0
  71. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/runner/write_run_json.py +0 -0
  72. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/save_note.py +0 -0
  73. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/saved_outputs.py +0 -0
  74. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/sort_href.py +0 -0
  75. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/sql_quote.py +0 -0
  76. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/static/experiment.js +0 -0
  77. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/static/labnotes.css +0 -0
  78. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/static/new-note.js +0 -0
  79. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/static/note-edit.js +0 -0
  80. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/static/note-editor.js +0 -0
  81. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/static/row-link.js +0 -0
  82. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/static/run-cell.js +0 -0
  83. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/static/view-toggle.js +0 -0
  84. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/summarize_config.py +0 -0
  85. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tables/__init__.py +0 -0
  86. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tables/experiment_rows.py +0 -0
  87. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tables/file_prefix.py +0 -0
  88. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tables/iso_time.py +0 -0
  89. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tables/list_experiment_runs.py +0 -0
  90. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tables/open_tables.py +0 -0
  91. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tables/path_glob.py +0 -0
  92. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tables/path_pattern.py +0 -0
  93. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tables/read_experiment.py +0 -0
  94. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tables/read_file.py +0 -0
  95. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tables/read_run.py +0 -0
  96. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tables/summarize_experiments.py +0 -0
  97. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/templates/base.html +0 -0
  98. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/templates/editor_imports.html +0 -0
  99. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/templates/experiment.html +0 -0
  100. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/templates/index.html +0 -0
  101. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/templates/macros.html +0 -0
  102. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tui/__init__.py +0 -0
  103. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tui/dim.py +0 -0
  104. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tui/format_elapsed.py +0 -0
  105. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tui/run_app.py +0 -0
  106. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tui/run_tui.py +0 -0
  107. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/tui/runs_table.py +0 -0
  108. {labnotes-0.0.61 → labnotes-0.0.62}/src/labnotes/wait_for_note.py +0 -0
  109. {labnotes-0.0.61 → labnotes-0.0.62}/testing-conventions.toml +0 -0
  110. {labnotes-0.0.61 → labnotes-0.0.62}/tests/integration/conftest.py +0 -0
  111. {labnotes-0.0.61 → labnotes-0.0.62}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: labnotes
3
- Version: 0.0.61
3
+ Version: 0.0.62
4
4
  Summary: Browse experiment designs, notes, and run data from a folder.
5
5
  Project-URL: Repository, https://github.com/thekevinscott/labnotes
6
6
  Author-email: Kevin Scott <me@thekevinscott.com>
@@ -31,7 +31,6 @@ from labnotes.notebook_kernel import NotebookKernel
31
31
  from labnotes.python_blocks import python_blocks
32
32
  from labnotes.save_note import save_note
33
33
  from labnotes.saved_outputs import saved_outputs
34
- from labnotes.stop_idle_kernels import stop_idle_kernels
35
34
  from labnotes.tables.open_tables import open_tables
36
35
  from labnotes.wait_for_note import wait_for_note
37
36
 
@@ -42,20 +41,16 @@ templates.env.filters["duration"] = format_duration
42
41
  templates.env.globals["asset"] = asset_url
43
42
 
44
43
 
45
- def create_app(config: LabnotesConfig, kernel_idle_minutes: float) -> Starlette:
44
+ def create_app(config: LabnotesConfig) -> Starlette:
46
45
  kernels: dict[tuple[str, str], NotebookKernel] = {}
47
46
 
48
47
  @asynccontextmanager
49
48
  async def lifespan(app: Starlette) -> AsyncIterator[None]:
50
49
  app.state.tables = open_tables(config)
51
50
  await app.state.tables.ready()
52
- watchers = [
53
- asyncio.create_task(drain_watch(app.state.tables)),
54
- asyncio.create_task(stop_idle_kernels(kernels, kernel_idle_minutes)),
55
- ]
51
+ watcher = asyncio.create_task(drain_watch(app.state.tables))
56
52
  yield
57
- for watcher in watchers:
58
- watcher.cancel()
53
+ watcher.cancel()
59
54
  for kernel in kernels.values():
60
55
  await kernel.stop()
61
56
 
@@ -1,7 +1,6 @@
1
1
  import asyncio
2
2
  import hashlib
3
3
  from pathlib import Path
4
- from time import monotonic
5
4
 
6
5
  from jupyter_client.asynchronous.client import AsyncKernelClient
7
6
  from jupyter_client.manager import AsyncKernelManager
@@ -24,7 +23,6 @@ class NotebookKernel:
24
23
  self._lock = asyncio.Lock()
25
24
  self._manager: AsyncKernelManager | None = None
26
25
  self._client: AsyncKernelClient | None = None
27
- self.last_used = monotonic()
28
26
 
29
27
  async def _start(self) -> None:
30
28
  self._manager = AsyncKernelManager(kernel_name="python3")
@@ -35,27 +33,24 @@ class NotebookKernel:
35
33
 
36
34
  async def run(self, code: str) -> str:
37
35
  async with self._lock:
38
- try:
39
- if self._client is None:
40
- await self._start()
41
- assert self._client is not None
42
- messages: list[dict[str, object]] = []
43
- await self._client.execute_interactive(
44
- code,
45
- output_hook=lambda message: messages.append(message),
46
- allow_stdin=False,
47
- )
48
- html = output_html(
49
- [
50
- {"msg_type": message["msg_type"], "content": message["content"]}
51
- for message in messages
52
- if message["msg_type"] not in QUIET
53
- ]
54
- )
55
- self.outputs[block_key(code)] = html
56
- return html
57
- finally:
58
- self.last_used = monotonic()
36
+ if self._client is None:
37
+ await self._start()
38
+ assert self._client is not None
39
+ messages: list[dict[str, object]] = []
40
+ await self._client.execute_interactive(
41
+ code,
42
+ output_hook=lambda message: messages.append(message),
43
+ allow_stdin=False,
44
+ )
45
+ html = output_html(
46
+ [
47
+ {"msg_type": message["msg_type"], "content": message["content"]}
48
+ for message in messages
49
+ if message["msg_type"] not in QUIET
50
+ ]
51
+ )
52
+ self.outputs[block_key(code)] = html
53
+ return html
59
54
 
60
55
  async def stop(self) -> None:
61
56
  client, manager = self._client, self._manager
@@ -68,7 +63,3 @@ class NotebookKernel:
68
63
  async def restart(self) -> None:
69
64
  async with self._lock:
70
65
  await self.stop()
71
-
72
- async def stop_if_idle(self, idle_seconds: float) -> None:
73
- if not self._lock.locked() and monotonic() - self.last_used > idle_seconds:
74
- await self.stop()
@@ -7,10 +7,7 @@ from labnotes.create_app import create_app
7
7
  from labnotes.read_config import read_config
8
8
 
9
9
  CONFIG = "LABNOTES_CONFIG"
10
- KERNEL_IDLE_MINUTES = "LABNOTES_KERNEL_IDLE_MINUTES"
11
10
 
12
11
 
13
12
  def serve_app() -> Starlette:
14
- return create_app(
15
- read_config(Path(os.environ[CONFIG])), int(os.environ[KERNEL_IDLE_MINUTES])
16
- )
13
+ return create_app(read_config(Path(os.environ[CONFIG])))
@@ -5,7 +5,7 @@ from uvicorn.main import main as uvicorn_main
5
5
 
6
6
  from labnotes.load_config import load_config
7
7
  from labnotes.read_config import CONFIG_FILE
8
- from labnotes.serve_app import CONFIG, KERNEL_IDLE_MINUTES
8
+ from labnotes.serve_app import CONFIG
9
9
 
10
10
  # labnotes owns these; the rest of uvicorn's options pass straight through.
11
11
  OWN_OPTIONS = {"app", "factory", "app_dir", "version", "host", "port"}
@@ -14,14 +14,6 @@ OWN_OPTIONS = {"app", "factory", "app_dir", "version", "host", "port"}
14
14
  @click.command(
15
15
  help="Serve the experiments, their notes and their runs as a local site."
16
16
  )
17
- @click.option(
18
- "--kernel-idle-minutes",
19
- type=click.IntRange(min=1),
20
- default=30,
21
- show_default=True,
22
- envvar=KERNEL_IDLE_MINUTES,
23
- help="Stop a note's Python kernel after this long without a run.",
24
- )
25
17
  @click.option(
26
18
  "-h",
27
19
  "--host",
@@ -31,19 +23,14 @@ OWN_OPTIONS = {"app", "factory", "app_dir", "version", "host", "port"}
31
23
  )
32
24
  @click.option("-p", "--port", type=int, default=8000, show_default=True)
33
25
  @click.pass_context
34
- def server(
35
- ctx: click.Context,
36
- kernel_idle_minutes: int,
37
- **server_options: object,
38
- ) -> None:
26
+ def server(ctx: click.Context, **server_options: object) -> None:
39
27
  config = load_config(ctx)
40
28
  if config is None:
41
29
  raise click.ClickException(
42
30
  f"No {CONFIG_FILE} here. Run labnotes init, or pass -c PATH."
43
31
  )
44
- # Through the environment so --reload and --workers child processes see them.
32
+ # Through the environment so --reload and --workers child processes see it.
45
33
  os.environ[CONFIG] = str(config.path.resolve())
46
- os.environ[KERNEL_IDLE_MINUTES] = str(kernel_idle_minutes)
47
34
  ctx.invoke(
48
35
  uvicorn_main,
49
36
  app="labnotes.serve_app:serve_app",
@@ -1,13 +0,0 @@
1
- import asyncio
2
- from collections.abc import Mapping
3
-
4
- from labnotes.notebook_kernel import NotebookKernel
5
-
6
-
7
- async def stop_idle_kernels(
8
- kernels: Mapping[tuple[str, str], NotebookKernel], idle_minutes: float
9
- ) -> None:
10
- while True:
11
- await asyncio.sleep(60)
12
- for kernel in list(kernels.values()):
13
- await kernel.stop_if_idle(idle_minutes * 60)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes