initial commit
This commit is contained in:
182
deps/inkex/tester/inx.py
vendored
Normal file
182
deps/inkex/tester/inx.py
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python3
|
||||
# coding=utf-8
|
||||
"""
|
||||
Test elements extra logic from svg xml lxml custom classes.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from importlib import resources
|
||||
|
||||
from lxml import etree, isoschematron
|
||||
|
||||
from ..utils import PY3
|
||||
from ..inx import InxFile
|
||||
|
||||
INTERNAL_ARGS = ("help", "output", "id", "selected-nodes")
|
||||
ARG_TYPES = {
|
||||
"Boolean": "bool",
|
||||
"Color": "color",
|
||||
"str": "string",
|
||||
"int": "int",
|
||||
"float": "float",
|
||||
}
|
||||
|
||||
|
||||
class InxMixin:
|
||||
"""Tools for Testing INX files, use as a mixin class:
|
||||
|
||||
class MyTests(InxMixin, TestCase):
|
||||
def test_inx_file(self):
|
||||
self.assertInxIsGood("some_inx_file.inx")
|
||||
"""
|
||||
|
||||
def assertInxIsGood(self, inx_file): # pylint: disable=invalid-name
|
||||
"""Test the inx file for consistancy and correctness"""
|
||||
self.assertTrue(PY3, "INX files can only be tested in python3")
|
||||
|
||||
inx = InxFile(inx_file)
|
||||
if "help" in inx.ident or inx.script.get("interpreter", None) != "python":
|
||||
return
|
||||
cls = inx.extension_class
|
||||
# Check class can be matched in python file
|
||||
self.assertTrue(cls, f"Can not find class for {inx.filename}")
|
||||
# Check name is reasonable for the class
|
||||
if not cls.multi_inx:
|
||||
self.assertEqual(
|
||||
cls.__name__,
|
||||
inx.slug,
|
||||
f"Name of extension class {cls.__module__}.{cls.__name__} "
|
||||
f"is different from ident {inx.slug}",
|
||||
)
|
||||
self.assertParams(inx, cls)
|
||||
|
||||
def assertParams(self, inx, cls): # pylint: disable=invalid-name
|
||||
"""Confirm the params in the inx match the python script
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
Also checks that the default values are identical"""
|
||||
params = {param.name: self.parse_param(param) for param in inx.params}
|
||||
args = dict(self.introspect_arg_parser(cls().arg_parser))
|
||||
mismatch_a = list(set(params) ^ set(args) & set(params))
|
||||
mismatch_b = list(set(args) ^ set(params) & set(args))
|
||||
self.assertFalse(
|
||||
mismatch_a, f"{inx.filename}: Inx params missing from arg parser"
|
||||
)
|
||||
self.assertFalse(
|
||||
mismatch_b, f"{inx.filename}: Script args missing from inx xml"
|
||||
)
|
||||
|
||||
for param in args:
|
||||
if params[param]["type"] and args[param]["type"]:
|
||||
self.assertEqual(
|
||||
params[param]["type"],
|
||||
args[param]["type"],
|
||||
f"Type is not the same for {inx.filename}:param:{param}",
|
||||
)
|
||||
inxdefault = params[param]["default"]
|
||||
argsdefault = args[param]["default"]
|
||||
if inxdefault and argsdefault:
|
||||
# for booleans, the inx is lowercase and the param is uppercase
|
||||
if params[param]["type"] == "bool":
|
||||
argsdefault = str(argsdefault).lower()
|
||||
elif params[param]["type"] not in ["string", None, "color"] or args[
|
||||
param
|
||||
]["type"] in ["int", "float"]:
|
||||
# try to parse the inx value to compare numbers to numbers
|
||||
inxdefault = float(inxdefault)
|
||||
if args[param]["type"] == "color" or callable(args[param]["default"]):
|
||||
# skip color, method types
|
||||
continue
|
||||
self.assertEqual(
|
||||
argsdefault,
|
||||
inxdefault,
|
||||
f"Default value is not the same for {inx.filename}:param:{param}",
|
||||
)
|
||||
inxchoices = params[param]["choices"]
|
||||
argschoices = args[param]["choices"]
|
||||
if argschoices is not None and len(argschoices) > 0:
|
||||
assert set(inxchoices).issubset(argschoices), (
|
||||
f"params don't match: inx={inxchoices}, py={argschoices}"
|
||||
)
|
||||
|
||||
def introspect_arg_parser(self, arg_parser):
|
||||
"""Pull apart the arg parser to find out what we have in it"""
|
||||
for action in arg_parser._optionals._actions: # pylint: disable=protected-access
|
||||
for opt in action.option_strings:
|
||||
# Ignore params internal to inkscape (thus not in the inx)
|
||||
if opt.startswith("--") and opt[2:] not in INTERNAL_ARGS:
|
||||
yield (opt[2:], self.introspect_action(action))
|
||||
|
||||
@staticmethod
|
||||
def introspect_action(action):
|
||||
"""Pull apart a single action to get at the juicy insides"""
|
||||
return {
|
||||
"type": ARG_TYPES.get((action.type or str).__name__, "string"),
|
||||
"default": action.default,
|
||||
"choices": action.choices,
|
||||
"help": action.help,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def parse_param(param):
|
||||
"""Pull apart the param element in the inx file"""
|
||||
if param.param_type in ("optiongroup", "notebook"):
|
||||
options = param.options
|
||||
return {
|
||||
"type": None,
|
||||
"choices": options,
|
||||
"default": options and options[0] or None,
|
||||
}
|
||||
param_type = param.param_type
|
||||
if param.param_type in ("path",):
|
||||
param_type = "string"
|
||||
return {
|
||||
"type": param_type,
|
||||
"default": param.text,
|
||||
"choices": None,
|
||||
}
|
||||
|
||||
def assertInxSchemaValid(self, inx_file): # pylint: disable=invalid-name
|
||||
"""Validate inx file schema."""
|
||||
self.assertTrue(INX_SCHEMAS, "no schema files found")
|
||||
with open(inx_file, "rb") as fp:
|
||||
inx_doc = etree.parse(fp)
|
||||
|
||||
for schema_name, schema in INX_SCHEMAS.items():
|
||||
with self.subTest(schema_file=schema_name):
|
||||
schema.assert_(inx_doc)
|
||||
|
||||
|
||||
def _load_inx_schemas():
|
||||
_SCHEMA_CLASSES = {
|
||||
".rng": etree.RelaxNG,
|
||||
".schema": isoschematron.Schematron,
|
||||
}
|
||||
|
||||
if sys.version_info > (3, 9):
|
||||
|
||||
def _contents(pkg):
|
||||
return [path.name for path in resources.files(pkg).iterdir()]
|
||||
else:
|
||||
_contents = resources.contents
|
||||
|
||||
for name in _contents(__package__):
|
||||
_, ext = os.path.splitext(name)
|
||||
schema_class = _SCHEMA_CLASSES.get(ext)
|
||||
if schema_class is None:
|
||||
continue
|
||||
|
||||
if sys.version_info > (3, 9):
|
||||
|
||||
def _open_binary(pkg, res):
|
||||
return resources.files(pkg).joinpath(res).open("rb")
|
||||
else:
|
||||
_open_binary = resources.open_binary
|
||||
|
||||
with _open_binary(__package__, name) as fp:
|
||||
schema_doc = etree.parse(fp)
|
||||
yield name, schema_class(schema_doc)
|
||||
|
||||
|
||||
INX_SCHEMAS = dict(_load_inx_schemas())
|
||||
Reference in New Issue
Block a user