ruby_pymill 0.2.0 → 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 +49 -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 +6 -4
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).
|
|
@@ -26,6 +30,7 @@ RubyPyMill enables reproducible notebook execution from the Ruby ecosystem.
|
|
|
26
30
|
“Ruby aims to connect people with people, and tools with tools.”
|
|
27
31
|
— Yukihiro “Matz” Matsumoto
|
|
28
32
|
|
|
33
|
+
|
|
29
34
|
## Project Structure
|
|
30
35
|
| Directory | Description |
|
|
31
36
|
|----------|-------------|
|
|
@@ -36,6 +41,15 @@ RubyPyMill enables reproducible notebook execution from the Ruby ecosystem.
|
|
|
36
41
|
| py/ | Python-side environment (Papermill execution) |
|
|
37
42
|
| examples/ | Example notebooks, parameters, and outputs |
|
|
38
43
|
|
|
44
|
+
## Installation
|
|
45
|
+
RubyPyMill is distributed as a RubyGem:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
gem install ruby_pymill
|
|
49
|
+
```
|
|
50
|
+
> Note: RubyPyMill executes Jupyter notebooks via Papermill, so a Python environment is required.
|
|
51
|
+
|
|
52
|
+
|
|
39
53
|
## Setup
|
|
40
54
|
|
|
41
55
|
### Ruby
|
|
@@ -67,6 +81,19 @@ bundle exec ruby bin/ruby_pymill exec <input.ipynb> \
|
|
|
67
81
|
[--dry-run]
|
|
68
82
|
```
|
|
69
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
|
+
|
|
70
97
|
## Processing Overview
|
|
71
98
|
1. Load the notebook as JSON.
|
|
72
99
|
2. Filter cells by specified tags (the `parameters` cell is always preserved).
|
|
@@ -90,6 +117,7 @@ This example demonstrates:
|
|
|
90
117
|
For a detailed explanation in Japanese, see `README.jp.md`.
|
|
91
118
|
|
|
92
119
|
## Programmatic Usage (Experimental)
|
|
120
|
+
|
|
93
121
|
RubyPyMill is primarily designed as a CLI tool.
|
|
94
122
|
|
|
95
123
|
Internally, it exposes a Ruby execution API (`RubyPyMill::API`),
|
|
@@ -101,6 +129,27 @@ or future web APIs.
|
|
|
101
129
|
The CLI is considered the stable interface.
|
|
102
130
|
The Ruby API is experimental and may change.
|
|
103
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
|
+
|
|
148
|
+
## Changelog
|
|
149
|
+
|
|
150
|
+
See [CHANGELOG.md](https://github.com/inoue-0852/RubyPyMill-OSS/blob/main/CHANGELOG.md) for release history.
|
|
151
|
+
|
|
152
|
+
|
|
104
153
|
## License
|
|
105
154
|
MIT License
|
|
106
155
|
Copyright (c) 2025 Hiroshi Inoue / OSS-Vision
|
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
|
|
@@ -38,10 +38,12 @@ files:
|
|
|
38
38
|
- lib/ruby_pymill.rb
|
|
39
39
|
- lib/ruby_pymill/api.rb
|
|
40
40
|
- lib/ruby_pymill/version.rb
|
|
41
|
-
homepage: https://github.com/inoue-0852/RubyPyMill
|
|
41
|
+
homepage: https://github.com/inoue-0852/RubyPyMill-OSS
|
|
42
42
|
licenses:
|
|
43
43
|
- MIT
|
|
44
|
-
metadata:
|
|
44
|
+
metadata:
|
|
45
|
+
changelog_uri: https://github.com/inoue-0852/RubyPyMill-OSS/blob/main/CHANGELOG.md
|
|
46
|
+
source_code_uri: https://github.com/inoue-0852/RubyPyMill-OSS
|
|
45
47
|
post_install_message:
|
|
46
48
|
rdoc_options: []
|
|
47
49
|
require_paths:
|