Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
@@ -0,0 +1,258 @@
# frozen_string_literal: true
module Nokogiri
module XML
module SAX
# :markup: markdown
#
# The SAX::Document class is used for registering types of events you are interested in
# handling. All of the methods on this class are available as possible events while parsing an
# \XML document. To register for any particular event, subclass this class and implement the
# methods you are interested in knowing about.
#
# To only be notified about start and end element events, write a class like this:
#
# class MyHandler < Nokogiri::XML::SAX::Document
# def start_element name, attrs = []
# puts "#{name} started!"
# end
#
# def end_element name
# puts "#{name} ended"
# end
# end
#
# You can use this event handler for any SAX-style parser included with Nokogiri.
#
# See also:
#
# - Nokogiri::XML::SAX
# - Nokogiri::HTML4::SAX
#
# ### Entity Handling
#
# ⚠ Entity handling is complicated in a SAX parser! Please read this section carefully if
# you're not getting the behavior you expect.
#
# Entities will be reported to the user via callbacks to #characters, to #reference, or
# possibly to both. The behavior is determined by a combination of _entity type_ and the value
# of ParserContext#replace_entities. (Recall that the default value of
# ParserContext#replace_entities is `false`.)
#
# ⚠ <b>It is UNSAFE to set ParserContext#replace_entities to `true`</b> when parsing untrusted
# documents.
#
# 💡 For more information on entity types, see [Wikipedia's page on
# DTDs](https://en.wikipedia.org/wiki/Document_type_definition#Entity_declarations).
#
# | Entity type | #characters | #reference |
# |--------------------------------------|------------------------------------|-------------------------------------|
# | Char ref (e.g., <tt>&#146;</tt>) | always | never |
# | Predefined (e.g., <tt>&amp;</tt>) | always | never |
# | Undeclared † | never | <tt>#replace_entities == false</tt> |
# | Internal | always | <tt>#replace_entities == false</tt> |
# | External † | <tt>#replace_entities == true</tt> | <tt>#replace_entities == false</tt> |
#
# &nbsp;
#
# † In the case where the replacement text for the entity is unknown (e.g., an undeclared entity
# or an external entity that could not be resolved because of network issues), then the
# replacement text will not be reported. If ParserContext#replace_entities is `true`, this
# means the #characters callback will not be invoked. If ParserContext#replace_entities is
# `false`, then the #reference callback will be invoked, but with `nil` for the `content`
# argument.
#
class Document
###
# Called when an \XML declaration is parsed.
#
# [Parameters]
# - +version+ (String) the version attribute
# - +encoding+ (String, nil) the encoding of the document if present, else +nil+
# - +standalone+ ("yes", "no", nil) the standalone attribute if present, else +nil+
def xmldecl(version, encoding, standalone)
end
###
# Called when document starts parsing.
def start_document
end
###
# Called when document ends parsing.
def end_document
end
###
# Called at the beginning of an element.
#
# [Parameters]
# - +name+ (String) the name of the element
# - +attrs+ (Array<Array<String>>) an assoc list of namespace declarations and attributes, e.g.:
# [ ["xmlns:foo", "http://sample.net"], ["size", "large"] ]
#
# 💡If you're dealing with XML and need to handle namespaces, use the
# #start_element_namespace method instead.
#
# Note that the element namespace and any attribute namespaces are not provided, and so any
# namespaced elements or attributes will be returned as strings including the prefix:
#
# parser.parse(<<~XML)
# <root xmlns:foo='http://foo.example.com/' xmlns='http://example.com/'>
# <foo:bar foo:quux="xxx">hello world</foo:bar>
# </root>
# XML
#
# assert_pattern do
# parser.document.start_elements => [
# ["root", [["xmlns:foo", "http://foo.example.com/"], ["xmlns", "http://example.com/"]]],
# ["foo:bar", [["foo:quux", "xxx"]]],
# ]
# end
#
def start_element(name, attrs = [])
end
###
# Called at the end of an element.
#
# [Parameters]
# - +name+ (String) the name of the element being closed
#
def end_element(name)
end
###
# Called at the beginning of an element.
#
# [Parameters]
# - +name+ (String) is the name of the element
# - +attrs+ (Array<Attribute>) is an array of structs with the following properties:
# - +localname+ (String) the local name of the attribute
# - +value+ (String) the value of the attribute
# - +prefix+ (String, nil) the namespace prefix of the attribute
# - +uri+ (String, nil) the namespace URI of the attribute
# - +prefix+ (String, nil) is the namespace prefix for the element
# - +uri+ (String, nil) is the associated URI for the element's namespace
# - +ns+ (Array<Array<String, String>>) is an assoc list of namespace declarations on the element
#
# 💡If you're dealing with HTML or don't care about namespaces, try #start_element instead.
#
# [Example]
# it "start_elements_namespace is called with namespaced attributes" do
# parser.parse(<<~XML)
# <root xmlns:foo='http://foo.example.com/'>
# <foo:a foo:bar='hello' />
# </root>
# XML
#
# assert_pattern do
# parser.document.start_elements_namespace => [
# [
# "root",
# [],
# nil, nil,
# [["foo", "http://foo.example.com/"]], # namespace declarations
# ], [
# "a",
# [Nokogiri::XML::SAX::Parser::Attribute(localname: "bar", prefix: "foo", uri: "http://foo.example.com/", value: "hello")], # prefixed attribute
# "foo", "http://foo.example.com/", # prefix and uri for the "a" element
# [],
# ]
# ]
# end
# end
#
def start_element_namespace(name, attrs = [], prefix = nil, uri = nil, ns = []) # rubocop:disable Metrics/ParameterLists
# Deal with SAX v1 interface
name = [prefix, name].compact.join(":")
attributes = ns.map do |ns_prefix, ns_uri|
[["xmlns", ns_prefix].compact.join(":"), ns_uri]
end + attrs.map do |attr|
[[attr.prefix, attr.localname].compact.join(":"), attr.value]
end
start_element(name, attributes)
end
###
# Called at the end of an element.
#
# [Parameters]
# - +name+ (String) is the name of the element
# - +prefix+ (String, nil) is the namespace prefix for the element
# - +uri+ (String, nil) is the associated URI for the element's namespace
#
def end_element_namespace(name, prefix = nil, uri = nil)
# Deal with SAX v1 interface
end_element([prefix, name].compact.join(":"))
end
###
# Called when character data is parsed, and for parsed entities when
# ParserContext#replace_entities is +true+.
#
# [Parameters]
# - +string+ contains the character data or entity replacement text
#
# ⚠ Please see Document@Entity+Handling for important information about how entities are handled.
#
# ⚠ This method might be called multiple times for a contiguous string of characters.
#
def characters(string)
end
###
# Called when a parsed entity is referenced and not replaced.
#
# [Parameters]
# - +name+ (String) is the name of the entity
# - +content+ (String, nil) is the replacement text for the entity, if known
#
# ⚠ Please see Document@Entity+Handling for important information about how entities are handled.
#
# ⚠ An internal entity may result in a call to both #characters and #reference.
#
# Since v1.17.0
#
def reference(name, content)
end
###
# Called when comments are encountered
# [Parameters]
# - +string+ contains the comment data
def comment(string)
end
###
# Called on document warnings
# [Parameters]
# - +string+ contains the warning
def warning(string)
end
###
# Called on document errors
# [Parameters]
# - +string+ contains the error
def error(string)
end
###
# Called when cdata blocks are found
# [Parameters]
# - +string+ contains the cdata content
def cdata_block(string)
end
###
# Called when processing instructions are found
# [Parameters]
# - +name+ is the target of the instruction
# - +content+ is the value of the instruction
def processing_instruction(name, content)
end
end
end
end
end
@@ -0,0 +1,199 @@
# frozen_string_literal: true
module Nokogiri
module XML
module SAX
###
# This parser is a SAX style parser that reads its input as it deems necessary. The parser
# takes a Nokogiri::XML::SAX::Document, an optional encoding, then given an XML input, sends
# messages to the Nokogiri::XML::SAX::Document.
#
# Here is an example of using this parser:
#
# # Create a subclass of Nokogiri::XML::SAX::Document and implement
# # the events we care about:
# class MyHandler < Nokogiri::XML::SAX::Document
# def start_element name, attrs = []
# puts "starting: #{name}"
# end
#
# def end_element name
# puts "ending: #{name}"
# end
# end
#
# parser = Nokogiri::XML::SAX::Parser.new(MyHandler.new)
#
# # Hand an IO object to the parser, which will read the XML from the IO.
# File.open(path_to_xml) do |f|
# parser.parse(f)
# end
#
# For more information about \SAX parsers, see Nokogiri::XML::SAX.
#
# Also see Nokogiri::XML::SAX::Document for the available events.
#
# For \HTML documents, use the subclass Nokogiri::HTML4::SAX::Parser.
#
class Parser
# to dynamically resolve ParserContext in inherited methods
include Nokogiri::ClassResolver
# Structure used for marshalling attributes for some callbacks in XML::SAX::Document.
class Attribute < Struct.new(:localname, :prefix, :uri, :value)
end
ENCODINGS = { # :nodoc:
"NONE" => 0, # No char encoding detected
"UTF-8" => 1, # UTF-8
"UTF16LE" => 2, # UTF-16 little endian
"UTF16BE" => 3, # UTF-16 big endian
"UCS4LE" => 4, # UCS-4 little endian
"UCS4BE" => 5, # UCS-4 big endian
"EBCDIC" => 6, # EBCDIC uh!
"UCS4-2143" => 7, # UCS-4 unusual ordering
"UCS4-3412" => 8, # UCS-4 unusual ordering
"UCS2" => 9, # UCS-2
"ISO-8859-1" => 10, # ISO-8859-1 ISO Latin 1
"ISO-8859-2" => 11, # ISO-8859-2 ISO Latin 2
"ISO-8859-3" => 12, # ISO-8859-3
"ISO-8859-4" => 13, # ISO-8859-4
"ISO-8859-5" => 14, # ISO-8859-5
"ISO-8859-6" => 15, # ISO-8859-6
"ISO-8859-7" => 16, # ISO-8859-7
"ISO-8859-8" => 17, # ISO-8859-8
"ISO-8859-9" => 18, # ISO-8859-9
"ISO-2022-JP" => 19, # ISO-2022-JP
"SHIFT-JIS" => 20, # Shift_JIS
"EUC-JP" => 21, # EUC-JP
"ASCII" => 22, # pure ASCII
}
REVERSE_ENCODINGS = ENCODINGS.invert # :nodoc:
deprecate_constant :ENCODINGS
# The Nokogiri::XML::SAX::Document where events will be sent.
attr_accessor :document
# The encoding beings used for this document.
attr_accessor :encoding
###
# :call-seq:
# new ⇒ SAX::Parser
# new(handler) ⇒ SAX::Parser
# new(handler, encoding) ⇒ SAX::Parser
#
# Create a new Parser.
#
# [Parameters]
# - +handler+ (optional Nokogiri::XML::SAX::Document) The document that will receive
# events. Will create a new Nokogiri::XML::SAX::Document if not given, which is accessible
# through the #document attribute.
# - +encoding+ (optional Encoding, String, nil) An Encoding or encoding name to use when
# parsing the input. (default +nil+ for auto-detection)
#
def initialize(doc = Nokogiri::XML::SAX::Document.new, encoding = nil)
@encoding = encoding
@document = doc
@warned = false
initialize_native unless Nokogiri.jruby?
end
###
# :call-seq:
# parse(input) { |parser_context| ... }
#
# Parse the input, sending events to the SAX::Document at #document.
#
# [Parameters]
# - +input+ (String, IO) The input to parse.
#
# If +input+ quacks like a readable IO object, this method forwards to Parser.parse_io,
# otherwise it forwards to Parser.parse_memory.
#
# [Yields]
# If a block is given, the underlying ParserContext object will be yielded. This can be used
# to set options on the parser context before parsing begins.
#
def parse(input, &block)
if input.respond_to?(:read) && input.respond_to?(:close)
parse_io(input, &block)
else
parse_memory(input, &block)
end
end
###
# :call-seq:
# parse_io(io) { |parser_context| ... }
# parse_io(io, encoding) { |parser_context| ... }
#
# Parse an input stream.
#
# [Parameters]
# - +io+ (IO) The readable IO object from which to read input
# - +encoding+ (optional Encoding, String, nil) An Encoding or encoding name to use when
# parsing the input, or +nil+ for auto-detection. (default #encoding)
#
# [Yields]
# If a block is given, the underlying ParserContext object will be yielded. This can be used
# to set options on the parser context before parsing begins.
#
def parse_io(io, encoding = @encoding)
ctx = related_class("ParserContext").io(io, encoding)
yield ctx if block_given?
ctx.parse_with(self)
end
###
# :call-seq:
# parse_memory(input) { |parser_context| ... }
# parse_memory(input, encoding) { |parser_context| ... }
#
# Parse an input string.
#
# [Parameters]
# - +input+ (String) The input string to be parsed.
# - +encoding+ (optional Encoding, String, nil) An Encoding or encoding name to use when
# parsing the input, or +nil+ for auto-detection. (default #encoding)
#
# [Yields]
# If a block is given, the underlying ParserContext object will be yielded. This can be used
# to set options on the parser context before parsing begins.
#
def parse_memory(input, encoding = @encoding)
ctx = related_class("ParserContext").memory(input, encoding)
yield ctx if block_given?
ctx.parse_with(self)
end
###
# :call-seq:
# parse_file(filename) { |parser_context| ... }
# parse_file(filename, encoding) { |parser_context| ... }
#
# Parse a file.
#
# [Parameters]
# - +filename+ (String) The path to the file to be parsed.
# - +encoding+ (optional Encoding, String, nil) An Encoding or encoding name to use when
# parsing the input, or +nil+ for auto-detection. (default #encoding)
#
# [Yields]
# If a block is given, the underlying ParserContext object will be yielded. This can be used
# to set options on the parser context before parsing begins.
#
def parse_file(filename, encoding = @encoding)
raise ArgumentError, "no filename provided" unless filename
raise Errno::ENOENT unless File.exist?(filename)
raise Errno::EISDIR if File.directory?(filename)
ctx = related_class("ParserContext").file(filename, encoding)
yield ctx if block_given?
ctx.parse_with(self)
end
end
end
end
end
@@ -0,0 +1,129 @@
# frozen_string_literal: true
module Nokogiri
module XML
module SAX
###
# Context object to invoke the XML SAX parser on the SAX::Document handler.
#
# 💡 This class is usually not instantiated by the user. Use Nokogiri::XML::SAX::Parser
# instead.
class ParserContext
class << self
###
# :call-seq:
# new(input)
# new(input, encoding)
#
# Create a parser context for an IO or a String. This is a shorthand method for
# ParserContext.io and ParserContext.memory.
#
# [Parameters]
# - +input+ (IO, String) A String or a readable IO object
# - +encoding+ (optional) (Encoding) The +Encoding+ to use, or the name of an
# encoding to use (default +nil+, encoding will be autodetected)
#
# If +input+ quacks like a readable IO object, this method forwards to ParserContext.io,
# otherwise it forwards to ParserContext.memory.
#
# [Returns] Nokogiri::XML::SAX::ParserContext
#
def new(input, encoding = nil)
if [:read, :close].all? { |x| input.respond_to?(x) }
io(input, encoding)
else
memory(input, encoding)
end
end
###
# :call-seq:
# io(input)
# io(input, encoding)
#
# Create a parser context for an +input+ IO which will assume +encoding+
#
# [Parameters]
# - +io+ (IO) The readable IO object from which to read input
# - +encoding+ (optional) (Encoding) The +Encoding+ to use, or the name of an
# encoding to use (default +nil+, encoding will be autodetected)
#
# [Returns] Nokogiri::XML::SAX::ParserContext
#
# 💡 Calling this method directly is discouraged. Use Nokogiri::XML::SAX::Parser parse
# methods which are more convenient for most use cases.
#
def io(input, encoding = nil)
native_io(input, resolve_encoding(encoding))
end
###
# :call-seq:
# memory(input)
# memory(input, encoding)
#
# Create a parser context for the +input+ String.
#
# [Parameters]
# - +input+ (String) The input string to be parsed.
# - +encoding+ (optional) (Encoding, String) The +Encoding+ to use, or the name of an encoding to
# use (default +nil+, encoding will be autodetected)
#
# [Returns] Nokogiri::XML::SAX::ParserContext
#
# 💡 Calling this method directly is discouraged. Use Nokogiri::XML::SAX::Parser parse methods
# which are more convenient for most use cases.
#
def memory(input, encoding = nil)
native_memory(input, resolve_encoding(encoding))
end
###
# :call-seq:
# file(path)
# file(path, encoding)
#
# Create a parser context for the file at +path+.
#
# [Parameters]
# - +path+ (String) The path to the input file
# - +encoding+ (optional) (Encoding, String) The +Encoding+ to use, or the name of an encoding to
# use (default +nil+, encoding will be autodetected)
#
# [Returns] Nokogiri::XML::SAX::ParserContext
#
# 💡 Calling this method directly is discouraged. Use Nokogiri::XML::SAX::Parser.parse_file which
# is more convenient for most use cases.
def file(input, encoding = nil)
native_file(input, resolve_encoding(encoding))
end
private def resolve_encoding(encoding)
case encoding
when Encoding
encoding
when nil
nil # totally fine, parser will guess encoding
when Integer
warn("Passing an integer to Nokogiri::XML::SAX::ParserContext.io is deprecated. Use an Encoding object instead. This will become an error in a future release.", uplevel: 2, category: :deprecated)
return nil if encoding == Parser::ENCODINGS["NONE"]
encoding = Parser::REVERSE_ENCODINGS[encoding]
raise ArgumentError, "Invalid libxml2 encoding id #{encoding}" if encoding.nil?
Encoding.find(encoding)
when String
Encoding.find(encoding)
else
raise ArgumentError, "Cannot resolve #{encoding.inspect} to an Encoding"
end
end
end
end
end
end
end
@@ -0,0 +1,64 @@
# frozen_string_literal: true
module Nokogiri
module XML
module SAX
###
# PushParser can parse a document that is fed to it manually. It
# must be given a SAX::Document object which will be called with
# SAX events as the document is being parsed.
#
# Calling PushParser#<< writes XML to the parser, calling any SAX
# callbacks it can.
#
# PushParser#finish tells the parser that the document is finished
# and calls the end_document SAX method.
#
# Example:
#
# parser = PushParser.new(Class.new(XML::SAX::Document) {
# def start_document
# puts "start document called"
# end
# }.new)
# parser << "<div>hello<"
# parser << "/div>"
# parser.finish
class PushParser
# The Nokogiri::XML::SAX::Document on which the PushParser will be
# operating
attr_accessor :document
###
# Create a new PushParser with +doc+ as the SAX Document, providing
# an optional +file_name+ and +encoding+
def initialize(doc = XML::SAX::Document.new, file_name = nil, encoding = "UTF-8")
@document = doc
@encoding = encoding
@sax_parser = XML::SAX::Parser.new(doc)
## Create our push parser context
initialize_native(@sax_parser, file_name)
end
###
# Write a +chunk+ of XML to the PushParser. Any callback methods
# that can be called will be called immediately.
def write(chunk, last_chunk = false)
native_write(chunk, last_chunk)
end
alias_method :<<, :write
###
# Finish the parsing. This method is only necessary for
# Nokogiri::XML::SAX::Document#end_document to be called.
#
# ⚠ Note that empty documents are treated as an error when using the libxml2-based
# implementation (CRuby), but are fine when using the Xerces-based implementation (JRuby).
def finish
write("", true)
end
end
end
end
end