Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
54 changes: 14 additions & 40 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,55 +7,29 @@ jobs:
name: Test Python ${{ matrix.python-version }} (${{ matrix.os }})
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: make setup
- name: Set up Python
run: uv python install

- name: Lint
run: make lint
- name: Install the project
run: uv sync --locked --all-extras --dev

- name: Run tests
run: make test

release:
name: Release
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Run linter
run: uv run ruff check

- name: Install requirements
run: python -m pip install wheel setuptools build
- name: Run format check
run: uv run ruff format --check

- name: Build a distribution
run: python -m build
- name: Run tests
run: uv run pytest

- name: Publish package to TestPyPI
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
repository_url: https://test.pypi.org/legacy/
skip_existing: true

- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@master
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
36 changes: 36 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: release

on:
push:
tags:
- v*

jobs:
release:
name: Release
runs-on: ubuntu-latest
environment:
name: pypi
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v7

- name: Install Python 3.13
run: uv python install

- name: Build
run: uv build

- name: Smoke test (wheel)
run: uv run --isolated --no-project --with dist/*.whl tests/test_untangle.py

- name: Smoke test (source distribution)
run: uv run --isolated --no-project --with dist/*.tar.gz tests/test_untangle.py

- name: publish
run: uv publish
3 changes: 2 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys, os
import sys
import os

# 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
Expand Down
3 changes: 1 addition & 2 deletions examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ def access_cdata():

examples = [
(
"Access children with parent.children and"
' attributes with element["attribute"]',
'Access children with parent.children and attributes with element["attribute"]',
access,
),
("Access siblings as list", siblings_list),
Expand Down
27 changes: 13 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
[tool.poetry]
[project]
authors = [{ name = "Christian Stefanescu" }]
name = "untangle"
version = "1.2.1"
description = "Converts XML to Python objects"
authors = ["Christian Stefanescu <hello@stchris.net>"]
license = "MIT"
readme = "README.md"
requires-python = ">=3.10"

[tool.poetry.dependencies]
python = "^3.7"
defusedxml = "^0.7.1"
dependencies = ["defusedxml==0.7.1"]

[tool.poetry.dev-dependencies]
pytest = "^7.1.2"
flake8 = "^4.0.1"
black = "^22.6.0"
build = "^0.8.0"
setuptools = "^62.6.0"
wheel = "^0.37.1"
[dependency-groups]
dev = [
"pytest>=9.0.2",
"ruff>=0.14.13",
"ty>=0.0.12",
]

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
requires = ["uv_build>=0.9.21,<0.10.0"]
build-backend = "uv_build"

38 changes: 0 additions & 38 deletions setup.py

This file was deleted.

37 changes: 14 additions & 23 deletions untangle.py → src/untangle/__init__.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,27 @@
"""
untangle
untangle

Converts xml to python objects.
Converts xml to python objects.

The only method you need to call is parse()
The only method you need to call is parse()

Partially inspired by xml2obj
(http://code.activestate.com/recipes/149368-xml2obj/)
Partially inspired by xml2obj
(http://code.activestate.com/recipes/149368-xml2obj/)

Author: Christian Stefanescu (http://0chris.com)
License: MIT License - http://www.opensource.org/licenses/mit-license.php
Author: Christian Stefanescu (http://0chris.com)
License: MIT License - http://www.opensource.org/licenses/mit-license.php
"""

import os
import keyword
from defusedxml.sax import make_parser
from xml.sax import handler


try:
from StringIO import StringIO
except ImportError:
from io import StringIO
try:
from types import StringTypes
import xml.sax

def is_string(x):
return isinstance(x, StringTypes)
from io import StringIO

except ImportError:

def is_string(x):
return isinstance(x, str)
def is_string(x):
return isinstance(x, str)


__version__ = "1.2.1"
Expand Down Expand Up @@ -132,7 +123,7 @@ def __contains__(self, key):
return key in dir(self)


class Handler(handler.ContentHandler):
class Handler(xml.sax.handler.ContentHandler):
"""
SAX handler which creates the Python object structure out of ``Element``s
"""
Expand Down Expand Up @@ -196,7 +187,7 @@ def parse(filename, **parser_features):
raise ValueError("parse() takes a filename, URL or XML string")
parser = make_parser()
for feature, value in parser_features.items():
parser.setFeature(getattr(handler, feature), value)
parser.setFeature(getattr(xml.sax.handler, feature), value)
sax_handler = Handler()
parser.setContentHandler(sax_handler)
if is_string(filename) and (os.path.exists(filename) or is_url(filename)):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_untangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import unittest
import untangle
import xml
import xml.sax

import defusedxml

Expand Down
Loading