rtype 0.6.8-java
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 +7 -0
- data/Gemfile +3 -0
- data/LICENSE +9 -0
- data/README.md +541 -0
- data/Rakefile +12 -0
- data/benchmark/benchmark.rb +200 -0
- data/lib/rtype/argument_type_error.rb +4 -0
- data/lib/rtype/behavior/and.rb +24 -0
- data/lib/rtype/behavior/base.rb +17 -0
- data/lib/rtype/behavior/core_ext.rb +174 -0
- data/lib/rtype/behavior/float_check.rb +17 -0
- data/lib/rtype/behavior/integer_check.rb +17 -0
- data/lib/rtype/behavior/nilable.rb +21 -0
- data/lib/rtype/behavior/not.rb +24 -0
- data/lib/rtype/behavior/numeric_check.rb +42 -0
- data/lib/rtype/behavior/typed_array.rb +26 -0
- data/lib/rtype/behavior/typed_hash.rb +29 -0
- data/lib/rtype/behavior/typed_set.rb +26 -0
- data/lib/rtype/behavior/xor.rb +25 -0
- data/lib/rtype/behavior.rb +17 -0
- data/lib/rtype/core_ext.rb +274 -0
- data/lib/rtype/method_annotator.rb +31 -0
- data/lib/rtype/return_type_error.rb +4 -0
- data/lib/rtype/rtype_proxy.rb +10 -0
- data/lib/rtype/type_signature.rb +10 -0
- data/lib/rtype/type_signature_error.rb +4 -0
- data/lib/rtype/version.rb +7 -0
- data/lib/rtype.rb +468 -0
- data/spec/rtype_spec.rb +1070 -0
- data/spec/spec_helper.rb +14 -0
- metadata +131 -0
data/lib/rtype.rb
ADDED
@@ -0,0 +1,468 @@
|
|
1
|
+
if Object.const_defined?(:RUBY_ENGINE)
|
2
|
+
case RUBY_ENGINE
|
3
|
+
when "jruby"
|
4
|
+
begin
|
5
|
+
require 'java'
|
6
|
+
require 'rtype/rtype_java'
|
7
|
+
# puts "Rtype with Java extension"
|
8
|
+
rescue LoadError
|
9
|
+
# puts "Rtype without native extension"
|
10
|
+
end
|
11
|
+
when "ruby"
|
12
|
+
begin
|
13
|
+
require "rtype/rtype_native"
|
14
|
+
# puts "Rtype with C native extension"
|
15
|
+
rescue LoadError
|
16
|
+
# puts "Rtype without native extension"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
require_relative 'rtype/rtype_proxy'
|
22
|
+
require_relative 'rtype/method_annotator'
|
23
|
+
require_relative 'rtype/core_ext'
|
24
|
+
require_relative 'rtype/version'
|
25
|
+
require_relative 'rtype/type_signature_error'
|
26
|
+
require_relative 'rtype/argument_type_error'
|
27
|
+
require_relative 'rtype/return_type_error'
|
28
|
+
require_relative 'rtype/type_signature'
|
29
|
+
require_relative 'rtype/behavior'
|
30
|
+
|
31
|
+
module Rtype
|
32
|
+
extend self
|
33
|
+
|
34
|
+
# This is just 'information'
|
35
|
+
# Any change of this doesn't affect type checking
|
36
|
+
@@type_signatures = Hash.new
|
37
|
+
|
38
|
+
# Makes the method typed
|
39
|
+
# @param owner Owner of the method
|
40
|
+
# @param [#to_sym] method_name
|
41
|
+
# @param [Hash] type_sig_info A type signature. e.g. `[Integer, Float] => Float`
|
42
|
+
# @return [void]
|
43
|
+
#
|
44
|
+
# @raise [ArgumentError] If method_name is nil
|
45
|
+
# @raise [TypeSignatureError] If type_sig_info is invalid
|
46
|
+
def define_typed_method(owner, method_name, type_sig_info)
|
47
|
+
method_name = method_name.to_sym
|
48
|
+
raise ArgumentError, "method_name is nil" if method_name.nil?
|
49
|
+
assert_valid_type_sig(type_sig_info)
|
50
|
+
|
51
|
+
el = type_sig_info.first
|
52
|
+
arg_sig = el[0]
|
53
|
+
return_sig = el[1]
|
54
|
+
|
55
|
+
if arg_sig.is_a?(Array)
|
56
|
+
expected_args = arg_sig.dup
|
57
|
+
if expected_args.last.is_a?(Hash)
|
58
|
+
expected_kwargs = expected_args.pop
|
59
|
+
else
|
60
|
+
expected_kwargs = {}
|
61
|
+
end
|
62
|
+
elsif arg_sig.is_a?(Hash)
|
63
|
+
expected_args = []
|
64
|
+
expected_kwargs = arg_sig
|
65
|
+
end
|
66
|
+
|
67
|
+
sig = TypeSignature.new
|
68
|
+
sig.argument_type = arg_sig
|
69
|
+
sig.return_type = return_sig
|
70
|
+
unless @@type_signatures.key?(owner)
|
71
|
+
@@type_signatures[owner] = {}
|
72
|
+
end
|
73
|
+
@@type_signatures[owner][method_name] = sig
|
74
|
+
|
75
|
+
define_typed_method_to_proxy(owner, method_name, expected_args, expected_kwargs, return_sig)
|
76
|
+
end
|
77
|
+
|
78
|
+
# @param owner Owner of the accessor
|
79
|
+
# @param [#to_sym] name
|
80
|
+
# @param type_behavior A type behavior. e.g. Integer
|
81
|
+
# @return [void]
|
82
|
+
#
|
83
|
+
# @raise [ArgumentError] If name is nil
|
84
|
+
# @raise [TypeSignatureError]
|
85
|
+
def define_typed_accessor(owner, name, type_behavior)
|
86
|
+
define_typed_reader(owner, name, type_behavior)
|
87
|
+
define_typed_writer(owner, name, type_behavior)
|
88
|
+
end
|
89
|
+
|
90
|
+
# @param owner Owner of the getter
|
91
|
+
# @param [#to_sym] name
|
92
|
+
# @param type_behavior A type behavior. e.g. Integer
|
93
|
+
# @return [void]
|
94
|
+
#
|
95
|
+
# @raise [ArgumentError] If name is nil
|
96
|
+
# @raise [TypeSignatureError]
|
97
|
+
def define_typed_reader(owner, name, type_behavior)
|
98
|
+
raise ArgumentError, "name is nil" if name.nil?
|
99
|
+
valid?(type_behavior, nil)
|
100
|
+
define_typed_method owner, name.to_sym, [] => type_behavior
|
101
|
+
end
|
102
|
+
|
103
|
+
# @param owner Owner of the setter
|
104
|
+
# @param [#to_sym] name
|
105
|
+
# @param type_behavior A type behavior. e.g. Integer
|
106
|
+
# @return [void]
|
107
|
+
#
|
108
|
+
# @raise [ArgumentError] If name is nil
|
109
|
+
# @raise [TypeSignatureError]
|
110
|
+
def define_typed_writer(owner, name, type_behavior)
|
111
|
+
raise ArgumentError, "name is nil" if name.nil?
|
112
|
+
valid?(type_behavior, nil)
|
113
|
+
define_typed_method owner, :"#{name.to_sym}=", [type_behavior] => Any
|
114
|
+
end
|
115
|
+
|
116
|
+
# This is just 'information'
|
117
|
+
# Any change of this doesn't affect type checking
|
118
|
+
#
|
119
|
+
# @return [Hash]
|
120
|
+
# @note type_signatures[owner][method_name]
|
121
|
+
def type_signatures
|
122
|
+
@@type_signatures
|
123
|
+
end
|
124
|
+
|
125
|
+
=begin
|
126
|
+
def assert_keyword_arguments_type(expected_kwargs, kwargs)
|
127
|
+
kwargs.each do |key, value|
|
128
|
+
expected = expected_kwargs[key]
|
129
|
+
unless expected.nil?
|
130
|
+
unless valid?(expected, value)
|
131
|
+
raise ArgumentTypeError, "for '#{key}' argument:\n" + type_error_message(expected, value)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
=end
|
137
|
+
|
138
|
+
# @param [Integer] idx
|
139
|
+
# @param expected A type behavior
|
140
|
+
# @param value
|
141
|
+
# @return [String] A error message
|
142
|
+
#
|
143
|
+
# @raise [ArgumentError] If expected is invalid
|
144
|
+
def arg_type_error_message(idx, expected, value)
|
145
|
+
"#{arg_message(idx)}\n" + type_error_message(expected, value)
|
146
|
+
end
|
147
|
+
|
148
|
+
# @param [String, Symbol] key
|
149
|
+
# @param expected A type behavior
|
150
|
+
# @param value
|
151
|
+
# @return [String] A error message
|
152
|
+
#
|
153
|
+
# @raise [ArgumentError] If expected is invalid
|
154
|
+
def kwarg_type_error_message(key, expected, value)
|
155
|
+
"#{kwarg_message(key)}\n" + type_error_message(expected, value)
|
156
|
+
end
|
157
|
+
|
158
|
+
# @return [String]
|
159
|
+
def arg_message(idx)
|
160
|
+
"for #{ordinalize_number(idx+1)} argument:"
|
161
|
+
end
|
162
|
+
|
163
|
+
# @return [String]
|
164
|
+
def kwarg_message(key)
|
165
|
+
"for '#{key}' argument:"
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns a error message for the pair of type behavior and value
|
169
|
+
#
|
170
|
+
# @param expected A type behavior
|
171
|
+
# @param value
|
172
|
+
# @return [String] error message
|
173
|
+
#
|
174
|
+
# @note This method doesn't check the value is valid
|
175
|
+
# @raise [TypeSignatureError] If expected is invalid
|
176
|
+
def type_error_message(expected, value)
|
177
|
+
case expected
|
178
|
+
when Rtype::Behavior::Base
|
179
|
+
expected.error_message(value)
|
180
|
+
when Module
|
181
|
+
"Expected #{value.inspect} to be a #{expected}"
|
182
|
+
when Symbol
|
183
|
+
"Expected #{value.inspect} to respond to :#{expected}"
|
184
|
+
when Regexp
|
185
|
+
"Expected stringified #{value.inspect} to match regexp #{expected.inspect}"
|
186
|
+
when Range
|
187
|
+
"Expected #{value.inspect} to be included in range #{expected.inspect}"
|
188
|
+
when Array
|
189
|
+
arr = expected.map { |e| type_error_message(e, value) }
|
190
|
+
arr.join("\nOR ")
|
191
|
+
when Hash
|
192
|
+
if value.is_a?(Hash)
|
193
|
+
arr = []
|
194
|
+
expected.each do |k, v|
|
195
|
+
if v.is_a?(Array) || v.is_a?(Hash)
|
196
|
+
arr << "- #{k} : {\n" + type_error_message(v, value[k]) + "\n}"
|
197
|
+
else
|
198
|
+
arr << "- #{k} : " + type_error_message(v, value[k])
|
199
|
+
end
|
200
|
+
end
|
201
|
+
"Expected #{value.inspect} to be a hash with #{expected.length} elements:\n" + arr.join("\n")
|
202
|
+
else
|
203
|
+
"Expected #{value.inspect} to be a hash"
|
204
|
+
end
|
205
|
+
when Proc
|
206
|
+
"Expected #{value.inspect} to return a truthy value for proc #{expected}"
|
207
|
+
when true
|
208
|
+
"Expected #{value.inspect} to be a truthy value"
|
209
|
+
when false
|
210
|
+
"Expected #{value.inspect} to be a falsy value"
|
211
|
+
when nil # for return
|
212
|
+
"Expected #{value.inspect} to be nil"
|
213
|
+
else
|
214
|
+
raise TypeSignatureError, "Invalid type behavior #{expected}"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Checks the type signature is valid
|
219
|
+
#
|
220
|
+
# e.g.
|
221
|
+
# `[Integer] => Any` is valid.
|
222
|
+
# `[Integer]` or `Any` are invalid
|
223
|
+
#
|
224
|
+
# @param sig A type signature
|
225
|
+
# @raise [TypeSignatureError] If sig is invalid
|
226
|
+
def assert_valid_type_sig(sig)
|
227
|
+
unless sig.is_a?(Hash)
|
228
|
+
raise TypeSignatureError, "Invalid type signature: type signature is not hash"
|
229
|
+
end
|
230
|
+
if sig.empty?
|
231
|
+
raise TypeSignatureError, "Invalid type signature: type signature is empty hash"
|
232
|
+
end
|
233
|
+
assert_valid_arguments_type_sig(sig.first[0])
|
234
|
+
assert_valid_return_type_sig(sig.first[1])
|
235
|
+
end
|
236
|
+
|
237
|
+
# Checks the arguments type signature is valid
|
238
|
+
#
|
239
|
+
# e.g.
|
240
|
+
# `[Integer]`, `{key: "value"}` are valid.
|
241
|
+
# `Integer` is invalid
|
242
|
+
#
|
243
|
+
# @param sig A arguments type signature
|
244
|
+
# @raise [TypeSignatureError] If sig is invalid
|
245
|
+
def assert_valid_arguments_type_sig(sig)
|
246
|
+
if sig.is_a?(Array)
|
247
|
+
sig = sig.dup
|
248
|
+
if sig.last.is_a?(Hash)
|
249
|
+
kwargs = sig.pop
|
250
|
+
else
|
251
|
+
kwargs = {}
|
252
|
+
end
|
253
|
+
sig.each { |e| assert_valid_argument_type_sig_element(e) }
|
254
|
+
if kwargs.keys.any? { |e| !e.is_a?(Symbol) }
|
255
|
+
raise TypeSignatureError, "Invalid type signature: keyword arguments contain non-symbol key"
|
256
|
+
end
|
257
|
+
kwargs.each_value { |e| assert_valid_argument_type_sig_element(e) }
|
258
|
+
elsif sig.is_a?(Hash)
|
259
|
+
if sig.keys.any? { |e| !e.is_a?(Symbol) }
|
260
|
+
raise TypeSignatureError, "Invalid type signature: keyword arguments contain non-symbol key"
|
261
|
+
end
|
262
|
+
sig.each_value { |e| assert_valid_argument_type_sig_element(e) }
|
263
|
+
else
|
264
|
+
raise TypeSignatureError, "Invalid type signature: arguments type signature is neither array nor hash"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Checks the type behavior is valid
|
269
|
+
#
|
270
|
+
# @param sig A type behavior
|
271
|
+
# @raise [TypeSignatureError] If sig is invalid
|
272
|
+
def assert_valid_argument_type_sig_element(sig)
|
273
|
+
case sig
|
274
|
+
when Rtype::Behavior::Base
|
275
|
+
when Module
|
276
|
+
when Symbol
|
277
|
+
when Regexp
|
278
|
+
when Range
|
279
|
+
when Array
|
280
|
+
sig.each do |e|
|
281
|
+
assert_valid_argument_type_sig_element(e)
|
282
|
+
end
|
283
|
+
when Hash
|
284
|
+
sig.each_value do |e|
|
285
|
+
assert_valid_argument_type_sig_element(e)
|
286
|
+
end
|
287
|
+
when Proc
|
288
|
+
when true
|
289
|
+
when false
|
290
|
+
when nil
|
291
|
+
else
|
292
|
+
raise TypeSignatureError, "Invalid type signature: Unknown type behavior #{sig}"
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# @see #assert_valid_argument_type_sig_element
|
297
|
+
def assert_valid_return_type_sig(sig)
|
298
|
+
assert_valid_argument_type_sig_element(sig)
|
299
|
+
end
|
300
|
+
|
301
|
+
unless respond_to?(:valid?)
|
302
|
+
# Checks the value is valid for the type behavior
|
303
|
+
#
|
304
|
+
# @param expected A type behavior
|
305
|
+
# @param value
|
306
|
+
# @return [Boolean]
|
307
|
+
#
|
308
|
+
# @raise [TypeSignatureError] If expected is invalid
|
309
|
+
def valid?(expected, value)
|
310
|
+
case expected
|
311
|
+
when Module
|
312
|
+
value.is_a? expected
|
313
|
+
when Symbol
|
314
|
+
value.respond_to? expected
|
315
|
+
when Regexp
|
316
|
+
!!(expected =~ value.to_s)
|
317
|
+
when Range
|
318
|
+
expected.include?(value)
|
319
|
+
when Hash
|
320
|
+
return false unless value.is_a?(Hash)
|
321
|
+
return false unless expected.keys == value.keys
|
322
|
+
expected.all? { |k, v| valid?(v, value[k]) }
|
323
|
+
when Array
|
324
|
+
expected.any? { |e| valid?(e, value) }
|
325
|
+
when Proc
|
326
|
+
!!expected.call(value)
|
327
|
+
when true
|
328
|
+
!!value
|
329
|
+
when false
|
330
|
+
!value
|
331
|
+
when Rtype::Behavior::Base
|
332
|
+
expected.valid? value
|
333
|
+
when nil
|
334
|
+
value.nil?
|
335
|
+
else
|
336
|
+
raise TypeSignatureError, "Invalid type signature: Unknown type behavior #{expected}"
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
unless respond_to?(:assert_arguments_type)
|
342
|
+
# Validates arguments
|
343
|
+
#
|
344
|
+
# @param [Array] expected_args A type signature for non-keyword arguments
|
345
|
+
# @param [Array] args
|
346
|
+
# @return [void]
|
347
|
+
#
|
348
|
+
# @raise [TypeSignatureError] If expected_args is invalid
|
349
|
+
# @raise [ArgumentTypeError] If args is invalid
|
350
|
+
def assert_arguments_type(expected_args, args)
|
351
|
+
e_len = expected_args.length
|
352
|
+
# `length.times` is faster than `each_with_index`
|
353
|
+
args.length.times do |i|
|
354
|
+
break if i >= e_len
|
355
|
+
expected = expected_args[i]
|
356
|
+
value = args[i]
|
357
|
+
unless valid?(expected, value)
|
358
|
+
raise ArgumentTypeError, "#{arg_message(i)}\n" + type_error_message(expected, value)
|
359
|
+
end
|
360
|
+
end
|
361
|
+
nil
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
unless respond_to?(:assert_arguments_type_with_keywords)
|
366
|
+
# Validates arguments and keyword arguments
|
367
|
+
#
|
368
|
+
# @param [Array] expected_args A type signature for non-keyword arguments
|
369
|
+
# @param [Array] args Arguments
|
370
|
+
# @param [Hash] expected_kwargs A type signature for keyword arguments
|
371
|
+
# @param [Hash] kwargs Keword arguments
|
372
|
+
# @return [void]
|
373
|
+
#
|
374
|
+
# @raise [TypeSignatureError] If expected_args or expected_kwargs are invalid
|
375
|
+
# @raise [ArgumentTypeError] If args or kwargs are invalid
|
376
|
+
def assert_arguments_type_with_keywords(expected_args, args, expected_kwargs, kwargs)
|
377
|
+
e_len = expected_args.length
|
378
|
+
# `length.times` is faster than `each_with_index`
|
379
|
+
args.length.times do |i|
|
380
|
+
break if i >= e_len
|
381
|
+
expected = expected_args[i]
|
382
|
+
value = args[i]
|
383
|
+
unless valid?(expected, value)
|
384
|
+
raise ArgumentTypeError, "#{arg_message(i)}\n" + type_error_message(expected, value)
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
kwargs.each do |key, value|
|
389
|
+
if expected_kwargs.key?(key)
|
390
|
+
expected = expected_kwargs[key]
|
391
|
+
unless valid?(expected, value)
|
392
|
+
raise ArgumentTypeError, "#{kwarg_message(key)}\n" + type_error_message(expected, value)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
nil
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
# Validates result
|
401
|
+
#
|
402
|
+
# @param expected A type behavior
|
403
|
+
# @param result
|
404
|
+
# @return [void]
|
405
|
+
#
|
406
|
+
# @raise [TypeSignatureError] If expected is invalid
|
407
|
+
# @raise [ReturnTypeError] If result is invalid
|
408
|
+
unless respond_to?(:assert_return_type)
|
409
|
+
def assert_return_type(expected, result)
|
410
|
+
unless valid?(expected, result)
|
411
|
+
raise ReturnTypeError, "for return:\n" + type_error_message(expected, result)
|
412
|
+
end
|
413
|
+
nil
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
private
|
418
|
+
# @param owner
|
419
|
+
# @param [Symbol] method_name
|
420
|
+
# @param [Array] expected_args
|
421
|
+
# @param [Hash] expected_kwargs
|
422
|
+
# @param return_sig
|
423
|
+
# @return [void]
|
424
|
+
def define_typed_method_to_proxy(owner, method_name, expected_args, expected_kwargs, return_sig)
|
425
|
+
priv = owner.private_method_defined?(method_name)
|
426
|
+
prot = owner.protected_method_defined?(method_name)
|
427
|
+
|
428
|
+
if expected_kwargs.empty?
|
429
|
+
# `send` is faster than `method(...).call`
|
430
|
+
owner.send(:_rtype_proxy).send :define_method, method_name do |*args, &block|
|
431
|
+
::Rtype::assert_arguments_type(expected_args, args)
|
432
|
+
result = super(*args, &block)
|
433
|
+
::Rtype::assert_return_type(return_sig, result)
|
434
|
+
result
|
435
|
+
end
|
436
|
+
else
|
437
|
+
# `send` is faster than `method(...).call`
|
438
|
+
owner.send(:_rtype_proxy).send :define_method, method_name do |*args, **kwargs, &block|
|
439
|
+
::Rtype::assert_arguments_type_with_keywords(expected_args, args, expected_kwargs, kwargs)
|
440
|
+
result = super(*args, **kwargs, &block)
|
441
|
+
::Rtype::assert_return_type(return_sig, result)
|
442
|
+
result
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
if priv
|
447
|
+
owner.send(:_rtype_proxy).send(:private, method_name)
|
448
|
+
elsif prot
|
449
|
+
owner.send(:_rtype_proxy).send(:protected, method_name)
|
450
|
+
end
|
451
|
+
nil
|
452
|
+
end
|
453
|
+
|
454
|
+
# @param [Integer] num
|
455
|
+
# @return [String]
|
456
|
+
def ordinalize_number(num)
|
457
|
+
if (11..13).include?(num % 100)
|
458
|
+
"#{num}th"
|
459
|
+
else
|
460
|
+
case num % 10
|
461
|
+
when 1; "#{num}st"
|
462
|
+
when 2; "#{num}nd"
|
463
|
+
when 3; "#{num}rd"
|
464
|
+
else "#{num}th"
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
468
|
+
end
|