piston_sdk 0.1.0 → 0.3.0

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -0
  3. data/.ruby-version +1 -0
  4. data/CHANGELOG.md +45 -0
  5. data/README.md +1 -1
  6. data/lib/piston_sdk/version.rb +1 -1
  7. data/lib/piston_sdk.rb +182 -63
  8. data/sorbet/config +4 -0
  9. data/sorbet/rbi/annotations/.gitattributes +1 -0
  10. data/sorbet/rbi/annotations/rainbow.rbi +269 -0
  11. data/sorbet/rbi/gems/.gitattributes +1 -0
  12. data/sorbet/rbi/gems/ast@2.4.3.rbi +585 -0
  13. data/sorbet/rbi/gems/benchmark@0.4.1.rbi +619 -0
  14. data/sorbet/rbi/gems/date@3.4.1.rbi +75 -0
  15. data/sorbet/rbi/gems/diff-lcs@1.6.2.rbi +1134 -0
  16. data/sorbet/rbi/gems/erb@5.0.2.rbi +878 -0
  17. data/sorbet/rbi/gems/erubi@1.13.1.rbi +155 -0
  18. data/sorbet/rbi/gems/io-console@0.8.1.rbi +9 -0
  19. data/sorbet/rbi/gems/json@2.14.1.rbi +2101 -0
  20. data/sorbet/rbi/gems/language_server-protocol@3.17.0.5.rbi +9 -0
  21. data/sorbet/rbi/gems/lint_roller@1.1.0.rbi +86 -0
  22. data/sorbet/rbi/gems/logger@1.7.0.rbi +963 -0
  23. data/sorbet/rbi/gems/netrc@0.11.0.rbi +159 -0
  24. data/sorbet/rbi/gems/parallel@1.27.0.rbi +291 -0
  25. data/sorbet/rbi/gems/parser@3.3.9.0.rbi +5535 -0
  26. data/sorbet/rbi/gems/pp@0.6.2.rbi +368 -0
  27. data/sorbet/rbi/gems/prettyprint@0.2.0.rbi +477 -0
  28. data/sorbet/rbi/gems/prism@1.5.1.rbi +42049 -0
  29. data/sorbet/rbi/gems/psych@5.2.6.rbi +2469 -0
  30. data/sorbet/rbi/gems/racc@1.8.1.rbi +160 -0
  31. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +403 -0
  32. data/sorbet/rbi/gems/rake@13.3.0.rbi +3036 -0
  33. data/sorbet/rbi/gems/rbi@0.3.6.rbi +6893 -0
  34. data/sorbet/rbi/gems/rbs@3.9.5.rbi +6978 -0
  35. data/sorbet/rbi/gems/rdoc@6.14.2.rbi +12688 -0
  36. data/sorbet/rbi/gems/regexp_parser@2.11.3.rbi +3845 -0
  37. data/sorbet/rbi/gems/reline@0.6.2.rbi +2441 -0
  38. data/sorbet/rbi/gems/rexml@3.4.4.rbi +5285 -0
  39. data/sorbet/rbi/gems/rspec-core@3.13.5.rbi +11103 -0
  40. data/sorbet/rbi/gems/rspec-expectations@3.13.5.rbi +8189 -0
  41. data/sorbet/rbi/gems/rspec-mocks@3.13.5.rbi +5350 -0
  42. data/sorbet/rbi/gems/rspec-support@3.13.6.rbi +1627 -0
  43. data/sorbet/rbi/gems/rspec@3.13.1.rbi +83 -0
  44. data/sorbet/rbi/gems/rubocop-ast@1.46.0.rbi +7734 -0
  45. data/sorbet/rbi/gems/rubocop@1.80.2.rbi +63356 -0
  46. data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1318 -0
  47. data/sorbet/rbi/gems/spoom@1.6.3.rbi +6985 -0
  48. data/sorbet/rbi/gems/stringio@3.1.7.rbi +9 -0
  49. data/sorbet/rbi/gems/tapioca@0.16.11.rbi +3628 -0
  50. data/sorbet/rbi/gems/thor@1.4.0.rbi +4399 -0
  51. data/sorbet/rbi/gems/unicode-display_width@3.2.0.rbi +132 -0
  52. data/sorbet/rbi/gems/unicode-emoji@4.1.0.rbi +251 -0
  53. data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +435 -0
  54. data/sorbet/rbi/gems/yard@0.9.37.rbi +18379 -0
  55. data/sorbet/tapioca/config.yml +13 -0
  56. data/sorbet/tapioca/require.rb +4 -0
  57. metadata +57 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0b14b2788360074d5bfbeda204336c00985fd5c02e208f278583da7451ad1da
4
- data.tar.gz: 406925f3cb0152ab40b0e3661d778429fc499cfb45e0cc960f17454e5e8c3f69
3
+ metadata.gz: a19908e88f95ee0dfb51692308d52c310e0d1ff3734a7da06aa7964473b82ae3
4
+ data.tar.gz: 82ca4c12f3fe3aace465b6a6f9604873903ddf9634670ce7822501309ed443c4
5
5
  SHA512:
6
- metadata.gz: 412636f7247d509bf44532b557de671657702c9fc955013d89be1ba7f65c9823f8b0a87ed51cc946d4b83cbe442aafc89f2b1f2744e904eab3c53e403f95cd3a
7
- data.tar.gz: e134bcf385bbc4fe5a737c3043409730522011cc9e5f31f0718bb9b15f671f8fff25131a675d973c6d33dd004a78b1ffcd1aa69b9b5553ea2f59945c349817c9
6
+ metadata.gz: a5cf669f74c56086fb33082828d672080ee7a552ae76554d309c6fded180808568be6179ed9709c966f681f78980796cc77adc2f823cb28e610a9b7057a7b67a
7
+ data.tar.gz: 3a0b082fedef872a23f3c61b7db717820ad95fd4b248521a5bf0b6b9f0c70ca88c015ec1a20701ce91b78d741071f6999df7c76d0a32235e90e1c837f59326b4
data/.rubocop.yml CHANGED
@@ -1,12 +1,19 @@
1
1
  AllCops:
2
+ NewCops: enable
2
3
  TargetRubyVersion: 3.1
3
4
 
5
+ Gemspec/RequireMFA:
6
+ Enabled: false
7
+
4
8
  Metrics/AbcSize:
5
9
  Enabled: false
6
10
 
7
11
  Metrics/BlockLength:
8
12
  Enabled: false
9
13
 
14
+ Metrics/ClassLength:
15
+ Enabled: false
16
+
10
17
  Metrics/CyclomaticComplexity:
11
18
  Enabled: false
12
19
 
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.1.4
data/CHANGELOG.md ADDED
@@ -0,0 +1,45 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Added
10
+
11
+ ### Changed
12
+
13
+ ### Deprecated
14
+
15
+ ### Removed
16
+
17
+ ### Fixed
18
+
19
+ ### Security
20
+
21
+ ## 0.3.0 2026-02-22
22
+
23
+ ### Added
24
+
25
+ - Support for new `token` parameter for optional authorization token.
26
+ - Minimum ruby version through `.ruby-version`.
27
+
28
+ ### Fixed
29
+
30
+ - `execute` no longer makes duplicate requests.
31
+
32
+ ## 0.2.0 2025-09-28
33
+
34
+ ### Added
35
+
36
+ - [Sorbet](https://sorbet.org/) type assertions.
37
+ - Support for platforms `x86_64-darwin-23` and `x86_64-linux`
38
+
39
+ ### Changed
40
+
41
+ - Improve documentation comments.
42
+
43
+ ## 0.1.0 - 2025-09-21
44
+
45
+ This is the first release of *piston_sdk*! :tada:
data/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  <div align="center">
14
14
 
15
- [![GitHub Actions](https://img.shields.io/github/actions/workflow/status/alvii147/piston-ruby-sdk/main.yml?branch=main&label=GitHub%20Actions&logo=github)](https://github.com/alvii147/piston-ruby-sdk/actions) [![License](https://img.shields.io/github/license/alvii147/piston-ruby-sdk)](https://github.com/alvii147/piston-ruby-sdk/blob/main/LICENSE)
15
+ [![Gem Version](https://badge.fury.io/rb/piston_sdk.svg)](https://badge.fury.io/rb/piston_sdk) [![GitHub Actions](https://img.shields.io/github/actions/workflow/status/alvii147/piston-ruby-sdk/main.yml?branch=main&label=GitHub%20Actions&logo=github)](https://github.com/alvii147/piston-ruby-sdk/actions) [![License](https://img.shields.io/github/license/alvii147/piston-ruby-sdk)](https://github.com/alvii147/piston-ruby-sdk/blob/main/LICENSE)
16
16
 
17
17
  </div>
18
18
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PistonSDK
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/piston_sdk.rb CHANGED
@@ -1,80 +1,159 @@
1
+ # typed: true
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require "net/http"
4
5
  require "json"
6
+ require "sorbet-runtime"
5
7
  require_relative "piston_sdk/version"
6
8
 
7
9
  # A minimal Ruby gem for using the [Piston API](https://github.com/engineer-man/piston).
8
10
  module PistonSDK
9
11
  # Data representation of a Piston runtime.
10
12
  class Runtime
11
- attr_reader :language, :version, :aliases, :runtime
13
+ extend T::Sig
14
+
15
+ sig { returns(String) }
16
+ attr_reader :language
17
+
18
+ sig { returns(String) }
19
+ attr_reader :version
20
+
21
+ sig { returns(T::Array[String]) }
22
+ attr_reader :aliases
23
+
24
+ sig { returns(T.nilable(String)) }
25
+ attr_reader :runtime
12
26
 
13
27
  # Create a new Piston runtime.
14
28
  #
15
- # @param data [Hash] JSON data.
29
+ # @param data [Hash] JSON data
30
+ sig { params(data: T::Hash[String, T.untyped]).void }
16
31
  def initialize(data)
17
- @language = data["language"]
18
- @version = data["version"]
19
- @aliases = data["aliases"]
20
- @runtime = data["runtime"]
32
+ @language = T.let(data["language"], String)
33
+ @version = T.let(data["version"], String)
34
+ @aliases = T.let(data["aliases"], T::Array[String])
35
+ @runtime = T.let(data["runtime"], T.nilable(String))
21
36
  end
22
37
  end
23
38
 
24
39
  # Data representation of Piston execution step details.
25
40
  class ExecutionStepDetails
26
- attr_reader :stdout, :stderr, :output, :code, :signal, :message, :status, :cpu_time, :wall_time, :memory
41
+ extend T::Sig
42
+
43
+ sig { returns(String) }
44
+ attr_reader :stdout
45
+
46
+ sig { returns(String) }
47
+ attr_reader :stderr
48
+
49
+ sig { returns(String) }
50
+ attr_reader :output
51
+
52
+ sig { returns(T.nilable(Integer)) }
53
+ attr_reader :code
54
+
55
+ sig { returns(T.nilable(String)) }
56
+ attr_reader :signal
57
+
58
+ sig { returns(T.nilable(String)) }
59
+ attr_reader :message
60
+
61
+ sig { returns(T.nilable(String)) }
62
+ attr_reader :status
63
+
64
+ sig { returns(T.nilable(Integer)) }
65
+ attr_reader :cpu_time
66
+
67
+ sig { returns(T.nilable(Integer)) }
68
+ attr_reader :wall_time
69
+
70
+ sig { returns(T.nilable(Integer)) }
71
+ attr_reader :memory
27
72
 
28
73
  # Create new Piston execution step details.
29
74
  #
30
- # @param data [Hash] JSON data.
75
+ # @param data [Hash] JSON data
76
+ sig { params(data: T::Hash[String, T.untyped]).void }
31
77
  def initialize(data)
32
- @stdout = data["stdout"] unless data["stdout"].nil?
33
- @stderr = data["stderr"] unless data["stderr"].nil?
34
- @output = data["output"] unless data["output"].nil?
35
- @code = data["code"] unless data["code"].nil?
36
- @signal = data["signal"] unless data["signal"].nil?
37
- @message = data["message"] unless data["message"].nil?
38
- @status = data["status"] unless data["status"].nil?
39
- @cpu_time = data["cpu_time"] unless data["cpu_time"].nil?
40
- @wall_time = data["wall_time"] unless data["wall_time"].nil?
41
- @memory = data["memory"] unless data["memory"].nil?
78
+ @stdout = T.let(data["stdout"], String)
79
+ @stderr = T.let(data["stderr"], String)
80
+ @output = T.let(data["output"], String)
81
+ @code = T.let(data["code"], T.nilable(Integer))
82
+ @signal = T.let(data["signal"], T.nilable(String))
83
+ @message = T.let(data["message"], T.nilable(String))
84
+ @status = T.let(data["status"], T.nilable(String))
85
+ @cpu_time = T.let(data["cpu_time"], T.nilable(Integer))
86
+ @wall_time = T.let(data["wall_time"], T.nilable(Integer))
87
+ @memory = T.let(data["memory"], T.nilable(Integer))
42
88
  end
43
89
  end
44
90
 
45
91
  # Data representation of Piston execution results.
46
92
  class ExecutionResults
47
- attr_reader :language, :version, :run, :compile
93
+ extend T::Sig
94
+
95
+ sig { returns(String) }
96
+ attr_reader :language
97
+
98
+ sig { returns(String) }
99
+ attr_reader :version
100
+
101
+ sig { returns(ExecutionStepDetails) }
102
+ attr_reader :run
103
+
104
+ sig { returns(T.nilable(ExecutionStepDetails)) }
105
+ attr_reader :compile
48
106
 
49
107
  # Create new Piston execution results.
50
108
  #
51
- # @param data [Hash] JSON data.
109
+ # @param data [Hash] JSON data
110
+ sig { params(data: T::Hash[String, T.untyped]).void }
52
111
  def initialize(data)
53
- @language = data["language"]
54
- @version = data["version"]
55
- @run = ExecutionStepDetails.new(data["run"]) unless data["run"].nil?
56
- @compile = ExecutionStepDetails.new(data["compile"]) unless data["compile"].nil?
112
+ @language = T.let(data["language"], String)
113
+ @version = T.let(data["version"], String)
114
+ @run = T.let(ExecutionStepDetails.new(data["run"]), ExecutionStepDetails)
115
+ @compile = T.let(
116
+ data["compile"] ? ExecutionStepDetails.new(data["compile"]) : nil,
117
+ T.nilable(ExecutionStepDetails)
118
+ )
57
119
  end
58
120
  end
59
121
 
60
122
  # HTTP client for the Piston API.
61
123
  class Client
124
+ extend T::Sig
125
+
62
126
  # Create a new client.
63
127
  #
64
- # @param base_url [String] Base URL for API.
128
+ # @param base_url [String] Base URL for API
129
+ # @param retries [Integer] Number of automatic retries to perform when rate limit is reached
65
130
  # @param compile_timeout [Integer, nil] The maximum wall-time allowed for the compile stage to finish before bailing
66
- # out in milliseconds.
131
+ # out in milliseconds
67
132
  # @param run_timeout [Integer, nil] The maximum wall-time allowed for the run stage to finish before bailing out in
68
- # milliseconds.
133
+ # milliseconds
69
134
  # @param compile_cpu_time [Integer, nil] The maximum CPU-time allowed for the compile stage to finish before bailing
70
- # out in milliseconds.
135
+ # out in milliseconds
71
136
  # @param run_cpu_time [Integer, nil] The maximum CPU-time allowed for the run stage to finish before bailing out in
72
- # milliseconds.
137
+ # milliseconds
73
138
  # @param compile_memory_limit [Integer, nil] The maximum amount of memory the compile stage is allowed to use in
74
- # bytes.
75
- # @param run_memory_limit [Integer, nil] The maximum amount of memory the run stage is allowed to use in bytes.
139
+ # bytes
140
+ # @param run_memory_limit [Integer, nil] The maximum amount of memory the run stage is allowed to use in bytes
141
+ sig do
142
+ params(
143
+ base_url: String,
144
+ token: T.nilable(String),
145
+ retries: Integer,
146
+ compile_timeout: T.nilable(Integer),
147
+ run_timeout: T.nilable(Integer),
148
+ compile_cpu_time: T.nilable(Integer),
149
+ run_cpu_time: T.nilable(Integer),
150
+ compile_memory_limit: T.nilable(Integer),
151
+ run_memory_limit: T.nilable(Integer)
152
+ ).void
153
+ end
76
154
  def initialize(
77
155
  base_url: "https://emkc.org/api/v2/piston",
156
+ token: nil,
78
157
  retries: 3,
79
158
  compile_timeout: nil,
80
159
  run_timeout: nil,
@@ -83,55 +162,81 @@ module PistonSDK
83
162
  compile_memory_limit: nil,
84
163
  run_memory_limit: nil
85
164
  )
86
- @uri = URI(base_url)
87
- @retries = retries
88
- @compile_timeout = compile_timeout
89
- @run_timeout = run_timeout
90
- @compile_cpu_time = compile_cpu_time
91
- @run_cpu_time = run_cpu_time
92
- @compile_memory_limit = compile_memory_limit
93
- @run_memory_limit = run_memory_limit
94
- @files = []
95
-
96
- @http = Net::HTTP.new(@uri.host, @uri.port)
165
+ @uri = T.let(URI(base_url), URI::Generic)
166
+ @token = T.let(token, T.nilable(String))
167
+ @retries = T.let(retries, Integer)
168
+ @compile_timeout = T.let(compile_timeout, T.nilable(Integer))
169
+ @run_timeout = T.let(run_timeout, T.nilable(Integer))
170
+ @compile_cpu_time = T.let(compile_cpu_time, T.nilable(Integer))
171
+ @run_cpu_time = T.let(run_cpu_time, T.nilable(Integer))
172
+ @compile_memory_limit = T.let(compile_memory_limit, T.nilable(Integer))
173
+ @run_memory_limit = T.let(run_memory_limit, T.nilable(Integer))
174
+ @files = T.let([], T::Array[T::Hash[Symbol, String]])
175
+
176
+ @http = T.let(Net::HTTP.new(@uri.host, @uri.port), Net::HTTP)
97
177
  @http.use_ssl = true
98
178
 
99
- @method_classes = Hash.new(Net::HTTP::Get)
100
- @method_classes[:get] = Net::HTTP::Get
101
- @method_classes[:post] = Net::HTTP::Post
179
+ @method_classes = T.let(
180
+ {
181
+ get: Net::HTTP::Get,
182
+ post: Net::HTTP::Post
183
+ },
184
+ T::Hash[Symbol, T.class_of(Net::HTTPRequest)]
185
+ )
102
186
  end
103
187
 
104
188
  # Perform raw HTTP request using exponential backoff on retries.
105
189
  #
106
- # @param method [Symbol] HTTP method.
107
- # @param path [String] URL Path.
190
+ # @param method [Symbol] HTTP method
191
+ # @param path [String] URL Path
108
192
  # @param body [Hash, nil] Optional request body
109
- # @return [Hash, Array] JSON response.
193
+ # @return [Hash, Array] JSON response
194
+ sig do
195
+ params(
196
+ method: Symbol,
197
+ path: String,
198
+ body: T.nilable(T::Hash[Symbol, T.untyped])
199
+ ).returns(
200
+ T.any(
201
+ T::Hash[T.untyped, T.untyped],
202
+ T::Array[T.untyped]
203
+ )
204
+ )
205
+ end
110
206
  def request(method: :get, path: "", body: nil)
111
- req = @method_classes[method].new("#{@uri}#{path}")
207
+ req = T.must(@method_classes[method]).new("#{@uri}#{path}")
112
208
  req["Content-Type"] = "application/json"
209
+ req["Authorization"] = @token unless @token.nil?
113
210
  req.body = body.to_json unless body.nil?
114
- res = @http.request(req)
115
211
 
116
212
  (0...@retries).each do |attempt|
117
213
  res = @http.request(req)
118
214
 
119
215
  case res
120
216
  when Net::HTTPOK
121
- return JSON.parse(res.body)
217
+ return JSON.parse(res.body || "{}")
122
218
  when Net::HTTPTooManyRequests
123
219
  sleep 2**attempt
124
220
  else
125
221
  raise "Request failed with status code #{res.code}, #{res.body}"
126
222
  end
127
223
  end
224
+
225
+ raise "Request failed due to rate limits after too many attempts"
128
226
  end
129
227
 
130
228
  # Add file to be sent to Piston for execution.
131
229
  #
132
- # @param content [String] Content of the files to upload.
133
- # @param name [String, nil] Name of the file to upload.
134
- # @param encoding [String, nil] Encoding scheme used for the file content.
230
+ # @param content [String] Content of the files to upload
231
+ # @param name [String, nil] Name of the file to upload
232
+ # @param encoding [String, nil] Encoding scheme used for the file content
233
+ sig do
234
+ params(
235
+ content: String,
236
+ name: T.nilable(String),
237
+ encoding: T.nilable(String)
238
+ ).void
239
+ end
135
240
  def add_file(content:, name: nil, encoding: nil)
136
241
  file = {
137
242
  name: name,
@@ -142,25 +247,37 @@ module PistonSDK
142
247
  @files << file
143
248
  end
144
249
 
145
- # Clear all files.
250
+ # Clear all files
251
+ sig { void }
146
252
  def clear_files
147
253
  @files.clear
148
254
  end
149
255
 
150
256
  # Get supported languages along with the current version and aliases.
151
257
  #
152
- # @return [Array<Runtime>] Array of runtimes.
258
+ # @return [Array<Runtime>] List of supported runtimes
259
+ sig { returns(T::Array[Runtime]) }
153
260
  def runtimes
154
- request(method: :get, path: "/runtimes").map { |data| Runtime.new(data) }
261
+ request(method: :get, path: "/runtimes").map do |data|
262
+ Runtime.new(T.cast(data, T::Hash[String, T.untyped]))
263
+ end
155
264
  end
156
265
 
157
266
  # Execute code for a given language and version using the added files.
158
267
  #
159
- # @param language [String] Language to use for execution.
160
- # @param version [String] Version of the language to use for execution.
161
- # @param stdin [String, nil] Text to pass as stdin to the program.
162
- # @param args [Array<String>] Arguments to pass to the program.
163
- # @return [ExecutionResults] Execution results.
268
+ # @param language [String] Language to use for execution
269
+ # @param version [String] Version of the language to use for execution
270
+ # @param stdin [String, nil] Text to pass as stdin to the program
271
+ # @param args [Array<String>] Arguments to pass to the program
272
+ # @return [ExecutionResults] Execution results
273
+ sig do
274
+ params(
275
+ language: String,
276
+ version: String,
277
+ stdin: T.nilable(String),
278
+ args: T.nilable(T::Array[String])
279
+ ).returns(ExecutionResults)
280
+ end
164
281
  def execute(language:, version:, stdin: nil, args: nil)
165
282
  body = {
166
283
  language: language,
@@ -176,7 +293,9 @@ module PistonSDK
176
293
  run_memory_limit: @run_memory_limit
177
294
  }.compact
178
295
 
179
- ExecutionResults.new(request(method: :post, path: "/execute", body: body))
296
+ ExecutionResults.new(
297
+ T.cast(request(method: :post, path: "/execute", body: body), T::Hash[String, T.untyped])
298
+ )
180
299
  end
181
300
 
182
301
  private :request
data/sorbet/config ADDED
@@ -0,0 +1,4 @@
1
+ --dir
2
+ .
3
+ --ignore=tmp/
4
+ --ignore=vendor/
@@ -0,0 +1 @@
1
+ **/*.rbi linguist-vendored=true