diff --git a/.coverage b/.coverage new file mode 100644 index 0000000..672c021 Binary files /dev/null and b/.coverage differ diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 4477245..9396564 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -13,6 +13,7 @@ jobs: fail-fast: false matrix: python-version: [pypy-3.10, '3.10', '3.11' , '3.12', '3.13'] + tox-python-version: ['pypy3.10', 'py310', 'py311' , 'py312', 'py313'] os: [ ubuntu-latest, windows-latest, @@ -20,36 +21,48 @@ jobs: ] include: # Add exact version 3.14.0-alpha.0 for ubuntu-latest only - - python-version: '3.14.0-alpha.0' + - python-version: '3.14.0-alpha.1' + tox-python-version: py314-full os: ubuntu-latest exclude: # Exclude other OSes with Python 3.14.0-alpha.0 - - python-version: '3.14.0-alpha.0' + - python-version: '3.14.0-alpha.1' + tox-python-version: py314-full os: windows-latest - - python-version: '3.14.0-alpha.0' + - python-version: '3.14.0-alpha.1' os: macos-latest + tox-python-version: py314-full steps: - - uses: actions/checkout@v4.1.7 + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5.2.0 with: python-version: ${{ matrix.python-version }} allow-prereleases: true + - name: install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: requirements-dev.txt + - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install --upgrade -r requirements-dev.txt - pip install . - - name: Test with pytest - run: | - pytest --cov --junitxml=junit.xml + run: uv pip install --system tox tox-uv + + - name: Run tox targets for ${{ matrix.python-version }} + run: tox -e ${{matrix.tox-python-version}} + - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} + - name: Upload test results to Codecov if: ${{ !cancelled() }} uses: codecov/test-results-action@v1 with: token: ${{ secrets.CODECOV_TOKEN }} + diff --git a/.gitignore b/.gitignore index da550e9..b97131e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,131 @@ -.idea -.tox -*.pyc -json2xml.egg-info -build -*.swp -dist/* -.mypy_cache -.eggs -.DS_Store -venv -.vscode -tests/*.ipynb -junit.xml +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +.idea/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ad27619 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,64 @@ +ci: + autofix_commit_msg: | + ci: auto fixes from pre-commit hooks + + for more information, see https://pre-commit.ci + autofix_prs: true + autoupdate_commit_msg: 'ci: pre-commit autoupdate' + autoupdate_schedule: monthly + +default_language_version: + python: python3.12 + +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-added-large-files + - id: check-case-conflict + - id: check-json + - id: check-merge-conflict + - id: check-symlinks + - id: check-toml + - id: end-of-file-fixer + - id: trailing-whitespace +- repo: https://github.com/tox-dev/pyproject-fmt + rev: v2.5.0 + hooks: + - id: pyproject-fmt +- repo: https://github.com/tox-dev/tox-ini-fmt + rev: 1.4.1 + hooks: + - id: tox-ini-fmt +- repo: https://github.com/rstcheck/rstcheck + rev: v6.2.4 + hooks: + - id: rstcheck + additional_dependencies: + - tomli==2.0.1 +- repo: https://github.com/asottile/pyupgrade + rev: v3.19.1 + hooks: + - id: pyupgrade + args: [--py38-plus] +- repo: https://github.com/psf/black-pre-commit-mirror + rev: 24.10.0 + hooks: + - id: black +- repo: https://github.com/adamchainz/blacken-docs + rev: 1.19.1 + hooks: + - id: blacken-docs + additional_dependencies: + - black==23.1.0 +- repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + name: isort (python) +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.13.0 + hooks: + - id: mypy + additional_dependencies: + - django-stubs==5.0.4 diff --git a/json2xml/dicttoxml.py b/json2xml/dicttoxml.py index 2ba6af9..d51d98b 100644 --- a/json2xml/dicttoxml.py +++ b/json2xml/dicttoxml.py @@ -269,10 +269,10 @@ def dict2xml_str( val_attr: dict[str, str] = item.pop("@attrs", attr) # update attr with custom @attr if exists rawitem = item["@val"] if "@val" in item else item if is_primitive_type(rawitem): + if isinstance(rawitem, dict): + subtree = escape_xml(str(rawitem)) if isinstance(rawitem, str): subtree = escape_xml(rawitem) - else: - subtree = rawitem else: # we can not use convert_dict, because rawitem could be non-dict subtree = convert( diff --git a/junit.xml b/junit.xml new file mode 100644 index 0000000..fef39a8 --- /dev/null +++ b/junit.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 9434ded..4f4dc6c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,56 @@ +[build-system] +requires = ["setuptools>=42", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "json2xml" +version = "5.0.5" # Replace with the dynamic version if needed +description = "Simple Python Library to convert JSON to XML" +readme = "README.rst" +requires-python = ">=3.10" +license = { text = "Apache Software License 2.0" } +keywords = ["json2xml"] +authors = [ + { name = "Vinit Kumar", email = "mail@vinitkumar.me" } +] +classifiers = [ + "Development Status :: 6 - Mature", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Natural Language :: English", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Software Development :: Libraries :: Python Modules" +] +dependencies = [ + "defusedxml", + "urllib3", + "xmltodict>=0.12.0", + "pytest", + "pytest-cov", + "coverage", + "py", + "setuptools", +] + +[project.urls] +Homepage = "https://github.com/vinitkumar/json2xml" + +[tool.setuptools.packages.find] +include = ["json2xml"] + +[project.optional-dependencies] +test = [ + "pytest==7.0.1", + "py==1.11.0" +] + +[tool.pytest.ini_options] +testpaths = ["tests"] [tool.ruff] exclude = [ ".env", diff --git a/setup.py b/setup.py index e986ed2..56db080 100644 --- a/setup.py +++ b/setup.py @@ -2,52 +2,6 @@ """The setup script.""" -from setuptools import setup, find_packages -from json2xml import __version__ +from setuptools import setup +setup() -with open("README.rst", encoding="utf-8") as readme_file: - readme = readme_file.read() - -with open("HISTORY.rst", encoding="utf-8") as history_file: - history = history_file.read() - -with open("requirements.in", encoding="utf-8") as requirements_in: - requirements = [requirements_in.read()] - -setup_requirements = [] - -test_requirements = ["pytest==7.0.1", "py==1.11.0"] - -setup( - author="Vinit Kumar", - author_email="mail@vinitkumar.me", - classifiers=[ - "Development Status :: 6 - Mature", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Natural Language :: English", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Topic :: Software Development :: Libraries :: Python Modules", - ], - description="Simple Python Library to convert JSON to XML", - install_requires=requirements, - license="Apache Software License 2.0", - long_description=readme + "\n\n" + history, - long_description_content_type="text/x-rst", - include_package_data=True, - keywords="json2xml", - name="json2xml", - packages=find_packages(include=["json2xml"]), - setup_requires=setup_requirements, - test_suite="tests", - tests_require=test_requirements, - url="https://github.com/vinitkumar/json2xml", - version=__version__, - zip_safe=False, - python_requires=">=3.10", -) diff --git a/tox.ini b/tox.ini index 0d5027e..9b77069 100644 --- a/tox.ini +++ b/tox.ini @@ -1,20 +1,12 @@ [tox] -envlist = py38, py39, py310, py311, py312, pypy38, pypy39 - - -[testenv:flake8] -basepython = python -deps = flake8 -commands = flake8 json2xml +envlist = py310, py311, py312, py313, pypy310, py314-full [testenv] -setenv = - PYTHONPATH = {toxinidir} +deps = + pytest + pytest-cov allowlist_externals = pytest commands = - pip install -r requirements-dev.txt - pytest - - + pytest --cov --junitxml=junit.xml