rtype 0.5.0 → 0.5.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/Gemfile +2 -2
- data/LICENSE +8 -8
- data/README.md +567 -567
- data/Rakefile +11 -11
- data/benchmark/benchmark.rb +191 -191
- data/lib/rtype/argument_type_error.rb +3 -3
- data/lib/rtype/behavior/and.rb +23 -23
- data/lib/rtype/behavior/base.rb +16 -16
- data/lib/rtype/behavior/core_ext.rb +21 -21
- data/lib/rtype/behavior/nilable.rb +20 -20
- data/lib/rtype/behavior/not.rb +23 -23
- data/lib/rtype/behavior/or.rb +23 -23
- data/lib/rtype/behavior/xor.rb +24 -24
- data/lib/rtype/behavior.rb +11 -11
- data/lib/rtype/core_ext.rb +98 -98
- data/lib/rtype/method_annotator.rb +30 -30
- data/lib/rtype/return_type_error.rb +3 -3
- data/lib/rtype/rtype_proxy.rb +9 -9
- data/lib/rtype/type_signature.rb +8 -8
- data/lib/rtype/type_signature_error.rb +3 -3
- data/lib/rtype/version.rb +3 -3
- data/lib/rtype.rb +324 -321
- data/spec/rtype_spec.rb +822 -822
- data/spec/spec_helper.rb +4 -4
- metadata +4 -3
data/lib/rtype.rb
CHANGED
@@ -1,321 +1,324 @@
|
|
1
|
-
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
2
|
-
begin
|
3
|
-
require 'java'
|
4
|
-
require 'rtype/rtype_java'
|
5
|
-
puts "Rtype with Java extension"
|
6
|
-
rescue LoadError
|
7
|
-
puts "Rtype without native extension"
|
8
|
-
end
|
9
|
-
else
|
10
|
-
begin
|
11
|
-
require "rtype/rtype_native"
|
12
|
-
puts "Rtype with C native extension"
|
13
|
-
rescue LoadError
|
14
|
-
puts "Rtype without native extension"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
require_relative 'rtype/rtype_proxy'
|
19
|
-
require_relative 'rtype/method_annotator'
|
20
|
-
require_relative 'rtype/core_ext'
|
21
|
-
require_relative 'rtype/version'
|
22
|
-
require_relative 'rtype/type_signature_error'
|
23
|
-
require_relative 'rtype/argument_type_error'
|
24
|
-
require_relative 'rtype/return_type_error'
|
25
|
-
require_relative 'rtype/type_signature'
|
26
|
-
require_relative 'rtype/behavior'
|
27
|
-
|
28
|
-
module Rtype
|
29
|
-
extend self
|
30
|
-
|
31
|
-
# This is just the 'information'
|
32
|
-
# Any change of this doesn't affect type checking
|
33
|
-
@@type_signatures = Hash.new
|
34
|
-
|
35
|
-
def define_typed_method(owner, method_name, type_sig_info)
|
36
|
-
method_name = method_name.to_sym
|
37
|
-
raise ArgumentError, "method_name is nil" if method_name.nil?
|
38
|
-
assert_valid_type_sig(type_sig_info)
|
39
|
-
|
40
|
-
el = type_sig_info.first
|
41
|
-
arg_sig = el[0]
|
42
|
-
return_sig = el[1]
|
43
|
-
|
44
|
-
if arg_sig.is_a?(Array)
|
45
|
-
expected_args = arg_sig.dup
|
46
|
-
if expected_args.last.is_a?(Hash)
|
47
|
-
expected_kwargs = expected_args.pop
|
48
|
-
else
|
49
|
-
expected_kwargs = {}
|
50
|
-
end
|
51
|
-
elsif arg_sig.is_a?(Hash)
|
52
|
-
expected_args = []
|
53
|
-
expected_kwargs = arg_sig
|
54
|
-
end
|
55
|
-
|
56
|
-
sig = TypeSignature.new
|
57
|
-
sig.argument_type = arg_sig
|
58
|
-
sig.return_type = return_sig
|
59
|
-
@@type_signatures
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
raise TypeSignatureError, "Invalid type signature: type signature is
|
162
|
-
end
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
kwargs.
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
sig.
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
when
|
196
|
-
when
|
197
|
-
when
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
::Rtype::
|
226
|
-
result = super(*args,
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
return false unless value.
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
end
|
1
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
2
|
+
begin
|
3
|
+
require 'java'
|
4
|
+
require 'rtype/rtype_java'
|
5
|
+
puts "Rtype with Java extension"
|
6
|
+
rescue LoadError
|
7
|
+
puts "Rtype without native extension"
|
8
|
+
end
|
9
|
+
else
|
10
|
+
begin
|
11
|
+
require "rtype/rtype_native"
|
12
|
+
puts "Rtype with C native extension"
|
13
|
+
rescue LoadError
|
14
|
+
puts "Rtype without native extension"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require_relative 'rtype/rtype_proxy'
|
19
|
+
require_relative 'rtype/method_annotator'
|
20
|
+
require_relative 'rtype/core_ext'
|
21
|
+
require_relative 'rtype/version'
|
22
|
+
require_relative 'rtype/type_signature_error'
|
23
|
+
require_relative 'rtype/argument_type_error'
|
24
|
+
require_relative 'rtype/return_type_error'
|
25
|
+
require_relative 'rtype/type_signature'
|
26
|
+
require_relative 'rtype/behavior'
|
27
|
+
|
28
|
+
module Rtype
|
29
|
+
extend self
|
30
|
+
|
31
|
+
# This is just the 'information'
|
32
|
+
# Any change of this doesn't affect type checking
|
33
|
+
@@type_signatures = Hash.new
|
34
|
+
|
35
|
+
def define_typed_method(owner, method_name, type_sig_info)
|
36
|
+
method_name = method_name.to_sym
|
37
|
+
raise ArgumentError, "method_name is nil" if method_name.nil?
|
38
|
+
assert_valid_type_sig(type_sig_info)
|
39
|
+
|
40
|
+
el = type_sig_info.first
|
41
|
+
arg_sig = el[0]
|
42
|
+
return_sig = el[1]
|
43
|
+
|
44
|
+
if arg_sig.is_a?(Array)
|
45
|
+
expected_args = arg_sig.dup
|
46
|
+
if expected_args.last.is_a?(Hash)
|
47
|
+
expected_kwargs = expected_args.pop
|
48
|
+
else
|
49
|
+
expected_kwargs = {}
|
50
|
+
end
|
51
|
+
elsif arg_sig.is_a?(Hash)
|
52
|
+
expected_args = []
|
53
|
+
expected_kwargs = arg_sig
|
54
|
+
end
|
55
|
+
|
56
|
+
sig = TypeSignature.new
|
57
|
+
sig.argument_type = arg_sig
|
58
|
+
sig.return_type = return_sig
|
59
|
+
unless @@type_signatures.key?(owner)
|
60
|
+
@@type_signatures[owner] = {}
|
61
|
+
end
|
62
|
+
@@type_signatures[owner][method_name] = sig
|
63
|
+
|
64
|
+
define_typed_method_to_proxy(owner, method_name, expected_args, expected_kwargs, return_sig)
|
65
|
+
end
|
66
|
+
|
67
|
+
def define_typed_accessor(owner, accessor_name, type_behavior)
|
68
|
+
getter = accessor_name.to_sym
|
69
|
+
setter = :"#{accessor_name}="
|
70
|
+
valid?(type_behavior, nil)
|
71
|
+
define_typed_method owner, getter, [] => type_behavior
|
72
|
+
define_typed_method owner, setter, [type_behavior] => Any
|
73
|
+
end
|
74
|
+
|
75
|
+
def type_signatures
|
76
|
+
@@type_signatures
|
77
|
+
end
|
78
|
+
|
79
|
+
=begin
|
80
|
+
def assert_keyword_arguments_type(expected_kwargs, kwargs)
|
81
|
+
kwargs.each do |key, value|
|
82
|
+
expected = expected_kwargs[key]
|
83
|
+
unless expected.nil?
|
84
|
+
unless valid?(expected, value)
|
85
|
+
raise ArgumentTypeError, "for '#{key}' argument:\n" + type_error_message(expected, value)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
=end
|
91
|
+
|
92
|
+
def arg_type_error_message(idx, expected, value)
|
93
|
+
"#{arg_message(idx)}\n" + type_error_message(expected, value)
|
94
|
+
end
|
95
|
+
|
96
|
+
def kwarg_type_error_message(key, expected, value)
|
97
|
+
"#{kwarg_message(key)}\n" + type_error_message(expected, value)
|
98
|
+
end
|
99
|
+
|
100
|
+
def arg_message(idx)
|
101
|
+
"for #{(idx+1).ordinalize} argument:"
|
102
|
+
end
|
103
|
+
|
104
|
+
def kwarg_message(key)
|
105
|
+
"for '#{key}' argument:"
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def type_error_message(expected, value)
|
110
|
+
case expected
|
111
|
+
when Rtype::Behavior::Base
|
112
|
+
expected.error_message(value)
|
113
|
+
when Module
|
114
|
+
"Expected #{value.inspect} to be a #{expected}"
|
115
|
+
when Symbol
|
116
|
+
"Expected #{value.inspect} to respond to :#{expected}"
|
117
|
+
when Regexp
|
118
|
+
"Expected stringified #{value.inspect} to match regexp #{expected.inspect}"
|
119
|
+
when Range
|
120
|
+
"Expected #{value.inspect} to be included in range #{expected.inspect}"
|
121
|
+
when Array
|
122
|
+
if value.is_a?(Array)
|
123
|
+
arr = expected.map.with_index do |e, idx|
|
124
|
+
if e.is_a?(Array) || e.is_a?(Hash)
|
125
|
+
"- [#{idx}] index : {\n" + type_error_message(e, value[idx]) + "\n}"
|
126
|
+
else
|
127
|
+
"- [#{idx}] index : " + type_error_message(e, value[idx])
|
128
|
+
end
|
129
|
+
end
|
130
|
+
"Expected #{value.inspect} to be an array with #{expected.length} elements:\n" + arr.join("\n")
|
131
|
+
else
|
132
|
+
"Expected #{value.inspect} to be an array"
|
133
|
+
end
|
134
|
+
when Hash
|
135
|
+
if value.is_a?(Hash)
|
136
|
+
arr = []
|
137
|
+
expected.each do |k, v|
|
138
|
+
if v.is_a?(Array) || v.is_a?(Hash)
|
139
|
+
arr << "- #{k} : {\n" + type_error_message(v, value[k]) + "\n}"
|
140
|
+
else
|
141
|
+
arr << "- #{k} : " + type_error_message(v, value[k])
|
142
|
+
end
|
143
|
+
end
|
144
|
+
"Expected #{value.inspect} to be an hash with #{expected.length} elements:\n" + arr.join("\n")
|
145
|
+
else
|
146
|
+
"Expected #{value.inspect} to be an hash"
|
147
|
+
end
|
148
|
+
when Proc
|
149
|
+
"Expected #{value.inspect} to return a truthy value for proc #{expected}"
|
150
|
+
when true
|
151
|
+
"Expected #{value.inspect} to be a truthy value"
|
152
|
+
when false
|
153
|
+
"Expected #{value.inspect} to be a falsy value"
|
154
|
+
when nil # for return
|
155
|
+
"Expected #{value.inspect} to be nil"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def assert_valid_type_sig(sig)
|
160
|
+
unless sig.is_a?(Hash)
|
161
|
+
raise TypeSignatureError, "Invalid type signature: type signature is not hash"
|
162
|
+
end
|
163
|
+
if sig.empty?
|
164
|
+
raise TypeSignatureError, "Invalid type signature: type signature is empty hash"
|
165
|
+
end
|
166
|
+
assert_valid_arguments_type_sig(sig.first[0])
|
167
|
+
assert_valid_return_type_sig(sig.first[1])
|
168
|
+
end
|
169
|
+
|
170
|
+
def assert_valid_arguments_type_sig(sig)
|
171
|
+
if sig.is_a?(Array)
|
172
|
+
sig = sig.dup
|
173
|
+
if sig.last.is_a?(Hash)
|
174
|
+
kwargs = sig.pop
|
175
|
+
else
|
176
|
+
kwargs = {}
|
177
|
+
end
|
178
|
+
sig.each { |e| assert_valid_argument_type_sig_element(e) }
|
179
|
+
if kwargs.keys.any? { |e| !e.is_a?(Symbol) }
|
180
|
+
raise TypeSignatureError, "Invalid type signature: keyword arguments contain non-symbol key"
|
181
|
+
end
|
182
|
+
kwargs.each_value { |e| assert_valid_argument_type_sig_element(e) }
|
183
|
+
elsif sig.is_a?(Hash)
|
184
|
+
if sig.keys.any? { |e| !e.is_a?(Symbol) }
|
185
|
+
raise TypeSignatureError, "Invalid type signature: keyword arguments contain non-symbol key"
|
186
|
+
end
|
187
|
+
sig.each_value { |e| assert_valid_argument_type_sig_element(e) }
|
188
|
+
else
|
189
|
+
raise TypeSignatureError, "Invalid type signature: arguments type signature is neither array nor hash"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def assert_valid_argument_type_sig_element(sig)
|
194
|
+
case sig
|
195
|
+
when Rtype::Behavior::Base
|
196
|
+
when Module
|
197
|
+
when Symbol
|
198
|
+
when Regexp
|
199
|
+
when Range
|
200
|
+
when Array
|
201
|
+
sig.each do |e|
|
202
|
+
assert_valid_argument_type_sig_element(e)
|
203
|
+
end
|
204
|
+
when Hash
|
205
|
+
sig.each_value do |e|
|
206
|
+
assert_valid_argument_type_sig_element(e)
|
207
|
+
end
|
208
|
+
when Proc
|
209
|
+
when true
|
210
|
+
when false
|
211
|
+
else
|
212
|
+
raise TypeSignatureError, "Invalid type signature: Unknown type behavior #{sig}"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def assert_valid_return_type_sig(sig)
|
217
|
+
assert_valid_argument_type_sig_element(sig) unless sig.nil?
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
def define_typed_method_to_proxy(owner, method_name, expected_args, expected_kwargs, return_sig)
|
222
|
+
# `send` is faster than `method(...).call`
|
223
|
+
owner.send(:_rtype_proxy).send :define_method, method_name do |*args, **kwargs, &block|
|
224
|
+
if kwargs.empty?
|
225
|
+
::Rtype::assert_arguments_type(expected_args, args)
|
226
|
+
result = super(*args, &block)
|
227
|
+
else
|
228
|
+
::Rtype::assert_arguments_type_with_keywords(expected_args, args, expected_kwargs, kwargs)
|
229
|
+
result = super(*args, **kwargs, &block)
|
230
|
+
end
|
231
|
+
::Rtype::assert_return_type(return_sig, result)
|
232
|
+
result
|
233
|
+
end
|
234
|
+
nil
|
235
|
+
end
|
236
|
+
|
237
|
+
public
|
238
|
+
unless respond_to?(:valid?)
|
239
|
+
# validate argument type
|
240
|
+
def valid?(expected, value)
|
241
|
+
case expected
|
242
|
+
when Module
|
243
|
+
value.is_a? expected
|
244
|
+
when Symbol
|
245
|
+
value.respond_to? expected
|
246
|
+
when Regexp
|
247
|
+
!!(expected =~ value.to_s)
|
248
|
+
when Range
|
249
|
+
expected.include?(value)
|
250
|
+
when Hash
|
251
|
+
return false unless value.is_a?(Hash)
|
252
|
+
return false unless expected.keys == value.keys
|
253
|
+
expected.all? { |k, v| valid?(v, value[k]) }
|
254
|
+
when Array
|
255
|
+
return false unless value.is_a?(Array)
|
256
|
+
return false unless expected.length == value.length
|
257
|
+
idx = -1
|
258
|
+
expected.all? { |e| idx += 1; valid?(e, value[idx]) }
|
259
|
+
when Proc
|
260
|
+
!!expected.call(value)
|
261
|
+
when true
|
262
|
+
!!value
|
263
|
+
when false
|
264
|
+
!value
|
265
|
+
when Rtype::Behavior::Base
|
266
|
+
expected.valid? value
|
267
|
+
else
|
268
|
+
raise TypeSignatureError, "Invalid type signature: Unknown type behavior #{expected}"
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
unless respond_to?(:assert_arguments_type)
|
274
|
+
def assert_arguments_type(expected_args, args)
|
275
|
+
# `length.times` is faster than `each_with_index`
|
276
|
+
args.length.times do |i|
|
277
|
+
expected = expected_args[i]
|
278
|
+
value = args[i]
|
279
|
+
unless expected.nil?
|
280
|
+
unless valid?(expected, value)
|
281
|
+
raise ArgumentTypeError, "#{arg_message(i)}\n" + type_error_message(expected, value)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
unless respond_to?(:assert_arguments_type_with_keywords)
|
289
|
+
def assert_arguments_type_with_keywords(expected_args, args, expected_kwargs, kwargs)
|
290
|
+
# `length.times` is faster than `each_with_index`
|
291
|
+
args.length.times do |i|
|
292
|
+
expected = expected_args[i]
|
293
|
+
value = args[i]
|
294
|
+
unless expected.nil?
|
295
|
+
unless valid?(expected, value)
|
296
|
+
raise ArgumentTypeError, "#{arg_message(i)}\n" + type_error_message(expected, value)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
kwargs.each do |key, value|
|
301
|
+
expected = expected_kwargs[key]
|
302
|
+
unless expected.nil?
|
303
|
+
unless valid?(expected, value)
|
304
|
+
raise ArgumentTypeError, "#{kwarg_message(key)}\n" + type_error_message(expected, value)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
unless respond_to?(:assert_return_type)
|
312
|
+
def assert_return_type(expected, result)
|
313
|
+
if expected.nil?
|
314
|
+
unless result.nil?
|
315
|
+
raise ReturnTypeError, "for return:\n" + type_error_message(expected, result)
|
316
|
+
end
|
317
|
+
else
|
318
|
+
unless valid?(expected, result)
|
319
|
+
raise ReturnTypeError, "for return:\n" + type_error_message(expected, result)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|