Source code for cioxml2.lib.cioset

"""Cioset management."""

from __future__ import annotations
from os.path import normpath, join, dirname
from re import sub as re_sub

from lxml.etree import Element, ElementTree, fromstring, tostring

from chrysalio.lib.utils import normalize_spaces, make_id
from cioprocessor.relaxng import RELAXNG_CIOSET
from ciowarehouse2.lib.ciopath import CioPath
from .utils import special_unprotect


# =============================================================================
[docs] def cioset_update(original: ElementTree, values: dict) -> ElementTree: """Update original Cioset XML according to values. :type original: lxml.etree._ElementTree :param original: Initial content of the file as a XML DOM object. :param dict values: Values of the form. :rtype: lxml.etree._ElementTree """ # Title namespace = RELAXNG_CIOSET['namespace'] value = normalize_spaces(values['cioset_title'].replace(' ', '‧')) title_elt = original.find(f"{{{namespace}}}title") if not value and title_elt is not None: original.getroot().remove(title_elt) elif value: if title_elt is None: title_elt = Element('title') original.getroot().insert(0, title_elt) title_elt.text = value # Division namespaces = {'set': namespace} for elt in original.xpath('//set:division', namespaces=namespaces): num = int( elt.xpath( 'count(preceding::set:division|ancestor::set:division)', namespaces=namespaces)) _update_title(values, f'division{num}', elt) # Resources, copies and values for tag in ('resources', 'copies', 'values'): for num, elt in enumerate(original.xpath( # yapf: disable f'//set:{tag}', namespaces=namespaces)): _update_title(values, f'{tag}{num}', elt) value = make_id(values.get(f'cioset_{tag}{num}_for', ''), 'class') if value: elt.set('for', value) else: elt.attrib.pop('for', None) # Defines and values for tag in ('define', 'value'): for num, elt in enumerate(original.xpath( # yapf: disable f'//set:{tag}', namespaces=namespaces)): elt.text = value2string(elt, values.get(f'cioset_{tag}{num}')) return ElementTree( fromstring( re_sub( r'<file>\s+<path>([^<]+)</path>\s+</file>', r'<file><path>\1</path></file>', special_unprotect( tostring(original, pretty_print=True, encoding='utf-8').decode('utf8')))))
# =============================================================================
[docs] def value2string(elt: Element, value: str | None) -> str: """Convert a string value according to type ``type_``. :type elt: lxml.etree.Element :param elt: DOM element owner of the value. :param str value: Type of the value. :rtype: str """ type_ = elt.get('type') value = value.replace('\r\n', '\n') if value is not None else '' if type_ == 'boolean': return 'true' \ if value.strip() not in ('', 'false', 'False', '0') else 'false' if type_ == 'text': indent = ' ' * 2 * (int(elt.xpath('count(ancestor::*)')) + 1) lines = value.split('\n') for num, line in enumerate(lines): line = normalize_spaces(line.replace(' ', '‧')) lines[num] = f'{indent}{line}' return '\n{0}\n{1}'.format( '\n'.join(lines).rstrip(), indent[2:]) \ if ''.join(lines).strip() else '' return value.strip()
# ============================================================================= def _update_title(values: dict, name: str, root_elt: Element): """Update or remove a title. :param dict values: Values of the form. :param str name: Name of the value representing the title. :type root_elt: lxml.etree.Element :param root_elt: DOM element owner of the title. """ value = normalize_spaces( values.get('cioset_{0}'.format(name), '').replace(' ', '‧')) title_elt = root_elt.find( '{{{0}}}title'.format(RELAXNG_CIOSET['namespace'])) if not value and title_elt is not None: root_elt.remove(title_elt) elif value: if title_elt is None: title_elt = Element('title') root_elt.insert(0, title_elt) title_elt.text = value # =============================================================================
[docs] def root_ciopath(ciopath: CioPath, elt: Element) -> CioPath: """Return the `CioPath` the root (division… the XML element ``elt``. :type ciopath: ciowarehouse2.lib.ciopath.CioPath :param ciopath: `CioPath` the `Cioset` file. :type elt: lxml.etree.Element :param elt: Cioset XML element for the current file or division. :rtype: ciowarehouse2.lib.ciopath.CioPath """ root = '.' namespace = RELAXNG_CIOSET['namespace'] root_elt = elt.xpath( 'ancestor-or-self::*[set:root][1]', namespaces={'set': namespace}) if root_elt: root = root_elt[0].findtext(f'{{{namespace}}}root').strip() if ':' in root: root = root[1:] if root[0] == '/' else root # Deprecated return CioPath.from_str(root, True) return CioPath( ciopath.wid, normpath(join(dirname(ciopath.path), root)), True)
# =============================================================================
[docs] def file_ciopath(ciopath: CioPath, file_elt: Element) -> CioPath: """Return the `CioPath` of the file pointed by ``file_elt``, possibly according to <root> tags. :type ciopath: ciowarehouse2.lib.ciopath.CioPath :param ciopath: `CioPath` of the cioset. :type file_elt: lxml.etree.Element :param file_elt: Cioset XML file element for the current file. :rtype: ciowarehouse2.lib.ciopath.CioPath """ # Get the file path namespace = RELAXNG_CIOSET['namespace'] path = file_elt.find(f'{{{namespace}}}path') path = file_elt.text.strip() if path is None else path.text.strip() if ':' in path: return CioPath.from_str(path) return root_ciopath(ciopath, file_elt).join(path)