From 1c19d3d13861a725ef49955c24cc12dd2503f14e Mon Sep 17 00:00:00 2001 From: Stephen L Arnold Date: Sat, 18 Jun 2022 16:03:38 -0700 Subject: [PATCH 01/10] chg: dev: flesh out package layout, update readme/project files * main module/script currently one direction only * update tox file for path changes * generate munch type stubs, apply isort fixes Signed-off-by: Stephen L Arnold --- .gitignore | 3 + README.rst | 5 +- pyproject.toml | 2 +- requirements-dev.txt => requirements.txt | 1 + scripts/genyaml.py | 26 +++- src/ymltoxml/data/ymltoxml.yaml | 10 ++ src/ymltoxml/ymltoxml.py | 148 +++++++++++++++++++++++ tox.ini | 28 +++-- 8 files changed, 204 insertions(+), 19 deletions(-) rename requirements-dev.txt => requirements.txt (82%) create mode 100644 src/ymltoxml/data/ymltoxml.yaml create mode 100644 src/ymltoxml/ymltoxml.py diff --git a/.gitignore b/.gitignore index b900fa0..a50b987 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ __pycache__/ # generated test files in.* out.* +munch/ +.*.yaml +*.xml # C extensions *.so diff --git a/README.rst b/README.rst index b871a04..445c6a3 100644 --- a/README.rst +++ b/README.rst @@ -17,6 +17,10 @@ Both mavlink and pymavlink require a (host) GCC toolchain for full builds, however, the basic workflow to generate the library headers requires only Git, Python, and Tox. +.. _Tox: https://github.com/tox-dev/tox +.. _XML: https://en.wikipedia.org/wiki/Extensible_Markup_Language +.. _YAML: https://en.wikipedia.org/wiki/YAML + In-repo workflow with Tox ------------------------- @@ -28,7 +32,6 @@ package manager, eg:: $ sudo apt-get update $ sudo apt-get install tox -.. _Tox: https://github.com/tox-dev/tox After cloning the repository, you can run the repo checks with the ``tox`` command. It will build a virtual python environment with diff --git a/pyproject.toml b/pyproject.toml index 1c501ae..4a7424c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ fail_under = 50 show_missing = true [tool.black] -line-length = 85 +line-length = 90 [tool.pycln] all = true diff --git a/requirements-dev.txt b/requirements.txt similarity index 82% rename from requirements-dev.txt rename to requirements.txt index f38c421..1629e3e 100644 --- a/requirements-dev.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ xmltodict +munch ruamel.yaml PyYAML diff --git a/scripts/genyaml.py b/scripts/genyaml.py index 83ae231..3ffe607 100644 --- a/scripts/genyaml.py +++ b/scripts/genyaml.py @@ -1,14 +1,32 @@ from pathlib import Path -import ruamel.yaml import xmltodict +from ruamel.yaml import YAML +from ruamel.yaml.compat import StringIO + + +class StrYAML(YAML): + """ + New API likes dumping straight to file/stdout, so we subclass and + create 'inefficient' custom string dumper. + """ + def dump(self, data, stream=None, **kw): + inefficient = False + if stream is None: + inefficient = True + stream = StringIO() + YAML.dump(self, data, stream, **kw) + if inefficient: + return stream.getvalue() + with open('in.xml', 'r+b') as xfile: payload = xmltodict.parse(xfile, process_comments=True) -yaml = ruamel.yaml.YAML() +yaml = StrYAML() yaml.indent(mapping=2, sequence=4, offset=2) yaml.preserve_quotes = True # type: ignore -pfile = Path('out.yaml') -yaml.dump(payload, pfile) +res = yaml.dump(payload) + +Path('out.yaml').write_text(res) diff --git a/src/ymltoxml/data/ymltoxml.yaml b/src/ymltoxml/data/ymltoxml.yaml new file mode 100644 index 0000000..40e24c5 --- /dev/null +++ b/src/ymltoxml/data/ymltoxml.yaml @@ -0,0 +1,10 @@ +file_encoding: 'utf-8' +process_comments: True +yml_opts: + - mapping: 2 + - sequence: 4 + - offset: 2 +xml_opts: + - short_empty_elements: False + - pretty: True + - indent: ' ' diff --git a/src/ymltoxml/ymltoxml.py b/src/ymltoxml/ymltoxml.py new file mode 100644 index 0000000..6a24403 --- /dev/null +++ b/src/ymltoxml/ymltoxml.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Transform mavlink-style xml files to/from xml and yaml. Note yaml format uses +custom markup for attributes and comments. See xmltodict docs for details. +""" +import os +import sys +from contextlib import contextmanager +from pathlib import Path + +import xmltodict +import yaml as yaml_loader +from ruamel.yaml import YAML +from ruamel.yaml.compat import StringIO + +from munch import Munch + +DEBUG = False + + +class StrYAML(YAML): + """ + New API likes dumping straight to file/stdout, so we subclass and + create 'inefficient' custom string dumper. + """ + def dump(self, data, stream=None, **kw): + inefficient = False + if stream is None: + inefficient = True + stream = StringIO() + YAML.dump(self, data, stream, **kw) + if inefficient: + return stream.getvalue() + + +def load_config(file_encoding='utf-8'): + """ + Load yaml configuration file and munchify the data. If local file is + not found in current directory, the default will be loaded. + :return: Munch cfg obj + """ + cfgfile = Path('.ymltoxml.yaml') + if not cfgfile.exists(): + pkg_path = os.path.dirname(sys.modules['ymltoxml']) + cfgfile = Path(os.path.join(pkg_path, 'data', 'ymltoxml.yaml')) + + return Munch.fromYAML(cfgfile.read_text(encoding=file_encoding)) + + +@contextmanager +def open_handle(filename, mode='r', file_encoding='utf-8'): + """ + Context manager for input data using file or pipe. + """ + if filename == '-': + fhandle = sys.stdin.buffer + fname = fhandle.name + else: + # make sure filename str is a Path obj + filename = Path(filename) + fhandle = open(filename, mode, encoding=file_encoding) + fname = filename.name + try: + yield (fhandle, fname) + finally: + if filename != '-': + fhandle.close() + + +def restore_xml_comments(xmls): + """ + Turn comment elements back into xml comments. + :param xmls: xml (file) string output from ``unparse`` + :return xmls: processed xml string + """ + + for rep in (("<#comment>", "")): + xmls = xmls.replace(*rep) + return xmls + + +def transform_data(payload, direction='to_xml'): + """ + Produce output data from dict-ish object using ``direction``. + :param payload: dict: output from xmltodict or yaml loader. + :param direction: output format, either to_yml or to_xml + :return: xml + """ + res = '' + if 'xml' in direction: + xml = xmltodict.unparse(payload, + short_empty_elements=False, + pretty=True, + indent=' ') + + res = restore_xml_comments(xml) + + else: + yaml = StrYAML() + yaml.indent(mapping=2, + sequence=4, + offset=2) + + yaml.preserve_quotes = True # type: ignore + res = yaml.dump(payload) + + return res + + +if __name__ == '__main__': + VERSION = '0.0.0' + + cfg = load_config() + + REDIRECT = '>' + args = sys.argv[1:] + if REDIRECT in args: + redirect_idx = args.index(REDIRECT) + redirect_args = args[redirect_idx:] + del args[redirect_idx:] + + if args == ['--version']: + print(f'[ymltoxml {VERSION}]') + sys.exit(0) + if not args: + args = ['-'] + elif args == ['--verbose']: + DEBUG = True + elif args == ['--selftest']: + print(f'cfg from yaml: {cfg}') + sys.exit(0) + + # for filearg in args: + # with open_handle(filearg) as handle: + # if DEBUG: + # print("Processing data from {}".format(handle[1])) + # process_file(handle[0], handle[1], debug=DEBUG) + + with open_handle('in.yaml') as handle: + data_in = yaml_loader.load(handle[0], Loader=yaml_loader.Loader) + + data_out = transform_data(data_in) + + with open_handle('out.xml', 'w+') as handle: + handle[0].write(data_out) + handle[0].write('\n') diff --git a/tox.ini b/tox.ini index d69434e..2e4d8c1 100644 --- a/tox.ini +++ b/tox.ini @@ -26,6 +26,7 @@ deps = skip_install = true setenv = + PYTHONPATH = {toxinidir} MDEF = {envsitepackagesdir}/pymavlink/message_definitions/v1.0 passenv = @@ -54,15 +55,15 @@ whitelist_externals = deps = {[base]deps} pymavlink - xmltodict - PyYAML - ruamel.yaml + -r requirements.txt commands = - bash -c 'cp $MDEF/paparazzi.xml in.xml' + bash -c 'cp src/ymltoxml/data/ymltoxml.yaml .ymltoxml.yaml' + bash -c 'cp $MDEF/{posargs:paparazzi}.xml in.xml' python scripts/genyaml.py bash -c 'cp out.yaml in.yaml' - python scripts/genxml.py + python src/ymltoxml/ymltoxml.py + #python scripts/genxml.py bash -c 'diff -u in.xml out.xml || true' [testenv:clean] @@ -73,7 +74,7 @@ deps = pip>=21.1 commands = - bash -c 'rm -f in.* out.* paparazzi.xml' + bash -c 'rm -rf in.* out.* paparazzi.xml munch/' [testenv:lint] passenv = @@ -85,10 +86,10 @@ setenv = PYTHONPATH = {toxinidir} deps = {[base]deps} pylint - -r requirements-dev.txt + -r requirements.txt commands = - pylint --fail-under=7 scripts/ + pylint --fail-under=7 src/ymltoxml/ymltoxml.py [testenv:style] passenv = @@ -102,7 +103,7 @@ deps = flake8-bugbear commands = - flake8 scripts/ + flake8 scripts/ src/ [testenv:mypy] skip_install = true @@ -112,10 +113,11 @@ setenv = PYTHONPATH = {toxinidir} deps = {[base]deps} mypy - -r requirements-dev.txt + -r requirements.txt commands = - python -m mypy --follow-imports=normal --install-types --non-interactive scripts/ + stubgen -m munch --export-less -o {toxinidir} + python -m mypy --follow-imports=normal --install-types --non-interactive scripts/ src/ [testenv:isort] skip_install = true @@ -125,7 +127,7 @@ setenv = PYTHONPATH = {toxinidir} deps = {[base]deps} isort - -r requirements-dev.txt + -r requirements.txt commands = - python -m isort scripts/ + python -m isort scripts/ src/ From dfb1385c845b857cc38f99b95e3460b8ccf0fb0e Mon Sep 17 00:00:00 2001 From: Stephen L Arnold Date: Sun, 19 Jun 2022 10:23:14 -0700 Subject: [PATCH 02/10] chg: dev: refactor input handling, update tox and readme files Signed-off-by: Stephen L Arnold --- README.rst | 3 +- src/ymltoxml/ymltoxml.py | 85 ++++++++++++++++++---------------------- tox.ini | 7 ++-- 3 files changed, 43 insertions(+), 52 deletions(-) diff --git a/README.rst b/README.rst index 445c6a3..fbc6268 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,8 @@ Some scripts, and eventually a package, to convert real-world XML_ files to YAML_ and back again, preserving attributes and comments (with minor -corrections). +corrections). The default file encoding for both types is UTF-8 without +a Byte-Order Marker. Developer workflow ================== diff --git a/src/ymltoxml/ymltoxml.py b/src/ymltoxml/ymltoxml.py index 6a24403..23f6551 100644 --- a/src/ymltoxml/ymltoxml.py +++ b/src/ymltoxml/ymltoxml.py @@ -7,7 +7,6 @@ """ import os import sys -from contextlib import contextmanager from pathlib import Path import xmltodict @@ -17,8 +16,6 @@ from munch import Munch -DEBUG = False - class StrYAML(YAML): """ @@ -49,24 +46,25 @@ def load_config(file_encoding='utf-8'): return Munch.fromYAML(cfgfile.read_text(encoding=file_encoding)) -@contextmanager -def open_handle(filename, mode='r', file_encoding='utf-8'): +def get_input_type(filepath): """ - Context manager for input data using file or pipe. + Check filename extension, open and process by file type, return type + flag and data from appropriate loader. + :param filepath: filename as Path obj + :return: tuple with file data and destination type flag """ - if filename == '-': - fhandle = sys.stdin.buffer - fname = fhandle.name - else: - # make sure filename str is a Path obj - filename = Path(filename) - fhandle = open(filename, mode, encoding=file_encoding) - fname = filename.name - try: - yield (fhandle, fname) - finally: - if filename != '-': - fhandle.close() + to_xml = False + data_in = None + + if filepath.name.lower().endswith(('.yml', '.yaml')): + with filepath.open() as infile: + data_in = yaml_loader.load(infile, Loader=yaml_loader.Loader) + to_xml = True + elif filepath.name.lower().endswith('.xml'): + with filepath.open('r+b') as infile: + data_in = xmltodict.parse(infile, process_comments=True) + + return to_xml, data_in def restore_xml_comments(xmls): @@ -75,21 +73,20 @@ def restore_xml_comments(xmls): :param xmls: xml (file) string output from ``unparse`` :return xmls: processed xml string """ - for rep in (("<#comment>", "")): xmls = xmls.replace(*rep) return xmls -def transform_data(payload, direction='to_xml'): +def transform_data(payload, to_xml=True): """ Produce output data from dict-ish object using ``direction``. :param payload: dict: output from xmltodict or yaml loader. - :param direction: output format, either to_yml or to_xml - :return: xml + :param to_xml: output format, ie, XML if to_xml is True + :return result: output data in to_format """ res = '' - if 'xml' in direction: + if to_xml: xml = xmltodict.unparse(payload, short_empty_elements=False, pretty=True, @@ -111,38 +108,32 @@ def transform_data(payload, direction='to_xml'): if __name__ == '__main__': VERSION = '0.0.0' + DEBUG = False + + if os.getenv('VERBOSE') and os.getenv('VERBOSE') == '1': + DEBUG = True cfg = load_config() - REDIRECT = '>' args = sys.argv[1:] - if REDIRECT in args: - redirect_idx = args.index(REDIRECT) - redirect_args = args[redirect_idx:] - del args[redirect_idx:] if args == ['--version']: print(f'[ymltoxml {VERSION}]') sys.exit(0) - if not args: - args = ['-'] - elif args == ['--verbose']: - DEBUG = True elif args == ['--selftest']: print(f'cfg from yaml: {cfg}') sys.exit(0) - # for filearg in args: - # with open_handle(filearg) as handle: - # if DEBUG: - # print("Processing data from {}".format(handle[1])) - # process_file(handle[0], handle[1], debug=DEBUG) - - with open_handle('in.yaml') as handle: - data_in = yaml_loader.load(handle[0], Loader=yaml_loader.Loader) - - data_out = transform_data(data_in) - - with open_handle('out.xml', 'w+') as handle: - handle[0].write(data_out) - handle[0].write('\n') + for filearg in args: + fpath = Path(filearg) + if not fpath.exists(): + print(f'Input file {fpath} not found! Skipping...') + else: + if DEBUG: + print(f'Processing data from {filearg}') + from_yml, indata = get_input_type(fpath) + outdata = transform_data(indata, to_xml=from_yml) + if from_yml: + fpath.with_suffix('.xml').write_text(outdata + '\n', encoding='utf-8') + else: + fpath.with_suffix('.yaml').write_text(outdata, encoding='utf-8') diff --git a/tox.ini b/tox.ini index 2e4d8c1..ecc3195 100644 --- a/tox.ini +++ b/tox.ini @@ -60,10 +60,9 @@ deps = commands = bash -c 'cp src/ymltoxml/data/ymltoxml.yaml .ymltoxml.yaml' bash -c 'cp $MDEF/{posargs:paparazzi}.xml in.xml' - python scripts/genyaml.py - bash -c 'cp out.yaml in.yaml' - python src/ymltoxml/ymltoxml.py - #python scripts/genxml.py + python src/ymltoxml/ymltoxml.py in.xml + bash -c 'cp in.yaml out.yaml' + python src/ymltoxml/ymltoxml.py out.yaml bash -c 'diff -u in.xml out.xml || true' [testenv:clean] From d5880aa0dd866e1cdce62b393d6913649749285c Mon Sep 17 00:00:00 2001 From: Stephen L Arnold Date: Sun, 19 Jun 2022 10:40:29 -0700 Subject: [PATCH 03/10] fix: dev: flesh out gh OS matrix Signed-off-by: Stephen L Arnold --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index ecc3195..6bd3685 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py3{6,7,8,9,10}-dev +envlist = py3{6,7,8,9,10}-{linux,macos,windows}-dev skip_missing_interpreters = true isolated_build = true skipsdist = true From 1afe62bfdfc53d389074b92a86dd66189dbf305c Mon Sep 17 00:00:00 2001 From: Stephen L Arnold Date: Sun, 19 Jun 2022 11:16:55 -0700 Subject: [PATCH 04/10] chg: dev: install pymavlink using pip without mavnative, rename MDEF var Signed-off-by: Stephen L Arnold --- tox.ini | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tox.ini b/tox.ini index 6bd3685..81dca21 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py3{6,7,8,9,10}-{linux,macos,windows}-dev +envlist = py3{6,7,8,9,10}-{linux,macos,windows} skip_missing_interpreters = true isolated_build = true skipsdist = true @@ -20,6 +20,9 @@ PLATFORM = [base] deps = pip>=21.1 + wheel + build + twine versioningit [testenv] @@ -27,7 +30,8 @@ skip_install = true setenv = PYTHONPATH = {toxinidir} - MDEF = {envsitepackagesdir}/pymavlink/message_definitions/v1.0 + DISABLE_MAVNATIVE = True + MDEF_PATH = {envsitepackagesdir}/pymavlink/message_definitions/v1.0 passenv = CC @@ -36,7 +40,6 @@ passenv = AR NM RANLIB - PYTHON DISPLAY XAUTHORITY HOME @@ -44,29 +47,28 @@ passenv = USER XDG_* CI - PYTHON + OS PYTHONIOENCODING - GITHUB* PIP_DOWNLOAD_CACHE -whitelist_externals = +allowlist_externals = bash deps = {[base]deps} - pymavlink -r requirements.txt + pymavlink commands = bash -c 'cp src/ymltoxml/data/ymltoxml.yaml .ymltoxml.yaml' - bash -c 'cp $MDEF/{posargs:paparazzi}.xml in.xml' + bash -c 'cp $MDEF_PATH/{posargs:paparazzi}.xml in.xml' python src/ymltoxml/ymltoxml.py in.xml bash -c 'cp in.yaml out.yaml' python src/ymltoxml/ymltoxml.py out.yaml bash -c 'diff -u in.xml out.xml || true' [testenv:clean] -whitelist_externals = +allowlist_externals = bash deps = From 44394d31b234879ea8603af1f050d751d2b1424f Mon Sep 17 00:00:00 2001 From: Stephen L Arnold Date: Sun, 19 Jun 2022 22:28:53 -0700 Subject: [PATCH 05/10] chg: dev: integrate version, add packaging files, flesh out cfg options Signed-off-by: Stephen L Arnold --- .gitignore | 3 +- pyproject.toml | 2 +- setup.cfg | 83 +++++++++++++++++++++++++ setup.py | 6 ++ src/ymltoxml/__init__.py | 8 +++ src/ymltoxml/data/ymltoxml.yaml | 13 ++-- src/ymltoxml/ymltoxml.py | 105 ++++++++++++++++++++++---------- tox.ini | 55 +++++++++++++++-- 8 files changed, 230 insertions(+), 45 deletions(-) create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 src/ymltoxml/__init__.py diff --git a/.gitignore b/.gitignore index a50b987..1d901fa 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,8 @@ __pycache__/ *.py[cod] *$py.class -# generated test files +# generated/user files +src/ymltoxml/_version.py in.* out.* munch/ diff --git a/pyproject.toml b/pyproject.toml index 4a7424c..66552f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,4 +45,4 @@ dirty = "{version}+d{build_date:%Y%m%d}" distance-dirty = "{next_version}.dev{distance}" [tool.versioningit.write] -file = "_version.py" +file = "src/ymltoxml/_version.py" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..0d9c1ae --- /dev/null +++ b/setup.cfg @@ -0,0 +1,83 @@ +[metadata] +name = ymltoxml +version = attr: ymltoxml.__version__ +url = https://github.com/sarnold/ymltoxml +author = Stephen Arnold +author_email = nerdboy@gentoo.org +description = Asynchronous MAVLink connection library. +long_description = file: README.rst +long_description_content_type = text/rst; charset=UTF-8 +license = GPLv2 +license_files = LICENSE +classifiers = + Development Status :: 4 - Beta + License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) + Intended Audience :: Developers + Programming Language :: Python + Environment :: Console + Topic :: Software Development + Topic :: Software Development :: Testing + +[options] +python_requires = >= 3.6 +install_requires = + importlib-metadata; python_version < "3.8" + xmltodict + munch + ruamel.yaml + PyYAML + +packages = find_namespace: +package_dir = + =src + +#include_package_data = True + +[options.packages.find] +where = src + +exclude = + tests + +[options.package_data] +ymltoxml.data = + *.yaml + +[options.entry_points] +console_scripts = + ymltoxml = ymltoxml.ymltoxml:main + +# deps are included here mainly for local/venv installs using pip +# otherwise deps are handled via tox, ci config files or pkg managers +[options.extras_require] +test = + pytest +cov = + pytest-cov + coverage[toml] + coverage_python_version +all = + %(cov)s + %(test)s + +[check] +metadata = true +restructuredtext = true +strict = false + +[check-manifest] +ignore = + .gitattributes + .gitignore + .pre-commit-config.yaml + +[flake8] +exclude = + .git, + __pycache__, + build, + dist, + docs, + tests + +max-line-length = 90 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..bac24a4 --- /dev/null +++ b/setup.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python + +import setuptools + +if __name__ == "__main__": + setuptools.setup() diff --git a/src/ymltoxml/__init__.py b/src/ymltoxml/__init__.py new file mode 100644 index 0000000..b168ac6 --- /dev/null +++ b/src/ymltoxml/__init__.py @@ -0,0 +1,8 @@ +""" +Command-line tool for bidirectional transformation of YAML and XML files. +""" +from ._version import __version__ + +version = __version__ + +__all__ = ["__version__", "version"] diff --git a/src/ymltoxml/data/ymltoxml.yaml b/src/ymltoxml/data/ymltoxml.yaml index 40e24c5..a3512a1 100644 --- a/src/ymltoxml/data/ymltoxml.yaml +++ b/src/ymltoxml/data/ymltoxml.yaml @@ -1,10 +1,11 @@ -file_encoding: 'utf-8' -process_comments: True +prog_opts: + - file_encoding: 'utf-8' + process_comments: True yml_opts: - mapping: 2 - - sequence: 4 - - offset: 2 + sequence: 4 + offset: 2 xml_opts: - short_empty_elements: False - - pretty: True - - indent: ' ' + pretty: True + indent: ' ' diff --git a/src/ymltoxml/ymltoxml.py b/src/ymltoxml/ymltoxml.py index 23f6551..208eaa4 100644 --- a/src/ymltoxml/ymltoxml.py +++ b/src/ymltoxml/ymltoxml.py @@ -1,14 +1,23 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- + +# Copyright 2022 Stephen L Arnold +# +# This is free software, licensed under the LGPL-2.1 license +# available in the accompanying LICENSE file. """ -Transform mavlink-style xml files to/from xml and yaml. Note yaml format uses -custom markup for attributes and comments. See xmltodict docs for details. +Converts YAML to XML and XML to YAML. """ + import os import sys from pathlib import Path +try: + from importlib.metadata import version +except ImportError: + from importlib_metadata import version + import xmltodict import yaml as yaml_loader from ruamel.yaml import YAML @@ -17,19 +26,20 @@ from munch import Munch +class FileTypeError(Exception): + """Raise when the file extension is not '.xml', '.yml', or '.yaml'""" + __module__ = Exception.__module__ + + class StrYAML(YAML): """ New API likes dumping straight to file/stdout, so we subclass and create 'inefficient' custom string dumper. """ def dump(self, data, stream=None, **kw): - inefficient = False - if stream is None: - inefficient = True - stream = StringIO() + stream = StringIO() YAML.dump(self, data, stream, **kw) - if inefficient: - return stream.getvalue() + return stream.getvalue() def load_config(file_encoding='utf-8'): @@ -46,7 +56,7 @@ def load_config(file_encoding='utf-8'): return Munch.fromYAML(cfgfile.read_text(encoding=file_encoding)) -def get_input_type(filepath): +def get_input_type(filepath, prog_opts): """ Check filename extension, open and process by file type, return type flag and data from appropriate loader. @@ -62,8 +72,10 @@ def get_input_type(filepath): to_xml = True elif filepath.name.lower().endswith('.xml'): with filepath.open('r+b') as infile: - data_in = xmltodict.parse(infile, process_comments=True) - + data_in = xmltodict.parse(infile, + process_comments=prog_opts['process_comments']) + else: + raise FileTypeError("FileTypeError: unknown input file extension") return to_xml, data_in @@ -78,27 +90,28 @@ def restore_xml_comments(xmls): return xmls -def transform_data(payload, to_xml=True): +def transform_data(payload, yml_opts, xml_opts, to_xml=True): """ Produce output data from dict-ish object using ``direction``. - :param payload: dict: output from xmltodict or yaml loader. - :param to_xml: output format, ie, XML if to_xml is True - :return result: output data in to_format + :param payload: input from xmltodict or yaml loader. + :param to_xml: output direction, ie, if to_xml is True then output + data is XML format. + :return result: output file (str) in specified format. """ res = '' if to_xml: xml = xmltodict.unparse(payload, - short_empty_elements=False, - pretty=True, - indent=' ') + short_empty_elements=xml_opts['short_empty_elements'], + pretty=xml_opts['pretty'], + indent=xml_opts['indent']) res = restore_xml_comments(xml) else: yaml = StrYAML() - yaml.indent(mapping=2, - sequence=4, - offset=2) + yaml.indent(mapping=yml_opts['mapping'], + sequence=yml_opts['sequence'], + offset=yml_opts['offset']) yaml.preserve_quotes = True # type: ignore res = yaml.dump(payload) @@ -106,22 +119,32 @@ def transform_data(payload, to_xml=True): return res -if __name__ == '__main__': - VERSION = '0.0.0' - DEBUG = False +def main(argv=None): + """ + Transform mavlink-style xml files to/from xml and yaml. Note yaml format uses + custom markup for attributes and comments. See xmltodict docs for details. + """ + + debug = False if os.getenv('VERBOSE') and os.getenv('VERBOSE') == '1': - DEBUG = True + debug = True cfg = load_config() + popts = Munch.toDict(cfg.prog_opts[0]) + yopts = Munch.toDict(cfg.yml_opts[0]) + xopts = Munch.toDict(cfg.xml_opts[0]) - args = sys.argv[1:] + if argv is None: + argv = sys.argv + args = argv[1:] if args == ['--version']: print(f'[ymltoxml {VERSION}]') sys.exit(0) elif args == ['--selftest']: - print(f'cfg from yaml: {cfg}') + print('cfg items from yaml:') + print(f' {cfg}') sys.exit(0) for filearg in args: @@ -129,11 +152,27 @@ def transform_data(payload, to_xml=True): if not fpath.exists(): print(f'Input file {fpath} not found! Skipping...') else: - if DEBUG: + if debug: print(f'Processing data from {filearg}') - from_yml, indata = get_input_type(fpath) - outdata = transform_data(indata, to_xml=from_yml) + + try: + from_yml, indata = get_input_type(fpath, popts) + except FileTypeError as exc: + print(f'{exc} => {fpath}') + break + + outdata = transform_data(indata, yopts, xopts, to_xml=from_yml) + if from_yml: - fpath.with_suffix('.xml').write_text(outdata + '\n', encoding='utf-8') + fpath.with_suffix('.xml').write_text(outdata + '\n', + encoding=popts['file_encoding']) else: - fpath.with_suffix('.yaml').write_text(outdata, encoding='utf-8') + fpath.with_suffix('.yaml').write_text(outdata, + encoding=popts['file_encoding']) + + +VERSION = version("ymltoxml") + + +if __name__ == '__main__': + main() diff --git a/tox.ini b/tox.ini index 81dca21..b1627e1 100644 --- a/tox.ini +++ b/tox.ini @@ -20,16 +20,19 @@ PLATFORM = [base] deps = pip>=21.1 - wheel + versioningit + +[build] +deps = + pip>=21.1 build twine - versioningit [testenv] skip_install = true setenv = - PYTHONPATH = {toxinidir} + PYTHONPATH = {toxinidir}/src DISABLE_MAVNATIVE = True MDEF_PATH = {envsitepackagesdir}/pymavlink/message_definitions/v1.0 @@ -66,6 +69,8 @@ commands = bash -c 'cp in.yaml out.yaml' python src/ymltoxml/ymltoxml.py out.yaml bash -c 'diff -u in.xml out.xml || true' + python src/ymltoxml/ymltoxml.py --version + python src/ymltoxml/ymltoxml.py out.txt [testenv:clean] allowlist_externals = @@ -77,6 +82,48 @@ deps = commands = bash -c 'rm -rf in.* out.* paparazzi.xml munch/' +[testenv:setup] +passenv = + CI + PYTHONIOENCODING + +setenv = PYTHONPATH = {toxinidir} + +deps = + {[base]deps} + #-r requirements.txt + +commands = + python setup.py egg_info + +[testenv:deploy] +skip_install = true + +passenv = + pythonLocation + CI + PYTHONIOENCODING + PIP_DOWNLOAD_CACHE + +deps = + {[build]deps} + +commands = + python -m build . + twine check dist/* + +[testenv:check] +skip_install = true +passenv = CI + +deps = + #{[base]deps} + pip>=21.1 + +commands = + pip install ymltoxml --force-reinstall --pre --prefer-binary -f dist/ + ymltoxml --version + [testenv:lint] passenv = CI @@ -90,7 +137,7 @@ deps = -r requirements.txt commands = - pylint --fail-under=7 src/ymltoxml/ymltoxml.py + pylint --fail-under=9.90 src/ymltoxml/ymltoxml.py [testenv:style] passenv = From 92213aa04309020fea08f233a9a08f6b17691314 Mon Sep 17 00:00:00 2001 From: Stephen L Arnold Date: Sun, 19 Jun 2022 23:02:02 -0700 Subject: [PATCH 06/10] add more CI workflows for wheels, pylint, release Signed-off-by: Stephen L Arnold --- .github/workflows/ci.yml | 1 + .github/workflows/pylint.yml | 111 ++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 91 ++++++++++++++++++++++++++++ .github/workflows/wheels.yml | 53 ++++++++++++++++ 4 files changed, 256 insertions(+) create mode 100644 .github/workflows/pylint.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/wheels.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d60f7d8..6a34142 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,6 +45,7 @@ jobs: - name: Run tests run: | + tox -e setup tox env: PLATFORM: ${{ matrix.os }} diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml new file mode 100644 index 0000000..0acc386 --- /dev/null +++ b/.github/workflows/pylint.yml @@ -0,0 +1,111 @@ +name: Pylint + +on: + workflow_dispatch: + pull_request: + push: + branches: [ main ] + +jobs: + pylint: + + runs-on: ubuntu-20.04 + defaults: + run: + shell: bash + outputs: + branch: ${{ steps.extract_branch.outputs.branch }} + rating: ${{ steps.analyze.outputs.rating }} + path: ${{ steps.analyze.outputs.path }} + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Extract base branch name + id: extract_branch + shell: bash + run: | + TMP_PULL_BASE_REF="${{ github.base_ref }}" + TMP_GITHUB_REF="${GITHUB_REF#refs/heads/}" + EXPORT_VALUE="" + if [ "${TMP_PULL_BASE_REF}" != "" ] + then + EXPORT_VALUE="${TMP_PULL_BASE_REF}" + else + EXPORT_VALUE="${TMP_GITHUB_REF}" + fi + echo "##[set-output name=branch;]${EXPORT_VALUE}" + + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Install tox + run: | + python -m pip install --upgrade pip wheel + pip install tox tox-gh-actions + + - name: Run pylint + id: analyze + env: + BADGE_PATH: badges/pylint-score.svg + run: | + rating=$(bash -c 'tox -e lint' | grep 'Your code has been rated at' | cut -f7 -d " ") + echo "Pylint score: ${rating}" + echo "##[set-output name=rating;]${rating}" + echo "##[set-output name=path;]${BADGE_PATH}" + + badge: + # Only generate and publish if these conditions are met: + # - The previous job/analyze step ended successfully + # - At least one of these is true: + # - This is a push event and the push event is on branch 'master' or 'develop' + # Note: if this repo is personal (ie, not an org repo) then you can + # use the following to change the scope of the next 2 jobs + # instead of running on branch push as shown below: + # - This is a pull request event and the pull actor is the same as the repo owner + # if: ${{ ( github.event_name == 'pull_request' && github.actor == github.repository_owner ) || github.ref == 'refs/heads/master' }} + name: Generate badge image with pylint score + runs-on: ubuntu-20.04 + needs: [pylint] + if: ${{ github.event_name == 'push' }} + + steps: + - uses: actions/checkout@v2 + with: + ref: badges + path: badges + + # Use the output from the `analyze` step + - name: Create pylint badge + uses: emibcn/badge-action@v1 + id: badge + with: + label: 'Pylint score' + status: ${{ needs.pylint.outputs.rating }} + color: 'green' + path: ${{ needs.pylint.outputs.path }} + + - name: Commit badge + env: + BRANCH: ${{ needs.pylint.outputs.branch }} + FILE: 'pylint-score.svg' + working-directory: ./badges + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + mkdir -p "${BRANCH}" + mv "${FILE}" "${BRANCH}" + git add "${BRANCH}/${FILE}" + # Will give error if badge has not changed + git commit -m "Add/Update badge" || true + + - name: Push badge commit + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: badges + directory: badges diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..f9a42ed --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,91 @@ +name: Release + +on: + push: + # release on tag push + tags: + - '*' + +jobs: + wheels: + + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + env: + PYTHONIOENCODING: utf-8 + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04, macos-latest, windows-latest] + python-version: [3.7, 3.8, 3.9, '3.10'] + + steps: + - name: Set git crlf/eol + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel + pip install tox tox-gh-actions + + - name: Build dist pkgs + run: | + tox -e deploy + + - name: Upload artifacts + if: matrix.python-version == 3.8 && runner.os != 'Windows' + uses: actions/upload-artifact@v2 + with: + name: wheels + path: ./dist/*.whl + + create_release: + name: Create Release + needs: [wheels] + runs-on: ubuntu-20.04 + + steps: + - name: Get version + id: get_version + run: | + echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV + echo ${{ env.VERSION }} + + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + # download all artifacts to project dir + - uses: actions/download-artifact@v2 + + - name: Generate changes file + uses: sarnold/gitchangelog-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN}} + + - name: Create release + id: create_release + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.VERSION }} + name: Release v${{ env.VERSION }} + body_path: CHANGES.md + draft: false + prerelease: false + files: | + wheels/yml*.whl diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml new file mode 100644 index 0000000..00dca6e --- /dev/null +++ b/.github/workflows/wheels.yml @@ -0,0 +1,53 @@ +name: Wheels + +on: + workflow_dispatch: + pull_request: + push: + branches: [ master ] + +jobs: + build: + + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + env: + PYTHONIOENCODING: utf-8 + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04, macos-latest, windows-latest] + python-version: [3.7, 3.8, 3.9, '3.10'] + + steps: + - name: Set git crlf/eol + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel + pip install tox tox-gh-actions + + - name: Build dist pkgs + run: | + tox -e deploy,check + + - name: Upload artifacts + if: matrix.python-version == 3.8 && runner.os == 'Linux' + uses: actions/upload-artifact@v2 + with: + name: wheels + path: ./dist/*.whl From 442f9f4cd460fb6c3e2d0961c73e4e6e2d4291d6 Mon Sep 17 00:00:00 2001 From: Stephen L Arnold Date: Sun, 19 Jun 2022 23:41:53 -0700 Subject: [PATCH 07/10] fix: replace old thing/new thing => use importlib for cfg file * use external importlib pkgs first * make mypy ignore one of the 2 importlib imports * install pkg for command-line test Signed-off-by: Stephen L Arnold --- setup.cfg | 6 ++---- src/ymltoxml/ymltoxml.py | 9 ++++++--- tox.ini | 15 ++++++++------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/setup.cfg b/setup.cfg index 0d9c1ae..b663098 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,7 +21,8 @@ classifiers = [options] python_requires = >= 3.6 install_requires = - importlib-metadata; python_version < "3.8" + importlib-metadata; python_version < '3.8' + importlib_resources; python_version < '3.10' xmltodict munch ruamel.yaml @@ -36,9 +37,6 @@ package_dir = [options.packages.find] where = src -exclude = - tests - [options.package_data] ymltoxml.data = *.yaml diff --git a/src/ymltoxml/ymltoxml.py b/src/ymltoxml/ymltoxml.py index 208eaa4..197395e 100644 --- a/src/ymltoxml/ymltoxml.py +++ b/src/ymltoxml/ymltoxml.py @@ -14,9 +14,13 @@ from pathlib import Path try: + from importlib_metadata import version +except ImportError: from importlib.metadata import version +try: + from importlib_resources import files except ImportError: - from importlib_metadata import version + from importlib.resources import files # type: ignore import xmltodict import yaml as yaml_loader @@ -50,8 +54,7 @@ def load_config(file_encoding='utf-8'): """ cfgfile = Path('.ymltoxml.yaml') if not cfgfile.exists(): - pkg_path = os.path.dirname(sys.modules['ymltoxml']) - cfgfile = Path(os.path.join(pkg_path, 'data', 'ymltoxml.yaml')) + cfgfile = files('ymltoxml.data').joinpath('ymltoxml.yaml') return Munch.fromYAML(cfgfile.read_text(encoding=file_encoding)) diff --git a/tox.ini b/tox.ini index b1627e1..c0d6178 100644 --- a/tox.ini +++ b/tox.ini @@ -59,18 +59,18 @@ allowlist_externals = deps = {[base]deps} - -r requirements.txt + #-r requirements.txt pymavlink + . commands = - bash -c 'cp src/ymltoxml/data/ymltoxml.yaml .ymltoxml.yaml' bash -c 'cp $MDEF_PATH/{posargs:paparazzi}.xml in.xml' - python src/ymltoxml/ymltoxml.py in.xml + ymltoxml in.xml bash -c 'cp in.yaml out.yaml' - python src/ymltoxml/ymltoxml.py out.yaml + ymltoxml out.yaml bash -c 'diff -u in.xml out.xml || true' - python src/ymltoxml/ymltoxml.py --version - python src/ymltoxml/ymltoxml.py out.txt + ymltoxml --version + ymltoxml out.txt [testenv:clean] allowlist_externals = @@ -161,11 +161,12 @@ setenv = PYTHONPATH = {toxinidir} deps = {[base]deps} mypy + importlib_resources -r requirements.txt commands = stubgen -m munch --export-less -o {toxinidir} - python -m mypy --follow-imports=normal --install-types --non-interactive scripts/ src/ + python -m mypy --follow-imports=normal --install-types --non-interactive src/ [testenv:isort] skip_install = true From 947393505e47d114ce2007074ab6527c00c2d75b Mon Sep 17 00:00:00 2001 From: Stephen L Arnold Date: Mon, 20 Jun 2022 11:23:17 -0700 Subject: [PATCH 08/10] chg: flatten cfg file, use single cfg object, cleanup doc strings Signed-off-by: Stephen L Arnold --- setup.cfg | 4 +-- src/ymltoxml/data/ymltoxml.yaml | 19 ++++++--------- src/ymltoxml/ymltoxml.py | 43 +++++++++++++++++---------------- 3 files changed, 31 insertions(+), 35 deletions(-) diff --git a/setup.cfg b/setup.cfg index b663098..6f59f85 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,8 +32,6 @@ packages = find_namespace: package_dir = =src -#include_package_data = True - [options.packages.find] where = src @@ -45,7 +43,7 @@ ymltoxml.data = console_scripts = ymltoxml = ymltoxml.ymltoxml:main -# deps are included here mainly for local/venv installs using pip +# extra deps are included here mainly for local/venv installs using pip # otherwise deps are handled via tox, ci config files or pkg managers [options.extras_require] test = diff --git a/src/ymltoxml/data/ymltoxml.yaml b/src/ymltoxml/data/ymltoxml.yaml index a3512a1..a6d74ae 100644 --- a/src/ymltoxml/data/ymltoxml.yaml +++ b/src/ymltoxml/data/ymltoxml.yaml @@ -1,11 +1,8 @@ -prog_opts: - - file_encoding: 'utf-8' - process_comments: True -yml_opts: - - mapping: 2 - sequence: 4 - offset: 2 -xml_opts: - - short_empty_elements: False - pretty: True - indent: ' ' +file_encoding: 'utf-8' +process_comments: True +mapping: 2 +sequence: 4 +offset: 2 +short_empty_elements: False +pretty: True +indent: ' ' diff --git a/src/ymltoxml/ymltoxml.py b/src/ymltoxml/ymltoxml.py index 197395e..864b823 100644 --- a/src/ymltoxml/ymltoxml.py +++ b/src/ymltoxml/ymltoxml.py @@ -50,7 +50,8 @@ def load_config(file_encoding='utf-8'): """ Load yaml configuration file and munchify the data. If local file is not found in current directory, the default will be loaded. - :return: Munch cfg obj + :param str file_encoding: + :return Munch: cfg obj """ cfgfile = Path('.ymltoxml.yaml') if not cfgfile.exists(): @@ -63,8 +64,8 @@ def get_input_type(filepath, prog_opts): """ Check filename extension, open and process by file type, return type flag and data from appropriate loader. - :param filepath: filename as Path obj - :return: tuple with file data and destination type flag + :param Path filepath: filename as Path obj + :return tuple: destination type flag and file data """ to_xml = False data_in = None @@ -84,37 +85,39 @@ def get_input_type(filepath, prog_opts): def restore_xml_comments(xmls): """ - Turn comment elements back into xml comments. - :param xmls: xml (file) string output from ``unparse`` - :return xmls: processed xml string + Turn tagged comment elements back into xml comments. + :param str xmls: xml (file) output from ``unparse`` + :return str xmls: processed xml string """ for rep in (("<#comment>", "")): xmls = xmls.replace(*rep) return xmls -def transform_data(payload, yml_opts, xml_opts, to_xml=True): +def transform_data(payload, prog_opts, to_xml=True): """ Produce output data from dict-ish object using ``direction``. :param payload: input from xmltodict or yaml loader. - :param to_xml: output direction, ie, if to_xml is True then output - data is XML format. - :return result: output file (str) in specified format. + :param dict prog_opts: configuration options + :param bool to_xml: output direction, ie, if to_xml is True then output + data is XML format. + :return str res: output file data in specified format. """ res = '' if to_xml: xml = xmltodict.unparse(payload, - short_empty_elements=xml_opts['short_empty_elements'], - pretty=xml_opts['pretty'], - indent=xml_opts['indent']) + short_empty_elements=prog_opts['short_empty_elements'], + pretty=prog_opts['pretty'], + indent=prog_opts['indent']) - res = restore_xml_comments(xml) + if prog_opts['process_comments']: + res = restore_xml_comments(xml) else: yaml = StrYAML() - yaml.indent(mapping=yml_opts['mapping'], - sequence=yml_opts['sequence'], - offset=yml_opts['offset']) + yaml.indent(mapping=prog_opts['mapping'], + sequence=prog_opts['sequence'], + offset=prog_opts['offset']) yaml.preserve_quotes = True # type: ignore res = yaml.dump(payload) @@ -134,9 +137,7 @@ def main(argv=None): debug = True cfg = load_config() - popts = Munch.toDict(cfg.prog_opts[0]) - yopts = Munch.toDict(cfg.yml_opts[0]) - xopts = Munch.toDict(cfg.xml_opts[0]) + popts = Munch.toDict(cfg) if argv is None: argv = sys.argv @@ -164,7 +165,7 @@ def main(argv=None): print(f'{exc} => {fpath}') break - outdata = transform_data(indata, yopts, xopts, to_xml=from_yml) + outdata = transform_data(indata, popts, to_xml=from_yml) if from_yml: fpath.with_suffix('.xml').write_text(outdata + '\n', From 05b6e74dcbcae90087080f32fb60e52e286dc11b Mon Sep 17 00:00:00 2001 From: Stephen L Arnold Date: Mon, 20 Jun 2022 12:27:29 -0700 Subject: [PATCH 09/10] new: add arg to dump default config yaml to stdout Signed-off-by: Stephen L Arnold --- src/ymltoxml/ymltoxml.py | 14 +++++++------- tox.ini | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/ymltoxml/ymltoxml.py b/src/ymltoxml/ymltoxml.py index 864b823..2026686 100644 --- a/src/ymltoxml/ymltoxml.py +++ b/src/ymltoxml/ymltoxml.py @@ -50,14 +50,15 @@ def load_config(file_encoding='utf-8'): """ Load yaml configuration file and munchify the data. If local file is not found in current directory, the default will be loaded. - :param str file_encoding: - :return Munch: cfg obj + :param str file_encoding: cfg file encoding + :return tuple: Munch cfg obj and cfg file as Path obj """ cfgfile = Path('.ymltoxml.yaml') if not cfgfile.exists(): cfgfile = files('ymltoxml.data').joinpath('ymltoxml.yaml') + cfgobj = Munch.fromYAML(cfgfile.read_text(encoding=file_encoding)) - return Munch.fromYAML(cfgfile.read_text(encoding=file_encoding)) + return cfgobj, cfgfile def get_input_type(filepath, prog_opts): @@ -136,7 +137,7 @@ def main(argv=None): if os.getenv('VERBOSE') and os.getenv('VERBOSE') == '1': debug = True - cfg = load_config() + cfg, pfile = load_config() popts = Munch.toDict(cfg) if argv is None: @@ -146,9 +147,8 @@ def main(argv=None): if args == ['--version']: print(f'[ymltoxml {VERSION}]') sys.exit(0) - elif args == ['--selftest']: - print('cfg items from yaml:') - print(f' {cfg}') + elif args == ['--dump-config']: + sys.stdout.write(pfile.read_text(encoding=popts['file_encoding'])) sys.exit(0) for filearg in args: diff --git a/tox.ini b/tox.ini index c0d6178..e5ccd25 100644 --- a/tox.ini +++ b/tox.ini @@ -71,6 +71,7 @@ commands = bash -c 'diff -u in.xml out.xml || true' ymltoxml --version ymltoxml out.txt + ymltoxml --dump-config [testenv:clean] allowlist_externals = From 1dd0e2d9c5d798f8f4b180148bbfa20387fa4465 Mon Sep 17 00:00:00 2001 From: Stephen L Arnold Date: Mon, 20 Jun 2022 15:06:19 -0700 Subject: [PATCH 10/10] cleanup metadata/packaging and workflow files Signed-off-by: Stephen L Arnold --- .github/workflows/wheels.yml | 2 +- setup.cfg | 5 ++--- src/ymltoxml/__init__.py | 6 ++---- src/ymltoxml/ymltoxml.py | 2 +- tox.ini | 2 +- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 00dca6e..d27bd81 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: pull_request: push: - branches: [ master ] + branches: [ main ] jobs: build: diff --git a/setup.cfg b/setup.cfg index 6f59f85..66c0ea9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,17 +1,16 @@ [metadata] name = ymltoxml version = attr: ymltoxml.__version__ +description = attr: ymltoxml.__description__ url = https://github.com/sarnold/ymltoxml author = Stephen Arnold author_email = nerdboy@gentoo.org -description = Asynchronous MAVLink connection library. long_description = file: README.rst long_description_content_type = text/rst; charset=UTF-8 -license = GPLv2 +license_expression = LGPL-2.1-or-later license_files = LICENSE classifiers = Development Status :: 4 - Beta - License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) Intended Audience :: Developers Programming Language :: Python Environment :: Console diff --git a/src/ymltoxml/__init__.py b/src/ymltoxml/__init__.py index b168ac6..dcd8b2d 100644 --- a/src/ymltoxml/__init__.py +++ b/src/ymltoxml/__init__.py @@ -1,8 +1,6 @@ -""" -Command-line tool for bidirectional transformation of YAML and XML files. -""" from ._version import __version__ version = __version__ +__description__ = "Console tool for bidirectional transformation of YAML and XML files." -__all__ = ["__version__", "version"] +__all__ = ["__description__", "__version__", "version"] diff --git a/src/ymltoxml/ymltoxml.py b/src/ymltoxml/ymltoxml.py index 2026686..3b95d58 100644 --- a/src/ymltoxml/ymltoxml.py +++ b/src/ymltoxml/ymltoxml.py @@ -6,7 +6,7 @@ # available in the accompanying LICENSE file. """ -Converts YAML to XML and XML to YAML. +Transform YAML to XML and XML to YAML. """ import os diff --git a/tox.ini b/tox.ini index e5ccd25..ffe6915 100644 --- a/tox.ini +++ b/tox.ini @@ -70,8 +70,8 @@ commands = ymltoxml out.yaml bash -c 'diff -u in.xml out.xml || true' ymltoxml --version - ymltoxml out.txt ymltoxml --dump-config + ymltoxml out.txt [testenv:clean] allowlist_externals =