httpx 1.4.2 → 1.4.3
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/doc/release_notes/1_4_3.md +11 -0
- data/lib/httpx/adapters/webmock.rb +2 -0
- data/lib/httpx/connection/http2.rb +31 -16
- data/lib/httpx/connection.rb +6 -4
- data/lib/httpx/errors.rb +0 -4
- data/lib/httpx/loggable.rb +5 -5
- data/lib/httpx/plugins/internal_telemetry.rb +21 -1
- data/lib/httpx/plugins/retries.rb +1 -1
- data/lib/httpx/request.rb +3 -1
- data/lib/httpx/response.rb +9 -4
- data/lib/httpx/transcoder/multipart/encoder.rb +2 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/connection/http2.rbs +4 -0
- data/sig/errors.rbs +0 -3
- data/sig/loggable.rbs +2 -2
- data/sig/plugins/query.rbs +18 -0
- data/sig/pool.rbs +2 -0
- data/sig/request.rbs +4 -0
- data/sig/response.rbs +8 -3
- data/sig/transcoder/json.rbs +1 -1
- data/sig/transcoder/multipart.rbs +1 -1
- data/sig/transcoder/utils/deflater.rbs +0 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 36f5b3d4da61a1a6c86602205a6eda217f51b40411865589587a09553eb263cb
|
4
|
+
data.tar.gz: 9b705a6b8bc7ebf1ec2e308ae6b49bfcc3ede818bbca0dcf4b2fb82dde1cace6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72759cee17931e45580673c119fffd7339afdfe149f6a4a14afa410d11c47b0d84146a70660b5ca8ff63b1d0b6c1d16a85008416348cd48cddd3f9a9c94f3c10
|
7
|
+
data.tar.gz: 1233281adc13e03e5b754ef6d5d830e84335f5f8b3cc90dea33e27c9f5751f2e3f7d20e1d6064b9273678612a3e4505d8d54b3038b85855bc25a72c6d73d914c
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# 1.4.3
|
2
|
+
|
3
|
+
## Bugfixes
|
4
|
+
|
5
|
+
* `webmock` adapter: reassign headers to signature after callbacks are called (these may change the headers before virtual send).
|
6
|
+
* do not close request (and its body) right after sending, instead only on response close
|
7
|
+
* prevents retries from failing under the `:retries` plugin
|
8
|
+
* fixes issue when using `faraday-multipart` request bodies
|
9
|
+
* retry request with HTTP/1 when receiving an HTTP/2 GOAWAY frame with `HTTP_1_1_REQUIRED` error code.
|
10
|
+
* fix wrong method call on HTTP/2 PING frame with unrecognized code.
|
11
|
+
* fix EOFError issues on connection termination for long running connections which may have already been terminated by peer and were wrongly trying to complete the HTTP/2 termination handshake.
|
@@ -122,6 +122,8 @@ module WebMock
|
|
122
122
|
request.transition(:done)
|
123
123
|
request.response = response
|
124
124
|
request.emit(:response, response)
|
125
|
+
request_signature.headers = request.headers.to_h
|
126
|
+
|
125
127
|
response << mock_response.body.dup unless response.is_a?(HTTPX::ErrorResponse)
|
126
128
|
elsif WebMock.net_connect_allowed?(request_signature.uri)
|
127
129
|
if WebMock::CallbackRegistry.any_callbacks?
|
@@ -16,6 +16,12 @@ module HTTPX
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
class PingError < Error
|
20
|
+
def initialize
|
21
|
+
super(0, :ping_error)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
19
25
|
class GoawayError < Error
|
20
26
|
def initialize
|
21
27
|
super(0, :no_error)
|
@@ -311,17 +317,20 @@ module HTTPX
|
|
311
317
|
@streams.delete(request)
|
312
318
|
|
313
319
|
if error
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
320
|
+
case error
|
321
|
+
when :http_1_1_required
|
322
|
+
emit(:error, request, error)
|
323
|
+
else
|
324
|
+
ex = Error.new(stream.id, error)
|
325
|
+
ex.set_backtrace(caller)
|
326
|
+
response = ErrorResponse.new(request, ex)
|
327
|
+
request.response = response
|
328
|
+
emit(:response, request, response)
|
329
|
+
end
|
319
330
|
else
|
320
331
|
response = request.response
|
321
332
|
if response && response.is_a?(Response) && response.status == 421
|
322
|
-
|
323
|
-
ex.set_backtrace(caller)
|
324
|
-
emit(:error, request, ex)
|
333
|
+
emit(:error, request, :http_1_1_required)
|
325
334
|
else
|
326
335
|
emit(:response, request, response)
|
327
336
|
end
|
@@ -352,7 +361,12 @@ module HTTPX
|
|
352
361
|
is_connection_closed = @connection.state == :closed
|
353
362
|
if error
|
354
363
|
@buffer.clear if is_connection_closed
|
355
|
-
|
364
|
+
case error
|
365
|
+
when :http_1_1_required
|
366
|
+
while (request = @pending.shift)
|
367
|
+
emit(:error, request, error)
|
368
|
+
end
|
369
|
+
when :no_error
|
356
370
|
ex = GoawayError.new
|
357
371
|
@pending.unshift(*@streams.keys)
|
358
372
|
@drains.clear
|
@@ -360,8 +374,11 @@ module HTTPX
|
|
360
374
|
else
|
361
375
|
ex = Error.new(0, error)
|
362
376
|
end
|
363
|
-
|
364
|
-
|
377
|
+
|
378
|
+
if ex
|
379
|
+
ex.set_backtrace(caller)
|
380
|
+
handle_error(ex)
|
381
|
+
end
|
365
382
|
end
|
366
383
|
return unless is_connection_closed && @streams.empty?
|
367
384
|
|
@@ -403,11 +420,9 @@ module HTTPX
|
|
403
420
|
end
|
404
421
|
|
405
422
|
def on_pong(ping)
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
close(:protocol_error, "ping payload did not match")
|
410
|
-
end
|
423
|
+
raise PingError unless @pings.delete(ping.to_s)
|
424
|
+
|
425
|
+
emit(:pong)
|
411
426
|
end
|
412
427
|
end
|
413
428
|
end
|
data/lib/httpx/connection.rb
CHANGED
@@ -423,6 +423,8 @@ module HTTPX
|
|
423
423
|
siz = @io.read(@window_size, @read_buffer)
|
424
424
|
log(level: 3, color: :cyan) { "IO READ: #{siz} bytes... (wsize: #{@window_size}, rbuffer: #{@read_buffer.bytesize})" }
|
425
425
|
unless siz
|
426
|
+
@write_buffer.clear
|
427
|
+
|
426
428
|
ex = EOFError.new("descriptor closed")
|
427
429
|
ex.set_backtrace(caller)
|
428
430
|
on_error(ex)
|
@@ -610,9 +612,9 @@ module HTTPX
|
|
610
612
|
parser.on(:timeout) do |tout|
|
611
613
|
@timeout = tout
|
612
614
|
end
|
613
|
-
parser.on(:error) do |request,
|
614
|
-
case
|
615
|
-
when
|
615
|
+
parser.on(:error) do |request, error|
|
616
|
+
case error
|
617
|
+
when :http_1_1_required
|
616
618
|
current_session = @current_session
|
617
619
|
current_selector = @current_selector
|
618
620
|
parser.close
|
@@ -628,7 +630,7 @@ module HTTPX
|
|
628
630
|
next unless request.active_timeouts.empty?
|
629
631
|
end
|
630
632
|
|
631
|
-
response = ErrorResponse.new(request,
|
633
|
+
response = ErrorResponse.new(request, error)
|
632
634
|
request.response = response
|
633
635
|
request.emit(:response, response)
|
634
636
|
end
|
data/lib/httpx/errors.rb
CHANGED
@@ -115,8 +115,4 @@ module HTTPX
|
|
115
115
|
@response.status
|
116
116
|
end
|
117
117
|
end
|
118
|
-
|
119
|
-
# error raised when a request was sent a server which can't reproduce a response, and
|
120
|
-
# has therefore returned an HTTP response using the 421 status code.
|
121
|
-
class MisdirectedRequestError < HTTPError; end
|
122
118
|
end
|
data/lib/httpx/loggable.rb
CHANGED
@@ -15,10 +15,10 @@ module HTTPX
|
|
15
15
|
|
16
16
|
USE_DEBUG_LOG = ENV.key?("HTTPX_DEBUG")
|
17
17
|
|
18
|
-
def log(level: @options.debug_level, color: nil, &msg)
|
19
|
-
return unless
|
18
|
+
def log(level: @options.debug_level, color: nil, debug_level: @options.debug_level, debug: @options.debug, &msg)
|
19
|
+
return unless debug_level >= level
|
20
20
|
|
21
|
-
debug_stream =
|
21
|
+
debug_stream = debug || ($stderr if USE_DEBUG_LOG)
|
22
22
|
|
23
23
|
return unless debug_stream
|
24
24
|
|
@@ -34,8 +34,8 @@ module HTTPX
|
|
34
34
|
debug_stream << message
|
35
35
|
end
|
36
36
|
|
37
|
-
def log_exception(ex, level: @options.debug_level, color: nil)
|
38
|
-
log(level: level, color: color) { ex.full_message }
|
37
|
+
def log_exception(ex, level: @options.debug_level, color: nil, debug_level: @options.debug_level, debug: @options.debug)
|
38
|
+
log(level: level, color: color, debug_level: debug_level, debug: debug) { ex.full_message }
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
@@ -13,6 +13,12 @@ module HTTPX
|
|
13
13
|
# by the end user in $http_init_time, different diff metrics can be shown. The "point of time" is calculated
|
14
14
|
# using the monotonic clock.
|
15
15
|
module InternalTelemetry
|
16
|
+
DEBUG_LEVEL = 3
|
17
|
+
|
18
|
+
def self.extra_options(options)
|
19
|
+
options.merge(debug_level: 3)
|
20
|
+
end
|
21
|
+
|
16
22
|
module TrackTimeMethods
|
17
23
|
private
|
18
24
|
|
@@ -28,7 +34,19 @@ module HTTPX
|
|
28
34
|
after_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
29
35
|
# $http_init_time = after_time
|
30
36
|
elapsed = after_time - prev_time
|
31
|
-
|
37
|
+
# klass = self.class
|
38
|
+
|
39
|
+
# until (class_name = klass.name)
|
40
|
+
# klass = klass.superclass
|
41
|
+
# end
|
42
|
+
log(
|
43
|
+
level: DEBUG_LEVEL,
|
44
|
+
color: :red,
|
45
|
+
debug_level: @options ? @options.debug_level : DEBUG_LEVEL,
|
46
|
+
debug: nil
|
47
|
+
) do
|
48
|
+
"[ELAPSED TIME]: #{label}: #{elapsed} (ms)" << "\e[0m"
|
49
|
+
end
|
32
50
|
end
|
33
51
|
end
|
34
52
|
|
@@ -88,6 +106,7 @@ module HTTPX
|
|
88
106
|
|
89
107
|
module RequestMethods
|
90
108
|
def self.included(klass)
|
109
|
+
klass.prepend Loggable
|
91
110
|
klass.prepend TrackTimeMethods
|
92
111
|
super
|
93
112
|
end
|
@@ -114,6 +133,7 @@ module HTTPX
|
|
114
133
|
|
115
134
|
module PoolMethods
|
116
135
|
def self.included(klass)
|
136
|
+
klass.prepend Loggable
|
117
137
|
klass.prepend TrackTimeMethods
|
118
138
|
super
|
119
139
|
end
|
@@ -167,7 +167,7 @@ module HTTPX
|
|
167
167
|
unless response.headers.key?("accept-ranges") &&
|
168
168
|
response.headers["accept-ranges"] == "bytes" && # there's nothing else supported though...
|
169
169
|
(original_body = response.body)
|
170
|
-
response.
|
170
|
+
response.body.close
|
171
171
|
return
|
172
172
|
end
|
173
173
|
|
data/lib/httpx/request.rb
CHANGED
@@ -50,6 +50,9 @@ module HTTPX
|
|
50
50
|
# will be +true+ when request body has been completely flushed.
|
51
51
|
def_delegator :@body, :empty?
|
52
52
|
|
53
|
+
# closes the body
|
54
|
+
def_delegator :@body, :close
|
55
|
+
|
53
56
|
# initializes the instance with the given +verb+ (an upppercase String, ex. 'GEt'),
|
54
57
|
# an absolute or relative +uri+ (either as String or URI::HTTP object), the
|
55
58
|
# request +options+ (instance of HTTPX::Options) and an optional Hash of +params+.
|
@@ -273,7 +276,6 @@ module HTTPX
|
|
273
276
|
when :done
|
274
277
|
return if @state == :expect
|
275
278
|
|
276
|
-
@body.close
|
277
279
|
end
|
278
280
|
@state = nextstate
|
279
281
|
emit(@state, self)
|
data/lib/httpx/response.rb
CHANGED
@@ -52,9 +52,6 @@ module HTTPX
|
|
52
52
|
# copies the response body to a different location.
|
53
53
|
def_delegator :@body, :copy_to
|
54
54
|
|
55
|
-
# closes the body.
|
56
|
-
def_delegator :@body, :close
|
57
|
-
|
58
55
|
# the corresponding request uri.
|
59
56
|
def_delegator :@request, :uri
|
60
57
|
|
@@ -74,6 +71,12 @@ module HTTPX
|
|
74
71
|
@content_type = nil
|
75
72
|
end
|
76
73
|
|
74
|
+
# closes the respective +@request+ and +@body+.
|
75
|
+
def close
|
76
|
+
@request.close
|
77
|
+
@body.close
|
78
|
+
end
|
79
|
+
|
77
80
|
# merges headers defined in +h+ into the response headers.
|
78
81
|
def merge_headers(h)
|
79
82
|
@headers = @headers.merge(h)
|
@@ -264,7 +267,7 @@ module HTTPX
|
|
264
267
|
|
265
268
|
# closes the error resources.
|
266
269
|
def close
|
267
|
-
@response.close if @response
|
270
|
+
@response.close if @response
|
268
271
|
end
|
269
272
|
|
270
273
|
# always true for error responses.
|
@@ -279,6 +282,8 @@ module HTTPX
|
|
279
282
|
|
280
283
|
# buffers lost chunks to error response
|
281
284
|
def <<(data)
|
285
|
+
return unless @response
|
286
|
+
|
282
287
|
@response << data
|
283
288
|
end
|
284
289
|
end
|
@@ -11,6 +11,7 @@ module HTTPX
|
|
11
11
|
@buffer = "".b
|
12
12
|
|
13
13
|
@form = form
|
14
|
+
@bytesize = 0
|
14
15
|
@parts = to_parts(form)
|
15
16
|
end
|
16
17
|
|
@@ -42,6 +43,7 @@ module HTTPX
|
|
42
43
|
aux << [key, val]
|
43
44
|
end
|
44
45
|
@form = form
|
46
|
+
@bytesize = 0
|
45
47
|
@parts = to_parts(form)
|
46
48
|
@part_index = 0
|
47
49
|
end
|
@@ -49,7 +51,6 @@ module HTTPX
|
|
49
51
|
private
|
50
52
|
|
51
53
|
def to_parts(form)
|
52
|
-
@bytesize = 0
|
53
54
|
params = form.each_with_object([]) do |(key, val), aux|
|
54
55
|
Transcoder.normalize_keys(key, val, MULTIPART_VALUE_COND) do |k, v|
|
55
56
|
next if v.nil?
|
data/lib/httpx/version.rb
CHANGED
data/sig/connection/http2.rbs
CHANGED
@@ -8,6 +8,7 @@ module HTTPX
|
|
8
8
|
attr_reader streams: Hash[Request, ::HTTP2::Stream]
|
9
9
|
attr_reader pending: Array[Request]
|
10
10
|
|
11
|
+
@connection: HTTP2::Client
|
11
12
|
@options: Options
|
12
13
|
@settings: Hash[Symbol, Integer | bool]
|
13
14
|
@max_concurrent_requests: Integer
|
@@ -95,5 +96,8 @@ module HTTPX
|
|
95
96
|
|
96
97
|
class GoawayError < Error
|
97
98
|
end
|
99
|
+
|
100
|
+
class PingError < Error
|
101
|
+
end
|
98
102
|
end
|
99
103
|
end
|
data/sig/errors.rbs
CHANGED
data/sig/loggable.rbs
CHANGED
@@ -8,8 +8,8 @@ module HTTPX
|
|
8
8
|
|
9
9
|
COLORS: Hash[Symbol, Integer]
|
10
10
|
|
11
|
-
def log: (?level: Integer?, ?color: Symbol?) { () -> String } -> void
|
11
|
+
def log: (?level: Integer?, ?color: Symbol?, ?debug_level: Integer, ?debug: _IOLogger?) { () -> String } -> void
|
12
12
|
|
13
|
-
def log_exception: (Exception error, ?level: Integer, ?color: Symbol) -> void
|
13
|
+
def log_exception: (Exception error, ?level: Integer, ?color: Symbol, ?debug_level: Integer, ?debug: _IOLogger?) -> void
|
14
14
|
end
|
15
15
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module HTTPX
|
2
|
+
module Plugins
|
3
|
+
module Query
|
4
|
+
def self.subplugins: () -> Hash[Symbol, Module]
|
5
|
+
|
6
|
+
module InstanceMethods
|
7
|
+
def query: (uri | [uri], **untyped) -> response
|
8
|
+
| (_Each[uri | [uri, request_params]], **untyped) -> Array[response]
|
9
|
+
end
|
10
|
+
|
11
|
+
module QueryRetries
|
12
|
+
module InstanceMethods
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
type sessionQuery = Session & Query::InstanceMethods
|
17
|
+
end
|
18
|
+
end
|
data/sig/pool.rbs
CHANGED
data/sig/request.rbs
CHANGED
@@ -28,6 +28,10 @@ module HTTPX
|
|
28
28
|
|
29
29
|
def initialize: (Symbol | String verb, generic_uri uri, Options options, ?request_params params) -> untyped
|
30
30
|
|
31
|
+
def empty?: () -> bool
|
32
|
+
|
33
|
+
def close: () -> void
|
34
|
+
|
31
35
|
def interests: () -> (:r | :w)
|
32
36
|
|
33
37
|
def merge_headers: (_Each[[String, headers_value]]) -> void
|
data/sig/response.rbs
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module HTTPX
|
2
2
|
interface _Response
|
3
|
+
def <<: (String data) -> void
|
4
|
+
|
3
5
|
def finished?: () -> bool
|
4
6
|
|
5
7
|
def raise_for_status: () -> self
|
@@ -23,6 +25,7 @@ module HTTPX
|
|
23
25
|
@options: Options
|
24
26
|
@request: Request
|
25
27
|
@content_type: ContentType
|
28
|
+
@finished: bool
|
26
29
|
|
27
30
|
def copy_to: (_ToPath | _Writer destination) -> void
|
28
31
|
|
@@ -38,9 +41,11 @@ module HTTPX
|
|
38
41
|
|
39
42
|
def content_type: () -> ContentType
|
40
43
|
|
44
|
+
def finish!: () -> void
|
45
|
+
|
41
46
|
def complete?: () -> bool
|
42
47
|
|
43
|
-
def json: (?
|
48
|
+
def json: (?JSON::options opts) -> untyped
|
44
49
|
|
45
50
|
def form: () -> Hash[String, untyped]
|
46
51
|
|
@@ -77,9 +82,9 @@ module HTTPX
|
|
77
82
|
@options: Options
|
78
83
|
@error: Exception
|
79
84
|
|
80
|
-
attr_reader request: Request
|
85
|
+
%a{pure} attr_reader request: Request
|
81
86
|
|
82
|
-
attr_reader response: Response?
|
87
|
+
%a{pure} attr_reader response: Response?
|
83
88
|
|
84
89
|
def status: () -> (Integer | _ToS)
|
85
90
|
|
data/sig/transcoder/json.rbs
CHANGED
@@ -5,7 +5,7 @@ module HTTPX::Transcoder
|
|
5
5
|
def self?.encode: (_ToJson json) -> Encoder
|
6
6
|
def self?.decode: (HTTPX::Response response) -> _Decoder
|
7
7
|
|
8
|
-
def self?.json_load: (string source, ?
|
8
|
+
def self?.json_load: (string source, ?JSON::options) -> untyped
|
9
9
|
def self?.json_dump: (_ToJson obj, *untyped) -> String
|
10
10
|
|
11
11
|
class Encoder
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httpx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Cardoso
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date: 2025-03-
|
10
|
+
date: 2025-03-25 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: http-2
|
@@ -152,6 +151,7 @@ extra_rdoc_files:
|
|
152
151
|
- doc/release_notes/1_4_0.md
|
153
152
|
- doc/release_notes/1_4_1.md
|
154
153
|
- doc/release_notes/1_4_2.md
|
154
|
+
- doc/release_notes/1_4_3.md
|
155
155
|
files:
|
156
156
|
- LICENSE.txt
|
157
157
|
- README.md
|
@@ -275,6 +275,7 @@ files:
|
|
275
275
|
- doc/release_notes/1_4_0.md
|
276
276
|
- doc/release_notes/1_4_1.md
|
277
277
|
- doc/release_notes/1_4_2.md
|
278
|
+
- doc/release_notes/1_4_3.md
|
278
279
|
- lib/httpx.rb
|
279
280
|
- lib/httpx/adapters/datadog.rb
|
280
281
|
- lib/httpx/adapters/faraday.rb
|
@@ -435,6 +436,7 @@ files:
|
|
435
436
|
- sig/plugins/proxy/socks5.rbs
|
436
437
|
- sig/plugins/proxy/ssh.rbs
|
437
438
|
- sig/plugins/push_promise.rbs
|
439
|
+
- sig/plugins/query.rbs
|
438
440
|
- sig/plugins/rate_limiter.rbs
|
439
441
|
- sig/plugins/response_cache.rbs
|
440
442
|
- sig/plugins/retries.rbs
|
@@ -480,7 +482,6 @@ metadata:
|
|
480
482
|
source_code_uri: https://gitlab.com/os85/httpx
|
481
483
|
homepage_uri: https://honeyryderchuck.gitlab.io/httpx/
|
482
484
|
rubygems_mfa_required: 'true'
|
483
|
-
post_install_message:
|
484
485
|
rdoc_options: []
|
485
486
|
require_paths:
|
486
487
|
- lib
|
@@ -495,8 +496,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
495
496
|
- !ruby/object:Gem::Version
|
496
497
|
version: '0'
|
497
498
|
requirements: []
|
498
|
-
rubygems_version: 3.
|
499
|
-
signing_key:
|
499
|
+
rubygems_version: 3.6.2
|
500
500
|
specification_version: 4
|
501
501
|
summary: HTTPX, to the future, and beyond
|
502
502
|
test_files: []
|