-
-
Notifications
You must be signed in to change notification settings - Fork 32
feat(rust): make PyO3 optional to fix Python 3.14 fuzz linking #270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Make pyo3 dependency optional behind 'python' feature flag - Add rlib crate-type to support both cdylib (Python) and rlib (fuzzing) - Gate all Python-specific code with #[cfg(feature = "python")] - Make pure Rust functions (escape_xml, wrap_cdata, etc.) public for fuzzing - Add comprehensive unit tests for XML utility functions Fixes linker error with Python 3.14 where PyUnicode_DATA and PyUnicode_KIND symbols are now inline macros, not exported functions. Fuzz targets can now build without linking against Python. Amp-Thread-ID: https://ampcode.com/threads/T-019c0425-ac62-76d8-9d59-4d6aba3edf45 Co-authored-by: Amp <amp@ampcode.com>
Reviewer's GuideMakes the Rust json2xml_rs crate usable both as a Python extension and as a pure Rust library for fuzzing by gating all PyO3 integration behind a Updated class diagram for json2xml_rs core XML utilities and Python-gated APIclassDiagram
class Json2xml_rs {
+escape_xml(s: &str) String
+wrap_cdata(s: &str) String
+is_valid_xml_name(key: &str) bool
+make_valid_xml_name(key: &str) (String, Option<(String, String)>)
+make_attr_string(attrs: &[(String, String)]) String
}
class ConvertConfig {
<<feature_python_only>>
-attr_type: bool
-cdata: bool
-item_wrap: bool
-list_headers: bool
}
class PythonApi {
<<feature_python_only>>
+dicttoxml(obj: &PyAny, root: bool, custom_root: &str, attr_type: bool, item_wrap: bool, cdata: bool, list_headers: bool) PyResult<PyBytes>
+escape_xml_py(s: &str) String
+wrap_cdata_py(s: &str) String
+json2xml_rs(m: &PyModule) PyResult<()>
}
class Converters {
<<feature_python_only>>
+convert_value(py: Python, obj: &PyAny, key: &str, config: &ConvertConfig, indent: usize, level: usize) PyResult<String>
+convert_string(key: &str, val: &str, config: &ConvertConfig) PyResult<String>
+convert_number(key: &str, val: &str, config: &ConvertConfig) PyResult<String>
+convert_bool(key: &str, val: bool, config: &ConvertConfig) PyResult<String>
+convert_none(key: &str, config: &ConvertConfig) PyResult<String>
+convert_dict(py: Python, dict: &PyDict, key: &str, config: &ConvertConfig, indent: usize, level: usize) PyResult<String>
+convert_list(py: Python, list: &PyList, key: &str, config: &ConvertConfig, indent: usize, level: usize) PyResult<String>
}
Json2xml_rs <.. PythonApi : depends_on
Json2xml_rs <.. Converters : uses
Converters --> ConvertConfig
PythonApi --> Converters
PythonApi --> Json2xml_rs
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Amp-Thread-ID: https://ampcode.com/threads/T-019c0425-ac62-76d8-9d59-4d6aba3edf45 Co-authored-by: Amp <amp@ampcode.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey - I've found 1 issue, and left some high level feedback:
- Exposing the XML helper functions as
pubjust for fuzzing broadens the public crate API; consider keeping them non-public and adding a dedicatedfuzzfeature or apub(crate)+ fuzz helper wrapper instead. - There are many repeated
#[cfg(feature = "python")]annotations on Python-related items; consider gating a larger module ormod python;submodule instead to reduce repetition and make the separation between core Rust logic and Python bindings clearer.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Exposing the XML helper functions as `pub` just for fuzzing broadens the public crate API; consider keeping them non-public and adding a dedicated `fuzz` feature or a `pub(crate)` + fuzz helper wrapper instead.
- There are many repeated `#[cfg(feature = "python")]` annotations on Python-related items; consider gating a larger module or `mod python;` submodule instead to reduce repetition and make the separation between core Rust logic and Python bindings clearer.
## Individual Comments
### Comment 1
<location> `rust/fuzz/fuzz_targets/fuzz_make_attr_string.rs:22-25` </location>
<code_context>
+ assert!(result.starts_with(' '), "Attribute string should start with space");
+
+ // 3. Each attribute should produce key="value" format
+ for (key, _value) in &input.attrs {
+ // Key should appear in the result
+ assert!(result.contains(key), "Key '{}' should appear in result", key);
+ }
+
</code_context>
<issue_to_address>
**suggestion (testing):** The key-presence invariant is quite weak and may pass even if attribute formatting is malformed.
Since `contains` is substring-based, this can pass even if the `key="value"` structure is broken or keys overlap (e.g. `"a"` vs `"aa"`). To make the fuzz test more robust, consider matching a more specific pattern like `format!(" {}=\"", key)` or parsing/splitting the attribute string and explicitly validating `key="value"` pairs.
```suggestion
// 2. Result should start with space (for XML formatting)
assert!(result.starts_with(' '), "Attribute string should start with space");
// 3. Each attribute should produce a ` key="value"`-like fragment.
// We check for the more specific pattern ` {key}="` to avoid
// passing on overlapping keys (e.g. "a" vs "aa") or malformed formatting.
for (key, _value) in &input.attrs {
let expected_fragment = format!(" {}=\"", key);
assert!(
result.contains(&expected_fragment),
"Attribute fragment '{}' should appear in result '{}'",
expected_fragment,
result
);
}
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #270 +/- ##
=======================================
Coverage 95.89% 95.89%
=======================================
Files 5 5
Lines 463 463
=======================================
Hits 444 444
Misses 19 19
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
Use more specific pattern matching (` key="`) instead of just checking if key exists as substring. This avoids false positives with overlapping keys (e.g. 'a' vs 'aa') or malformed attribute formatting. Suggested-by: sourcery-ai Amp-Thread-ID: https://ampcode.com/threads/T-019c0425-ac62-76d8-9d59-4d6aba3edf45 Co-authored-by: Amp <amp@ampcode.com>
Summary
Fixes linker error when building fuzz targets with Python 3.14, where
PyUnicode_DATAandPyUnicode_KINDsymbols are now inline macros instead of exported functions.Changes
pyo3dependency optional behindpythonfeature flag (enabled by default)rlibcrate-type to support both cdylib (Python extension) and rlib (fuzzing)#[cfg(feature = "python")]escape_xml,wrap_cdata,is_valid_xml_name,make_valid_xml_name,make_attr_stringTesting
Summary by Sourcery
Make the Rust json2xml library usable both as a Python extension and as a pure Rust crate by gating Python bindings behind a feature flag and exposing core XML utilities for broader use and testing.
New Features:
Bug Fixes:
Enhancements:
pythonfeature to support non-Python builds.Build:
rlibto the crate types to support both Python cdylib builds and pure Rust consumers.pythonenabled by default and makepyo3an optional dependency.Tests: