-
Notifications
You must be signed in to change notification settings - Fork 85
Deprecate accepting array as an element in XPath.match, first and each #252
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
XPath.match, first, each accepted array as an element. This behavior is not documented, and making hard to optimize and refactor. The second argument of XPathParser#parse and XPathParser#match is also changed from nodeset to node
In this case, I made further improvements to #249, which eliminated the slowness.
How about instead of removing a feature, a deprecated message should be displayed if it is specified in an array? What do you think @kou? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm OK that we drop support for nodeset because we don't have enough resource to complete nodeset support.
In general, we want to keep backward compatibility as much as possible. But we can remove the feature without keeping backward compatibility because:
- It's not documented
- It doesn't work in some cases
But could you report a warning as @naitoh suggested something like:
diff --git a/lib/rexml/xpath_parser.rb b/lib/rexml/xpath_parser.rb
index 5eb1e5a..a2b2ef5 100644
--- a/lib/rexml/xpath_parser.rb
+++ b/lib/rexml/xpath_parser.rb
@@ -136,11 +136,12 @@ module REXML
end
- def match(path_stack, nodeset)
- nodeset = nodeset.collect.with_index do |node, i|
- position = i + 1
- XPathNode.new(node, position: position)
+ def match(path_stack, node)
+ if node.is_a?(Array)
+ warn("REXML::XPath.XXX dropped support for nodeset...", uplevel: N)
+ node = node.first
end
+ nodeset = [XPathNode.new(node, position: 1)]
result = expr(path_stack, nodeset)
case result
when Array # nodesetCo-authored-by: Sutou Kouhei <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Thanks!
|
@tompng |
|
@naitoh |
|
Thanks! |
|
Just FTR, this likely breaks vagrant-libvirt: I have no idea ATM how to fix this :/ |
REXML 3.4.2+ deprecated accepting array as an element in `XPath.match`
[1]. This led to test errors such as:
~~~
3) VagrantPlugins::ProviderLibvirt::Action::ResolveDiskSettings#call when vm box is in use when box metadata is not available when multiple volumes in domain config should populate domain volumes with devices
Failure/Error:
expect(env[:domain_volumes]).to match(
[
hash_including(
device: 'vda',
absolute_path: '/var/lib/libvirt/images/vagrant-test_default.img'
),
hash_including(
device: 'vdb',
absolute_path: '/var/lib/libvirt/images/vagrant-test_default_1.img'
),
expected [{absolute_path: "/var/lib/libvirt/images/vagrant-test_default.img", bus: "virtio", cache: "default", device: "vda", name: "vagrant-test_default.img"}] to match [#<RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher:0x00007fff921b3db8 @expected={device: "vda", absolute_path: "/var/lib/libvirt/images/vagrant-test_default.img"}>, #<RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher:0x00007fff921b3d40 @expected={device: "vdb", absolute_path: "/var/lib/libvirt/images/vagrant-test_default_1.img"}>, #<RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher:0x00007fff921b3cc8 @expected={device: "vdc", absolute_path: "/var/lib/libvirt/images/vagrant-test_default_2.img"}>]
Diff:
@@ -1,4 +1,6 @@
-[hash_including(device: "vda", absolute_path: "/var/lib/libvirt/images/vagrant-test_default.img"),
- hash_including(device: "vdb", absolute_path: "/var/lib/libvirt/images/vagrant-test_default_1.img"),
- hash_including(device: "vdc", absolute_path: "/var/lib/libvirt/images/vagrant-test_default_2.img")]
+[{absolute_path: "/var/lib/libvirt/images/vagrant-test_default.img",
+ bus: "virtio",
+ cache: "default",
+ device: "vda",
+ name: "vagrant-test_default.img"}]
# ./spec/unit/action/resolve_disk_settings_spec.rb:200:in 'block (6 levels) in <top (required)>'
# ./spec/support/unit_context.rb:51:in 'block (3 levels) in <top (required)>'
# ./spec/support/unit_context.rb:43:in 'block (2 levels) in <top (required)>'
# ./spec/support/unit_context.rb:51:in 'block (3 levels) in <top (required)>'
# ./spec/support/unit_context.rb:43:in 'block (2 levels) in <top (required)>'
~~~
This changes the logic in a way, that XPath is matching against whole
XML document, instead of array of XML elements.
[1]: ruby/rexml#252
REXML 3.4.2+ deprecated accepting array as an element in `XPath.match`
[[1]]. This led to test errors such as:
~~~
3) VagrantPlugins::ProviderLibvirt::Action::ResolveDiskSettings#call when vm box is in use when box metadata is not available when multiple volumes in domain config should populate domain volumes with devices
Failure/Error:
expect(env[:domain_volumes]).to match(
[
hash_including(
device: 'vda',
absolute_path: '/var/lib/libvirt/images/vagrant-test_default.img'
),
hash_including(
device: 'vdb',
absolute_path: '/var/lib/libvirt/images/vagrant-test_default_1.img'
),
expected [{absolute_path: "/var/lib/libvirt/images/vagrant-test_default.img", bus: "virtio", cache: "default", device: "vda", name: "vagrant-test_default.img"}] to match [#<RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher:0x00007fff921b3db8 @expected={device: "vda", absolute_path: "/var/lib/libvirt/images/vagrant-test_default.img"}>, #<RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher:0x00007fff921b3d40 @expected={device: "vdb", absolute_path: "/var/lib/libvirt/images/vagrant-test_default_1.img"}>, #<RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher:0x00007fff921b3cc8 @expected={device: "vdc", absolute_path: "/var/lib/libvirt/images/vagrant-test_default_2.img"}>]
Diff:
@@ -1,4 +1,6 @@
-[hash_including(device: "vda", absolute_path: "/var/lib/libvirt/images/vagrant-test_default.img"),
- hash_including(device: "vdb", absolute_path: "/var/lib/libvirt/images/vagrant-test_default_1.img"),
- hash_including(device: "vdc", absolute_path: "/var/lib/libvirt/images/vagrant-test_default_2.img")]
+[{absolute_path: "/var/lib/libvirt/images/vagrant-test_default.img",
+ bus: "virtio",
+ cache: "default",
+ device: "vda",
+ name: "vagrant-test_default.img"}]
# ./spec/unit/action/resolve_disk_settings_spec.rb:200:in 'block (6 levels) in <top (required)>'
# ./spec/support/unit_context.rb:51:in 'block (3 levels) in <top (required)>'
# ./spec/support/unit_context.rb:43:in 'block (2 levels) in <top (required)>'
# ./spec/support/unit_context.rb:51:in 'block (3 levels) in <top (required)>'
# ./spec/support/unit_context.rb:43:in 'block (2 levels) in <top (required)>'
~~~
This changes the logic in a way, that XPath is matching against whole
XML document, instead of array of XML elements.
[1]: ruby/rexml#252
We apologize for causing incompatibility with vagrant-libvirt.
Fixed example:$ git diff -ub
diff --git a/lib/vagrant-libvirt/action/resolve_disk_settings.rb b/lib/vagrant-libvirt/action/resolve_disk_settings.rb
index f1a3f67..bf5d951 100644
--- a/lib/vagrant-libvirt/action/resolve_disk_settings.rb
+++ b/lib/vagrant-libvirt/action/resolve_disk_settings.rb
@@ -51,14 +51,16 @@ module VagrantPlugins
xml_descr = REXML::Document.new(domain_xml)
domain_name = xml_descr.elements['domain'].elements['name'].text
disks_xml = REXML::XPath.match(xml_descr, '/domain/devices/disk[@device="disk"]')
- have_aliases = !REXML::XPath.match(disks_xml, './alias[@name="ua-box-volume-0"]').first.nil?
+ have_aliases = !REXML::XPath.match(disks_xml.first, './alias[@name="ua-box-volume-0"]').first.nil?
env[:ui].warn(I18n.t('vagrant_libvirt.domain_xml.obsolete_method')) unless have_aliases
if have_aliases
- REXML::XPath.match(disks_xml,
+ disks_xml.each do |disk_xml|
+ REXML::XPath.match(disk_xml,
'./alias[contains(@name, "ua-box-volume-")]').each_with_index do |alias_xml, idx|
domain_volumes.push(volume_from_xml(alias_xml.parent, domain_name, idx))
end
+ end
else
# fallback to try and infer which boxes are box images, as they are listed first
# as soon as there is no match, can exit |
Thanks, very useful to see real life example from experts 👍 Since I have proposed slightly different fix in vagrant-libvirt/vagrant-libvirt#1861 would you mind to check it and elaborate there what are the pros and cons of your vs my proposal? |
The existing logic remains unchanged, so there is no impact on performance.
Nothing. vagrant-libvirt/vagrant-libvirt#1861
XPath specifies elements starting from the top of the XML document, making the processing easier to understand.
Each time, since we specify the XPath from the top of the XML, the number of processing steps increases. |
That was also my worry, right. Thanks for confirming. On top of that, I think your proposal uncovers flaw in the logic which was earlier elaborated in the original description: "XPath.match only traverse the first element of the array for some selectors.", because I does not seem to me it was intention to look only at first "disk". But I am not an author of the code, so I'll leave the decision to the upstream. Thx for the suggestion and elaborating 👍 I appreciate that. |
XPath.match,XPath.first,XPath.each,XPathParser#parseandXPathParser#matchaccepted nodeset as element.This pull request changes the first parameter of these method to be an element instead of nodeset.
Passing nodeset will be deprecated.
Background
#249 will introduce a temporary cache.
But the signature
XPathParser#match(path, nodeset)does not guarantee that all nodes in the nodeset has the same root document.So cache does not work in the code below. It's still slow.
The interface is holding our back, so I propose to drop accepting array as element.
This change is a backward incompatibility, but it just drops undocumented feature. I think only the test code was unintentionally using this feature.
XPath.match with array
XPath.match only traverse the first element of the array for some selectors.
It indicates that
XPath.matchis not designed to search inside multiple nodes/documents.