Skip to content

[feature] Support Python projects using pyproject.toml #706#707

Open
BHARATH0153 wants to merge 4 commits into
openwisp:masterfrom
BHARATH0153:master
Open

[feature] Support Python projects using pyproject.toml #706#707
BHARATH0153 wants to merge 4 commits into
openwisp:masterfrom
BHARATH0153:master

Conversation

@BHARATH0153

@BHARATH0153 BHARATH0153 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

closes #706

Changes:

  • Detect pyproject.toml as a Python package marker (setup.py still takes priority)
  • Read version from [project] version field in pyproject.toml (PEP 621)
  • Bump version by updating the version string in pyproject.toml
  • Graceful fallback when tomllib is not available (Python <3.11)
  • Added tests for detection, version reading, version bumping, and priority

Checklist

  • I have read the OpenWISP Contributing Guidelines.
  • I have manually tested the changes proposed in this pull request.
  • I have written new test cases for new code and/or updated existing tests for changes to existing code.
  • I have updated the documentation.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: edfa80c8-8016-4ddc-990b-79abdf79468a

📥 Commits

Reviewing files that changed from the base of the PR and between 1f13121 and 9759f4d.

📒 Files selected for processing (5)
  • openwisp_utils/releaser/config.py
  • openwisp_utils/releaser/tests/test_config.py
  • openwisp_utils/releaser/tests/test_version_bumping.py
  • openwisp_utils/releaser/version.py
  • setup.py
🔥 Files not summarized due to errors (5)
  • openwisp_utils/releaser/config.py: Server error: no LLM provider could handle the message
  • setup.py: Server error: no LLM provider could handle the message
  • openwisp_utils/releaser/tests/test_config.py: Server error: no LLM provider could handle the message
  • openwisp_utils/releaser/tests/test_version_bumping.py: Server error: no LLM provider could handle the message
  • openwisp_utils/releaser/version.py: Server error: no LLM provider could handle the message

📝 Walkthrough

Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Bug Fixes ❌ Error pyproject load_config still raises NameError from leftover project_name code, so the root bug isn’t fixed; one regression test also uses undefined written_content. Remove the stray fallback block in _handle_pyproject_toml_version, make load_config return pyproject config cleanly, and fix or drop the malformed bump test assertion.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is descriptive, uses the required feature prefix, and matches the pyproject.toml support changes.
Description check ✅ Passed The description includes the issue reference, change summary, and checklist, which is mostly complete for this template.
Linked Issues check ✅ Passed The changes implement pyproject.toml detection, version reading, version bumping, and keep setup.py prioritized as requested by #706.
Out of Scope Changes check ✅ Passed The added TOML fallback dependency and tests are directly related to the pyproject.toml support work.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kilo-code-bot

kilo-code-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 0
Files Reviewed (5 files)
  • openwisp_utils/releaser/config.py - 0 issues (coverage pragmas added to import-fallback paths; no behavior change)
  • openwisp_utils/releaser/version.py - 0 issues (coverage pragmas added; no behavior change)
  • openwisp_utils/releaser/tests/test_config.py - 0 issues (new malformed-TOML and non-numeric-version tests verified against current handler logic)
  • openwisp_utils/releaser/tests/test_version_bumping.py - 0 issues (new malformed-TOML bump test correctly falls through to regex → RuntimeError)
  • setup.py - 0 issues (explicit tomli>=1.1.0; python_version < '3.11' in releaser extra resolves the prior implicit-dependency concern)
Notes
  • Incremental review of commit 2d35d60 (prior review at e2b39ad).
  • The prior docs-not-updated WARNING targeted docs/developer/releaser-tool.rst, which is unchanged in this incremental diff; per incremental workflow it is not carried forward.
  • Verified the three active CodeRabbit findings remain addressed at HEAD: _handle_pyproject_toml_version assigns package_type/version_path only after a successful parse (config.py:97-106), and _bump_pyproject_toml_version guards the literal replace with if old in content before falling through to regex (version.py:74-78).
  • The explicit tomli declaration in the releaser extra now makes the dependency intentional rather than transitively relied upon.
Previous Review Summaries (2 snapshots, latest commit e2b39ad)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit e2b39ad)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
docs/developer/releaser-tool.rst 92 Documentation not updated for the new pyproject.toml version source. The "Summary of Automated Steps" still states the version is updated in __init__.py only; PEP 621 pyproject.toml projects are now also supported (per AGENTS.md: update docs when supported version sources change).
Notes
  • Verified the two CodeRabbit findings are already fixed at HEAD: _handle_pyproject_toml_version now assigns package_type/version_path only after a successful parse (openwisp_utils/releaser/config.py:97-106), and _bump_pyproject_toml_version guards the literal replace with if old in content before falling through to regex (openwisp_utils/releaser/version.py:74-78).
  • The open CodeRabbit inline comment at test_config.py:439 (Python 3.10 / tomli availability) is not duplicated here. Note: in this repo's CI tomli is available transitively on Python <3.11 via commitizen (declared in the qa extra), so the assertion is satisfied in practice; declaring tomli explicitly in the releaser extra would make the dependency intentional rather than implicit.
  • The feature is well covered by new tests (detection, version reading, bumping, invalid/missing versions, and setup.py-takes-priority).
  • Could not fetch issue [feature] Support Python projects using pyproject.toml #706 directly (read-only constraints); alignment is inferred from the PR description, which matches the implemented changes.
Files Reviewed (4 files)
  • openwisp_utils/releaser/config.py - 0 issues (prior findings fixed)
  • openwisp_utils/releaser/version.py - 0 issues (prior findings fixed)
  • openwisp_utils/releaser/tests/test_config.py - 0 new issues (tomli concern already tracked inline)
  • openwisp_utils/releaser/tests/test_version_bumping.py - 0 issues

Fix these issues in Kilo Cloud

Previous review

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
docs/developer/releaser-tool.rst 92 Documentation not updated to reflect new pyproject.toml version-source support. The "Summary of Automated Steps" still states the version is updated in __init__.py; pyproject.toml-based projects are now also supported but the docs do not mention this (per AGENTS.md: update docs when behavior/supported version sources change).
Notes
  • The implementation is clean and well-structured: detection, version reading, version bumping, and the setup.py-takes-priority rule are all covered by new tests (test_config.py, test_version_bumping.py).
  • The graceful tomllib/tomli fallback for Python < 3.11 is handled correctly in both config.py and version.py.
  • Verified existing behavior is preserved when no pyproject.toml is present (e.g. test_version_tuple_not_in_init_py still passes), since the new fall-through returns early when the file is absent.
  • Could not fetch issue [feature] Support Python projects using pyproject.toml #706 directly (read rejected); alignment with the issue is inferred from the PR description, which matches the implemented changes.
Files Reviewed (4 files)
  • openwisp_utils/releaser/config.py - 0 issues
  • openwisp_utils/releaser/version.py - 0 issues
  • openwisp_utils/releaser/tests/test_config.py - 0 issues
  • openwisp_utils/releaser/tests/test_version_bumping.py - 0 issues

Fix these issues in Kilo Cloud


Reviewed by glm-5.2-20260616 · Input: 199.1K · Output: 3.2K · Cached: 49.5K

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openwisp_utils/releaser/config.py`:
- Around line 97-110: The version parsing in the config builder leaves a partial
state in the try block when parsing fails after setting pyproject fields. In the
config routine that handles version_str, move the package_type and version_path
assignments until after all int(parts[..]) conversions succeed, so a
ValueError/TypeError leaves the config fully unset instead of mixing
CURRENT_VERSION=None with pyproject metadata. Keep the early return behavior for
non-X.Y.Z inputs consistent with the rest of the function.

In `@openwisp_utils/releaser/tests/test_config.py`:
- Around line 429-439: The pyproject.toml package detection test in
test_pyproject_toml_package_detection depends on load_config resolving TOML
parsing through openwisp_utils.releaser.config, which falls back to tomli on
Python 3.10. Fix this by either adding tomli to the test/runtime dependencies
used by the releaser tests or guarding this assertion so it is skipped when
tomli is unavailable on Python 3.10, keeping the test aligned with the
package_type and version_path checks in load_config.

In `@openwisp_utils/releaser/version.py`:
- Around line 67-84: The version bump in version.py can falsely succeed when
tomllib parses a version but the literal text does not match version = "x", so
update the logic in the tomllib branch to verify the replacement actually
changed content before returning. In the path that uses tomllib.loads and
current, make the replacement conditional on a real match; otherwise fall
through to _bump_with_regex so formats like single quotes or different spacing
are handled correctly. Also avoid blindly using str.replace on the whole file,
since it can rewrite unrelated identical version strings elsewhere.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1cf961f5-4dfb-47f0-a82a-87ecf2441e37

📥 Commits

Reviewing files that changed from the base of the PR and between 595ddbf and 869ed74.

📒 Files selected for processing (4)
  • openwisp_utils/releaser/config.py
  • openwisp_utils/releaser/tests/test_config.py
  • openwisp_utils/releaser/tests/test_version_bumping.py
  • openwisp_utils/releaser/version.py
📜 Review details
⏰ Context from checks skipped due to timeout. (15)
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.0.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.0.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.0.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Kilo Code Review
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{py,html,txt}

📄 CodeRabbit inference engine (Custom checks)

For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework

Files:

  • openwisp_utils/releaser/tests/test_config.py
  • openwisp_utils/releaser/tests/test_version_bumping.py
  • openwisp_utils/releaser/version.py
  • openwisp_utils/releaser/config.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Place imports at the top of the file; only defer imports when necessary (e.g., Django model imports inside functions or methods where the app registry is not yet ready)
Avoid unnecessary blank lines inside function and method bodies
Add or update tests for every behavior change
Run openwisp-qa-format after editing
Prefer in-process tests so coverage tools can measure changed code
When checking coverage for a changed module, use python -m pytest <test_path> --cov=<dotted.module.path> --cov-report=term-missing
Watch for unsafe file paths, unsafe subprocess usage, token or secret exposure, and changes that could weaken QA or release safeguards
Write comments and docstrings only when they explain why code is shaped a certain way; place comments before the relevant code block instead of scattering them inside it

Files:

  • openwisp_utils/releaser/tests/test_config.py
  • openwisp_utils/releaser/tests/test_version_bumping.py
  • openwisp_utils/releaser/version.py
  • openwisp_utils/releaser/config.py
**/tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

For bug fixes, write the regression test first, run it against the unfixed code, confirm it fails for the expected reason, then implement the fix

Files:

  • openwisp_utils/releaser/tests/test_config.py
  • openwisp_utils/releaser/tests/test_version_bumping.py
🧠 Learnings (1)
📚 Learning: 2026-06-15T22:45:01.529Z
Learnt from: nemesifier
Repo: openwisp/openwisp-utils PR: 701
File: setup.py:72-72
Timestamp: 2026-06-15T22:45:01.529Z
Learning: When reviewing changes that require a minimum Selenium Python version for specific APIs (e.g., BiDi features like `Options.enable_bidi`, console/message handlers like `Script.add_console_message_handler`, or event handlers like `BrowsingContext.add_event_handler`), do not use the Selenium API docs site (`https://www.selenium.dev/selenium/docs/api/py/`) as the source of introduction/version history, since it reflects the latest API shape. Instead, confirm feature availability by checking the official Python changelog (`py/CHANGES` in the Selenium repo) and/or inspecting the Selenium source (tags/commits corresponding to candidate versions). Only accept the stated minimum version after verifying that the referenced APIs exist in that Selenium version (e.g., Selenium 4.32.0 includes the BiDi APIs used by openwisp-utils’ BiDi-related PR `#701` as of that version).

Applied to files:

  • openwisp_utils/releaser/tests/test_config.py
  • openwisp_utils/releaser/tests/test_version_bumping.py
  • openwisp_utils/releaser/version.py
  • openwisp_utils/releaser/config.py
🪛 ast-grep (0.44.0)
openwisp_utils/releaser/config.py

[warning] 67-67: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(init_py_path, "r")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

🔇 Additional comments (4)
openwisp_utils/releaser/config.py (1)

7-14: LGTM!

Also applies to: 33-33, 237-237

openwisp_utils/releaser/version.py (1)

152-152: LGTM!

openwisp_utils/releaser/tests/test_version_bumping.py (1)

374-440: LGTM!

openwisp_utils/releaser/tests/test_config.py (1)

442-482: LGTM!

Comment thread openwisp_utils/releaser/config.py Outdated
Comment thread openwisp_utils/releaser/tests/test_config.py
Comment thread openwisp_utils/releaser/version.py
@coveralls

coveralls commented Jun 24, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 97.746% (+0.1%) from 97.641% — BHARATH0153:master into openwisp:master

Closes openwisp#706

- Detect pyproject.toml as package type python
- Read version from [project] version field
- Bump version using exact string replacement + regex fallback
- Add tomli dependency for Python < 3.11
- Add tests for malformed TOML, non-numeric version, and priority
- Add pragma: no cover to version-specific import branches
@BHARATH0153 BHARATH0153 marked this pull request as ready for review June 24, 2026 06:50
@BHARATH0153

Copy link
Copy Markdown
Contributor Author

@pandafy @nemesifier had a coverage drop from the pyproject.toml changes. The cause is the conditional import tomllib block — on Python 3.11+ the except ImportError branch is never executed (tomllib is built-in), on 3.10 the import tomllib line is never reached (it fails and falls through to tomli). can't cover both paths on any single Python version.
so I have used pragma: no cover on only those version-dependent import branches. Zero business logic is skipped. This is a broadly accepted pattern — openwisp already uses it in admin.py:29. The 5 remaining uncovered lines in the releaser (config.py:112/186/212, version.py:92-93/179) are pre-existing in npm/ansible/openwrt handlers, unrelated to this PR.

@BHARATH0153

BHARATH0153 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review please

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cat -n openwisp_utils/releaser/config.py
echo "---"
cat -n openwisp_utils/releaser/version.py
echo "---"
cat -n openwisp_utils/releaser/tests/test_config.py
echo "---"
cat -n openwisp_utils/releaser/tests/test_version_bumping.py

Length of output: 61798



@BHARATH0153 Thanks for the thorough context on coverage. The pragma: no cover approach is appropriate here. Below is my review of the PR.


Review

Bug: PACKAGE_VERSION_HANDLERS["pyproject"] in config.py is dead code

get_package_type_from_setup() (Line 29–42) maps "pyproject.toml""python" (not "pyproject"):

package_type_files = {
    "setup.py": "python",
    "pyproject.toml": "python",   # ← always "python"
    ...
}

So load_config() dispatches to PACKAGE_VERSION_HANDLERS["python"] = _handle_python_version, which internally calls _handle_pyproject_toml_version, which then mutates config["package_type"] = "pyproject" (Line 104). The "pyproject" key in PACKAGE_VERSION_HANDLERS (Line 233) is never reached via load_config() and is dead code. Consider removing it or rethinking the dispatch model.


Silent fallthrough may produce unexpected behaviour

If a project has setup.py and pyproject.toml, but __init__.py exists with no VERSION tuple, _handle_python_version will silently fall through to _handle_pyproject_toml_version (Line 79) and use pyproject.toml as the version source. For an OpenWISP project that simply forgot to define the VERSION tuple, this could cause a hard-to-diagnose release with the wrong version source. A warning log before the fallthrough would be safer:

# After failing to find VERSION in __init__.py:
import warnings
warnings.warn(
    "setup.py found but no VERSION tuple in __init__.py; "
    "falling back to pyproject.toml.",
    stacklevel=2,
)
_handle_pyproject_toml_version(config)

Pre-release versions silently return None

_handle_pyproject_toml_version (Line 101) does int(parts[2]), which raises ValueError for PEP 440 pre-release strings like "1.2.3a1", "1.2.3.dev0", or "1.2.3rc1". The except clause silently returns with CURRENT_VERSION = None. A project in alpha/RC state would get no version detection. Consider adding a test and at minimum a descriptive error message.


Broad except Exception silently swallows TOML parse errors

config.py Line 91–92:

except Exception:
    return

Any failure during tomllib.load(f) — including PermissionError, OSError, or unexpected TOML errors — is silently ignored. Consider narrowing to except tomllib.TOMLDecodeError (or equivalent) and re-raising or at least printing a warning for non-parse errors.


Misleading test name

test_bump_version_pyproject_toml_not_found (Line 421) actually tests the case where the version field is missing from pyproject.toml, not the case where the file itself is missing. A clearer name would be test_bump_version_pyproject_toml_missing_version_field.


Minor: misleading function name

get_package_type_from_setup() (Line 29) now detects pyproject.toml in addition to setup.py. The name no longer reflects its full behaviour. Consider get_package_type() or detect_package_type().


Summary

Severity Issue
🔴 Bug PACKAGE_VERSION_HANDLERS["pyproject"] in config.py is dead/unreachable code
🟠 Issue Silent fallthrough from setup.pypyproject.toml when VERSION tuple is absent
🟠 Issue PEP 440 pre-release versions silently produce None
🟡 Minor except Exception too broad in _handle_pyproject_toml_version
🟡 Minor Misleading test name test_bump_version_pyproject_toml_not_found
🟡 Minor Misleading function name get_package_type_from_setup

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the main changed files
echo "=== config.py ==="
cat -n openwisp_utils/releaser/config.py

echo ""
echo "=== version.py ==="
cat -n openwisp_utils/releaser/version.py

echo ""
echo "=== test_config.py ==="
cat -n openwisp_utils/releaser/tests/test_config.py

echo ""
echo "=== test_version_bumping.py ==="
cat -n openwisp_utils/releaser/tests/test_version_bumping.py

Length of output: 62025


🏁 Script executed:

#!/bin/bash
echo "=== setup.py or pyproject.toml dependencies ==="
fd -e toml -e cfg -e txt -e py --max-depth 2 -x grep -l "tomli" {}

echo ""
echo "=== setup.cfg or requirements ==="
fd "requirements" --max-depth 2 -x cat {}

echo ""
echo "=== pyproject.toml at root ==="
cat pyproject.toml 2>/dev/null || echo "not found"

echo ""
echo "=== setup.cfg ==="
cat setup.cfg 2>/dev/null || echo "not found"

echo ""
echo "=== conftest.py for fixtures ==="
fd "conftest.py" openwisp_utils/releaser --exec cat {}

Length of output: 8544


🏁 Script executed:

#!/bin/bash
echo "=== setup.py extras / install_requires ==="
cat setup.py

echo ""
echo "=== grep for tomli in setup.py ==="
grep -n "tomli" setup.py || echo "tomli not found in setup.py"

Length of output: 3828



@BHARATH0153 Here is a focused review of the PR. The implementation is well-structured, tests are solid, and the pragma: no cover usage is appropriate. A few things worth addressing:


🔴 Correctness: Section-ambiguous version replacement in _bump_pyproject_toml_version

openwisp_utils/releaser/version.py, Lines 65–85

Both the TOML-parse path and the regex fallback replace the first occurrence of version = "..." in the file, regardless of which TOML section it belongs to. Consider a valid pyproject.toml like:

[tool.poetry]
version = "1.2.3"   # ← this line appears first

[project]
name = "my-package"
version = "1.2.3"   # ← this is the one we want to bump

The TOML parse correctly reads project.version = "1.2.3" and builds old = 'version = "1.2.3"', but content.replace(old, new, 1) replaces the [tool.poetry] entry. The regex fallback r'^version\s*=\s*"([^"]+)"' has the same problem.

A safer approach is to anchor the replacement to the [project] section header. For example:

def _bump_pyproject_toml_version(content, new_version, version_path):
    """Handles version bumping in pyproject.toml."""
    # Match version = "..." only within the [project] section,
    # stopping before the next section header.
    pattern = r'(\[project\].*?)^version\s*=\s*"[^"]+"'
    replacement = rf'\g<1>version = "{new_version}"'
    new_content, count = re.subn(
        pattern,
        replacement,
        content,
        count=1,
        flags=re.MULTILINE | re.DOTALL,
    )
    if count == 0:
        raise RuntimeError(
            f"Failed to find and bump version in pyproject.toml in {version_path}."
        )
    return new_content

The existing tests don't catch this because PYPROJECT_TOML_CONTENT has no version field in [build-system].


🟡 Logic: _handle_python_version fallthrough when setup.py exists but has no VERSION in __init__.py

openwisp_utils/releaser/config.py, Lines 61–79

When setup.py is present but the package directory or __init__.py doesn't contain a VERSION tuple, _handle_python_version silently falls through to _handle_pyproject_toml_version. If a pyproject.toml also exists (e.g. the project uses both), the version will be sourced from pyproject.toml and package_type will be switched to "pyproject", which means bump_version will write to pyproject.toml rather than a Python version file. This could be surprising.

The existing priority test (test_setup_py_takes_priority_over_pyproject_toml) only covers the happy path where __init__.py does contain VERSION. A test for the missing-VERSION + both-files-present case would help document the intended behaviour.


🟡 Naming: get_package_type_from_setup() now handles pyproject.toml

openwisp_utils/releaser/config.py, Line 29

The function name implies it only looks at setup.py, but it now also maps pyproject.toml"python". A more neutral name like detect_package_type() (matching the existing detect_changelog_style) would avoid confusion.


🔵 Design: PACKAGE_VERSION_HANDLERS["pyproject"] is never dispatched to by load_config()

openwisp_utils/releaser/config.py, Lines 230–238

load_config() dispatches handlers based on the value returned by get_package_type_from_setup(), which returns "python" for pyproject.toml, never "pyproject". The "pyproject" type is only set as a side effect inside _handle_pyproject_toml_version. So PACKAGE_VERSION_HANDLERS["pyproject"] is effectively unreachable from load_config(), though it is used correctly by bump_version(). A comment explaining this design (the handler exists for the bump path, not for config loading) would help future readers.


✅ Things done well

  • tomli>=1.1.0; python_version < '3.11' is correctly listed as a conditional dependency in setup.py under extras_require["releaser"].
  • pragma: no cover placement on the version-gated import branches is standard and appropriate.
  • Tests cover the malformed, missing-version, invalid-format, and non-numeric edge cases thoroughly.
  • Binary open ("rb") in _handle_pyproject_toml_version for tomllib.load and string mode for tomllib.loads in the bump handler are both correct.

@BHARATH0153

Copy link
Copy Markdown
Contributor Author

@pandafy please need your perspective check when you have time thanks!

@openwisp-companion

Copy link
Copy Markdown
<h3>Flake8 and Test Failures Detected</h3>

Hello @BHARATH0153,
(Analysis for commit fadd47a)

  • Code Style/QA Failure: The CI detected issues with code style and quality.

  • Fix: Run the command openwisp-qa-format to automatically fix most of these issues.

  • Manual Fix Required: Specifically, the following errors need manual correction:

  • F821 undefined name 'project_name' on line 113 of openwisp_utils/releaser/config.py. This means a variable named project_name is being used before it has been assigned a value. You need to ensure project_name is defined before its usage.

  • F821 undefined name 'written_content' on line 470 of openwisp_utils/releaser/tests/test_version_bumping.py. Similar to the above, written_content is used without being defined. Ensure it is properly initialized.

  • Test Failure (TypeError): The test test_config_version_non_iterable_type_error in openwisp_utils/releaser/tests/test_config.py failed with a TypeError: 'int' object is not iterable. This typically happens when code expects an iterable (like a tuple or list) but receives a single integer.

  • Fix: Examine the logic around line 81 in openwisp_utils/releaser/config.py where list(version_tuple) is called. Ensure that version_tuple is always an iterable and not a single integer.

  • Test Failure (AssertionError): Multiple tests in openwisp_utils/releaser/tests/test_config.py failed with AssertionError because an expected value was None instead of the actual path.

  • test_python_version_detection_from_version_py_for_netjsonconfig_compatibility: Expected 'my_test_package/version.py', but got None.

  • test_python_version_detection_for_netjsonconfig_when_init_py_lacks_version: Expected 'my_test_package/version.py', but got None.

  • Fix: Review the logic in openwisp_utils/releaser/config.py that determines the version_path. Ensure it correctly identifies and sets the version_path when __init__.py does not contain version information but version.py does.

  • Test Failure (NameError):

  • test_pyproject_toml_package_detection in openwisp_utils/releaser/tests/test_config.py failed with NameError: name 'project_name' is not defined. This means project_name was used on line 113 of openwisp_utils/releaser/config.py without being defined.

  • test_bump_version_pyproject_toml_malformed in openwisp_utils/releaser/tests/test_version_bumping.py failed with NameError: name 'written_content' is not defined. This means written_content was used without being defined.

  • Fix: For test_pyproject_toml_package_detection, ensure project_name is correctly obtained or passed to the relevant function. For test_bump_version_pyproject_toml_malformed, ensure written_content is properly defined before use.

  • Infrastructure Failure: The command cat geckodriver.log failed because the file geckodriver.log does not exist. This indicates a potential issue with the Selenium setup or test execution environment, rather than a code error. The CI has been restarted automatically.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openwisp_utils/releaser/config.py`:
- Around line 110-131: Remove the candidate_files scan from
_handle_pyproject_toml_version and keep this method focused only on
pyproject.toml handling. The use of project_name here is invalid in this scope,
so delete the fallback logic that builds package_directory, iterates over
__init__.py/version.py, and parses VERSION from those files. Let
_handle_python_version own any filesystem fallback/version-file scanning
instead, and preserve the pyproject.toml-derived version_path and
CURRENT_VERSION values in this method.

In `@openwisp_utils/releaser/tests/test_version_bumping.py`:
- Around line 466-470: The test in test_version_bumping.py has a stray assertion
after the pytest.raises(RuntimeError) block that references written_content, but
that variable is never defined in this test. Remove the trailing assert from the
test case around bump_version and keep the malformed_content scenario focused on
asserting the RuntimeError from bump_version(config, "1.2.4"), using the
existing mock_open/patch setup to locate the affected test.

In `@openwisp_utils/releaser/version.py`:
- Around line 79-85: The version bump logic can rewrite the wrong version field
because both the regex fallback in _bump_with_regex and the tomllib-based
replace in bump_version only target the first matching occurrence. Update
bump_version/version.py to locate the [project] section explicitly, then replace
only the version entry inside that section, using the existing _bump_with_regex
and bump_version symbols as the place to fix the section-scoped matching.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c8475dc5-765a-40d8-8011-d1eaf70895f2

📥 Commits

Reviewing files that changed from the base of the PR and between 869ed74 and fadd47a.

📒 Files selected for processing (5)
  • openwisp_utils/releaser/config.py
  • openwisp_utils/releaser/tests/test_config.py
  • openwisp_utils/releaser/tests/test_version_bumping.py
  • openwisp_utils/releaser/version.py
  • setup.py
📜 Review details
⏰ Context from checks skipped due to timeout. (14)
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.0.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.0.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.0.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{py,html,txt}

📄 CodeRabbit inference engine (Custom checks)

For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework

Files:

  • openwisp_utils/releaser/version.py
  • openwisp_utils/releaser/tests/test_config.py
  • setup.py
  • openwisp_utils/releaser/tests/test_version_bumping.py
  • openwisp_utils/releaser/config.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Place imports at the top of the file; only defer imports when necessary (e.g., Django model imports inside functions or methods where the app registry is not yet ready)
Avoid unnecessary blank lines inside function and method bodies
Add or update tests for every behavior change
Run openwisp-qa-format after editing
Prefer in-process tests so coverage tools can measure changed code
When checking coverage for a changed module, use python -m pytest <test_path> --cov=<dotted.module.path> --cov-report=term-missing
Watch for unsafe file paths, unsafe subprocess usage, token or secret exposure, and changes that could weaken QA or release safeguards
Write comments and docstrings only when they explain why code is shaped a certain way; place comments before the relevant code block instead of scattering them inside it

Files:

  • openwisp_utils/releaser/version.py
  • openwisp_utils/releaser/tests/test_config.py
  • setup.py
  • openwisp_utils/releaser/tests/test_version_bumping.py
  • openwisp_utils/releaser/config.py
**/tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

For bug fixes, write the regression test first, run it against the unfixed code, confirm it fails for the expected reason, then implement the fix

Files:

  • openwisp_utils/releaser/tests/test_config.py
  • openwisp_utils/releaser/tests/test_version_bumping.py
🧠 Learnings (1)
📚 Learning: 2026-06-15T22:45:01.529Z
Learnt from: nemesifier
Repo: openwisp/openwisp-utils PR: 701
File: setup.py:72-72
Timestamp: 2026-06-15T22:45:01.529Z
Learning: When reviewing changes that require a minimum Selenium Python version for specific APIs (e.g., BiDi features like `Options.enable_bidi`, console/message handlers like `Script.add_console_message_handler`, or event handlers like `BrowsingContext.add_event_handler`), do not use the Selenium API docs site (`https://www.selenium.dev/selenium/docs/api/py/`) as the source of introduction/version history, since it reflects the latest API shape. Instead, confirm feature availability by checking the official Python changelog (`py/CHANGES` in the Selenium repo) and/or inspecting the Selenium source (tags/commits corresponding to candidate versions). Only accept the stated minimum version after verifying that the referenced APIs exist in that Selenium version (e.g., Selenium 4.32.0 includes the BiDi APIs used by openwisp-utils’ BiDi-related PR `#701` as of that version).

Applied to files:

  • openwisp_utils/releaser/version.py
  • openwisp_utils/releaser/tests/test_config.py
  • setup.py
  • openwisp_utils/releaser/tests/test_version_bumping.py
  • openwisp_utils/releaser/config.py
🪛 ast-grep (0.44.0)
openwisp_utils/releaser/config.py

[warning] 73-73: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(init_py_path, "r")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)


[warning] 120-120: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(version_path, "r")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

🔇 Additional comments (2)
openwisp_utils/releaser/tests/test_config.py (1)

592-602: 🩺 Stability & Availability | ⚡ Quick win

Verify tomli is available in the test environment on Python 3.10.

config.py falls back to tomli when stdlib tomllib is unavailable (< 3.11). With pyproject.toml mapped to "python", this test will fail on 3.10 unless tomli is installed in the test deps. The setup.py change adds it under releaser extras—confirm the CI test job installs that extra (or tomli directly).

#!/bin/bash
rg -nP 'tomli|releaser|3\.10|python-version' tox.ini .github/workflows/ 2>/dev/null
setup.py (1)

78-78: LGTM!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openwisp_utils/releaser/config.py`:
- Around line 110-131: Remove the candidate_files scan from
_handle_pyproject_toml_version and keep this method focused only on
pyproject.toml handling. The use of project_name here is invalid in this scope,
so delete the fallback logic that builds package_directory, iterates over
__init__.py/version.py, and parses VERSION from those files. Let
_handle_python_version own any filesystem fallback/version-file scanning
instead, and preserve the pyproject.toml-derived version_path and
CURRENT_VERSION values in this method.

In `@openwisp_utils/releaser/tests/test_version_bumping.py`:
- Around line 466-470: The test in test_version_bumping.py has a stray assertion
after the pytest.raises(RuntimeError) block that references written_content, but
that variable is never defined in this test. Remove the trailing assert from the
test case around bump_version and keep the malformed_content scenario focused on
asserting the RuntimeError from bump_version(config, "1.2.4"), using the
existing mock_open/patch setup to locate the affected test.

In `@openwisp_utils/releaser/version.py`:
- Around line 79-85: The version bump logic can rewrite the wrong version field
because both the regex fallback in _bump_with_regex and the tomllib-based
replace in bump_version only target the first matching occurrence. Update
bump_version/version.py to locate the [project] section explicitly, then replace
only the version entry inside that section, using the existing _bump_with_regex
and bump_version symbols as the place to fix the section-scoped matching.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c8475dc5-765a-40d8-8011-d1eaf70895f2

📥 Commits

Reviewing files that changed from the base of the PR and between 869ed74 and fadd47a.

📒 Files selected for processing (5)
  • openwisp_utils/releaser/config.py
  • openwisp_utils/releaser/tests/test_config.py
  • openwisp_utils/releaser/tests/test_version_bumping.py
  • openwisp_utils/releaser/version.py
  • setup.py
📜 Review details
🔇 Additional comments (2)
openwisp_utils/releaser/tests/test_config.py (1)

592-602: 🩺 Stability & Availability | ⚡ Quick win

Verify tomli is available in the test environment on Python 3.10.

config.py falls back to tomli when stdlib tomllib is unavailable (< 3.11). With pyproject.toml mapped to "python", this test will fail on 3.10 unless tomli is installed in the test deps. The setup.py change adds it under releaser extras—confirm the CI test job installs that extra (or tomli directly).

#!/bin/bash
rg -nP 'tomli|releaser|3\.10|python-version' tox.ini .github/workflows/ 2>/dev/null
setup.py (1)

78-78: LGTM!

🛑 Comments failed to post (3)
openwisp_utils/releaser/config.py (1)

110-131: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm project_name is not defined in _handle_pyproject_toml_version scope
# and check where version.py fallback is handled.
ast-grep outline openwisp_utils/releaser/config.py
rg -nP '\bproject_name\b' openwisp_utils/releaser/config.py
rg -nP 'version\.py' openwisp_utils/releaser/config.py

Repository: openwisp/openwisp-utils

Length of output: 1058


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '63,140p' openwisp_utils/releaser/config.py | cat -n
printf '\n---\n'
rg -n "pyproject|version.py|_handle_python_version|_handle_pyproject_toml_version" -S tests openwisp_utils | head -n 200

Repository: openwisp/openwisp-utils

Length of output: 13346


Remove the candidate_files scan from _handle_pyproject_toml_version. project_name is out of scope here, so any valid pyproject.toml version will raise NameError. Even if that were fixed, rescanning __init__.py/version.py here would overwrite the pyproject.toml result; that fallback belongs in _handle_python_version.

🧰 Tools
🪛 ast-grep (0.44.0)

[warning] 120-120: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(version_path, "r")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openwisp_utils/releaser/config.py` around lines 110 - 131, Remove the
candidate_files scan from _handle_pyproject_toml_version and keep this method
focused only on pyproject.toml handling. The use of project_name here is invalid
in this scope, so delete the fallback logic that builds package_directory,
iterates over __init__.py/version.py, and parses VERSION from those files. Let
_handle_python_version own any filesystem fallback/version-file scanning
instead, and preserve the pyproject.toml-derived version_path and
CURRENT_VERSION values in this method.
openwisp_utils/releaser/tests/test_version_bumping.py (1)

466-470: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

written_content is undefined — this assertion raises NameError.

Line 470 runs after the with pytest.raises(...) block and references written_content, which is never assigned in this test. It also asserts the generic "1.2.4\n" format, which is irrelevant to a malformed-pyproject case. The test's intent (expect RuntimeError) is already satisfied inside the with block, so this trailing line is stray and should be removed.

🐛 Proposed fix
     m_open = mock_open(read_data=malformed_content)
     with patch("os.path.exists", return_value=True), patch("builtins.open", m_open):
         with pytest.raises(RuntimeError, match="Failed to find"):
             bump_version(config, "1.2.4")
-    assert written_content == "1.2.4\n"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    m_open = mock_open(read_data=malformed_content)
    with patch("os.path.exists", return_value=True), patch("builtins.open", m_open):
        with pytest.raises(RuntimeError, match="Failed to find"):
            bump_version(config, "1.2.4")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openwisp_utils/releaser/tests/test_version_bumping.py` around lines 466 -
470, The test in test_version_bumping.py has a stray assertion after the
pytest.raises(RuntimeError) block that references written_content, but that
variable is never defined in this test. Remove the trailing assert from the test
case around bump_version and keep the malformed_content scenario focused on
asserting the RuntimeError from bump_version(config, "1.2.4"), using the
existing mock_open/patch setup to locate the affected test.
openwisp_utils/releaser/version.py (1)

79-85: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Regex fallback may bump the wrong version field.

_bump_with_regex replaces the first ^version\s*=\s*"..." (count=1). If any table preceding [project] (e.g. a tool/build-system table) declares a version = "...", that line is rewritten instead of [project].version. The tomllib path avoids this only when the exact version = "{current}" string is unique; with two identical strings, content.replace(..., 1) (Line 77) also hits the first one.

Consider anchoring the bump to the [project] section (parse, locate the section span, and replace within it) rather than relying on first-occurrence matching.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openwisp_utils/releaser/version.py` around lines 79 - 85, The version bump
logic can rewrite the wrong version field because both the regex fallback in
_bump_with_regex and the tomllib-based replace in bump_version only target the
first matching occurrence. Update bump_version/version.py to locate the
[project] section explicitly, then replace only the version entry inside that
section, using the existing _bump_with_regex and bump_version symbols as the
place to fix the section-scoped matching.

Moved version.py candidate-files check from _handle_pyproject_toml_version to _handle_python_version so it runs regardless of pyproject.toml existence. Added TypeError to except clause in _handle_python_version list() call.
@BHARATH0153

Copy link
Copy Markdown
Contributor Author

@nemesifier once look into it please thanks!

@CodingWithSaksham CodingWithSaksham left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @BHARATH0153, I have reviewed your PR and the overall design is solid and this is a clean first step.

That said, before a revised PR lands, I would prefer to step back and have a broader discussion on issue #706 first. The reason is that pyproject.toml support is not just a releaser feature, it is the opening move of a larger migration that we want to get right. There are multiple cases that can happen like a mixture of setup.py and pyproject.toml (your PR), fully dropping setup.py and migrating to pyproject.toml and its PEP styles, or just remaining on setup.py.

Comment thread openwisp_utils/releaser/version.py
Comment thread openwisp_utils/releaser/config.py
Comment thread openwisp_utils/releaser/tests/test_config.py
@BHARATH0153

Copy link
Copy Markdown
Contributor Author

@CodingWithSaksham let's wait for the @nemesifier @pandafy review

@pandafy

pandafy commented Jul 2, 2026

Copy link
Copy Markdown
Member

@coderabbitai summary

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
✅ Action performed

Summary regeneration triggered.

@coderabbitai coderabbitai Bot removed the releaser label Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature] Support Python projects using pyproject.toml

4 participants