diff --git a/docs/changelog.rst b/docs/changelog.rst
index 3f030c1..fcbe99c 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -3,7 +3,7 @@ Changelog
This document records all notable changes to `Xul `_.
-`Unreleased `_ (2025-01-11)
+`Unreleased `_ (2025-01-12)
--------------------------------------------------------------------------------------
* Drop support for Python <= 3.8.
* Code checks: ruff, black, isort, mypy.
@@ -11,8 +11,9 @@ This document records all notable changes to `Xul `
* Test script for local testing with Docker Compose.
* GitHub Action: code checks.
* Output formatting (f-strings).
-* :doc:`xp `: fix boolean result (Python >= 3.12)
-* :doc:`xp `: fix string result representation (Python 3)
+* :doc:`xp `: fix boolean result (Python >= 3.12).
+* :doc:`xp `: fix string result representation (Python 3).
+* :doc:`xp `: improved printing of namespaces.
`2.5.1 `_ (2024-12-26)
----------------------------------------------------------------------------------
diff --git a/docs/xp.rst b/docs/xp.rst
index 685d2d6..de56d4f 100644
--- a/docs/xp.rst
+++ b/docs/xp.rst
@@ -40,33 +40,29 @@ Options
$ xp --help
- usage: xp [-h] [-V] [-e] [-d DEFAULT_NS_PREFIX] [-r] [-p] [-m] [-f | -F] [-q]
- xpath_expr [xml_source [xml_source ...]]
+ usage: xp [-h] [-V] [-e] [-d DEFAULT_NS_PREFIX] [-q] [-p] [-r] [-f | -F] [-m] xpath_expr [xml_source ...]
Select nodes in an XML source with an XPath expression.
positional arguments:
- xpath_expr XPath expression
- xml_source XML source (file, , http://...)
-
- optional arguments:
- -h, --help show this help message and exit
- -V, --version show program's version number and exit
- -e, --exslt add EXSLT XML namespace prefixes
- -d DEFAULT_NS_PREFIX, --default-prefix DEFAULT_NS_PREFIX
- set the prefix for the default namespace in XPath
- [default: 'd']
- -r, --result-xpath print the XPath expression of the result element (or
- its parent)
- -p, --pretty-element pretty print the result element
- -m, --method use ElementTree.xpath method instead of XPath class
- -f, -l, --files-with-hits
- only the names of files with a non-false and non-NaN
- result are written to standard output
- -F, -L, --files-without-hits
- only the names of files with a false or NaN result, or
- without any results are written to standard output
- -q, --quiet don't print the XML namespace list
+ xpath_expr XPath expression
+ xml_source XML source (file, , http://...)
+
+ options:
+ -h, --help show this help message and exit
+ -V, --version show program's version number and exit
+ -e, --exslt add EXSLT XML namespaces
+ -d DEFAULT_NS_PREFIX, --default-prefix DEFAULT_NS_PREFIX
+ set the prefix for the default namespace in XPath [default: 'd']
+ -q, --quiet don't print XML source namespaces
+ -p, --pretty-element pretty print the result element
+ -r, --result-xpath print the XPath expression of the result element (or its parent)
+ -f, -l, --files-with-hits
+ only the names of files with a non-false and non-NaN result are written to standard output
+ -F, -L, --files-without-hits
+ only the names of files with a false or NaN result, or without any results are written to
+ standard output
+ -m, --method use ElementTree.xpath method instead of XPath class
.. index::
diff --git a/src/xul/cmd/xp.py b/src/xul/cmd/xp.py
index e76b6a7..dd8d0b3 100644
--- a/src/xul/cmd/xp.py
+++ b/src/xul/cmd/xp.py
@@ -30,7 +30,7 @@ def parse_cl() -> argparse.Namespace:
action="store_true",
default=False,
dest="exslt",
- help="add EXSLT XML namespace prefixes",
+ help="add EXSLT XML namespaces",
)
parser.add_argument(
"-d",
@@ -41,12 +41,12 @@ def parse_cl() -> argparse.Namespace:
help="set the prefix for the default namespace in XPath [default: '%(default)s']",
)
parser.add_argument(
- "-r",
- "--result-xpath",
- action="store_true",
- default=False,
- dest="result_xpath",
- help="print the XPath expression of the result element (or its parent)",
+ "-q",
+ "--quiet",
+ action="store_false",
+ default=True,
+ dest="verbose",
+ help="don't print XML source namespaces",
)
parser.add_argument(
"-p",
@@ -57,12 +57,12 @@ def parse_cl() -> argparse.Namespace:
help="pretty print the result element",
)
parser.add_argument(
- "-m",
- "--method",
+ "-r",
+ "--result-xpath",
action="store_true",
default=False,
- dest="lxml_method",
- help="use ElementTree.xpath method instead of XPath class",
+ dest="result_xpath",
+ help="print the XPath expression of the result element (or its parent)",
)
file_group = parser.add_mutually_exclusive_group(required=False)
file_group.add_argument(
@@ -86,12 +86,12 @@ def parse_cl() -> argparse.Namespace:
+ "or without any results are written to standard output",
)
parser.add_argument(
- "-q",
- "--quiet",
- action="store_false",
- default=True,
- dest="verbose",
- help="don't print the XML namespace list",
+ "-m",
+ "--method",
+ action="store_true",
+ default=False,
+ dest="lxml_method",
+ help="use ElementTree.xpath method instead of XPath class",
)
return parser.parse_args()
@@ -151,18 +151,20 @@ def xp_prepare(
def print_xmlns(ns_map: dict[str, str], root: etree._Element) -> None:
- """Print XML namespaces.
+ """Print XML source namespaces (prefix: namespace URI).
:param ns_map: XML namespaces (xmlns) 'prefix': 'URI' dict
:param root: root (document) element
"""
- if None in root.nsmap:
- print(f"Default XML namespace URI: {root.nsmap[None]}")
if ns_map:
- # Print all XML namespaces -- prefix: namespace URI.
- print("XML namespaces:")
+ print("XML namespaces (prefix: URI):")
for key in ns_map:
- print(f"{key:>9}: {ns_map[key]}")
+ if None in root.nsmap and ns_map[key] == root.nsmap[None]:
+ print(f"{key:>9}: {ns_map[key]} (default namespace)")
+ else:
+ print(f"{key:>9}: {ns_map[key]}")
+ elif None in root.nsmap:
+ print(f"Default XML namespace URI: {root.nsmap[None]}")
def element_repr(node) -> str:
@@ -315,7 +317,10 @@ def print_result_list(result_list, el_tree: etree._ElementTree, args: argparse.N
elif isinstance(node, tuple):
prefix, uri = node
# No line number.
- print(f"prefix: {str(prefix):<8} URI: {uri}")
+ if prefix is None:
+ print(f"prefix: {args.default_ns_prefix:<8} URI: {uri}")
+ else:
+ print(f"prefix: {prefix:<8} URI: {uri}")
# ?
else:
@@ -355,14 +360,11 @@ def print_result_header(source_name: str, xp_result) -> None:
print(f"{xp_r_len} results.")
-def print_xp_result(
- xp_result, el_tree: etree._ElementTree, ns_map: dict[str, str], args: argparse.Namespace
-) -> None:
+def print_xp_result(xp_result, el_tree: etree._ElementTree, args: argparse.Namespace) -> None:
"""Print XPath results.
:param xp_result: XPath result
:param el_tree: lxml ElementTree
- :param ns_map: XML namespaces (xmlns) 'prefix': 'URI' dict
:param args: command-line arguments
Prints:
@@ -372,9 +374,6 @@ def print_xp_result(
XPath return values:
https://lxml.de/xpathxslt.html#xpath-return-values
"""
- if args.verbose:
- print_xmlns(ns_map, el_tree.getroot())
-
# STRING - string - smart string | Namespace URI.
if isinstance(xp_result, etree._ElementUnicodeResult):
print_smart_string(xp_result, el_tree, args)
@@ -453,10 +452,13 @@ def xpath_on_xml(
print(source_name)
return True
+ # XML namespaces.
+ if args.verbose:
+ print_xmlns(ns_map, el_tree.getroot())
# Result header.
print_result_header(source_name, xp_result)
# XPath results.
- print_xp_result(xp_result, el_tree, ns_map, args)
+ print_xp_result(xp_result, el_tree, args)
return True