Skip to content
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

Update dependencies and fix XPath selection on document schema #47

Merged
merged 3 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Display Python version
Expand All @@ -29,7 +29,6 @@ jobs:
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
- name: Lint with flake8
if: ${{ matrix.python-version != '3.7' }}
run: |
flake8 qeschema --max-line-length=100 --statistics
- name: Run tests
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
# -- Project information -----------------------------------------------------

project = 'qeschema'
copyright = '2015-2023, Quantum Espresso Foundation and SISSA'
copyright = '2015-2024, Quantum Espresso Foundation and SISSA'
author = 'Davide Brunato, Pietro Delugas'

# The full version, including alpha/beta/rc tags
release = '1.5.1'
release = '1.5.2'


# -- General configuration ---------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion qeschema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from .exceptions import QESchemaError, XmlDocumentError
from .utils import set_logger

__version__ = '1.5.1'
__version__ = '1.5.2'

__all__ = [
'XmlDocument', 'QeDocument', 'PwDocument', 'PhononDocument', 'NebDocument',
Expand Down
2 changes: 1 addition & 1 deletion qeschema/cards.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def labnl(nnum, lnum):

label = labnl(value['n2_number'], value['l2_number'])
if 'two' in background:
label = f"{label}-{labnl(value['n3_number'],value['l3_number'])}"
label = f"{label}-{labnl(value['n3_number'], value['l3_number'])}"
lines.append(f"U {specie}-{label} {value['$']:8.3f}")
elif tag == 'V':
speclab1 = f"{value['@specie1']}-{value['@label1']}"
Expand Down
53 changes: 30 additions & 23 deletions qeschema/documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import xmlschema
from xmlschema import etree_tostring


try:
import yaml
except ImportError:
Expand All @@ -27,7 +26,7 @@
NebInputConverter, TdInputConverter, TdSpectrumInputConverter, \
XSpectraInputConverter, EPWInputConverter
from .exceptions import XmlDocumentError
from .utils import etree_iter_path
from .utils import etree_iter_path, get_target_prefix

logger = logging.getLogger('qeschema')

Expand All @@ -48,7 +47,7 @@ def removeprefix(s, prefix):
return s[len(prefix):] if s.startswith(prefix) else s


class XmlDocument(object):
class XmlDocument:
"""
Base class for a generic XML document based on an XSD schema. The schema
associated is used for checking types, validation of the XML data and for
Expand Down Expand Up @@ -118,11 +117,15 @@ def __init__(self, source=None, schema=None):
def namespaces(self):
"""
XML data namespaces map, a dictionary that maps prefixes to URI. An empty
dictionary if the XML data file is not loaded or it doesn't contain any
dictionary if the XML data file is not loaded or if it doesn't contain any
namespace declaration.
"""
return {k: v for k, v in self._namespaces.items()}

@property
def default_namespace(self):
return self._namespaces.get('', '')

@classmethod
def fetch_schema(cls, filename):
filename = filename.strip()
Expand Down Expand Up @@ -442,20 +445,25 @@ def __init__(self, source=None, schema=None, input_builder=None):
self.input_builder = self.DEFAULT_INPUT_BUILDER
elif not isinstance(input_builder, type) or \
not issubclass(input_builder, RawInputConverter):
msg = "3rd argument must be a {!r} subclass"
raise XmlDocumentError(msg.format(RawInputConverter))
raise XmlDocumentError(f"3rd argument must be a {RawInputConverter!r} subclass")
else:
self.input_builder = input_builder

self.default_namespace = self.schema.target_namespace
qe_prefixes = ['qes', 'neb', 'qes_ph', 'qes_lr', 'qes_spectrum',
'qes_xspectra', 'epw']
qe_nslist = list(map(self.schema.namespaces.get, qe_prefixes))
if self.default_namespace not in qe_nslist:
self._xpath_namespaces = {k: v for k, v in self.schema.namespaces.items() if k}

prefix = get_target_prefix(self.schema.namespaces, self.schema.target_namespace)
if prefix not in ('qes', 'neb', 'qes_ph', 'qes_lr',
'qes_spectrum', 'qes_xspectra', 'epw'):
raise NotImplementedError(
"Converter not implemented for this schema {}".format(self.default_namespace)
f"Converter not implemented for namespace {self.schema.target_namespace!r}"
)

def _xsd_find(self, path, xsd_element=None):
if xsd_element is None:
return self.schema.find(path, self._xpath_namespaces)
else:
return xsd_element.find(path, self._xpath_namespaces)

@property
def input_path(self):
"""The path to XML input section."""
Expand Down Expand Up @@ -491,15 +499,15 @@ def get_fortran_input(self, use_defaults=True):
raise XmlDocumentError("Missing input {!r} in XML data!".format(input_path))

for schema_root in self.schema.elements.values():
if schema_root.find(input_path):
if self._xsd_find(input_path, schema_root):
break
else:
raise XmlDocumentError("Missing input element in XSD schema!")

# Extract values from input's subtree of the XML document
for elem, path in etree_iter_path(input_root, path=input_path):
rel_path = path.replace(input_path, '.')
xsd_element = schema_root.find(path)
xsd_element = self._xsd_find(path, schema_root)
if xsd_element is None:
logger.error("%r doesn't match any element!", path)
continue
Expand Down Expand Up @@ -546,7 +554,7 @@ def get_atomic_positions(self):
path = './/output//atomic_positions'
elem = self.find(path)
if elem is not None:
atomic_positions = self.schema.find(path).decode(elem)
atomic_positions = self._xsd_find(path).decode(elem)
atoms = atomic_positions.get('atom')
if not isinstance(atoms, list):
atoms = [atoms]
Expand All @@ -564,7 +572,7 @@ def get_cell_parameters(self):
path = './/output//cell'
elem = self.find(path)
if elem is not None:
cell = self.schema.find(path).decode(elem)
cell = self._xsd_find(path).decode(elem)
return [cell['a1'], cell['a2'], cell['a3']]

@requires_xml_data
Expand All @@ -577,7 +585,7 @@ def get_stress(self):
path = './/output//stress'
elem = self.find(path)
if elem is not None:
stress = self.schema.find(path).decode(elem)
stress = self._xsd_find(path).decode(elem)
try:
stress = stress['$']
except TypeError:
Expand All @@ -595,10 +603,9 @@ def get_forces(self):
path = './/output/forces'
elem = self.find(path)
if elem is not None:
forces = self.schema.find(path).decode(elem)
forces = self._xsd_find(path).decode(elem)
path = './/output//atomic_positions'
breakpoint()
atomic_positions = self.schema.find(path).decode(self.find(path))
atomic_positions = self._xsd_find(path).decode(self.find(path))
atoms = atomic_positions.get('atom', [])
if not isinstance(atoms, list):
atoms = [atoms]
Expand All @@ -616,7 +623,7 @@ def get_k_points(self):
:return: nested list with k_points
"""
path = './/output//ks_energies/k_point'
return [self.schema.find(path).decode(e)['$'] for e in self.findall(path)]
return [self._xsd_find(path).decode(e)['$'] for e in self.findall(path)]

@requires_xml_data
def get_ks_eigenvalues(self):
Expand All @@ -628,7 +635,7 @@ def get_ks_eigenvalues(self):
path = './/output//ks_energies/eigenvalues'
eigenvalues = []
for e in self.findall(path):
obj = self.schema.find(path).decode(e)
obj = self._xsd_find(path).decode(e)
if isinstance(obj, dict):
eigenvalues.append(obj['$']) # pragma: no cover
else:
Expand All @@ -644,7 +651,7 @@ def get_total_energy(self):
:return: total energy in Hartree Units
"""
path = './/output//etot'
return self.schema.find(path).decode(self.find(path))
return self._xsd_find(path).decode(self.find(path))


class PhononDocument(QeDocument):
Expand Down
Loading
Loading