mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
[ruby/csv] Add support for Ractor (https://github.com/ruby/csv/pull/218)
a802690e11
This commit is contained in:
parent
274882be62
commit
ee948fc1b4
Notes:
git
2021-10-24 05:58:02 +09:00
5 changed files with 94 additions and 6 deletions
21
lib/csv.rb
21
lib/csv.rb
|
@ -536,6 +536,14 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
|
||||||
#
|
#
|
||||||
# There is no such storage structure for write headers.
|
# There is no such storage structure for write headers.
|
||||||
#
|
#
|
||||||
|
# In order for the parsing methods to access stored converters in non-main-Ractors, the
|
||||||
|
# storage structure must be made shareable first.
|
||||||
|
# Therefore, <tt>Ractor.make_shareable(CSV::Converters)</tt> and
|
||||||
|
# <tt>Ractor.make_shareable(CSV::HeaderConverters)</tt> must be called before the creation
|
||||||
|
# of Ractors that use the converters stored in these structures. (Since making the storage
|
||||||
|
# structures shareable involves freezing them, any custom converters that are to be used
|
||||||
|
# must be added first.)
|
||||||
|
#
|
||||||
# ===== Converter Lists
|
# ===== Converter Lists
|
||||||
#
|
#
|
||||||
# A _converter_ _list_ is an \Array that may include any assortment of:
|
# A _converter_ _list_ is an \Array that may include any assortment of:
|
||||||
|
@ -908,6 +916,7 @@ class CSV
|
||||||
gsub(/\s+/, "_").to_sym
|
gsub(/\s+/, "_").to_sym
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Default values for method options.
|
# Default values for method options.
|
||||||
DEFAULT_OPTIONS = {
|
DEFAULT_OPTIONS = {
|
||||||
# For both parsing and generating.
|
# For both parsing and generating.
|
||||||
|
@ -946,6 +955,8 @@ class CSV
|
||||||
# Creates or retrieves cached \CSV objects.
|
# Creates or retrieves cached \CSV objects.
|
||||||
# For arguments and options, see CSV.new.
|
# For arguments and options, see CSV.new.
|
||||||
#
|
#
|
||||||
|
# This API is not Ractor-safe.
|
||||||
|
#
|
||||||
# ---
|
# ---
|
||||||
#
|
#
|
||||||
# With no block given, returns a \CSV object.
|
# With no block given, returns a \CSV object.
|
||||||
|
@ -1873,6 +1884,10 @@ class CSV
|
||||||
# csv.converters # => [:integer]
|
# csv.converters # => [:integer]
|
||||||
# csv.convert(proc {|x| x.to_s })
|
# csv.convert(proc {|x| x.to_s })
|
||||||
# csv.converters
|
# csv.converters
|
||||||
|
#
|
||||||
|
# Notes that you need to call
|
||||||
|
# +Ractor.make_shareable(CSV::Converters)+ on the main Ractor to use
|
||||||
|
# this method.
|
||||||
def converters
|
def converters
|
||||||
parser_fields_converter.map do |converter|
|
parser_fields_converter.map do |converter|
|
||||||
name = Converters.rassoc(converter)
|
name = Converters.rassoc(converter)
|
||||||
|
@ -1935,6 +1950,10 @@ class CSV
|
||||||
# Returns an \Array containing header converters; used for parsing;
|
# Returns an \Array containing header converters; used for parsing;
|
||||||
# see {Header Converters}[#class-CSV-label-Header+Converters]:
|
# see {Header Converters}[#class-CSV-label-Header+Converters]:
|
||||||
# CSV.new('').header_converters # => []
|
# CSV.new('').header_converters # => []
|
||||||
|
#
|
||||||
|
# Notes that you need to call
|
||||||
|
# +Ractor.make_shareable(CSV::HeaderConverters)+ on the main Ractor
|
||||||
|
# to use this method.
|
||||||
def header_converters
|
def header_converters
|
||||||
header_fields_converter.map do |converter|
|
header_fields_converter.map do |converter|
|
||||||
name = HeaderConverters.rassoc(converter)
|
name = HeaderConverters.rassoc(converter)
|
||||||
|
@ -2655,6 +2674,8 @@ end
|
||||||
# io = StringIO.new
|
# io = StringIO.new
|
||||||
# CSV(io, col_sep: ";") { |csv| csv << ["a", "b", "c"] }
|
# CSV(io, col_sep: ";") { |csv| csv << ["a", "b", "c"] }
|
||||||
#
|
#
|
||||||
|
# This API is not Ractor-safe.
|
||||||
|
#
|
||||||
def CSV(*args, **options, &block)
|
def CSV(*args, **options, &block)
|
||||||
CSV.instance(*args, **options, &block)
|
CSV.instance(*args, **options, &block)
|
||||||
end
|
end
|
||||||
|
|
|
@ -60,5 +60,5 @@ Gem::Specification.new do |spec|
|
||||||
spec.add_development_dependency "bundler"
|
spec.add_development_dependency "bundler"
|
||||||
spec.add_development_dependency "rake"
|
spec.add_development_dependency "rake"
|
||||||
spec.add_development_dependency "benchmark_driver"
|
spec.add_development_dependency "benchmark_driver"
|
||||||
spec.add_development_dependency "test-unit", ">= 3.4.3"
|
spec.add_development_dependency "test-unit", ">= 3.4.8"
|
||||||
end
|
end
|
||||||
|
|
|
@ -480,9 +480,9 @@ class CSV
|
||||||
begin
|
begin
|
||||||
StringScanner.new("x").scan("x")
|
StringScanner.new("x").scan("x")
|
||||||
rescue TypeError
|
rescue TypeError
|
||||||
@@string_scanner_scan_accept_string = false
|
STRING_SCANNER_SCAN_ACCEPT_STRING = false
|
||||||
else
|
else
|
||||||
@@string_scanner_scan_accept_string = true
|
STRING_SCANNER_SCAN_ACCEPT_STRING = true
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare_separators
|
def prepare_separators
|
||||||
|
@ -506,7 +506,7 @@ class CSV
|
||||||
@first_column_separators = Regexp.new(@escaped_first_column_separator +
|
@first_column_separators = Regexp.new(@escaped_first_column_separator +
|
||||||
"+".encode(@encoding))
|
"+".encode(@encoding))
|
||||||
else
|
else
|
||||||
if @@string_scanner_scan_accept_string
|
if STRING_SCANNER_SCAN_ACCEPT_STRING
|
||||||
@column_end = @column_separator
|
@column_end = @column_separator
|
||||||
else
|
else
|
||||||
@column_end = Regexp.new(@escaped_column_separator)
|
@column_end = Regexp.new(@escaped_column_separator)
|
||||||
|
@ -725,6 +725,8 @@ class CSV
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
SCANNER_TEST_CHUNK_SIZE =
|
||||||
|
Integer((ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] || "1"), 10)
|
||||||
def build_scanner
|
def build_scanner
|
||||||
inputs = @samples.collect do |sample|
|
inputs = @samples.collect do |sample|
|
||||||
UnoptimizedStringIO.new(sample)
|
UnoptimizedStringIO.new(sample)
|
||||||
|
@ -734,10 +736,9 @@ class CSV
|
||||||
else
|
else
|
||||||
inputs << @input
|
inputs << @input
|
||||||
end
|
end
|
||||||
chunk_size = ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] || "1"
|
|
||||||
InputsScanner.new(inputs,
|
InputsScanner.new(inputs,
|
||||||
@encoding,
|
@encoding,
|
||||||
chunk_size: Integer(chunk_size, 10))
|
chunk_size: SCANNER_TEST_CHUNK_SIZE)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
def build_scanner
|
def build_scanner
|
||||||
|
|
|
@ -32,6 +32,24 @@ class TestCSVInterfaceRead < Test::Unit::TestCase
|
||||||
assert_equal(@rows, rows)
|
assert_equal(@rows, rows)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if respond_to?(:ractor)
|
||||||
|
ractor
|
||||||
|
def test_foreach_in_ractor
|
||||||
|
ractor = Ractor.new(@input.path) do |path|
|
||||||
|
rows = []
|
||||||
|
CSV.foreach(path, col_sep: "\t", row_sep: "\r\n").each do |row|
|
||||||
|
rows << row
|
||||||
|
end
|
||||||
|
rows
|
||||||
|
end
|
||||||
|
rows = [
|
||||||
|
["1", "2", "3"],
|
||||||
|
["4", "5"],
|
||||||
|
]
|
||||||
|
assert_equal(rows, ractor.take)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_foreach_mode
|
def test_foreach_mode
|
||||||
rows = []
|
rows = []
|
||||||
CSV.foreach(@input.path, "r", col_sep: "\t", row_sep: "\r\n").each do |row|
|
CSV.foreach(@input.path, "r", col_sep: "\t", row_sep: "\r\n").each do |row|
|
||||||
|
@ -240,6 +258,20 @@ class TestCSVInterfaceRead < Test::Unit::TestCase
|
||||||
CSV.read(@input.path, col_sep: "\t", row_sep: "\r\n"))
|
CSV.read(@input.path, col_sep: "\t", row_sep: "\r\n"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if respond_to?(:ractor)
|
||||||
|
ractor
|
||||||
|
def test_read_in_ractor
|
||||||
|
ractor = Ractor.new(@input.path) do |path|
|
||||||
|
CSV.read(path, col_sep: "\t", row_sep: "\r\n")
|
||||||
|
end
|
||||||
|
rows = [
|
||||||
|
["1", "2", "3"],
|
||||||
|
["4", "5"],
|
||||||
|
]
|
||||||
|
assert_equal(rows, ractor.take)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_readlines
|
def test_readlines
|
||||||
assert_equal(@rows,
|
assert_equal(@rows,
|
||||||
CSV.readlines(@input.path, col_sep: "\t", row_sep: "\r\n"))
|
CSV.readlines(@input.path, col_sep: "\t", row_sep: "\r\n"))
|
||||||
|
|
|
@ -25,6 +25,21 @@ class TestCSVInterfaceWrite < Test::Unit::TestCase
|
||||||
CSV
|
CSV
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if respond_to?(:ractor)
|
||||||
|
ractor
|
||||||
|
def test_generate_default_in_ractor
|
||||||
|
ractor = Ractor.new do
|
||||||
|
CSV.generate do |csv|
|
||||||
|
csv << [1, 2, 3] << [4, nil, 5]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assert_equal(<<-CSV, ractor.take)
|
||||||
|
1,2,3
|
||||||
|
4,,5
|
||||||
|
CSV
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_generate_append
|
def test_generate_append
|
||||||
csv_text = <<-CSV
|
csv_text = <<-CSV
|
||||||
1,2,3
|
1,2,3
|
||||||
|
@ -101,6 +116,25 @@ a,b,c
|
||||||
CSV
|
CSV
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if respond_to?(:ractor)
|
||||||
|
ractor
|
||||||
|
def test_append_row_in_ractor
|
||||||
|
ractor = Ractor.new(@output.path) do |path|
|
||||||
|
CSV.open(path, "wb") do |csv|
|
||||||
|
csv <<
|
||||||
|
CSV::Row.new([], ["1", "2", "3"]) <<
|
||||||
|
CSV::Row.new([], ["a", "b", "c"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ractor.take
|
||||||
|
assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
|
||||||
|
1,2,3
|
||||||
|
a,b,c
|
||||||
|
CSV
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_append_hash
|
def test_append_hash
|
||||||
CSV.open(@output.path, "wb", headers: true) do |csv|
|
CSV.open(@output.path, "wb", headers: true) do |csv|
|
||||||
csv << [:a, :b, :c]
|
csv << [:a, :b, :c]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue