ruby_pymill 0.2.1 → 0.3.1
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.
- checksums.yaml +4 -4
- data/README.md +34 -0
- data/lib/ruby_pymill/api.rb +66 -36
- data/lib/ruby_pymill/version.rb +1 -1
- data/lib/ruby_pymill.rb +30 -9
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c2419632f9e7eb3cbb8cf35cd28fa922da156e5487de259cf16ea2eec1471088
|
|
4
|
+
data.tar.gz: c43e0a5ef36b8be315d41a157589e1d4f5298a2dfa1bc52f9a4f1e56dc107afd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 63f499bd9dce06e1728c26fed3f39ad1be2c00e9dd97007580008a4b8a493d8107f6d04849e310528a0ffec653cf5c6a4451fc518bf16232babb04a0f22e26d5
|
|
7
|
+
data.tar.gz: d564e1b1b6d23596a6b1708b7d39ffe72058799d1f4912cd55ad006bbab96f444c61969abe3b18b686ae55646df0e811b8edb0080eee3cf920995173fc14b312
|
data/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# RubyPyMill
|
|
2
2
|
Running Notebooks the Ruby Way — RubyPyMill and the Art of PoC Automation
|
|
3
3
|
|
|
4
|
+
RubyPyMill bridges Ruby orchestration with Python's Jupyter notebook execution ecosystem.
|
|
5
|
+
|
|
6
|
+
**RubyPyMill v0.3.0 introduces the experimental Ruby API.**
|
|
7
|
+
|
|
4
8
|
## Background and Purpose
|
|
5
9
|
RubyPyMill is a lightweight runner and automation starter that allows Ruby
|
|
6
10
|
to control Papermill (a Python Notebook runner).
|
|
@@ -77,6 +81,19 @@ bundle exec ruby bin/ruby_pymill exec <input.ipynb> \
|
|
|
77
81
|
[--dry-run]
|
|
78
82
|
```
|
|
79
83
|
|
|
84
|
+
### Windows (PowerShell)
|
|
85
|
+
|
|
86
|
+
PowerShell does not support `\` for line continuation.
|
|
87
|
+
Use backticks (`) or run the command in a single line.
|
|
88
|
+
|
|
89
|
+
```powershell
|
|
90
|
+
bundle exec ruby bin/ruby_pymill exec examples/notebooks/lang_radar.ipynb `
|
|
91
|
+
--output examples/outputs/lang_radar_out.ipynb `
|
|
92
|
+
--params examples/params/lang_radar.json `
|
|
93
|
+
--kernel rpymill `
|
|
94
|
+
--cell_tags "parameters,setup,graph_view,graph_output"
|
|
95
|
+
```
|
|
96
|
+
|
|
80
97
|
## Processing Overview
|
|
81
98
|
1. Load the notebook as JSON.
|
|
82
99
|
2. Filter cells by specified tags (the `parameters` cell is always preserved).
|
|
@@ -100,6 +117,7 @@ This example demonstrates:
|
|
|
100
117
|
For a detailed explanation in Japanese, see `README.jp.md`.
|
|
101
118
|
|
|
102
119
|
## Programmatic Usage (Experimental)
|
|
120
|
+
|
|
103
121
|
RubyPyMill is primarily designed as a CLI tool.
|
|
104
122
|
|
|
105
123
|
Internally, it exposes a Ruby execution API (`RubyPyMill::API`),
|
|
@@ -111,6 +129,22 @@ or future web APIs.
|
|
|
111
129
|
The CLI is considered the stable interface.
|
|
112
130
|
The Ruby API is experimental and may change.
|
|
113
131
|
|
|
132
|
+
```ruby
|
|
133
|
+
require "ruby_pymill"
|
|
134
|
+
|
|
135
|
+
result = RubyPyMill::API.run(
|
|
136
|
+
input: "examples/notebooks/lang_radar.ipynb",
|
|
137
|
+
output: "examples/outputs/lang_radar_out.ipynb",
|
|
138
|
+
params: "examples/params/lang_radar.json",
|
|
139
|
+
kernel: "rpymill",
|
|
140
|
+
cell_tags: "parameters,setup,graph_output"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
puts result.output
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
114
148
|
## Changelog
|
|
115
149
|
|
|
116
150
|
See [CHANGELOG.md](https://github.com/inoue-0852/RubyPyMill-OSS/blob/main/CHANGELOG.md) for release history.
|
data/lib/ruby_pymill/api.rb
CHANGED
|
@@ -1,59 +1,89 @@
|
|
|
1
1
|
# lib/ruby_pymill/api.rb
|
|
2
|
-
require "
|
|
2
|
+
require "fileutils"
|
|
3
3
|
|
|
4
4
|
module RubyPyMill
|
|
5
|
+
class Error < StandardError; end
|
|
6
|
+
class ExecutionError < Error; end
|
|
7
|
+
|
|
8
|
+
Result = Struct.new(
|
|
9
|
+
:success,
|
|
10
|
+
:output,
|
|
11
|
+
:filtered_input,
|
|
12
|
+
:command,
|
|
13
|
+
:stdout,
|
|
14
|
+
:stderr,
|
|
15
|
+
keyword_init: true
|
|
16
|
+
) do
|
|
17
|
+
def success?
|
|
18
|
+
success
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
5
22
|
module API
|
|
6
|
-
# Ruby から notebook
|
|
23
|
+
# Ruby から notebook を実行する公開API
|
|
7
24
|
#
|
|
8
25
|
# 例:
|
|
9
|
-
# RubyPyMill::API.run(
|
|
10
|
-
#
|
|
11
|
-
# output:
|
|
12
|
-
# kernel:
|
|
13
|
-
# cell_tags:"setup,
|
|
14
|
-
# params:
|
|
15
|
-
# log:
|
|
26
|
+
# result = RubyPyMill::API.run(
|
|
27
|
+
# input: "examples/notebooks/lang_radar.ipynb",
|
|
28
|
+
# output: "examples/outputs/lang_radar_out.ipynb",
|
|
29
|
+
# kernel: "rpymill",
|
|
30
|
+
# cell_tags: "parameters,setup,graph_output",
|
|
31
|
+
# params: "examples/params/lang_radar.json",
|
|
32
|
+
# log: "examples/logs/lang_radar.log"
|
|
33
|
+
# )
|
|
34
|
+
#
|
|
35
|
+
# puts result.output
|
|
36
|
+
#
|
|
37
|
+
# 後方互換: input: の旧名称 notebook: も引き続き使用可能
|
|
38
|
+
# result = RubyPyMill::API.run(
|
|
39
|
+
# notebook: "examples/notebooks/lang_radar.ipynb",
|
|
40
|
+
# ...
|
|
16
41
|
# )
|
|
17
42
|
#
|
|
18
43
|
def self.run(
|
|
19
|
-
|
|
44
|
+
input: nil,
|
|
45
|
+
notebook: nil,
|
|
20
46
|
output:,
|
|
21
47
|
kernel: "rpymill",
|
|
22
48
|
cell_tags: nil,
|
|
23
49
|
params: nil,
|
|
24
|
-
log: nil
|
|
50
|
+
log: nil,
|
|
51
|
+
dry_run: false,
|
|
52
|
+
logger: $stdout,
|
|
53
|
+
cwd: nil
|
|
25
54
|
)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
# notebook: は input: の旧名称(後方互換)
|
|
56
|
+
# Add `notebook:` as a backward-compatible alias for `input:` in API.run
|
|
57
|
+
resolved_input = input || notebook
|
|
58
|
+
raise ArgumentError, "input: (or notebook:) is required" if resolved_input.nil?
|
|
59
|
+
|
|
60
|
+
runner = Runner.new(
|
|
61
|
+
kernel: kernel,
|
|
62
|
+
cwd: cwd,
|
|
63
|
+
logger: logger,
|
|
64
|
+
cell_tags: cell_tags
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
result = runner.run(
|
|
68
|
+
input_ipynb: resolved_input,
|
|
69
|
+
output_ipynb: output,
|
|
70
|
+
params_json: params,
|
|
71
|
+
kernel: kernel,
|
|
72
|
+
dry_run: dry_run,
|
|
73
|
+
cell_tags: cell_tags
|
|
74
|
+
)
|
|
45
75
|
|
|
46
76
|
if log
|
|
47
77
|
log_dir = File.dirname(log)
|
|
48
|
-
|
|
49
|
-
File.write(log,
|
|
78
|
+
FileUtils.mkdir_p(log_dir)
|
|
79
|
+
File.write(log, [result.stdout, result.stderr].reject(&:empty?).join)
|
|
50
80
|
end
|
|
51
81
|
|
|
52
|
-
unless
|
|
53
|
-
raise "ruby_pymill failed
|
|
82
|
+
unless result.success?
|
|
83
|
+
raise ExecutionError, "ruby_pymill failed"
|
|
54
84
|
end
|
|
55
85
|
|
|
56
|
-
|
|
86
|
+
result
|
|
57
87
|
end
|
|
58
88
|
end
|
|
59
|
-
end
|
|
89
|
+
end
|
data/lib/ruby_pymill/version.rb
CHANGED
data/lib/ruby_pymill.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "ruby_pymill/version"
|
|
4
|
-
require_relative "ruby_pymill/api"
|
|
5
4
|
require "json"
|
|
6
5
|
require "open3"
|
|
7
6
|
require "tmpdir"
|
|
@@ -12,14 +11,14 @@ module RubyPyMill
|
|
|
12
11
|
@kernel = kernel
|
|
13
12
|
@cwd = cwd
|
|
14
13
|
@logger = logger
|
|
15
|
-
@cell_tags = normalize_tags(cell_tags)
|
|
14
|
+
@cell_tags = normalize_tags(cell_tags)
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
# params_json: path to json file or JSON string
|
|
19
18
|
# cell_tags : initialize の指定を上書き可能(カンマ/空白区切り文字列 or 配列)
|
|
20
19
|
def run(input_ipynb:, output_ipynb:, params_json: nil, kernel: nil, dry_run: false, cell_tags: nil)
|
|
21
20
|
k = kernel || @kernel
|
|
22
|
-
tags = normalize_tags(cell_tags.nil? ? @cell_tags : cell_tags)
|
|
21
|
+
tags = normalize_tags(cell_tags.nil? ? @cell_tags : cell_tags)
|
|
23
22
|
|
|
24
23
|
# 1) タグ指定があればノートを事前フィルタ
|
|
25
24
|
filtered_input = tags.empty? ? input_ipynb : filter_by_tags(input_ipynb, tags)
|
|
@@ -38,14 +37,32 @@ module RubyPyMill
|
|
|
38
37
|
end
|
|
39
38
|
end
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
@logger.puts "[RubyPyMill] #{dry_run ? 'DRY' : 'RUN'}: #{
|
|
43
|
-
return true if dry_run
|
|
40
|
+
cmd_str = args.join(" ")
|
|
41
|
+
@logger.puts "[RubyPyMill] #{dry_run ? 'DRY' : 'RUN'}: #{cmd_str}"
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
if dry_run
|
|
44
|
+
return Result.new(
|
|
45
|
+
success: true,
|
|
46
|
+
output: output_ipynb,
|
|
47
|
+
filtered_input: filtered_input,
|
|
48
|
+
command: cmd_str,
|
|
49
|
+
stdout: "",
|
|
50
|
+
stderr: ""
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
stdout_str, stderr_str, status = Open3.capture3(*args, chdir: @cwd || Dir.pwd)
|
|
46
55
|
@logger.puts stdout_str unless stdout_str.empty?
|
|
47
56
|
@logger.puts stderr_str unless stderr_str.empty?
|
|
48
|
-
|
|
57
|
+
|
|
58
|
+
Result.new(
|
|
59
|
+
success: status.success?,
|
|
60
|
+
output: output_ipynb,
|
|
61
|
+
filtered_input: filtered_input,
|
|
62
|
+
command: cmd_str,
|
|
63
|
+
stdout: stdout_str,
|
|
64
|
+
stderr: stderr_str
|
|
65
|
+
)
|
|
49
66
|
end
|
|
50
67
|
|
|
51
68
|
private
|
|
@@ -71,7 +88,9 @@ module RubyPyMill
|
|
|
71
88
|
|
|
72
89
|
# 万一ゼロ件なら parameters 系のみ確保
|
|
73
90
|
if kept.empty?
|
|
74
|
-
kept = cells.select
|
|
91
|
+
kept = cells.select do |c|
|
|
92
|
+
(Array(c.dig("metadata", "tags")) & %w[parameters injected-parameters]).any?
|
|
93
|
+
end
|
|
75
94
|
end
|
|
76
95
|
|
|
77
96
|
filtered = data.dup
|
|
@@ -84,3 +103,5 @@ module RubyPyMill
|
|
|
84
103
|
end
|
|
85
104
|
end
|
|
86
105
|
end
|
|
106
|
+
|
|
107
|
+
require_relative "ruby_pymill/api"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_pymill
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hiroshi Inoue
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-03-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|