[前][次][番号順一覧][スレッド一覧]

ruby-changes:69372

From: rm155 <ko1@a...>
Date: Sun, 24 Oct 2021 05:58:14 +0900 (JST)
Subject: [ruby-changes:69372] ee948fc1b4 (master): [ruby/csv] Add support for Ractor (https://github.com/ruby/csv/pull/218)

https://git.ruby-lang.org/ruby.git/commit/?id=ee948fc1b4

From ee948fc1b4cb1ad382beee709008bb93b8f6ba75 Mon Sep 17 00:00:00 2001
From: rm155 <86454369+rm155@u...>
Date: Sun, 10 Oct 2021 22:21:42 -0400
Subject: [ruby/csv] Add support for Ractor
 (https://github.com/ruby/csv/pull/218)

https://github.com/ruby/csv/commit/a802690e11
---
 lib/csv.rb                       | 21 +++++++++++++++++++++
 lib/csv/csv.gemspec              |  2 +-
 lib/csv/parser.rb                | 11 ++++++-----
 test/csv/interface/test_read.rb  | 32 ++++++++++++++++++++++++++++++++
 test/csv/interface/test_write.rb | 34 ++++++++++++++++++++++++++++++++++
 5 files changed, 94 insertions(+), 6 deletions(-)

diff --git a/lib/csv.rb b/lib/csv.rb
index 87c3a4be31..42e99435cb 100644
--- a/lib/csv.rb
+++ b/lib/csv.rb
@@ -536,6 +536,14 @@ using CSV::MatchP if CSV.const_defined?(:MatchP) https://github.com/ruby/ruby/blob/trunk/lib/csv.rb#L536
 #
 # 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
 #
 # A _converter_ _list_ is an \Array that may include any assortment of:
@@ -908,6 +916,7 @@ class CSV https://github.com/ruby/ruby/blob/trunk/lib/csv.rb#L916
                                            gsub(/\s+/, "_").to_sym
     }
   }
+
   # Default values for method options.
   DEFAULT_OPTIONS = {
     # For both parsing and generating.
@@ -946,6 +955,8 @@ class CSV https://github.com/ruby/ruby/blob/trunk/lib/csv.rb#L955
     # Creates or retrieves cached \CSV objects.
     # For arguments and options, see CSV.new.
     #
+    # This API is not Ractor-safe.
+    #
     # ---
     #
     # With no block given, returns a \CSV object.
@@ -1873,6 +1884,10 @@ class CSV https://github.com/ruby/ruby/blob/trunk/lib/csv.rb#L1884
   #   csv.converters # => [:integer]
   #   csv.convert(proc {|x| x.to_s })
   #   csv.converters
+  #
+  # Notes that you need to call
+  # +Ractor.make_shareable(CSV::Converters)+ on the main Ractor to use
+  # this method.
   def converters
     parser_fields_converter.map do |converter|
       name = Converters.rassoc(converter)
@@ -1935,6 +1950,10 @@ class CSV https://github.com/ruby/ruby/blob/trunk/lib/csv.rb#L1950
   # Returns an \Array containing header converters; used for parsing;
   # see {Header Converters}[#class-CSV-label-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
     header_fields_converter.map do |converter|
       name = HeaderConverters.rassoc(converter)
@@ -2655,6 +2674,8 @@ end https://github.com/ruby/ruby/blob/trunk/lib/csv.rb#L2674
 #   io = StringIO.new
 #   CSV(io, col_sep: ";") { |csv| csv << ["a", "b", "c"] }
 #
+# This API is not Ractor-safe.
+#
 def CSV(*args, **options, &block)
   CSV.instance(*args, **options, &block)
 end
diff --git a/lib/csv/csv.gemspec b/lib/csv/csv.gemspec
index 6948e9a72f..11c5b0f2a6 100644
--- a/lib/csv/csv.gemspec
+++ b/lib/csv/csv.gemspec
@@ -60,5 +60,5 @@ Gem::Specification.new do |spec| https://github.com/ruby/ruby/blob/trunk/lib/csv/csv.gemspec#L60
   spec.add_development_dependency "bundler"
   spec.add_development_dependency "rake"
   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
diff --git a/lib/csv/parser.rb b/lib/csv/parser.rb
index 0d8a157fd7..3334acfbdd 100644
--- a/lib/csv/parser.rb
+++ b/lib/csv/parser.rb
@@ -480,9 +480,9 @@ class CSV https://github.com/ruby/ruby/blob/trunk/lib/csv/parser.rb#L480
     begin
       StringScanner.new("x").scan("x")
     rescue TypeError
-      @@string_scanner_scan_accept_string = false
+      STRING_SCANNER_SCAN_ACCEPT_STRING = false
     else
-      @@string_scanner_scan_accept_string = true
+      STRING_SCANNER_SCAN_ACCEPT_STRING = true
     end
 
     def prepare_separators
@@ -506,7 +506,7 @@ class CSV https://github.com/ruby/ruby/blob/trunk/lib/csv/parser.rb#L506
         @first_column_separators = Regexp.new(@escaped_first_column_separator +
                                               "+".encode(@encoding))
       else
-        if @@string_scanner_scan_accept_string
+        if STRING_SCANNER_SCAN_ACCEPT_STRING
           @column_end = @column_separator
         else
           @column_end = Regexp.new(@escaped_column_separator)
@@ -725,6 +725,8 @@ class CSV https://github.com/ruby/ruby/blob/trunk/lib/csv/parser.rb#L725
         end
       end
 
+      SCANNER_TEST_CHUNK_SIZE =
+        Integer((ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] || "1"), 10)
       def build_scanner
         inputs = @samples.collect do |sample|
           UnoptimizedStringIO.new(sample)
@@ -734,10 +736,9 @@ class CSV https://github.com/ruby/ruby/blob/trunk/lib/csv/parser.rb#L736
         else
           inputs << @input
         end
-        chunk_size = ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] || "1"
         InputsScanner.new(inputs,
                           @encoding,
-                          chunk_size: Integer(chunk_size, 10))
+                          chunk_size: SCANNER_TEST_CHUNK_SIZE)
       end
     else
       def build_scanner
diff --git a/test/csv/interface/test_read.rb b/test/csv/interface/test_read.rb
index b86c54fc9f..d73622d554 100644
--- a/test/csv/interface/test_read.rb
+++ b/test/csv/interface/test_read.rb
@@ -32,6 +32,24 @@ class TestCSVInterfaceRead < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/csv/interface/test_read.rb#L32
     assert_equal(@rows, rows)
   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
     rows = []
     CSV.foreach(@input.path, "r", col_sep: "\t", row_sep: "\r\n").each do |row|
@@ -240,6 +258,20 @@ class TestCSVInterfaceRead < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/csv/interface/test_read.rb#L258
                  CSV.read(@input.path, col_sep: "\t", row_sep: "\r\n"))
   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
     assert_equal(@rows,
                  CSV.readlines(@input.path, col_sep: "\t", row_sep: "\r\n"))
diff --git a/test/csv/interface/test_write.rb b/test/csv/interface/test_write.rb
index 8650ecd624..02c2c5c5ce 100644
--- a/test/csv/interface/test_write.rb
+++ b/test/csv/interface/test_write.rb
@@ -25,6 +25,21 @@ class TestCSVInterfaceWrite < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/csv/interface/test_write.rb#L25
     CSV
   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
     csv_text = <<-CSV
 1,2,3
@@ -101,6 +116,25 @@ a,b,c https://github.com/ruby/ruby/blob/trunk/test/csv/interface/test_write.rb#L116
     CSV
   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
     CSV.open(@output.path, "wb", headers: true) do |csv|
       csv << [:a, :b, :c]
-- 
cgit v1.2.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]