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