Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .coverage
Binary file not shown.
113 changes: 97 additions & 16 deletions .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
name: Json2xml

on: [push, pull_request]
on:
push:
branches: [main, master]
paths-ignore:
- 'docs/**'
- '*.md'
- '*.rst'
pull_request:
paths-ignore:
- 'docs/**'
- '*.md'
- '*.rst'

permissions:
contents: read
Expand All @@ -12,31 +23,27 @@ concurrency:
cancel-in-progress: true

jobs:
build:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: [pypy-3.10, pypy-3.11, '3.10', '3.11', '3.12', '3.13']
tox-python-version: [pypy3.10, pypy3.11, py310, py311, py312, py313]
os: [
ubuntu-latest,
windows-latest,
macos-latest,
]
include:
# Add exact version 3.14.0-alpha.0 for ubuntu-latest only
# Add exact version 3.14.0-beta.1 for ubuntu-latest only
- python-version: 3.14.0-beta.1
tox-python-version: py314-full
os: ubuntu-latest
exclude:
# Exclude other OSes with Python 3.14.0-alpha.0
# Exclude other OSes with Python 3.14.0-beta.1
- python-version: 3.14.0-beta.1
tox-python-version: py314-full
os: windows-latest
- python-version: 3.14.0-beta.1
os: macos-latest
tox-python-version: py314-full

steps:
- uses: actions/checkout@v4
Expand All @@ -48,27 +55,101 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: install uv
uses: astral-sh/setup-uv@v3

- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
cache-dependency-glob: requirements-dev.txt
cache-dependency-glob: |
requirements*.txt
requirements-dev.in
pyproject.toml

- name: Install dependencies
run: uv pip install --system tox tox-uv
run: |
uv pip install --system -e .
uv pip install --system pytest pytest-xdist pytest-cov

- name: Run tox targets for ${{ matrix.python-version }}
run: tox -e ${{matrix.tox-python-version}}
- name: Create coverage directory
run: mkdir -p coverage/reports

- name: Run tests
run: |
pytest --cov=json2xml --cov-report=xml:coverage/reports/coverage.xml --cov-report=term -xvs tests -n auto
env:
PYTHONPATH: ${{ github.workspace }}

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
if: success()
with:
directory: ./coverage/reports/
env_vars: OS,PYTHON
fail_ci_if_error: true
files: ./coverage.xml,./coverage2.xml,!./cache
fail_ci_if_error: false # Don't fail CI if codecov upload fails
files: ./coverage/reports/coverage.xml
flags: unittests
token: ${{ secrets.CODECOV_TOKEN }}
name: codecov-umbrella
verbose: true
env:
OS: ${{ matrix.os }}
PYTHON: ${{ matrix.python-version }}

lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false

- name: Set up Python 3.12
uses: actions/setup-python@v5.2.0
with:
python-version: '3.12'

- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
cache-dependency-glob: |
requirements*.txt
requirements-dev.in
pyproject.toml

- name: Install dependencies
run: |
uv pip install --system -e .
uv pip install --system ruff>=0.3.0

- name: Run ruff
run: ruff check json2xml tests

typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false

- name: Set up Python 3.12
uses: actions/setup-python@v5.2.0
with:
python-version: '3.12'

- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
cache-dependency-glob: |
requirements*.txt
requirements-dev.in
pyproject.toml

- name: Install dependencies
run: |
uv pip install --system -e .
uv pip install --system mypy>=1.0.0 types-setuptools

- name: Run mypy
run: mypy json2xml tests

22 changes: 22 additions & 0 deletions AGENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# json2xml AGENT.md

## Build/Test Commands
- Test: `pytest -vv` (all tests) or `pytest tests/test_<name>.py -vv` (single test file)
- Test with coverage: `pytest --cov=json2xml --cov-report=xml:coverage/reports/coverage.xml --cov-report=term -xvs`
- Lint: `ruff check json2xml tests`
- Type check: `mypy json2xml tests`
- Test all Python versions: `tox`
- Clean artifacts: `make clean`

## Architecture
- Main module: `json2xml/` with `json2xml.py` (main converter), `dicttoxml.py` (core conversion), `utils.py` (utilities)
- Core functionality: JSON to XML conversion via `Json2xml` class wrapping `dicttoxml`
- Tests: `tests/` with test files following `test_*.py` pattern

## Code Style (from .cursorrules)
- Always add typing annotations to functions/classes with descriptive docstrings (PEP 257)
- Use pytest (no unittest), all tests in `./tests/` with typing annotations
- Import typing fixtures when TYPE_CHECKING: `CaptureFixture`, `FixtureRequest`, `LogCaptureFixture`, `MonkeyPatch`, `MockerFixture`
- Ruff formatting: line length 119, ignores E501, F403, E701, F401
- Python 3.10+ required, supports up to 3.14
- Dependencies: defusedxml, urllib3, xmltodict, pytest, pytest-cov
20 changes: 14 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,27 @@ clean-pyc: ## remove Python file artifacts
find . -name '__pycache__' -exec rm -fr {} +

clean-test: ## remove test and coverage artifacts
rm -fr .tox/
rm -f .coverage
rm -fr htmlcov/
rm -fr .pytest_cache
rm -fr coverage/

lint: ## check style with flake8
flake8 json2xml tests
lint: ## check style with ruff
ruff check json2xml tests

lint-fix: ## automatically fix ruff issues
ruff check --fix json2xml tests

typecheck: ## check types with mypy
mypy json2xml tests

test: ## run tests quickly with the default Python
pytest -vv
pytest --cov=json2xml --cov-report=xml:coverage/reports/coverage.xml --cov-report=term -xvs tests -n auto

test-simple: ## run tests without coverage
pytest -vv tests

test-all: ## run tests on every Python version with tox
tox
check-all: lint typecheck test ## run all checks (lint, typecheck, test)

coverage: ## check code coverage quickly with the default Python
coverage run -m pytest -vv --disable-warnings
Expand Down
47 changes: 40 additions & 7 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -191,17 +191,50 @@ Outputs this:
The methods are simple and easy to use and there are also checks inside of code to exit cleanly
in case any of the input(file, string or API URL) returns invalid JSON.

How to run tests
^^^^^^^^^^^^^^^^
Development
^^^^^^^^^^^

This is provided by pytest, which is straight forward.
This project uses modern Python development practices. Here's how to set up a development environment:

.. code-block:: console

virtualenv venv -p $(which python3.9)
pip install -r requirements-dev.txt
python setup.py install
pytest -vv
# Create and activate virtual environment (using uv - recommended)
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate

# Install dependencies
uv pip install -r requirements-dev.txt
uv pip install -e .

**Running Tests and Checks**

We provide several ways to run tests and quality checks:

Using Make (recommended):

.. code-block:: console

make test # Run tests with coverage
make lint # Run linting with ruff
make typecheck # Run type checking with mypy
make check-all # Run all checks (lint, typecheck, test)

Using the development script:

.. code-block:: console

python dev.py # Run all checks
python dev.py test # Run tests only
python dev.py lint # Run linting only
python dev.py typecheck # Run type checking only

Using tools directly:

.. code-block:: console

pytest --cov=json2xml --cov-report=term -xvs tests -n auto
ruff check json2xml tests
mypy json2xml tests


Help and Support to maintain this project
Expand Down
60 changes: 60 additions & 0 deletions dev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""Development script for running tests, linting, and type checking."""

import subprocess
import sys
from pathlib import Path


def run_command(cmd: list[str], description: str) -> bool:
"""Run a command and return True if successful."""
print(f"\n🔍 {description}...")
try:
result = subprocess.run(cmd, check=True, cwd=Path(__file__).parent)

Check notice

Code scanning / CodeQL

Unused local variable Note

Variable result is not used.

Copilot Autofix

AI 4 months ago

To fix the issue, we will remove the unused variable result from the code. Since the assignment to result does not have any side effects and is not used elsewhere in the function, we can safely remove the left-hand side of the assignment while keeping the call to subprocess.run. This ensures that the function's behavior remains unchanged.


Suggested changeset 1
dev.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/dev.py b/dev.py
--- a/dev.py
+++ b/dev.py
@@ -12,3 +12,3 @@
     try:
-        result = subprocess.run(cmd, check=True, cwd=Path(__file__).parent)
+        subprocess.run(cmd, check=True, cwd=Path(__file__).parent)
         print(f"✅ {description} passed!")
EOF
@@ -12,3 +12,3 @@
try:
result = subprocess.run(cmd, check=True, cwd=Path(__file__).parent)
subprocess.run(cmd, check=True, cwd=Path(__file__).parent)
print(f"✅ {description} passed!")
Copilot is powered by AI and may make mistakes. Always verify output.
print(f"✅ {description} passed!")
return True
except subprocess.CalledProcessError as e:
print(f"❌ {description} failed with exit code {e.returncode}")
return False


def main() -> None:
"""Run development checks."""
if len(sys.argv) > 1:
command = sys.argv[1]
else:
command = "all"

success = True

if command in ("lint", "all"):
success &= run_command(["ruff", "check", "json2xml", "tests"], "Linting")

if command in ("test", "all"):
success &= run_command([
"pytest", "--cov=json2xml", "--cov-report=term",
"-xvs", "tests", "-n", "auto"
], "Tests")

if command in ("typecheck", "all"):
success &= run_command(["mypy", "json2xml", "tests"], "Type checking")

if command == "help":
print("Usage: python dev.py [command]")
print("Commands:")
print(" all - Run all checks (default)")
print(" lint - Run linting only")
print(" test - Run tests only")
print(" typecheck - Run type checking only")
print(" help - Show this help")
return

if not success:
print(f"\n❌ Some checks failed!")
sys.exit(1)
else:
print(f"\n🎉 All checks passed!")


if __name__ == "__main__":
main()
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@
# relative to the documentation root, use os.path.abspath to make it
# absolute, like shown here.
#
import datetime
import os
import sys
import datetime

sys.path.insert(0, os.path.abspath('..'))

import json2xml


year = datetime.datetime.now().year

# -- General configuration ---------------------------------------------
Expand Down
Loading
Loading