Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-ini2toml for openSUSE:Factory checked in at 2024-05-05 12:10:27 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-ini2toml (Old) and /work/SRC/openSUSE:Factory/.python-ini2toml.new.1880 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-ini2toml" Sun May 5 12:10:27 2024 rev:10 rq:1171769 version:0.14 Changes: -------- --- /work/SRC/openSUSE:Factory/python-ini2toml/python-ini2toml.changes 2024-03-21 17:00:29.824498110 +0100 +++ /work/SRC/openSUSE:Factory/.python-ini2toml.new.1880/python-ini2toml.changes 2024-05-05 12:10:33.839395439 +0200 @@ -1,0 +2,17 @@ +Fri May 3 07:41:22 UTC 2024 - Dirk Müller <dmuel...@suse.com> + +- update to 0.14: + * Introduce LiteTranslator and FullTranslator as convenience + classes for more deterministic behaviour, :pr:`95`. + * setuptools plugin: Fix ValueError when setup.cfg contains + [options.packages.find] but also lists [options] packages = + ... explicitly as a list of package names, :issue:`93`. + * Fix ValueError when setup.cfg contains + [options.packages.find] but also lists [options] packages = + ... explicitly as a list of package names, :issue:`93`. + * pytest plugin: Remove comments when converting addopts with + multi-line values, :issue:`98`. + * Remove comments when converting addopts with multi-line + values, :issue:`98`. + +------------------------------------------------------------------- Old: ---- ini2toml-0.13.tar.gz New: ---- ini2toml-0.14.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-ini2toml.spec ++++++ --- /var/tmp/diff_new_pack.ZlgmL8/_old 2024-05-05 12:10:35.583458557 +0200 +++ /var/tmp/diff_new_pack.ZlgmL8/_new 2024-05-05 12:10:35.587458701 +0200 @@ -50,7 +50,7 @@ %define skip_python2 1 Name: python-ini2toml%{psuffix} -Version: 0.13 +Version: 0.14 Release: 0 Summary: Automatic conversion of .ini/cfg files to TOML equivalents License: MPL-2.0 @@ -157,6 +157,8 @@ %if %{with test} %check %if %{without all} && %{without full} +rm -v src/ini2toml/drivers/configupdater.py +rm -v src/ini2toml/drivers/full_toml.py ignoretests=( --ignore tests/test_examples.py --ignore tests/test_transformations.py ++++++ ini2toml-0.13.tar.gz -> ini2toml-0.14.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/.cirrus.yml new/ini2toml-0.14/.cirrus.yml --- old/ini2toml-0.13/.cirrus.yml 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/.cirrus.yml 2024-04-20 21:23:26.000000000 +0200 @@ -99,8 +99,7 @@ - name: test (Linux - 3.11) container: {image: "python:3.11-bullseye"} - name: test (Linux - 3.12) - container: {image: "python:3.12-rc-bullseye"} - allow_failures: true # Experimental + container: {image: "python:3.12-bullseye"} install_script: - python -m pip install --upgrade pip tox pipx <<: *test-template @@ -124,7 +123,7 @@ freebsd_task: name: test (freebsd - 3.9) - freebsd_instance: {image_family: freebsd-13-1} + freebsd_instance: {image_family: freebsd-14-0} install_script: - pkg remove -y python lang/python - pkg install -y git python39 py39-pip py39-gdbm py39-sqlite3 py39-tox py39-pipx py39-tomli diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/.pre-commit-config.yaml new/ini2toml-0.14/.pre-commit-config.yaml --- old/ini2toml-0.13/.pre-commit-config.yaml 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/.pre-commit-config.yaml 2024-04-20 21:23:26.000000000 +0200 @@ -2,7 +2,7 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: trailing-whitespace exclude: 'test_(.*)\.py$' @@ -19,7 +19,7 @@ args: ['--fix=lf'] - repo: https://github.com/PyCQA/autoflake - rev: v2.2.1 + rev: v2.3.1 hooks: - id: autoflake args: [ @@ -29,12 +29,12 @@ ] - repo: https://github.com/PyCQA/isort - rev: 5.12.0 + rev: 5.13.2 hooks: - id: isort - repo: https://github.com/psf/black - rev: 23.9.1 + rev: 24.4.0 hooks: - id: black language_version: python3 @@ -51,13 +51,13 @@ - id: codespell # See setup.cfg for args - repo: https://github.com/PyCQA/flake8 - rev: 6.1.0 + rev: 7.0.0 hooks: - id: flake8 additional_dependencies: [flake8-bugbear>=23.2.13] - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + rev: v3.15.2 hooks: - id: pyupgrade args: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/CHANGELOG.rst new/ini2toml-0.14/CHANGELOG.rst --- old/ini2toml-0.13/CHANGELOG.rst 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/CHANGELOG.rst 2024-04-20 21:23:26.000000000 +0200 @@ -2,6 +2,21 @@ Changelog ========= +Version 0.14 +============ + +* Introduce ``LiteTranslator`` and ``FullTranslator`` as + convenience classes for more deterministic behaviour, :pr:`95`. +* ``setuptools`` plugin: + + * Fix ``ValueError`` when ``setup.cfg`` contains ``[options.packages.find]`` + but also lists ``[options] packages = ...`` explicitly as a list of package + names, :issue:`93`. + +* ``pytest`` plugin: + + * Remove comments when converting ``addopts`` with multi-line values, :issue:`98`. + Version 0.13 ============ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/CONTRIBUTING.rst new/ini2toml-0.14/CONTRIBUTING.rst --- old/ini2toml-0.13/CONTRIBUTING.rst 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/CONTRIBUTING.rst 2024-04-20 21:23:26.000000000 +0200 @@ -77,9 +77,9 @@ ================== Understanding how the project works -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +----------------------------------- -If you have a change in mind, please have a look in our :doc:`dev-guide`. +If you have a change in mind, please have a look in our :doc:`/dev-guide`. It explains the main aspects of the project and provide a brief overview on how it is organised and how to implement :ref:`plugins`. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/PKG-INFO new/ini2toml-0.14/PKG-INFO --- old/ini2toml-0.13/PKG-INFO 2023-10-23 14:17:02.442848400 +0200 +++ new/ini2toml-0.14/PKG-INFO 2024-04-20 21:24:34.163375100 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: ini2toml -Version: 0.13 +Version: 0.14 Summary: Automatically conversion of .ini/.cfg files to TOML equivalents Home-page: https://github.com/abravalheri/ini2toml/ Author: Anderson Bravalheri @@ -138,9 +138,86 @@ To do so, don't forget to add it to your `virtual environment`_ or specify it as a `project dependency`_. +Note that the class ``Translator`` will try to guess which flavour to use based +on the available installed dependencies. If you need something more +deterministic, consider using ``LiteTranslator`` and ``FullTranslator``, +or explicitly specifying the ``ini_loads_fn`` and ``toml_dumps_fn`` keyword +arguments in the constructor. + More details about ``ini2toml`` and its Python API can be found in `our docs`_. +Limitations +=========== + +``ini2toml`` will try its best to create good quality translations from +``.ini/.cfg`` into ``.toml`` files. However the tool comes with a set of +well known limitations: + +* Although ``ini2toml`` attempts to keep the same order/sequence as the original + information was written, sometimes that is not compatible with the TOML + syntax, and things end up moving around a bit. + +* ``ini2toml`` uses `ConfigParser`_ + `tomli-w`_ for implementing the *"lite"* flavour + and `ConfigUpdater`_ + `tomlkit`_ for implementing the *"full"* flavour. + Therefore it inherits the limitations from those libraries (please check + their documentation for more information). For example: + + * `ConfigUpdater`_, will have trouble to parse interpolations and the related + escaping sequence (``%%``) (in this respect, it behaves more similarly to + ``RawConfigParser`` than ``ConfigParser``). + + * `tomli-w`_ is not very flexible regarding formatting and will have trouble + with multi-line strings. + +* ``ini2toml`` *expects the input to be valid* and will not perform extensive + checks on the provided document. If something in the output is not working as you would + expect, it might be a good idea to check the input. + +* ``.ini/.cfg`` files are used in a plethora of use cases and it is impossible + to cover all of them in a single code base. Even when considering + ``setup.cfg``, there are many packages that define different sections in the + document in addition to the basic definition by ``setuptools``. + Because of that ``ini2toml``, adopts a "best-effort" approach, that might not + correspond to what you expect. If that is the case please consider + contributing or creating your own `plugin`_. + +* The translation procedure analyse only the given input. If the original + ``.ini/.cfg`` file contains references to other files, or behaves differently + depending on the existence/presence of other files and directories, the + translation will not take that into consideration. + +Therefore it is recommended to double check the output and fix any +problems before using the ``.toml`` files in production. + + +Can ``ini2toml`` also translate ``setup.py`` into ``pyproject.toml``? +===================================================================== + +Working with ``.py`` files is not in the scope of the ``ini2toml`` project, +and therefore this feature is not implemented. + +However, you can probably find some tools on PyPI to translate from +``setup.py`` into ``setup.cfg``, like `setup-py-upgrade`_ and +`setuptools-py2cfg`_ [#untested]_. + +Once you have ``setup.cfg``, you can use ``ini2toml`` [#setuppy]_. + +.. [#untested] Such tools are neither maintained by this project, + nor tested for integration by ``ini2toml``. + It is best to try some of them out and find the one that works for you. + Manual corrections might be needed. + +.. [#setuppy] Please note that ``setup.py`` is a very dynamic + format and that not everything can be represented in ``setup.cfg`` or + ``pyproject.toml``. Indeed, the `setuptools' docs`_ explicitly say that + ``setup.py`` can be used in tandem with ``pyproject.toml``: ideally all the + declarative metadata goes to ``pyproject.toml``, but you can keep the + dynamic bits in ``setup.py``. + Remember: ``setup.py`` is a perfectly valid and non deprecated configuration file; + what is deprecated is running it as a CLI tool, i.e. ``python setup.py ...``. + + .. _pyscaffold-notes: .. tip:: @@ -167,8 +244,14 @@ .. _ini_cfg: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure .. _our docs: https://ini2toml.readthedocs.io .. _PEP 621: https://www.python.org/dev/peps/pep-0621/ -.. _pipx: https://pypa.github.io/pipx/ +.. _pipx: https://pipx.pypa.io/stable/ .. _project dependency: https://packaging.python.org/tutorials/managing-dependencies/ +.. _plugin: https://ini2toml.readthedocs.io/en/latest/dev-guide.html#plugins +.. _setup-py-upgrade: https://pypi.org/project/setup-cfg-fmt/ +.. _setuptools-py2cfg: https://pypi.org/project/setuptools-py2cfg/ +.. _setuptools' docs: https://setuptools.pypa.io/en/latest/userguide/quickstart.html#setuppy-discouraged .. _TOML: https://toml.io/en/ .. _TOML library: https://github.com/sdispater/tomlkit +.. _tomli-w: https://pypi.org/project/tomli-w/ +.. _tomlkit: https://tomlkit.readthedocs.io/en/latest/ .. _virtual environment: https://realpython.com/python-virtual-environments-a-primer/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/README.rst new/ini2toml-0.14/README.rst --- old/ini2toml-0.13/README.rst 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/README.rst 2024-04-20 21:23:26.000000000 +0200 @@ -93,9 +93,86 @@ To do so, don't forget to add it to your `virtual environment`_ or specify it as a `project dependency`_. +Note that the class ``Translator`` will try to guess which flavour to use based +on the available installed dependencies. If you need something more +deterministic, consider using ``LiteTranslator`` and ``FullTranslator``, +or explicitly specifying the ``ini_loads_fn`` and ``toml_dumps_fn`` keyword +arguments in the constructor. + More details about ``ini2toml`` and its Python API can be found in `our docs`_. +Limitations +=========== + +``ini2toml`` will try its best to create good quality translations from +``.ini/.cfg`` into ``.toml`` files. However the tool comes with a set of +well known limitations: + +* Although ``ini2toml`` attempts to keep the same order/sequence as the original + information was written, sometimes that is not compatible with the TOML + syntax, and things end up moving around a bit. + +* ``ini2toml`` uses `ConfigParser`_ + `tomli-w`_ for implementing the *"lite"* flavour + and `ConfigUpdater`_ + `tomlkit`_ for implementing the *"full"* flavour. + Therefore it inherits the limitations from those libraries (please check + their documentation for more information). For example: + + * `ConfigUpdater`_, will have trouble to parse interpolations and the related + escaping sequence (``%%``) (in this respect, it behaves more similarly to + ``RawConfigParser`` than ``ConfigParser``). + + * `tomli-w`_ is not very flexible regarding formatting and will have trouble + with multi-line strings. + +* ``ini2toml`` *expects the input to be valid* and will not perform extensive + checks on the provided document. If something in the output is not working as you would + expect, it might be a good idea to check the input. + +* ``.ini/.cfg`` files are used in a plethora of use cases and it is impossible + to cover all of them in a single code base. Even when considering + ``setup.cfg``, there are many packages that define different sections in the + document in addition to the basic definition by ``setuptools``. + Because of that ``ini2toml``, adopts a "best-effort" approach, that might not + correspond to what you expect. If that is the case please consider + contributing or creating your own `plugin`_. + +* The translation procedure analyse only the given input. If the original + ``.ini/.cfg`` file contains references to other files, or behaves differently + depending on the existence/presence of other files and directories, the + translation will not take that into consideration. + +Therefore it is recommended to double check the output and fix any +problems before using the ``.toml`` files in production. + + +Can ``ini2toml`` also translate ``setup.py`` into ``pyproject.toml``? +===================================================================== + +Working with ``.py`` files is not in the scope of the ``ini2toml`` project, +and therefore this feature is not implemented. + +However, you can probably find some tools on PyPI to translate from +``setup.py`` into ``setup.cfg``, like `setup-py-upgrade`_ and +`setuptools-py2cfg`_ [#untested]_. + +Once you have ``setup.cfg``, you can use ``ini2toml`` [#setuppy]_. + +.. [#untested] Such tools are neither maintained by this project, + nor tested for integration by ``ini2toml``. + It is best to try some of them out and find the one that works for you. + Manual corrections might be needed. + +.. [#setuppy] Please note that ``setup.py`` is a very dynamic + format and that not everything can be represented in ``setup.cfg`` or + ``pyproject.toml``. Indeed, the `setuptools' docs`_ explicitly say that + ``setup.py`` can be used in tandem with ``pyproject.toml``: ideally all the + declarative metadata goes to ``pyproject.toml``, but you can keep the + dynamic bits in ``setup.py``. + Remember: ``setup.py`` is a perfectly valid and non deprecated configuration file; + what is deprecated is running it as a CLI tool, i.e. ``python setup.py ...``. + + .. _pyscaffold-notes: .. tip:: @@ -122,8 +199,14 @@ .. _ini_cfg: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure .. _our docs: https://ini2toml.readthedocs.io .. _PEP 621: https://www.python.org/dev/peps/pep-0621/ -.. _pipx: https://pypa.github.io/pipx/ +.. _pipx: https://pipx.pypa.io/stable/ .. _project dependency: https://packaging.python.org/tutorials/managing-dependencies/ +.. _plugin: https://ini2toml.readthedocs.io/en/latest/dev-guide.html#plugins +.. _setup-py-upgrade: https://pypi.org/project/setup-cfg-fmt/ +.. _setuptools-py2cfg: https://pypi.org/project/setuptools-py2cfg/ +.. _setuptools' docs: https://setuptools.pypa.io/en/latest/userguide/quickstart.html#setuppy-discouraged .. _TOML: https://toml.io/en/ .. _TOML library: https://github.com/sdispater/tomlkit +.. _tomli-w: https://pypi.org/project/tomli-w/ +.. _tomlkit: https://tomlkit.readthedocs.io/en/latest/ .. _virtual environment: https://realpython.com/python-virtual-environments-a-primer/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/docs/conf.py new/ini2toml-0.14/docs/conf.py --- old/ini2toml-0.13/docs/conf.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/docs/conf.py 2024-04-20 21:23:26.000000000 +0200 @@ -18,41 +18,16 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.join(__location__)) sys.path.insert(0, os.path.join(__location__, "../src")) -# -- Run sphinx-apidoc ------------------------------------------------------- -# This hack is necessary since RTD does not issue `sphinx-apidoc` before running -# `sphinx-build -b html . _build/html`. See Issue: -# https://github.com/readthedocs/readthedocs.org/issues/1139 -# DON'T FORGET: Check the box "Install your project inside a virtualenv using -# setup.py install" in the RTD Advanced Settings. -# Additionally it helps us to avoid running apidoc manually +# -- Automatically generated content ------------------------------------------ -try: # for Sphinx >= 1.7 - from sphinx.ext import apidoc -except ImportError: - from sphinx import apidoc +import public_api_docs output_dir = os.path.join(__location__, "api") module_dir = os.path.join(__location__, "../src/ini2toml") -try: - shutil.rmtree(output_dir) -except FileNotFoundError: - pass - -try: - import sphinx - - cmd_line = f"sphinx-apidoc --implicit-namespaces -f -o {output_dir} {module_dir}" - - args = cmd_line.split(" ") - if tuple(sphinx.__version__.split(".")) >= ("1", "7"): - # This is a rudimentary parse_version to avoid external dependencies - args = args[1:] - - apidoc.main(args) -except Exception as e: - print("Running `sphinx-apidoc` failed!\n{}".format(e)) +public_api_docs.gen_stubs(module_dir, output_dir) # -- General configuration --------------------------------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/docs/dev-guide.rst new/ini2toml-0.14/docs/dev-guide.rst --- old/ini2toml-0.13/docs/dev-guide.rst 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/docs/dev-guide.rst 2024-04-20 21:23:26.000000000 +0200 @@ -96,8 +96,7 @@ .. code-block:: python - def text_process(file_contents: str) -> str: - ... + def text_process(file_contents: str) -> str: ... .. important:: All processors are called in sequence, so the output of one is @@ -119,8 +118,7 @@ .. code-block:: python - def intermediate_process(intermediate: IntermediateRepr) -> IntermediateRepr: - ... + def intermediate_process(intermediate: IntermediateRepr) -> IntermediateRepr: ... :class:`~ini2toml.intermediate_repr.IntermediateRepr` is a special kind of Python object with characteristics of both :obj:`dict` and :obj:`list`. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/docs/public_api_docs.py new/ini2toml-0.14/docs/public_api_docs.py --- old/ini2toml-0.13/docs/public_api_docs.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ini2toml-0.14/docs/public_api_docs.py 2024-04-20 21:23:26.000000000 +0200 @@ -0,0 +1,77 @@ +import shutil +from pathlib import Path + +TOC_TEMPLATE = """ +Module Reference +================ + +.. toctree:: + :glob: + :maxdepth: 2 + + ini2toml.api + ini2toml.errors + ini2toml.types + +.. toctree:: + :maxdepth: 1 + + ini2toml.transformations + +.. toctree:: + :caption: Plugins + :glob: + :maxdepth: 1 + + plugins/* +""" + +MODULE_TEMPLATE = """ +``{name}`` +~~{underline}~~ + +.. automodule:: {name} + :members:{_members} + :undoc-members: + :show-inheritance: +""" + + +def gen_stubs(module_dir: str, output_dir: str): + try_rmtree(output_dir) # Always start fresh + Path(output_dir, "plugins").mkdir(parents=True, exist_ok=True) + for module in iter_public(): + text = module_template(module) + Path(output_dir, f"{module}.rst").write_text(text, encoding="utf-8") + for module in iter_plugins(module_dir): + text = module_template(module, "activate") + Path(output_dir, f"plugins/{module}.rst").write_text(text, encoding="utf-8") + Path(output_dir, "modules.rst").write_text(TOC_TEMPLATE, encoding="utf-8") + + +def iter_public(): + lines = (x.strip() for x in TOC_TEMPLATE.splitlines()) + return (x for x in lines if x.startswith("ini2toml.")) + + +def iter_plugins(module_dir: str): + return ( + f'ini2toml.plugins.{path.with_suffix("").name}' + for path in Path(module_dir, "plugins").iterdir() + if path.is_file() + and path.name not in {".", "..", "__init__.py"} + and not path.name.startswith("_") + ) + + +def try_rmtree(target_dir: str): + try: + shutil.rmtree(target_dir) + except FileNotFoundError: + pass + + +def module_template(name: str, *members: str) -> str: + underline = "~" * len(name) + _members = (" " + ", ".join(members)) if members else "" + return MODULE_TEMPLATE.format(name=name, underline=underline, _members=_members) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/setup.cfg new/ini2toml-0.14/setup.cfg --- old/ini2toml-0.13/setup.cfg 2023-10-23 14:17:02.446848400 +0200 +++ new/ini2toml-0.14/setup.cfg 2024-04-20 21:24:34.163375100 +0200 @@ -75,12 +75,12 @@ [tool:pytest] addopts = - --cov ini2toml --cov-report term-missing -vv + --cov ini2toml --cov-report term-missing -vv --doctest-modules norecursedirs = dist build .tox -testpaths = tests +testpaths = src tests [devpi:upload] no_vcs = 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/setup.py new/ini2toml-0.14/setup.py --- old/ini2toml-0.13/setup.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/setup.py 2024-04-20 21:23:26.000000000 +0200 @@ -6,6 +6,7 @@ PyScaffold helps you to put up the scaffold of your new Python project. Learn more under: https://pyscaffold.org/ """ + from setuptools import setup if __name__ == "__main__": diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml/api.py new/ini2toml-0.14/src/ini2toml/api.py --- old/ini2toml-0.13/src/ini2toml/api.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml/api.py 2024-04-20 21:23:26.000000000 +0200 @@ -13,13 +13,17 @@ for checking `structural polymorphism`_ during static analysis). These should be preferred when writing type hints and signatures. -Plugin authors can also rely on the functions exported by -:mod:`~ini2toml.transformations`. +Plugin authors can also use functions exported by :mod:`~ini2toml.transformations`. .. _structural polymorphism: https://www.python.org/dev/peps/pep-0544/ """ -from . import errors, transformations, types + from .base_translator import BaseTranslator -from .translator import Translator +from .translator import FullTranslator, LiteTranslator, Translator -__all__ = ["BaseTranslator", "Translator", "errors", "types", "transformations"] +__all__ = [ + "BaseTranslator", + "FullTranslator", + "LiteTranslator", + "Translator", +] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml/base_translator.py new/ini2toml-0.14/src/ini2toml/base_translator.py --- old/ini2toml-0.13/src/ini2toml/base_translator.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml/base_translator.py 2024-04-20 21:23:26.000000000 +0200 @@ -18,7 +18,7 @@ class BaseTranslator(Generic[T]): """Translator object that follows the public API defined in - :class:`ini2toml.types.Translator`. See :doc:`dev-guide` for a quick explanation of + :class:`ini2toml.types.Translator`. See :doc:`/dev-guide` for a quick explanation of concepts such as plugins, profiles, profile augmentations, etc. Arguments @@ -68,7 +68,8 @@ Tip --- - Most of the times the usage of :class:`~ini2toml.translator.Translator` is preferred + Most of the times the usage of :class:`~ini2toml.translator.Translator` + (or its deterministic variants ``LiteTranslator``, ``FullTranslator``) is preferred over :class:`~ini2toml.base_translator.BaseTranslator` (unless you are vendoring ``ini2toml`` and wants to reduce the number of files included in your project). """ @@ -121,7 +122,7 @@ help_text: str = "", ): """Register a profile augmentation function to be called after the - profile is selected and before the actual translation (see :doc:`dev-guide`). + profile is selected and before the actual translation (see :doc:`/dev-guide`). """ name = (name or fn.__name__).strip() InvalidAugmentationName.check(name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml/drivers/full_toml.py new/ini2toml-0.14/src/ini2toml/drivers/full_toml.py --- old/ini2toml-0.13/src/ini2toml/drivers/full_toml.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml/drivers/full_toml.py 2024-04-20 21:23:26.000000000 +0200 @@ -3,6 +3,7 @@ It makes it easy to swap between implementations for testing (by means of search and replace). """ + from collections import UserList from collections.abc import Mapping, MutableSequence, Sequence from functools import singledispatch @@ -317,8 +318,9 @@ literal = '"' in obj or "\\" in single_line if multiline and not obj.startswith("\n"): - # TOML will automatically strip an starting newline - # so let's add it, since it is better for reading + # TOML feature: during parsing a starting newline character in a multi-line + # string will be stripped away. + # So we are free to add it, since it is better for reading obj = "\n" + obj try: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml/drivers/lite_toml.py new/ini2toml-0.14/src/ini2toml/drivers/lite_toml.py --- old/ini2toml-0.13/src/ini2toml/drivers/lite_toml.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml/drivers/lite_toml.py 2024-04-20 21:23:26.000000000 +0200 @@ -3,6 +3,7 @@ It makes it easy to swap between implementations for testing (by means of search and replace). """ + try: from tomli_w import dumps except ImportError: # pragma: no cover diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml/drivers/plain_builtins.py new/ini2toml-0.14/src/ini2toml/drivers/plain_builtins.py --- old/ini2toml-0.13/src/ini2toml/drivers/plain_builtins.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml/drivers/plain_builtins.py 2024-04-20 21:23:26.000000000 +0200 @@ -4,6 +4,7 @@ This is **not a loss-less** process, since comments are not preserved. """ + from collections.abc import Mapping, MutableMapping from functools import singledispatch from typing import Any, TypeVar diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml/errors.py new/ini2toml-0.14/src/ini2toml/errors.py --- old/ini2toml-0.13/src/ini2toml/errors.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml/errors.py 2024-04-20 21:23:26.000000000 +0200 @@ -15,6 +15,7 @@ @classmethod def check(cls, name: str, available: List[str]): + """:meta private:""" if name not in available: raise cls(name, available) @@ -37,6 +38,7 @@ def check( cls, name: str, fn: Callable, registry: Mapping[str, types.ProfileAugmentation] ): + """:meta private:""" if name in registry: raise cls(name, fn, registry[name].fn) @@ -63,7 +65,7 @@ class InvalidCfgBlock(ValueError): # pragma: no cover -- not supposed to happen - """Something is wrong with the provided CFG AST, the given block is not valid.""" + """Something is wrong with the provided ``.ini/.cfg`` AST""" def __init__(self, block): super().__init__(f"{block.__class__}: {block}", {"block_object": block}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml/intermediate_repr.py new/ini2toml-0.14/src/ini2toml/intermediate_repr.py --- old/ini2toml-0.13/src/ini2toml/intermediate_repr.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml/intermediate_repr.py 2024-04-20 21:23:26.000000000 +0200 @@ -1,6 +1,7 @@ """Intermediate representations used by ``ini2toml`` when transforming between the INI and TOML syntaxes. """ + from collections import UserList from enum import Enum from itertools import chain diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml/plugins/best_effort.py new/ini2toml-0.14/src/ini2toml/plugins/best_effort.py --- old/ini2toml-0.13/src/ini2toml/plugins/best_effort.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml/plugins/best_effort.py 2024-04-20 21:23:26.000000000 +0200 @@ -7,8 +7,8 @@ M = TypeVar("M", bound=IntermediateRepr) -SECTION_SPLITTER = re.compile(r"\.|:|\\") -KEY_SEP = "=" +_SECTION_SPLITTER = re.compile(r"\.|:|\\") +_KEY_SEP = "=" def activate(translator: Translator): @@ -23,12 +23,12 @@ def __init__( self, - key_sep=KEY_SEP, - section_splitter=SECTION_SPLITTER, + key_sep=_KEY_SEP, + section_splitter=_SECTION_SPLITTER, ): self.key_sep = key_sep self.section_splitter = section_splitter - self.split_dict = partial(split_kv_pairs, key_sep=KEY_SEP) + self.split_dict = partial(split_kv_pairs, key_sep=key_sep) def process_values(self, doc: M) -> M: doc_items = list(doc.items()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml/plugins/profile_independent_tasks.py new/ini2toml-0.14/src/ini2toml/plugins/profile_independent_tasks.py --- old/ini2toml-0.13/src/ini2toml/plugins/profile_independent_tasks.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml/plugins/profile_independent_tasks.py 2024-04-20 21:23:26.000000000 +0200 @@ -1,4 +1,5 @@ """Profile-independent tasks implemented via *profile augmentation*.""" + import re from functools import wraps from typing import Callable diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml/plugins/pytest.py new/ini2toml-0.14/src/ini2toml/plugins/pytest.py --- old/ini2toml-0.13/src/ini2toml/plugins/pytest.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml/plugins/pytest.py 2024-04-20 21:23:26.000000000 +0200 @@ -1,16 +1,19 @@ # https://docs.pytest.org/en/latest/reference/reference.html#configuration-options # https://docs.pytest.org/en/latest/reference/customize.html#config-file-formats +import logging from collections.abc import MutableMapping from functools import partial -from typing import TypeVar +from typing import TypeVar, Union -from ..transformations import coerce_scalar, split_list -from ..types import IntermediateRepr, Translator +from ..transformations import coerce_scalar, remove_comments, split_comment, split_list +from ..types import Commented, IntermediateRepr, Translator R = TypeVar("R", bound=IntermediateRepr) -split_spaces = partial(split_list, sep=" ") -split_lines = partial(split_list, sep="\n") +_logger = logging.getLogger(__name__) + +_split_spaces = partial(split_list, sep=" ") +_split_lines = partial(split_list, sep="\n") # ^ most of the list values in pytest use whitespace separators, # but markers/filterwarnings are a special case. @@ -60,8 +63,30 @@ if field in self.DONT_TOUCH: continue if field in self.LINE_SEPARATED_LIST_VALUES: - section[field] = split_lines(section[field]) + section[field] = _split_lines(section[field]) elif field in self.SPACE_SEPARATED_LIST_VALUES: - section[field] = split_spaces(section[field]) + section[field] = _split_spaces(section[field]) + elif hasattr(self, f"_process_{field}"): + section[field] = getattr(self, f"_process_{field}")(section[field]) else: section[field] = coerce_scalar(section[field]) + + def _process_addopts(self, content: str) -> Union[Commented[str], str]: + # pytest-dev/pytest#12228: pytest maintainers recommend addopts as string. + # However, it cannot handle embedded comments, so we have to strip them. + + if "\n" not in content: + # It is easy to handle inline comments for a single line. + return split_comment(content) + + if "#" not in content: + return content + + msg = ( + "Stripping comments from `tool.pytest.ini_options.addopts`.\n" + "This field is recommended to be a string, however it cannot " + "contain embedded comments (ref: pytest-dev/pytest#12228)." + ) + _logger.warning(msg) + + return remove_comments(content) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml/plugins/setuptools_pep621.py new/ini2toml-0.14/src/ini2toml/plugins/setuptools_pep621.py --- old/ini2toml-0.13/src/ini2toml/plugins/setuptools_pep621.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml/plugins/setuptools_pep621.py 2024-04-20 21:23:26.000000000 +0200 @@ -438,8 +438,13 @@ options = doc["options"] # Abort when not using find or find_namespace packages = options.get("packages") + if not isinstance(packages, Directive): + if "options.packages.find" in doc: + _ConfusingPackagesConfig.emit() + doc.pop("options.packages.find", None) return doc + prefix = packages.kind.replace("_", "-") # Enhancement #1: Unify find and find_namespaces, using `namespaces` as a flag options["packages"] = Directive("find", {"namespaces": "namespace" in prefix}) @@ -845,3 +850,24 @@ keep1, keep2 = values1[:-1], values2[1:] shared = values1[-1].strip().strip("\\").strip() + " " + values2[0].strip() return Commented(keep1 + [shared] + keep2, line2.comment) + + +class _ConfusingPackagesConfig(UserWarning): + _MSG = """Confusing configuration `[options.packages.find]`. + + Original configuration sets both: + + - `[options] packages = ...` as a list of named packages + - `[options.packages.find]` + + The confusion comes from the fact that `[options.packages.find]` should be used + with `[options] packages = find:` or `[options] packages = find_namespace:`. + + Conversion will ignore `[options.packages.find]`, as it cannot be written in the + TOML format when `[options] packages = ...` is already given. + """ + __doc__ = _MSG + + @classmethod + def emit(cls, msg=_MSG, stacklevel=1): + warnings.warn(msg, category=cls, stacklevel=stacklevel + 1) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml/transformations.py new/ini2toml-0.14/src/ini2toml/transformations.py --- old/ini2toml-0.13/src/ini2toml/transformations.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml/transformations.py 2024-04-20 21:23:26.000000000 +0200 @@ -1,4 +1,17 @@ -"""Reusable value and type casting transformations""" +""" +Reusable value and type casting transformations + +This module is stable on a "best effort"-basis, and small backwards incompatibilities +can be introduced in "minor"/"patch" version bumps (it will not be considered a +regression automatically). +While it can be used by plugin writers, it is not intended for general public use. + +.. testsetup:: * + + # workaround for missing import in sphinx-doctest + from ini2toml.transformations import * +""" + import warnings from collections.abc import MutableMapping from functools import reduce, wraps @@ -45,7 +58,7 @@ For example: transforming ``"2"`` (string) into ``2`` (integer). - The second one tries to preserve metadata (such as comments) from the original CFG/INI file. This kind of transformation processes a string value into an intermediary - representation (e.g. :obj:`Commented`, :obj:`CommentedList`, obj:`CommentedKV`) + representation (e.g. :obj:`Commented`, :obj:`CommentedList`, :obj:`CommentedKV`) that needs to be properly handled before adding to the TOML document. In a higher level we can also consider an ensemble of transformations that transform an @@ -60,15 +73,18 @@ def noop(x: T) -> T: + """Return the value unchanged""" return x def is_true(value: str) -> bool: + """``value in ("true", "1", "yes", "on")``""" value = value.lower() return value in ("true", "1", "yes", "on") def is_false(value: str) -> bool: + """``value in ("false", "0", "no", "off", "none", "null", "nil")``""" value = value.lower() return value in ("false", "0", "no", "off", "none", "null", "nil") @@ -79,6 +95,7 @@ def coerce_bool(value: str) -> bool: + """Convert the value based on :func:`~.is_true` and :func:`~.is_false`.""" if is_true(value): return True if is_false(value): @@ -140,18 +157,17 @@ @overload -def split_comment(value: str, *, comment_prefixes=CP) -> Commented[str]: - ... +def split_comment(value: str, *, comment_prefixes=CP) -> Commented[str]: ... @overload def split_comment( value: str, coerce_fn: CoerceFn[T], comment_prefixes=CP -) -> Commented[T]: - ... +) -> Commented[T]: ... def split_comment(value, coerce_fn=noop, comment_prefixes=CP): + """Split a "comment suffix" from the value.""" if not isinstance(value, str): return value value = value.strip() @@ -162,14 +178,15 @@ return Commented(coerce_fn(value)) if any(value.startswith(p) for p in comment_prefixes): - return Commented(comment=_strip_comment(value, comment_prefixes)) + return Commented(comment=_strip_prefix(value, comment_prefixes)) prefix = prefixes[0] # We can only analyse one... value, _, cmt = value.partition(prefix) - return Commented(coerce_fn(value.strip()), _strip_comment(cmt, comment_prefixes)) + return Commented(coerce_fn(value.strip()), _strip_prefix(cmt, comment_prefixes)) def split_scalar(value: str, *, comment_prefixes=CP) -> Commented[Scalar]: + """Combination of :func:`~.split_comment` and :func:`~.coerce_scalar`.""" return split_comment(value, coerce_scalar, comment_prefixes) @@ -181,8 +198,7 @@ subsplit_dangling=True, comment_prefixes=CP, force_multiline=False, -) -> CommentedList[str]: - ... +) -> CommentedList[str]: ... @overload @@ -194,8 +210,7 @@ subsplit_dangling=True, comment_prefixes=CP, force_multiline=False, -) -> CommentedList[T]: - ... +) -> CommentedList[T]: ... @overload @@ -206,8 +221,7 @@ subsplit_dangling=True, comment_prefixes=CP, force_multiline=False, -) -> CommentedList[T]: - ... +) -> CommentedList[T]: ... def split_list( @@ -247,8 +261,7 @@ pair_sep=",", subsplit_dangling=True, comment_prefixes=CP, -) -> CommentedKV[str]: - ... +) -> CommentedKV[str]: ... @overload @@ -260,8 +273,7 @@ pair_sep=",", subsplit_dangling=True, comment_prefixes=CP, -) -> CommentedKV[T]: - ... +) -> CommentedKV[T]: ... @overload @@ -272,8 +284,7 @@ pair_sep=",", subsplit_dangling=True, comment_prefixes=CP, -) -> CommentedKV[T]: - ... +) -> CommentedKV[T]: ... def split_kv_pairs( @@ -326,25 +337,21 @@ @overload -def pipe(fn1: FN[S, T], fn2: FN[T, U]) -> FN[S, U]: - ... +def pipe(fn1: FN[S, T], fn2: FN[T, U]) -> FN[S, U]: ... @overload -def pipe(fn1: FN[S, T], fn2: FN[T, U], fn3: FN[U, V]) -> FN[S, V]: - ... +def pipe(fn1: FN[S, T], fn2: FN[T, U], fn3: FN[U, V]) -> FN[S, V]: ... @overload -def pipe(fn1: FN[S, T], fn2: FN[T, U], fn3: FN[U, V], fn4: FN[V, X]) -> FN[S, X]: - ... +def pipe(fn1: FN[S, T], fn2: FN[T, U], fn3: FN[U, V], fn4: FN[V, X]) -> FN[S, X]: ... @overload def pipe( fn1: FN[S, T], fn2: FN[T, U], fn3: FN[U, V], fn4: FN[V, X], fn5: FN[X, Y] -) -> FN[S, Y]: - ... +) -> FN[S, Y]: ... @overload @@ -355,8 +362,7 @@ fn4: FN[V, X], fn5: FN[X, Y], *fn: FN[Y, Y], -) -> FN[S, Y]: - ... +) -> FN[S, Y]: ... def pipe(*fns): @@ -369,10 +375,44 @@ return lambda x: reduce(apply, fns, x) +def remove_comments(text: str, comment_mark="#") -> str: + r""" + >>> remove_comments("\nhello\n#comment\nworld\n") + '\nhello\nworld\n' + >>> remove_comments("\n hello\n\nworld\n") + '\n hello\n\nworld\n' + >>> remove_comments("\nhello\n \nworld\n") + '\nhello\n \nworld\n' + """ + lines = (_line_remove_comment(x, comment_mark) for x in text.splitlines()) + suffix = "\n" if text.strip(" ").endswith("\n") else "" + return "\n".join(x for x in lines if x is not None) + suffix + + # ---- Private Helpers ---- -def _strip_comment(msg: Optional[str], prefixes: Sequence[str] = CP) -> Optional[str]: +def _line_remove_comment(line: str, comment_mark="#") -> Optional[str]: + """ + >>> print(_line_remove_comment(" # hello world")) + None + >>> _line_remove_comment(" abc # hello world") + ' abc' + >>> _line_remove_comment(" ") + ' ' + >>> _line_remove_comment("") + '' + """ + if comment_mark not in line: + # Keep empty lines intentionally added by the user + return line + if line.strip().startswith(comment_mark): + # Remove empty lines that were just inserted as a replacement for comments + return None + return line.partition(comment_mark)[0].rstrip() + + +def _strip_prefix(msg: Optional[str], prefixes: Sequence[str] = CP) -> Optional[str]: if not msg: return None return remove_prefixes(msg, prefixes) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml/translator.py new/ini2toml-0.14/src/ini2toml/translator.py --- old/ini2toml-0.13/src/ini2toml/translator.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml/translator.py 2024-04-20 21:23:26.000000000 +0200 @@ -8,6 +8,10 @@ _logger = logging.getLogger(__name__) +# TODO: Once MyPy/other type checkers handle ``partial`` and ``partialmethod`` +# we probably can use them to simply the implementations in this module. + + class Translator(BaseTranslator[str]): """``Translator`` is the main public Python API exposed by the ``ini2toml``, to convert strings representing ``.ini/.cfg`` files into the ``TOML`` syntax. @@ -20,6 +24,8 @@ while ``BaseTranslator`` requires the user to explicitly set these parameters. For most of the users ``Translator`` is recommended over ``BaseTranslator``. + Most of the times ``Translator`` (or its deterministic variants ``LiteTranslator``, + ``FullTranslator``) is recommended over ``BaseTranslator``. See :class:`~ini2toml.base_translator.BaseTranslator` for a description of the instantiation parameters. @@ -67,3 +73,73 @@ raise return convert + + +class LiteTranslator(Translator): + """Similar to ``Translator``, but instead of trying to figure out ``ini_loads_fn`` + and ``toml_dumps_fn`` is will always try to use the ``lite`` flavour + (ignoring comments). + """ + + def __init__( + self, + plugins: Optional[Sequence[types.Plugin]] = None, + profiles: Sequence[types.Profile] = (), + profile_augmentations: Sequence[types.ProfileAugmentation] = (), + ini_parser_opts: Mapping = EMPTY, + ini_loads_fn: Optional[types.IniLoadsFn] = None, + toml_dumps_fn: Optional[types.TomlDumpsFn] = None, + ): + if ini_loads_fn: + parse = ini_loads_fn + else: + from .drivers.configparser import parse + + if toml_dumps_fn: + convert = toml_dumps_fn + else: + from .drivers.lite_toml import convert + + super().__init__( + ini_loads_fn=parse, + toml_dumps_fn=convert, + plugins=plugins, + ini_parser_opts=ini_parser_opts, + profiles=profiles, + profile_augmentations=profile_augmentations, + ) + + +class FullTranslator(Translator): + """Similar to ``Translator``, but instead of trying to figure out ``ini_loads_fn`` + and ``toml_dumps_fn`` is will always try to use the ``full`` flavour + (best effort to maintain comments). + """ + + def __init__( + self, + plugins: Optional[Sequence[types.Plugin]] = None, + profiles: Sequence[types.Profile] = (), + profile_augmentations: Sequence[types.ProfileAugmentation] = (), + ini_parser_opts: Mapping = EMPTY, + ini_loads_fn: Optional[types.IniLoadsFn] = None, + toml_dumps_fn: Optional[types.TomlDumpsFn] = None, + ): + if ini_loads_fn: + parse = ini_loads_fn + else: + from .drivers.configupdater import parse + + if toml_dumps_fn: + convert = toml_dumps_fn + else: + from .drivers.full_toml import convert + + super().__init__( + ini_loads_fn=parse, + toml_dumps_fn=convert, + plugins=plugins, + ini_parser_opts=ini_parser_opts, + profiles=profiles, + profile_augmentations=profile_augmentations, + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml/types.py new/ini2toml-0.14/src/ini2toml/types.py --- old/ini2toml-0.13/src/ini2toml/types.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml/types.py 2024-04-20 21:23:26.000000000 +0200 @@ -43,6 +43,8 @@ class CLIChoice(Protocol): + """:meta private:""" + name: str help_text: str @@ -60,14 +62,14 @@ name: str help_text: str - def fn(self, profile: Profile): - ... + def fn(self, profile: Profile): ... def is_active(self, explicitly_active: Optional[bool] = None) -> bool: """``explicitly_active`` is a tree-state variable: ``True`` if the user explicitly asked for the augmentation, ``False`` if the user explicitly denied the augmentation, or ``None`` otherwise. """ + ... class Translator(Protocol): @@ -75,6 +77,7 @@ """Create and register (and return) a translation :class:`Profile` (or return a previously registered one) (see :ref:`core-concepts`). """ + ... def augment_profiles( self, @@ -89,6 +92,7 @@ strings), ``name`` is taken from ``fn.__name__`` and ``help_text`` is taken from ``fn.__doc__`` (docstring). """ + ... Plugin = Callable[[Translator], None] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml.egg-info/PKG-INFO new/ini2toml-0.14/src/ini2toml.egg-info/PKG-INFO --- old/ini2toml-0.13/src/ini2toml.egg-info/PKG-INFO 2023-10-23 14:17:02.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml.egg-info/PKG-INFO 2024-04-20 21:24:34.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: ini2toml -Version: 0.13 +Version: 0.14 Summary: Automatically conversion of .ini/.cfg files to TOML equivalents Home-page: https://github.com/abravalheri/ini2toml/ Author: Anderson Bravalheri @@ -138,9 +138,86 @@ To do so, don't forget to add it to your `virtual environment`_ or specify it as a `project dependency`_. +Note that the class ``Translator`` will try to guess which flavour to use based +on the available installed dependencies. If you need something more +deterministic, consider using ``LiteTranslator`` and ``FullTranslator``, +or explicitly specifying the ``ini_loads_fn`` and ``toml_dumps_fn`` keyword +arguments in the constructor. + More details about ``ini2toml`` and its Python API can be found in `our docs`_. +Limitations +=========== + +``ini2toml`` will try its best to create good quality translations from +``.ini/.cfg`` into ``.toml`` files. However the tool comes with a set of +well known limitations: + +* Although ``ini2toml`` attempts to keep the same order/sequence as the original + information was written, sometimes that is not compatible with the TOML + syntax, and things end up moving around a bit. + +* ``ini2toml`` uses `ConfigParser`_ + `tomli-w`_ for implementing the *"lite"* flavour + and `ConfigUpdater`_ + `tomlkit`_ for implementing the *"full"* flavour. + Therefore it inherits the limitations from those libraries (please check + their documentation for more information). For example: + + * `ConfigUpdater`_, will have trouble to parse interpolations and the related + escaping sequence (``%%``) (in this respect, it behaves more similarly to + ``RawConfigParser`` than ``ConfigParser``). + + * `tomli-w`_ is not very flexible regarding formatting and will have trouble + with multi-line strings. + +* ``ini2toml`` *expects the input to be valid* and will not perform extensive + checks on the provided document. If something in the output is not working as you would + expect, it might be a good idea to check the input. + +* ``.ini/.cfg`` files are used in a plethora of use cases and it is impossible + to cover all of them in a single code base. Even when considering + ``setup.cfg``, there are many packages that define different sections in the + document in addition to the basic definition by ``setuptools``. + Because of that ``ini2toml``, adopts a "best-effort" approach, that might not + correspond to what you expect. If that is the case please consider + contributing or creating your own `plugin`_. + +* The translation procedure analyse only the given input. If the original + ``.ini/.cfg`` file contains references to other files, or behaves differently + depending on the existence/presence of other files and directories, the + translation will not take that into consideration. + +Therefore it is recommended to double check the output and fix any +problems before using the ``.toml`` files in production. + + +Can ``ini2toml`` also translate ``setup.py`` into ``pyproject.toml``? +===================================================================== + +Working with ``.py`` files is not in the scope of the ``ini2toml`` project, +and therefore this feature is not implemented. + +However, you can probably find some tools on PyPI to translate from +``setup.py`` into ``setup.cfg``, like `setup-py-upgrade`_ and +`setuptools-py2cfg`_ [#untested]_. + +Once you have ``setup.cfg``, you can use ``ini2toml`` [#setuppy]_. + +.. [#untested] Such tools are neither maintained by this project, + nor tested for integration by ``ini2toml``. + It is best to try some of them out and find the one that works for you. + Manual corrections might be needed. + +.. [#setuppy] Please note that ``setup.py`` is a very dynamic + format and that not everything can be represented in ``setup.cfg`` or + ``pyproject.toml``. Indeed, the `setuptools' docs`_ explicitly say that + ``setup.py`` can be used in tandem with ``pyproject.toml``: ideally all the + declarative metadata goes to ``pyproject.toml``, but you can keep the + dynamic bits in ``setup.py``. + Remember: ``setup.py`` is a perfectly valid and non deprecated configuration file; + what is deprecated is running it as a CLI tool, i.e. ``python setup.py ...``. + + .. _pyscaffold-notes: .. tip:: @@ -167,8 +244,14 @@ .. _ini_cfg: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure .. _our docs: https://ini2toml.readthedocs.io .. _PEP 621: https://www.python.org/dev/peps/pep-0621/ -.. _pipx: https://pypa.github.io/pipx/ +.. _pipx: https://pipx.pypa.io/stable/ .. _project dependency: https://packaging.python.org/tutorials/managing-dependencies/ +.. _plugin: https://ini2toml.readthedocs.io/en/latest/dev-guide.html#plugins +.. _setup-py-upgrade: https://pypi.org/project/setup-cfg-fmt/ +.. _setuptools-py2cfg: https://pypi.org/project/setuptools-py2cfg/ +.. _setuptools' docs: https://setuptools.pypa.io/en/latest/userguide/quickstart.html#setuppy-discouraged .. _TOML: https://toml.io/en/ .. _TOML library: https://github.com/sdispater/tomlkit +.. _tomli-w: https://pypi.org/project/tomli-w/ +.. _tomlkit: https://tomlkit.readthedocs.io/en/latest/ .. _virtual environment: https://realpython.com/python-virtual-environments-a-primer/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/src/ini2toml.egg-info/SOURCES.txt new/ini2toml-0.14/src/ini2toml.egg-info/SOURCES.txt --- old/ini2toml-0.13/src/ini2toml.egg-info/SOURCES.txt 2023-10-23 14:17:02.000000000 +0200 +++ new/ini2toml-0.14/src/ini2toml.egg-info/SOURCES.txt 2024-04-20 21:24:34.000000000 +0200 @@ -24,6 +24,7 @@ docs/dev-guide.rst docs/index.rst docs/license.rst +docs/public_api_docs.py docs/readme.rst docs/requirements.txt docs/setuptools_pep621.rst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/tests/examples/pyscaffold/setup.cfg new/ini2toml-0.14/tests/examples/pyscaffold/setup.cfg --- old/ini2toml-0.13/tests/examples/pyscaffold/setup.cfg 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/tests/examples/pyscaffold/setup.cfg 2024-04-20 21:23:26.000000000 +0200 @@ -117,9 +117,8 @@ # in order to write a coverage file that can be read by Jenkins. # CAUTION: --cov flags may prohibit setting breakpoints while debugging. # Comment those flags to avoid this pytest issue. -addopts = - --cov pyscaffold --cov-config .coveragerc --cov-report term-missing - --verbose +addopts = --cov pyscaffold --cov-config .coveragerc --cov-report term-missing + --verbose # In order to use xdist, the developer can add, for example, the following # arguments: # --dist=load --numprocesses=auto diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/tests/plugins/test_pytest.py new/ini2toml-0.14/tests/plugins/test_pytest.py --- old/ini2toml-0.13/tests/plugins/test_pytest.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/tests/plugins/test_pytest.py 2024-04-20 21:23:26.000000000 +0200 @@ -1,54 +1,77 @@ from textwrap import dedent +import pytest import tomli from ini2toml.drivers import full_toml, lite_toml -from ini2toml.plugins import pytest -from ini2toml.translator import Translator +from ini2toml.plugins.pytest import activate +from ini2toml.translator import FullTranslator +EXAMPLES = { + "simple": { + "example": """\ + [pytest] + minversion = 6.0 + addopts = -ra -q --cov ini2toml + testpaths = tests + python_files = test_*.py check_*.py example_*.py + required_plugins = pytest-django>=3.0.0,<4.0.0 pytest-html pytest-xdist>=1.0.0 + norecursedirs = + dist + build + .tox + filterwarnings= + error + ignore:Please use `dok_matrix` from the `scipy\\.sparse` namespace, the `scipy\\.sparse\\.dok` namespace is deprecated.:DeprecationWarning + """, # noqa + "expected": """\ + [pytest] + [pytest.ini_options] + minversion = "6.0" + addopts = "-ra -q --cov ini2toml" + testpaths = ["tests"] + python_files = ["test_*.py", "check_*.py", "example_*.py"] + required_plugins = ["pytest-django>=3.0.0,<4.0.0", "pytest-html", "pytest-xdist>=1.0.0"] + norecursedirs = [ + "dist", + "build", + ".tox", + ] + filterwarnings = [ + "error", + 'ignore:Please use `dok_matrix` from the `scipy\\.sparse` namespace, the `scipy\\.sparse\\.dok` namespace is deprecated.:DeprecationWarning', + ] + """, # noqa + }, + "multiline-addopts": { + "example": """\ + [pytest] + addopts = -ra -q + # comment + --cov ini2toml + """, + "expected": '''\ + [pytest.ini_options] + addopts = """ + -ra -q + --cov ini2toml""" + ''', + }, +} -def test_pytest(): - example = """\ - [pytest] - minversion = 6.0 - addopts = -ra -q --cov ini2toml - testpaths = tests - python_files = test_*.py check_*.py example_*.py - required_plugins = pytest-django>=3.0.0,<4.0.0 pytest-html pytest-xdist>=1.0.0 - norecursedirs = - dist - build - .tox - filterwarnings= - error - ignore:Please use `dok_matrix` from the `scipy\\.sparse` namespace, the `scipy\\.sparse\\.dok` namespace is deprecated.:DeprecationWarning - """ # noqa - expected = """\ - [pytest] - [pytest.ini_options] - minversion = "6.0" - addopts = "-ra -q --cov ini2toml" - testpaths = ["tests"] - python_files = ["test_*.py", "check_*.py", "example_*.py"] - required_plugins = ["pytest-django>=3.0.0,<4.0.0", "pytest-html", "pytest-xdist>=1.0.0"] - norecursedirs = [ - "dist", - "build", - ".tox", - ] - filterwarnings = [ - "error", - 'ignore:Please use `dok_matrix` from the `scipy\\.sparse` namespace, the `scipy\\.sparse\\.dok` namespace is deprecated.:DeprecationWarning', - ] - """ # noqa - for convert in (lite_toml.convert, full_toml.convert): - translator = Translator(plugins=[pytest.activate], toml_dumps_fn=convert) - out = translator.translate(dedent(example), "pytest.ini").strip() - expected = dedent(expected).strip() - print("expected=\n" + expected + "\n***") - print("out=\n" + out) - try: - assert expected in out - except AssertionError: - # At least the Python-equivalents when parsing should be the same - assert tomli.loads(expected) == tomli.loads(out) + +@pytest.mark.parametrize("example_name", EXAMPLES.keys()) +@pytest.mark.parametrize("convert", [full_toml.convert, lite_toml.convert]) +def test_pytest(example_name, convert): + example = EXAMPLES[example_name]["example"] + expected = EXAMPLES[example_name]["expected"] + translator = FullTranslator(plugins=[activate], toml_dumps_fn=convert) + out = translator.translate(dedent(example), "pytest.ini").strip() + expected = dedent(expected).strip() + print("expected=\n" + expected + "\n***") + print("out=\n" + out) + try: + assert expected in out + except AssertionError: + # At least the Python-equivalents when parsing should be the same + assert tomli.loads(expected) == tomli.loads(out) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/tests/plugins/test_setuptools_pep621.py new/ini2toml-0.14/tests/plugins/test_setuptools_pep621.py --- old/ini2toml-0.13/tests/plugins/test_setuptools_pep621.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/tests/plugins/test_setuptools_pep621.py 2024-04-20 21:23:26.000000000 +0200 @@ -2,7 +2,12 @@ import tomli from ini2toml.plugins.profile_independent_tasks import remove_empty_table_headers -from ini2toml.plugins.setuptools_pep621 import Directive, SetuptoolsPEP621, activate +from ini2toml.plugins.setuptools_pep621 import ( + Directive, + SetuptoolsPEP621, + _ConfusingPackagesConfig, + activate, +) from ini2toml.translator import Translator @@ -738,3 +743,37 @@ print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") print(doc) assert convert(doc) == expected_convert_directives + + +# ---- + +# Example taken from https://github.com/aio-libs/aiohttp/ +# In general, you should either list packages explicit or use find, +# but some configs change and `options.packages.find` is left behind +example_explicit_packages_with_find = """\ +[options] +packages = aiohttp +[options.packages.find] +exclude = + examples +""" + +# In those cases, `ini2toml` should remove `[options.packages.find]` +# And warn about it +expected_explicit_packages_with_find = """\ +[tool.setuptools] +packages = ["aiohttp"] +""" + + +def test_explicit_packages_with_find(plugin, parse, convert): + doc = parse(example_explicit_packages_with_find) + print(doc) + doc = plugin.apply_value_processing(doc) + print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") + with pytest.warns(_ConfusingPackagesConfig, match="[options.packages.find]"): + doc = plugin.handle_packages_find(doc) + doc.rename("options", ("tool", "setuptools")) + print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") + print(doc) + assert convert(doc) == expected_explicit_packages_with_find diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/tests/test_examples.py new/ini2toml-0.14/tests/test_examples.py --- old/ini2toml-0.13/tests/test_examples.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/tests/test_examples.py 2024-04-20 21:23:26.000000000 +0200 @@ -9,7 +9,7 @@ from ini2toml import cli from ini2toml.drivers import configparser, full_toml, lite_toml -from ini2toml.translator import Translator +from ini2toml.translator import FullTranslator, LiteTranslator def examples(): @@ -39,7 +39,7 @@ @pytest.mark.filterwarnings("ignore::DeprecationWarning") @pytest.mark.parametrize(("original", "expected"), list(examples())) def test_examples_api(original, expected, validate): - translator = Translator() + translator = FullTranslator() available_profiles = list(translator.profiles.keys()) profile = cli.guess_profile(None, original, available_profiles) orig = Path(original) @@ -71,7 +71,7 @@ @pytest.mark.parametrize(("original", "expected"), list(examples())) def test_examples_api_lite(original, expected, validate): opts = {"ini_loads_fn": configparser.parse, "toml_dumps_fn": lite_toml.convert} - translator = Translator(**opts) + translator = LiteTranslator(**opts) available_profiles = list(translator.profiles.keys()) profile = cli.guess_profile(None, original, available_profiles) # We cannot compare "flake8" sections (currently not handled) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ini2toml-0.13/tests/test_translator.py new/ini2toml-0.14/tests/test_translator.py --- old/ini2toml-0.13/tests/test_translator.py 2023-10-23 14:15:48.000000000 +0200 +++ new/ini2toml-0.14/tests/test_translator.py 2024-04-20 21:23:26.000000000 +0200 @@ -5,7 +5,7 @@ from ini2toml.errors import UndefinedProfile from ini2toml.plugins import profile_independent_tasks from ini2toml.profile import Profile, ProfileAugmentation -from ini2toml.translator import Translator +from ini2toml.translator import FullTranslator, LiteTranslator, Translator def test_simple_example(): @@ -139,3 +139,31 @@ ] translator = Translator(plugins=plugins) assert len(translator.plugins) == 1 + + +minimal_example = """\ +# comment + +[section1] +option1 = value +option2 = value # option comments are considered part of the value +""" + + +def test_lite_translator(): + parser_opts = {"inline_comment_prefixes": ["#"]} + translator = LiteTranslator(plugins=[], ini_parser_opts=parser_opts) + # ensure profile exists + translator["simple"] + out = translator.translate(dedent(minimal_example), "simple") + assert "# comment" not in out + assert "part of the value" not in out + + +def test_full_translator(): + translator = FullTranslator(plugins=[]) + # ensure profile exists + translator["simple"] + out = translator.translate(dedent(minimal_example), "simple") + assert "# comment" in out + assert "part of the value" in out