diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 97ca688..740dbf2 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,2 @@ -github: ["EmilStenstrom"] +github: ["EmilStenstrom", "JuroOravec"] diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f3c5fd9..363eec7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -8,7 +8,7 @@ name: Publish to PyPI on: push: tags: - - '*' + - "*" # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -22,29 +22,29 @@ jobs: strategy: matrix: platform: - - runner: ubuntu-22.04 + - runner: ubuntu-latest target: x86_64 - - runner: ubuntu-22.04 + - runner: ubuntu-latest target: x86 - - runner: ubuntu-22.04 + - runner: ubuntu-latest target: aarch64 - - runner: ubuntu-22.04 + - runner: ubuntu-latest target: armv7 - - runner: ubuntu-22.04 + - runner: ubuntu-latest target: s390x - - runner: ubuntu-22.04 + - runner: ubuntu-latest target: ppc64le steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 with: - python-version: "3.13" + python-version: "3.14" - name: Build wheels uses: PyO3/maturin-action@v1 with: target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter - sccache: 'true' + sccache: "true" manylinux: auto - name: Upload wheels uses: actions/upload-artifact@v4 @@ -57,25 +57,25 @@ jobs: strategy: matrix: platform: - - runner: ubuntu-22.04 + - runner: ubuntu-latest target: x86_64 - - runner: ubuntu-22.04 + - runner: ubuntu-latest target: x86 - - runner: ubuntu-22.04 + - runner: ubuntu-latest target: aarch64 - - runner: ubuntu-22.04 + - runner: ubuntu-latest target: armv7 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 with: - python-version: "3.13" + python-version: "3.14" - name: Build wheels uses: PyO3/maturin-action@v1 with: target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter - sccache: 'true' + sccache: "true" manylinux: musllinux_1_2 - name: Upload wheels uses: actions/upload-artifact@v4 @@ -93,17 +93,17 @@ jobs: - runner: windows-latest target: x86 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 with: - python-version: "3.13" + python-version: "3.14" architecture: ${{ matrix.platform.target }} - name: Build wheels uses: PyO3/maturin-action@v1 with: target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter - sccache: 'true' + sccache: "true" - name: Upload wheels uses: actions/upload-artifact@v4 with: @@ -120,16 +120,16 @@ jobs: - runner: macos-14 target: aarch64 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 with: - python-version: "3.13" + python-version: "3.14" - name: Build wheels uses: PyO3/maturin-action@v1 with: target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter - sccache: 'true' + sccache: "true" - name: Upload wheels uses: actions/upload-artifact@v4 with: @@ -139,7 +139,7 @@ jobs: sdist: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build sdist uses: PyO3/maturin-action@v1 with: @@ -164,11 +164,11 @@ jobs: # Used to generate artifact attestation attestations: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 - name: Generate artifact attestation - uses: actions/attest-build-provenance@v2 + uses: actions/attest-build-provenance@v3 with: - subject-path: 'wheels-*/*' + subject-path: "wheels-*/*" - name: Publish to PyPI if: ${{ startsWith(github.ref, 'refs/tags/') }} uses: PyO3/maturin-action@v1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 161a84e..ef40687 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -3,8 +3,8 @@ name: Run tests on: push: branches: - - 'main' - - 'dev' + - "main" + - "dev" pull_request: workflow_dispatch: @@ -13,46 +13,46 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] - os: [ubuntu-20.04, windows-latest] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + os: [ubuntu-latest, windows-latest] steps: - - uses: actions/checkout@v4 - - # First check Rust tests - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - components: rustfmt, clippy - - - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 - - - name: Run Rust tests - run: cargo test - - # After Rust tests pass, run Python tests next - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: "pip" - - - name: Install Python dependencies - run: | - # NOTE: maturin requires a virtual environment to be active - python -m venv .venv - ${{ runner.os == 'Windows' && '.venv\Scripts\activate' || 'source .venv/bin/activate' }} - python -m pip install --upgrade pip - python -m pip install -r requirements-ci.txt - - - name: Build Python package - run: | - ${{ runner.os == 'Windows' && '.venv\Scripts\activate' || 'source .venv/bin/activate' }} - maturin develop - - - name: Run Python tests - run: | - ${{ runner.os == 'Windows' && '.venv\Scripts\activate' || 'source .venv/bin/activate' }} - pytest + - uses: actions/checkout@v5 + + # First check Rust tests + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + components: rustfmt, clippy + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + + - name: Run Rust tests + run: cargo test + + # After Rust tests pass, run Python tests next + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python-version }} + cache: "pip" + + - name: Install Python dependencies + run: | + # NOTE: maturin requires a virtual environment to be active + python -m venv .venv + ${{ runner.os == 'Windows' && '.venv\Scripts\activate' || 'source .venv/bin/activate' }} + python -m pip install --upgrade pip + python -m pip install -r requirements-ci.txt + + - name: Build Python package + run: | + ${{ runner.os == 'Windows' && '.venv\Scripts\activate' || 'source .venv/bin/activate' }} + maturin develop + + - name: Run Python tests + run: | + ${{ runner.os == 'Windows' && '.venv\Scripts\activate' || 'source .venv/bin/activate' }} + pytest diff --git a/CHANGELOG.md b/CHANGELOG.md index 57b956a..4160fc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release notes +## v1.0.3 + +- Update to Python 3.14 + ## v1.0.2 - Add build for Python 3.13 for Windows. diff --git a/Cargo.lock b/Cargo.lock index c7e960c..bc7c6a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,31 +8,28 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" -[[package]] -name = "bitflags" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - [[package]] name = "djc_core_html_parser" -version = "1.0.2" +version = "1.0.3" dependencies = [ "pyo3", "quick-xml", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "indoc" -version = "1.0.9" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] [[package]] name = "libc" @@ -40,16 +37,6 @@ version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "memchr" version = "2.7.4" @@ -67,32 +54,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] -name = "parking_lot" -version = "0.12.3" +name = "portable-atomic" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "proc-macro2" @@ -105,15 +75,15 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.19.2" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e681a6cfdc4adcc93b4d3cf993749a4552018ee0a9b65fc0ccfad74352c72a38" +checksum = "fa8e48c12afdeb26aa4be4e5c49fb5e11c3efa0878db783a960eea2b9ac6dd19" dependencies = [ - "cfg-if", "indoc", "libc", "memoffset", - "parking_lot", + "once_cell", + "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", @@ -122,19 +92,18 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.19.2" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076c73d0bc438f7a4ef6fdd0c3bb4732149136abd952b110ac93e4edb13a6ba5" +checksum = "bc1989dbf2b60852e0782c7487ebf0b4c7f43161ffe820849b56cf05f945cee1" dependencies = [ - "once_cell", "target-lexicon", ] [[package]] name = "pyo3-ffi" -version = "0.19.2" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53cee42e77ebe256066ba8aa77eff722b3bb91f3419177cf4cd0f304d3284d9" +checksum = "c808286da7500385148930152e54fb6883452033085bf1f857d85d4e82ca905c" dependencies = [ "libc", "pyo3-build-config", @@ -142,9 +111,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.19.2" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfeb4c99597e136528c6dd7d5e3de5434d1ceaf487436a3f03b2d56b6fc9efd1" +checksum = "83a0543c16be0d86cf0dbf2e2b636ece9fd38f20406bb43c255e0bc368095f92" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -154,20 +123,22 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.19.2" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "947dc12175c254889edc0c02e399476c2f652b4b9ebd123aa655c224de259536" +checksum = "2a00da2ce064dcd582448ea24a5a26fa9527e0483103019b741ebcbe632dcd29" dependencies = [ + "heck", "proc-macro2", + "pyo3-build-config", "quote", "syn", ] [[package]] name = "quick-xml" -version = "0.37.2" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" +checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" dependencies = [ "memchr", ] @@ -182,31 +153,16 @@ dependencies = [ ] [[package]] -name = "redox_syscall" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" -dependencies = [ - "bitflags", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" dependencies = [ "proc-macro2", "quote", @@ -215,9 +171,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.16" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "unicode-ident" @@ -227,70 +183,6 @@ checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unindent" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" diff --git a/Cargo.toml b/Cargo.toml index 340f74f..dae24e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "djc_core_html_parser" -version = "1.0.2" +version = "1.0.3" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -9,8 +9,8 @@ name = "djc_core_html_parser" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "0.19.0", features = ["extension-module"] } -quick-xml = "0.37.2" +pyo3 = { version = "0.27.0", features = ["extension-module"] } +quick-xml = "0.38.3" # https://ohadravid.github.io/posts/2023-03-rusty-python [profile.release] diff --git a/pyproject.toml b/pyproject.toml index a557a9e..5e4bb6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "djc_core_html_parser" -version = "1.0.2" +version = "1.0.3" requires-python = ">=3.8, <4.0" description = "HTML parser used by django-components written in Rust." keywords = ["django", "components", "html"] @@ -21,6 +21,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Rust", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", diff --git a/requirements-ci.txt b/requirements-ci.txt index 0607ebc..cc9e114 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -6,7 +6,7 @@ # iniconfig==2.0.0 # via pytest -maturin==1.8.1 +maturin==1.9.6 # via -r requirements-ci.in packaging==24.2 # via pytest diff --git a/requirements-dev.txt b/requirements-dev.txt index ea6c360..44e3a8c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,7 +6,7 @@ # iniconfig==2.0.0 # via pytest -maturin==1.8.1 +maturin==1.9.6 # via -r requirements-dev.in packaging==24.2 # via pytest diff --git a/src/html_parser.rs b/src/html_parser.rs index 5bb7eef..844e002 100644 --- a/src/html_parser.rs +++ b/src/html_parser.rs @@ -38,6 +38,7 @@ const VOID_ELEMENTS: [&str; 14] = [ /// Raises: /// ValueError: If the HTML is malformed or cannot be parsed. #[pyfunction] +#[pyo3(signature = (html, root_attributes, all_attributes, check_end_names=None, watch_on_attribute=None))] #[pyo3( text_signature = "(html, root_attributes, all_attributes, *, check_end_names=False, watch_on_attribute=None)" )] @@ -48,7 +49,7 @@ pub fn set_html_attributes( all_attributes: Vec, check_end_names: Option, watch_on_attribute: Option, -) -> PyResult { +) -> PyResult> { let config = HtmlTransformerConfig::new( root_attributes, all_attributes, @@ -64,8 +65,12 @@ pub fn set_html_attributes( captured_dict.set_item(id, attrs)?; } - let result = PyTuple::new(py, &[html.into_py(py), captured_dict.into_py(py)]); - Ok(result.into()) + // Convert items to Bound for the tuple + use pyo3::types::PyString; + let html_obj = PyString::new(py, &html).as_any().clone(); + let dict_obj = captured_dict.as_any().clone(); + let result = PyTuple::new(py, vec![html_obj, dict_obj])?; + Ok(result.into_any().unbind()) } Err(e) => Err(PyValueError::new_err(e.to_string())), } @@ -149,6 +154,9 @@ pub fn transform( let mut reader = Reader::from_str(html); let reader_config = reader.config_mut(); reader_config.check_end_names = config.check_end_names; + // Allow bare & in HTML content (e.g. "Hello & Welcome" instead of requiring "Hello & Welcome") + // This is needed for compatibility with HTML5 which is more lenient than strict XML + reader_config.allow_dangling_amp = true; // We transform the HTML by reading it and writing it simultaneously let mut writer = Writer::new(Cursor::new(Vec::new())); diff --git a/src/lib.rs b/src/lib.rs index 050fa3e..4d591d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,10 @@ use pyo3::prelude::*; -use pyo3::types::PyModule; mod html_parser; /// A Python module implemented in Rust for high-performance HTML transformation. #[pymodule] -fn djc_core_html_parser(_py: Python, m: &PyModule) -> PyResult<()> { +fn djc_core_html_parser(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(html_parser::set_html_attributes, m)?)?; Ok(()) }