feat: Add JSON indentation option to decode() method - #37

Open
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation
Open

feat: Add JSON indentation option to decode() method#37
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation

Conversation

@jreakin

Copy link
Copy Markdown

Description

Implements Issue #10 by adding optional JSON indentation support to the decode() function. Users can now request JSON-formatted output with configurable indentation.

Changes Made

1. src/toon_format/types.py

  • Added json_indent: Union[int, None] = None parameter to DecodeOptions class
  • Enhanced docstring to document the new Python-specific feature

2. src/toon_format/decoder.py

  • Added import json for JSON serialization
  • Updated decode() return type from JsonValue to Any
  • Implemented conditional JSON formatting when json_indent is specified
  • Enhanced docstring with usage examples

3. tests/test_api.py

  • Added TestDecodeJSONIndentation class with 11 comprehensive tests
  • Coverage: basic usage, indent levels, nested structures, arrays, unicode, edge cases
  • All tests passing (11/11)

Type of Change

  • New feature (non-breaking change that adds functionality)

SPEC Compliance

  • Non-breaking change
  • Backward compatible (default behavior unchanged)
  • Python-specific feature (output formatting enhancement)
  • Allowed per SPEC §13 (implementation-specific options)

Testing

✅ All 11 new tests pass
✅ All existing tests pass (47 passing in test_api.py)
✅ Code quality: ruff check passes, mypy passes
✅ No breaking changes

Code Quality

  • Type hints present (mypy: 0 errors)
  • Ruff linting passes (0 errors)
  • Code formatted
  • Python 3.8+ compatible

Example Usage

fromtoon_formatimportdecode, DecodeOptions# Default - returns Python objectresult=decode('name: Alice\nage: 30')
# {'name': 'Alice', 'age': 30}# With indentation - returns JSON stringresult=decode('name: Alice\nage: 30', DecodeOptions(json_indent=2))
# {# "name": "Alice",# "age": 30# }

Checklist

  • My code follows the project's coding standards
  • I have added type hints to new code
  • I have added tests that prove my fix/feature works
  • New and existing tests pass locally
  • I have updated documentation
  • My changes do not introduce new dependencies
  • I have maintained Python 3.8+ compatibility
  • I have reviewed the TOON specification

Closes#10

## Description
Implements Issue toon-format#10 by adding optional JSON indentation support to the
decode() function. Users can now request JSON-formatted output with
configurable indentation by passing a json_indent parameter to DecodeOptions.
## Changes Made
- Added json_indent parameter to DecodeOptions class
- Updated decode() to return JSON string when json_indent is specified
- Enhanced docstrings with usage examples
- Added 11 comprehensive tests covering all use cases
## Type of Change
- [x] New feature (non-breaking change that adds functionality)
## SPEC Compliance
- [x] Non-breaking change (default behavior unchanged)
- [x] Backward compatible (json_indent=None by default)
- [x] Python-specific feature (output formatting enhancement)
## Testing
- [x] All existing tests pass
- [x] Added new tests (11 total, 100% pass rate)
- [x] Comprehensive coverage (basic, nested, arrays, unicode, edge cases)
- [x] No breaking changes
## Code Quality
- [x] All type hints present (mypy passes)
- [x] Ruff linting passes (0 errors)
- [x] Code formatted (ruff format)
- [x] Python 3.8+ compatible
## Example Usage
```python
from toon_format import decode, DecodeOptions
# Default behavior - returns Python object
result = decode("name: Alice\nage: 30")
# {'name': 'Alice', 'age': 30}
# With JSON indentation - returns formatted JSON string
result = decode("name: Alice\nage: 30", DecodeOptions(json_indent=2))
# {\n "name": "Alice",\n "age": 30\n}
```
@jreakin
jreakin requested a review from a team as a code ownerNovember 16, 2025 08:21
CopilotAI review requested due to automatic review settings November 16, 2025 08:21

CopilotAI 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.

Pull Request Overview

This PR adds optional JSON indentation support to the decode() function, allowing users to request JSON-formatted output with configurable indentation instead of returning Python objects. This is a Python-specific enhancement that maintains backward compatibility.

Key changes:

  • Added json_indent parameter to DecodeOptions class for output formatting control
  • Modified decode() to conditionally return JSON strings when json_indent is specified
  • Added comprehensive test coverage with 11 new tests covering various edge cases

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

FileDescription
src/toon_format/types.pyAdded json_indent parameter to DecodeOptions with detailed documentation
src/toon_format/decoder.pyImplemented JSON formatting logic and updated return type from JsonValue to Any
tests/test_api.pyAdded 11 comprehensive tests for the new JSON indentation feature

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Per Copilot review, import statements should be at the module level for
consistency and readability. Moved all 'import json' statements from inside
test functions to the top of test_api.py.
- Added 'import json' to module imports (line 13)
- Removed 3 inline imports from test functions
- All tests still passing (11/11)
- Code quality checks pass (ruff, mypy)

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Removed 11 redundant hardcoded test cases and added 19 parametrized tests
that validate json_indent against [TOON spec fixtures](https://github.com/toon-format/spec/tree/main/tests/fixtures/decode).
The feature is now validated across 160+ spec test cases covering primitives,
arrays, objects, nested structures, Unicode, emoji, escape sequences,
different indent sizes and edge cases.
Single source of truth: official TOON specification fixtures. Addresses
maintainer concern about test coverage while maintaining code quality and
reducing test duplication.
Refactored to import and reuse get_all_decode_fixtures() from test_spec_fixtures.py
instead of duplicating fixture loading logic. This reduces code duplication and
ensures both test modules use the same official TOON spec fixtures.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py
Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 5 commits November 16, 2025 16:25
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Removed empty TestDecodeJSONIndentation class (was adding unnecessary code)
- Fixed flawed assertion logic in test_json_indent_with_different_indent_sizes
- Changed return type from Any to Union[JsonValue, str] for better type safety
(makes it clear the function returns either a Python value or a JSON string)
- Added Union to typing imports in decoder.py
All 19 spec-based json_indent tests passing ✓
Kept remote version of TestDecodeJSONIndentation with @pytest.mark.skip decorator
as suggested by Copilot review. This is a better approach than removing the class entirely.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadsrc/toon_format/decoder.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 3 commits November 16, 2025 16:40
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Fix bug where ToonDecodeError from decode_object was incorrectly caught
when checking if input is a key-value pair, causing unterminated strings
to be treated as root primitives instead of raising an error
- Refactor to use try/except/else pattern consistent with codebase style
- Fix linting issues in test_api.py:
- Fix import sorting
- Fix line length violations
- Remove trailing whitespace from blank lines
- Apply code formatting

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
pass


def _get_sample_decode_fixtures() -> List[tuple]:

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The return type hint for _get_sample_decode_fixtures() is List[tuple] which is imprecise. Based on how the return value is used (lines 337-338 and 365-366), it should be List[Tuple[str, Dict[str, Any]]] to match the structure of the tuples being appended at line 325.

Consider updating the import and return type:

fromtypingimportAny, Dict, List, Tuple# Add Tuple to importsdef_get_sample_decode_fixtures() ->List[Tuple[str, Dict[str, Any]]]:

Copilot uses AI. Check for mistakes.
# If json_indent is specified, return JSON-formatted string
if options.json_indent is not None:
if options.json_indent < 0:
raise ToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The error raised for negative json_indent values uses ToonDecodeError, but the docstring documents ValueError. Since json_indent is a formatting option unrelated to TOON format parsing, raising ValueError would be more semantically appropriate and consistent with the documented behavior.

Consider changing to:

raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")
Suggested change
raiseToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")
raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")

Copilot uses AI. Check for mistakes.
Comment on lines 54 to +57
indent: Number of spaces per indentation level (default: 2)
Used for parsing TOON format.
strict: Enable strict validation (default: True)
Enforces spec conformance checks.

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The comment on lines 55-57 uses inconsistent terminology compared to the rest of the codebase. Line 55 says "Used for parsing TOON format" while line 57 says "Enforces spec conformance checks." The wording for indent could be more precise to match the style of the strict parameter.

Consider revising for consistency:

indent: Numberofspacesperindentationlevel (default: 2).
UsedforparsingTOONformatinput.
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.
Suggested change
indent: Numberofspacesperindentationlevel (default: 2)
UsedforparsingTOONformat.
strict: Enablestrictvalidation (default: True)
Enforcesspecconformancechecks.
indent: Numberofspacesperindentationlevel (default: 2).
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +298 to +311
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
class TestDecodeJSONIndentation:
"""Test decode() JSON indentation feature (Issue #10).

Comprehensive tests for the json_indent feature are in
TestDecodeJSONIndentationWithSpecFixtures, which validates against official
TOON specification fixtures.
"""

pass


CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The class TestDecodeJSONIndentation is marked with @pytest.mark.skip but contains only a pass statement. This creates dead code that provides no value.

Since the TODO comment indicates that "comprehensive tests are in TestDecodeJSONIndentationWithSpecFixtures," and that class already exists with actual tests, this entire empty skipped class should be removed to keep the codebase clean. If targeted unit tests are needed in the future, they can be added at that time.

Suggested change
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
classTestDecodeJSONIndentation:
"""Testdecode() JSONindentationfeature (Issue#10).
Comprehensivetestsforthejson_indentfeaturearein
TestDecodeJSONIndentationWithSpecFixtures, whichvalidatesagainstofficial
TOONspecificationfixtures.
"""
pass

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +323 to +325
for test_id, test_data, fixture_name in all_fixtures:
if f"{fixture_name}.json" in selected_files and len(test_cases) < 9:
test_cases.append((test_id, test_data))

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The unpacking in this loop is incorrect. get_all_decode_fixtures() returns tuples of (test_id, test_data, fixture_name) (3 elements), but the code unpacks only 2 elements into (test_id, test_data). This will cause a ValueError: too many values to unpack at runtime.

Fix the unpacking to include all three elements:

fortest_id, test_data, fixture_nameinall_fixtures:
iff"{fixture_name}.json"inselected_filesandlen(test_cases) <9:
test_cases.append((test_id, test_data))

Copilot uses AI. Check for mistakes.
- Add Tuple to typing imports for better type precision
- Update _get_sample_decode_fixtures() return type from List[tuple] to List[Tuple[str, Dict[str, Any]]]
- Improves code clarity and type safety for static analysis tools
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSON indentation option in decode method

2 participants

@jreakin
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat: Add JSON indentation option to decode() method - #37

Open
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation
Open

feat: Add JSON indentation option to decode() method#37
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation

Conversation

@jreakin

Copy link
Copy Markdown

Description

Implements Issue #10 by adding optional JSON indentation support to the decode() function. Users can now request JSON-formatted output with configurable indentation.

Changes Made

1. src/toon_format/types.py

  • Added json_indent: Union[int, None] = None parameter to DecodeOptions class
  • Enhanced docstring to document the new Python-specific feature

2. src/toon_format/decoder.py

  • Added import json for JSON serialization
  • Updated decode() return type from JsonValue to Any
  • Implemented conditional JSON formatting when json_indent is specified
  • Enhanced docstring with usage examples

3. tests/test_api.py

  • Added TestDecodeJSONIndentation class with 11 comprehensive tests
  • Coverage: basic usage, indent levels, nested structures, arrays, unicode, edge cases
  • All tests passing (11/11)

Type of Change

  • New feature (non-breaking change that adds functionality)

SPEC Compliance

  • Non-breaking change
  • Backward compatible (default behavior unchanged)
  • Python-specific feature (output formatting enhancement)
  • Allowed per SPEC §13 (implementation-specific options)

Testing

✅ All 11 new tests pass
✅ All existing tests pass (47 passing in test_api.py)
✅ Code quality: ruff check passes, mypy passes
✅ No breaking changes

Code Quality

  • Type hints present (mypy: 0 errors)
  • Ruff linting passes (0 errors)
  • Code formatted
  • Python 3.8+ compatible

Example Usage

fromtoon_formatimportdecode, DecodeOptions# Default - returns Python objectresult=decode('name: Alice\nage: 30')
# {'name': 'Alice', 'age': 30}# With indentation - returns JSON stringresult=decode('name: Alice\nage: 30', DecodeOptions(json_indent=2))
# {# "name": "Alice",# "age": 30# }

Checklist

  • My code follows the project's coding standards
  • I have added type hints to new code
  • I have added tests that prove my fix/feature works
  • New and existing tests pass locally
  • I have updated documentation
  • My changes do not introduce new dependencies
  • I have maintained Python 3.8+ compatibility
  • I have reviewed the TOON specification

Closes#10

## Description
Implements Issue toon-format#10 by adding optional JSON indentation support to the
decode() function. Users can now request JSON-formatted output with
configurable indentation by passing a json_indent parameter to DecodeOptions.
## Changes Made
- Added json_indent parameter to DecodeOptions class
- Updated decode() to return JSON string when json_indent is specified
- Enhanced docstrings with usage examples
- Added 11 comprehensive tests covering all use cases
## Type of Change
- [x] New feature (non-breaking change that adds functionality)
## SPEC Compliance
- [x] Non-breaking change (default behavior unchanged)
- [x] Backward compatible (json_indent=None by default)
- [x] Python-specific feature (output formatting enhancement)
## Testing
- [x] All existing tests pass
- [x] Added new tests (11 total, 100% pass rate)
- [x] Comprehensive coverage (basic, nested, arrays, unicode, edge cases)
- [x] No breaking changes
## Code Quality
- [x] All type hints present (mypy passes)
- [x] Ruff linting passes (0 errors)
- [x] Code formatted (ruff format)
- [x] Python 3.8+ compatible
## Example Usage
```python
from toon_format import decode, DecodeOptions
# Default behavior - returns Python object
result = decode("name: Alice\nage: 30")
# {'name': 'Alice', 'age': 30}
# With JSON indentation - returns formatted JSON string
result = decode("name: Alice\nage: 30", DecodeOptions(json_indent=2))
# {\n "name": "Alice",\n "age": 30\n}
```
@jreakin
jreakin requested a review from a team as a code ownerNovember 16, 2025 08:21
CopilotAI review requested due to automatic review settings November 16, 2025 08:21

CopilotAI 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.

Pull Request Overview

This PR adds optional JSON indentation support to the decode() function, allowing users to request JSON-formatted output with configurable indentation instead of returning Python objects. This is a Python-specific enhancement that maintains backward compatibility.

Key changes:

  • Added json_indent parameter to DecodeOptions class for output formatting control
  • Modified decode() to conditionally return JSON strings when json_indent is specified
  • Added comprehensive test coverage with 11 new tests covering various edge cases

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

FileDescription
src/toon_format/types.pyAdded json_indent parameter to DecodeOptions with detailed documentation
src/toon_format/decoder.pyImplemented JSON formatting logic and updated return type from JsonValue to Any
tests/test_api.pyAdded 11 comprehensive tests for the new JSON indentation feature

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Per Copilot review, import statements should be at the module level for
consistency and readability. Moved all 'import json' statements from inside
test functions to the top of test_api.py.
- Added 'import json' to module imports (line 13)
- Removed 3 inline imports from test functions
- All tests still passing (11/11)
- Code quality checks pass (ruff, mypy)

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Removed 11 redundant hardcoded test cases and added 19 parametrized tests
that validate json_indent against [TOON spec fixtures](https://github.com/toon-format/spec/tree/main/tests/fixtures/decode).
The feature is now validated across 160+ spec test cases covering primitives,
arrays, objects, nested structures, Unicode, emoji, escape sequences,
different indent sizes and edge cases.
Single source of truth: official TOON specification fixtures. Addresses
maintainer concern about test coverage while maintaining code quality and
reducing test duplication.
Refactored to import and reuse get_all_decode_fixtures() from test_spec_fixtures.py
instead of duplicating fixture loading logic. This reduces code duplication and
ensures both test modules use the same official TOON spec fixtures.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py
Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 5 commits November 16, 2025 16:25
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Removed empty TestDecodeJSONIndentation class (was adding unnecessary code)
- Fixed flawed assertion logic in test_json_indent_with_different_indent_sizes
- Changed return type from Any to Union[JsonValue, str] for better type safety
(makes it clear the function returns either a Python value or a JSON string)
- Added Union to typing imports in decoder.py
All 19 spec-based json_indent tests passing ✓
Kept remote version of TestDecodeJSONIndentation with @pytest.mark.skip decorator
as suggested by Copilot review. This is a better approach than removing the class entirely.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadsrc/toon_format/decoder.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 3 commits November 16, 2025 16:40
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Fix bug where ToonDecodeError from decode_object was incorrectly caught
when checking if input is a key-value pair, causing unterminated strings
to be treated as root primitives instead of raising an error
- Refactor to use try/except/else pattern consistent with codebase style
- Fix linting issues in test_api.py:
- Fix import sorting
- Fix line length violations
- Remove trailing whitespace from blank lines
- Apply code formatting

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
pass


def _get_sample_decode_fixtures() -> List[tuple]:

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The return type hint for _get_sample_decode_fixtures() is List[tuple] which is imprecise. Based on how the return value is used (lines 337-338 and 365-366), it should be List[Tuple[str, Dict[str, Any]]] to match the structure of the tuples being appended at line 325.

Consider updating the import and return type:

fromtypingimportAny, Dict, List, Tuple# Add Tuple to importsdef_get_sample_decode_fixtures() ->List[Tuple[str, Dict[str, Any]]]:

Copilot uses AI. Check for mistakes.
# If json_indent is specified, return JSON-formatted string
if options.json_indent is not None:
if options.json_indent < 0:
raise ToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The error raised for negative json_indent values uses ToonDecodeError, but the docstring documents ValueError. Since json_indent is a formatting option unrelated to TOON format parsing, raising ValueError would be more semantically appropriate and consistent with the documented behavior.

Consider changing to:

raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")
Suggested change
raiseToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")
raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")

Copilot uses AI. Check for mistakes.
Comment on lines 54 to +57
indent: Number of spaces per indentation level (default: 2)
Used for parsing TOON format.
strict: Enable strict validation (default: True)
Enforces spec conformance checks.

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The comment on lines 55-57 uses inconsistent terminology compared to the rest of the codebase. Line 55 says "Used for parsing TOON format" while line 57 says "Enforces spec conformance checks." The wording for indent could be more precise to match the style of the strict parameter.

Consider revising for consistency:

indent: Numberofspacesperindentationlevel (default: 2).
UsedforparsingTOONformatinput.
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.
Suggested change
indent: Numberofspacesperindentationlevel (default: 2)
UsedforparsingTOONformat.
strict: Enablestrictvalidation (default: True)
Enforcesspecconformancechecks.
indent: Numberofspacesperindentationlevel (default: 2).
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +298 to +311
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
class TestDecodeJSONIndentation:
"""Test decode() JSON indentation feature (Issue #10).

Comprehensive tests for the json_indent feature are in
TestDecodeJSONIndentationWithSpecFixtures, which validates against official
TOON specification fixtures.
"""

pass


CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The class TestDecodeJSONIndentation is marked with @pytest.mark.skip but contains only a pass statement. This creates dead code that provides no value.

Since the TODO comment indicates that "comprehensive tests are in TestDecodeJSONIndentationWithSpecFixtures," and that class already exists with actual tests, this entire empty skipped class should be removed to keep the codebase clean. If targeted unit tests are needed in the future, they can be added at that time.

Suggested change
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
classTestDecodeJSONIndentation:
"""Testdecode() JSONindentationfeature (Issue#10).
Comprehensivetestsforthejson_indentfeaturearein
TestDecodeJSONIndentationWithSpecFixtures, whichvalidatesagainstofficial
TOONspecificationfixtures.
"""
pass

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +323 to +325
for test_id, test_data, fixture_name in all_fixtures:
if f"{fixture_name}.json" in selected_files and len(test_cases) < 9:
test_cases.append((test_id, test_data))

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The unpacking in this loop is incorrect. get_all_decode_fixtures() returns tuples of (test_id, test_data, fixture_name) (3 elements), but the code unpacks only 2 elements into (test_id, test_data). This will cause a ValueError: too many values to unpack at runtime.

Fix the unpacking to include all three elements:

fortest_id, test_data, fixture_nameinall_fixtures:
iff"{fixture_name}.json"inselected_filesandlen(test_cases) <9:
test_cases.append((test_id, test_data))

Copilot uses AI. Check for mistakes.
- Add Tuple to typing imports for better type precision
- Update _get_sample_decode_fixtures() return type from List[tuple] to List[Tuple[str, Dict[str, Any]]]
- Improves code clarity and type safety for static analysis tools
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSON indentation option in decode method

2 participants

@jreakin
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: Add JSON indentation option to decode() method - #37

Open
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation
Open

feat: Add JSON indentation option to decode() method#37
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation

Conversation

@jreakin

Copy link
Copy Markdown

Description

Implements Issue #10 by adding optional JSON indentation support to the decode() function. Users can now request JSON-formatted output with configurable indentation.

Changes Made

1. src/toon_format/types.py

  • Added json_indent: Union[int, None] = None parameter to DecodeOptions class
  • Enhanced docstring to document the new Python-specific feature

2. src/toon_format/decoder.py

  • Added import json for JSON serialization
  • Updated decode() return type from JsonValue to Any
  • Implemented conditional JSON formatting when json_indent is specified
  • Enhanced docstring with usage examples

3. tests/test_api.py

  • Added TestDecodeJSONIndentation class with 11 comprehensive tests
  • Coverage: basic usage, indent levels, nested structures, arrays, unicode, edge cases
  • All tests passing (11/11)

Type of Change

  • New feature (non-breaking change that adds functionality)

SPEC Compliance

  • Non-breaking change
  • Backward compatible (default behavior unchanged)
  • Python-specific feature (output formatting enhancement)
  • Allowed per SPEC §13 (implementation-specific options)

Testing

✅ All 11 new tests pass
✅ All existing tests pass (47 passing in test_api.py)
✅ Code quality: ruff check passes, mypy passes
✅ No breaking changes

Code Quality

  • Type hints present (mypy: 0 errors)
  • Ruff linting passes (0 errors)
  • Code formatted
  • Python 3.8+ compatible

Example Usage

fromtoon_formatimportdecode, DecodeOptions# Default - returns Python objectresult=decode('name: Alice\nage: 30')
# {'name': 'Alice', 'age': 30}# With indentation - returns JSON stringresult=decode('name: Alice\nage: 30', DecodeOptions(json_indent=2))
# {# "name": "Alice",# "age": 30# }

Checklist

  • My code follows the project's coding standards
  • I have added type hints to new code
  • I have added tests that prove my fix/feature works
  • New and existing tests pass locally
  • I have updated documentation
  • My changes do not introduce new dependencies
  • I have maintained Python 3.8+ compatibility
  • I have reviewed the TOON specification

Closes#10

## Description
Implements Issue toon-format#10 by adding optional JSON indentation support to the
decode() function. Users can now request JSON-formatted output with
configurable indentation by passing a json_indent parameter to DecodeOptions.
## Changes Made
- Added json_indent parameter to DecodeOptions class
- Updated decode() to return JSON string when json_indent is specified
- Enhanced docstrings with usage examples
- Added 11 comprehensive tests covering all use cases
## Type of Change
- [x] New feature (non-breaking change that adds functionality)
## SPEC Compliance
- [x] Non-breaking change (default behavior unchanged)
- [x] Backward compatible (json_indent=None by default)
- [x] Python-specific feature (output formatting enhancement)
## Testing
- [x] All existing tests pass
- [x] Added new tests (11 total, 100% pass rate)
- [x] Comprehensive coverage (basic, nested, arrays, unicode, edge cases)
- [x] No breaking changes
## Code Quality
- [x] All type hints present (mypy passes)
- [x] Ruff linting passes (0 errors)
- [x] Code formatted (ruff format)
- [x] Python 3.8+ compatible
## Example Usage
```python
from toon_format import decode, DecodeOptions
# Default behavior - returns Python object
result = decode("name: Alice\nage: 30")
# {'name': 'Alice', 'age': 30}
# With JSON indentation - returns formatted JSON string
result = decode("name: Alice\nage: 30", DecodeOptions(json_indent=2))
# {\n "name": "Alice",\n "age": 30\n}
```
@jreakin
jreakin requested a review from a team as a code ownerNovember 16, 2025 08:21
CopilotAI review requested due to automatic review settings November 16, 2025 08:21

CopilotAI 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.

Pull Request Overview

This PR adds optional JSON indentation support to the decode() function, allowing users to request JSON-formatted output with configurable indentation instead of returning Python objects. This is a Python-specific enhancement that maintains backward compatibility.

Key changes:

  • Added json_indent parameter to DecodeOptions class for output formatting control
  • Modified decode() to conditionally return JSON strings when json_indent is specified
  • Added comprehensive test coverage with 11 new tests covering various edge cases

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

FileDescription
src/toon_format/types.pyAdded json_indent parameter to DecodeOptions with detailed documentation
src/toon_format/decoder.pyImplemented JSON formatting logic and updated return type from JsonValue to Any
tests/test_api.pyAdded 11 comprehensive tests for the new JSON indentation feature

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Per Copilot review, import statements should be at the module level for
consistency and readability. Moved all 'import json' statements from inside
test functions to the top of test_api.py.
- Added 'import json' to module imports (line 13)
- Removed 3 inline imports from test functions
- All tests still passing (11/11)
- Code quality checks pass (ruff, mypy)

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Removed 11 redundant hardcoded test cases and added 19 parametrized tests
that validate json_indent against [TOON spec fixtures](https://github.com/toon-format/spec/tree/main/tests/fixtures/decode).
The feature is now validated across 160+ spec test cases covering primitives,
arrays, objects, nested structures, Unicode, emoji, escape sequences,
different indent sizes and edge cases.
Single source of truth: official TOON specification fixtures. Addresses
maintainer concern about test coverage while maintaining code quality and
reducing test duplication.
Refactored to import and reuse get_all_decode_fixtures() from test_spec_fixtures.py
instead of duplicating fixture loading logic. This reduces code duplication and
ensures both test modules use the same official TOON spec fixtures.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py
Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 5 commits November 16, 2025 16:25
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Removed empty TestDecodeJSONIndentation class (was adding unnecessary code)
- Fixed flawed assertion logic in test_json_indent_with_different_indent_sizes
- Changed return type from Any to Union[JsonValue, str] for better type safety
(makes it clear the function returns either a Python value or a JSON string)
- Added Union to typing imports in decoder.py
All 19 spec-based json_indent tests passing ✓
Kept remote version of TestDecodeJSONIndentation with @pytest.mark.skip decorator
as suggested by Copilot review. This is a better approach than removing the class entirely.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadsrc/toon_format/decoder.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 3 commits November 16, 2025 16:40
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Fix bug where ToonDecodeError from decode_object was incorrectly caught
when checking if input is a key-value pair, causing unterminated strings
to be treated as root primitives instead of raising an error
- Refactor to use try/except/else pattern consistent with codebase style
- Fix linting issues in test_api.py:
- Fix import sorting
- Fix line length violations
- Remove trailing whitespace from blank lines
- Apply code formatting

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
pass


def _get_sample_decode_fixtures() -> List[tuple]:

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The return type hint for _get_sample_decode_fixtures() is List[tuple] which is imprecise. Based on how the return value is used (lines 337-338 and 365-366), it should be List[Tuple[str, Dict[str, Any]]] to match the structure of the tuples being appended at line 325.

Consider updating the import and return type:

fromtypingimportAny, Dict, List, Tuple# Add Tuple to importsdef_get_sample_decode_fixtures() ->List[Tuple[str, Dict[str, Any]]]:

Copilot uses AI. Check for mistakes.
# If json_indent is specified, return JSON-formatted string
if options.json_indent is not None:
if options.json_indent < 0:
raise ToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The error raised for negative json_indent values uses ToonDecodeError, but the docstring documents ValueError. Since json_indent is a formatting option unrelated to TOON format parsing, raising ValueError would be more semantically appropriate and consistent with the documented behavior.

Consider changing to:

raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")
Suggested change
raiseToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")
raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")

Copilot uses AI. Check for mistakes.
Comment on lines 54 to +57
indent: Number of spaces per indentation level (default: 2)
Used for parsing TOON format.
strict: Enable strict validation (default: True)
Enforces spec conformance checks.

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The comment on lines 55-57 uses inconsistent terminology compared to the rest of the codebase. Line 55 says "Used for parsing TOON format" while line 57 says "Enforces spec conformance checks." The wording for indent could be more precise to match the style of the strict parameter.

Consider revising for consistency:

indent: Numberofspacesperindentationlevel (default: 2).
UsedforparsingTOONformatinput.
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.
Suggested change
indent: Numberofspacesperindentationlevel (default: 2)
UsedforparsingTOONformat.
strict: Enablestrictvalidation (default: True)
Enforcesspecconformancechecks.
indent: Numberofspacesperindentationlevel (default: 2).
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +298 to +311
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
class TestDecodeJSONIndentation:
"""Test decode() JSON indentation feature (Issue #10).

Comprehensive tests for the json_indent feature are in
TestDecodeJSONIndentationWithSpecFixtures, which validates against official
TOON specification fixtures.
"""

pass


CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The class TestDecodeJSONIndentation is marked with @pytest.mark.skip but contains only a pass statement. This creates dead code that provides no value.

Since the TODO comment indicates that "comprehensive tests are in TestDecodeJSONIndentationWithSpecFixtures," and that class already exists with actual tests, this entire empty skipped class should be removed to keep the codebase clean. If targeted unit tests are needed in the future, they can be added at that time.

Suggested change
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
classTestDecodeJSONIndentation:
"""Testdecode() JSONindentationfeature (Issue#10).
Comprehensivetestsforthejson_indentfeaturearein
TestDecodeJSONIndentationWithSpecFixtures, whichvalidatesagainstofficial
TOONspecificationfixtures.
"""
pass

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +323 to +325
for test_id, test_data, fixture_name in all_fixtures:
if f"{fixture_name}.json" in selected_files and len(test_cases) < 9:
test_cases.append((test_id, test_data))

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The unpacking in this loop is incorrect. get_all_decode_fixtures() returns tuples of (test_id, test_data, fixture_name) (3 elements), but the code unpacks only 2 elements into (test_id, test_data). This will cause a ValueError: too many values to unpack at runtime.

Fix the unpacking to include all three elements:

fortest_id, test_data, fixture_nameinall_fixtures:
iff"{fixture_name}.json"inselected_filesandlen(test_cases) <9:
test_cases.append((test_id, test_data))

Copilot uses AI. Check for mistakes.
- Add Tuple to typing imports for better type precision
- Update _get_sample_decode_fixtures() return type from List[tuple] to List[Tuple[str, Dict[str, Any]]]
- Improves code clarity and type safety for static analysis tools
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSON indentation option in decode method

2 participants

@jreakin
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: Add JSON indentation option to decode() method - #37

Open
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation
Open

feat: Add JSON indentation option to decode() method#37
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation

Conversation

@jreakin

Copy link
Copy Markdown

Description

Implements Issue #10 by adding optional JSON indentation support to the decode() function. Users can now request JSON-formatted output with configurable indentation.

Changes Made

1. src/toon_format/types.py

  • Added json_indent: Union[int, None] = None parameter to DecodeOptions class
  • Enhanced docstring to document the new Python-specific feature

2. src/toon_format/decoder.py

  • Added import json for JSON serialization
  • Updated decode() return type from JsonValue to Any
  • Implemented conditional JSON formatting when json_indent is specified
  • Enhanced docstring with usage examples

3. tests/test_api.py

  • Added TestDecodeJSONIndentation class with 11 comprehensive tests
  • Coverage: basic usage, indent levels, nested structures, arrays, unicode, edge cases
  • All tests passing (11/11)

Type of Change

  • New feature (non-breaking change that adds functionality)

SPEC Compliance

  • Non-breaking change
  • Backward compatible (default behavior unchanged)
  • Python-specific feature (output formatting enhancement)
  • Allowed per SPEC §13 (implementation-specific options)

Testing

✅ All 11 new tests pass
✅ All existing tests pass (47 passing in test_api.py)
✅ Code quality: ruff check passes, mypy passes
✅ No breaking changes

Code Quality

  • Type hints present (mypy: 0 errors)
  • Ruff linting passes (0 errors)
  • Code formatted
  • Python 3.8+ compatible

Example Usage

fromtoon_formatimportdecode, DecodeOptions# Default - returns Python objectresult=decode('name: Alice\nage: 30')
# {'name': 'Alice', 'age': 30}# With indentation - returns JSON stringresult=decode('name: Alice\nage: 30', DecodeOptions(json_indent=2))
# {# "name": "Alice",# "age": 30# }

Checklist

  • My code follows the project's coding standards
  • I have added type hints to new code
  • I have added tests that prove my fix/feature works
  • New and existing tests pass locally
  • I have updated documentation
  • My changes do not introduce new dependencies
  • I have maintained Python 3.8+ compatibility
  • I have reviewed the TOON specification

Closes#10

## Description
Implements Issue toon-format#10 by adding optional JSON indentation support to the
decode() function. Users can now request JSON-formatted output with
configurable indentation by passing a json_indent parameter to DecodeOptions.
## Changes Made
- Added json_indent parameter to DecodeOptions class
- Updated decode() to return JSON string when json_indent is specified
- Enhanced docstrings with usage examples
- Added 11 comprehensive tests covering all use cases
## Type of Change
- [x] New feature (non-breaking change that adds functionality)
## SPEC Compliance
- [x] Non-breaking change (default behavior unchanged)
- [x] Backward compatible (json_indent=None by default)
- [x] Python-specific feature (output formatting enhancement)
## Testing
- [x] All existing tests pass
- [x] Added new tests (11 total, 100% pass rate)
- [x] Comprehensive coverage (basic, nested, arrays, unicode, edge cases)
- [x] No breaking changes
## Code Quality
- [x] All type hints present (mypy passes)
- [x] Ruff linting passes (0 errors)
- [x] Code formatted (ruff format)
- [x] Python 3.8+ compatible
## Example Usage
```python
from toon_format import decode, DecodeOptions
# Default behavior - returns Python object
result = decode("name: Alice\nage: 30")
# {'name': 'Alice', 'age': 30}
# With JSON indentation - returns formatted JSON string
result = decode("name: Alice\nage: 30", DecodeOptions(json_indent=2))
# {\n "name": "Alice",\n "age": 30\n}
```
@jreakin
jreakin requested a review from a team as a code ownerNovember 16, 2025 08:21
CopilotAI review requested due to automatic review settings November 16, 2025 08:21

CopilotAI 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.

Pull Request Overview

This PR adds optional JSON indentation support to the decode() function, allowing users to request JSON-formatted output with configurable indentation instead of returning Python objects. This is a Python-specific enhancement that maintains backward compatibility.

Key changes:

  • Added json_indent parameter to DecodeOptions class for output formatting control
  • Modified decode() to conditionally return JSON strings when json_indent is specified
  • Added comprehensive test coverage with 11 new tests covering various edge cases

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

FileDescription
src/toon_format/types.pyAdded json_indent parameter to DecodeOptions with detailed documentation
src/toon_format/decoder.pyImplemented JSON formatting logic and updated return type from JsonValue to Any
tests/test_api.pyAdded 11 comprehensive tests for the new JSON indentation feature

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Per Copilot review, import statements should be at the module level for
consistency and readability. Moved all 'import json' statements from inside
test functions to the top of test_api.py.
- Added 'import json' to module imports (line 13)
- Removed 3 inline imports from test functions
- All tests still passing (11/11)
- Code quality checks pass (ruff, mypy)

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Removed 11 redundant hardcoded test cases and added 19 parametrized tests
that validate json_indent against [TOON spec fixtures](https://github.com/toon-format/spec/tree/main/tests/fixtures/decode).
The feature is now validated across 160+ spec test cases covering primitives,
arrays, objects, nested structures, Unicode, emoji, escape sequences,
different indent sizes and edge cases.
Single source of truth: official TOON specification fixtures. Addresses
maintainer concern about test coverage while maintaining code quality and
reducing test duplication.
Refactored to import and reuse get_all_decode_fixtures() from test_spec_fixtures.py
instead of duplicating fixture loading logic. This reduces code duplication and
ensures both test modules use the same official TOON spec fixtures.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py
Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 5 commits November 16, 2025 16:25
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Removed empty TestDecodeJSONIndentation class (was adding unnecessary code)
- Fixed flawed assertion logic in test_json_indent_with_different_indent_sizes
- Changed return type from Any to Union[JsonValue, str] for better type safety
(makes it clear the function returns either a Python value or a JSON string)
- Added Union to typing imports in decoder.py
All 19 spec-based json_indent tests passing ✓
Kept remote version of TestDecodeJSONIndentation with @pytest.mark.skip decorator
as suggested by Copilot review. This is a better approach than removing the class entirely.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadsrc/toon_format/decoder.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 3 commits November 16, 2025 16:40
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Fix bug where ToonDecodeError from decode_object was incorrectly caught
when checking if input is a key-value pair, causing unterminated strings
to be treated as root primitives instead of raising an error
- Refactor to use try/except/else pattern consistent with codebase style
- Fix linting issues in test_api.py:
- Fix import sorting
- Fix line length violations
- Remove trailing whitespace from blank lines
- Apply code formatting

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
pass


def _get_sample_decode_fixtures() -> List[tuple]:

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The return type hint for _get_sample_decode_fixtures() is List[tuple] which is imprecise. Based on how the return value is used (lines 337-338 and 365-366), it should be List[Tuple[str, Dict[str, Any]]] to match the structure of the tuples being appended at line 325.

Consider updating the import and return type:

fromtypingimportAny, Dict, List, Tuple# Add Tuple to importsdef_get_sample_decode_fixtures() ->List[Tuple[str, Dict[str, Any]]]:

Copilot uses AI. Check for mistakes.
# If json_indent is specified, return JSON-formatted string
if options.json_indent is not None:
if options.json_indent < 0:
raise ToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The error raised for negative json_indent values uses ToonDecodeError, but the docstring documents ValueError. Since json_indent is a formatting option unrelated to TOON format parsing, raising ValueError would be more semantically appropriate and consistent with the documented behavior.

Consider changing to:

raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")
Suggested change
raiseToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")
raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")

Copilot uses AI. Check for mistakes.
Comment on lines 54 to +57
indent: Number of spaces per indentation level (default: 2)
Used for parsing TOON format.
strict: Enable strict validation (default: True)
Enforces spec conformance checks.

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The comment on lines 55-57 uses inconsistent terminology compared to the rest of the codebase. Line 55 says "Used for parsing TOON format" while line 57 says "Enforces spec conformance checks." The wording for indent could be more precise to match the style of the strict parameter.

Consider revising for consistency:

indent: Numberofspacesperindentationlevel (default: 2).
UsedforparsingTOONformatinput.
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.
Suggested change
indent: Numberofspacesperindentationlevel (default: 2)
UsedforparsingTOONformat.
strict: Enablestrictvalidation (default: True)
Enforcesspecconformancechecks.
indent: Numberofspacesperindentationlevel (default: 2).
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +298 to +311
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
class TestDecodeJSONIndentation:
"""Test decode() JSON indentation feature (Issue #10).

Comprehensive tests for the json_indent feature are in
TestDecodeJSONIndentationWithSpecFixtures, which validates against official
TOON specification fixtures.
"""

pass


CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The class TestDecodeJSONIndentation is marked with @pytest.mark.skip but contains only a pass statement. This creates dead code that provides no value.

Since the TODO comment indicates that "comprehensive tests are in TestDecodeJSONIndentationWithSpecFixtures," and that class already exists with actual tests, this entire empty skipped class should be removed to keep the codebase clean. If targeted unit tests are needed in the future, they can be added at that time.

Suggested change
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
classTestDecodeJSONIndentation:
"""Testdecode() JSONindentationfeature (Issue#10).
Comprehensivetestsforthejson_indentfeaturearein
TestDecodeJSONIndentationWithSpecFixtures, whichvalidatesagainstofficial
TOONspecificationfixtures.
"""
pass

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +323 to +325
for test_id, test_data, fixture_name in all_fixtures:
if f"{fixture_name}.json" in selected_files and len(test_cases) < 9:
test_cases.append((test_id, test_data))

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The unpacking in this loop is incorrect. get_all_decode_fixtures() returns tuples of (test_id, test_data, fixture_name) (3 elements), but the code unpacks only 2 elements into (test_id, test_data). This will cause a ValueError: too many values to unpack at runtime.

Fix the unpacking to include all three elements:

fortest_id, test_data, fixture_nameinall_fixtures:
iff"{fixture_name}.json"inselected_filesandlen(test_cases) <9:
test_cases.append((test_id, test_data))

Copilot uses AI. Check for mistakes.
- Add Tuple to typing imports for better type precision
- Update _get_sample_decode_fixtures() return type from List[tuple] to List[Tuple[str, Dict[str, Any]]]
- Improves code clarity and type safety for static analysis tools
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSON indentation option in decode method

2 participants

@jreakin
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat: Add JSON indentation option to decode() method - #37

Open
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation
Open

feat: Add JSON indentation option to decode() method#37
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation

Conversation

@jreakin

Copy link
Copy Markdown

Description

Implements Issue #10 by adding optional JSON indentation support to the decode() function. Users can now request JSON-formatted output with configurable indentation.

Changes Made

1. src/toon_format/types.py

  • Added json_indent: Union[int, None] = None parameter to DecodeOptions class
  • Enhanced docstring to document the new Python-specific feature

2. src/toon_format/decoder.py

  • Added import json for JSON serialization
  • Updated decode() return type from JsonValue to Any
  • Implemented conditional JSON formatting when json_indent is specified
  • Enhanced docstring with usage examples

3. tests/test_api.py

  • Added TestDecodeJSONIndentation class with 11 comprehensive tests
  • Coverage: basic usage, indent levels, nested structures, arrays, unicode, edge cases
  • All tests passing (11/11)

Type of Change

  • New feature (non-breaking change that adds functionality)

SPEC Compliance

  • Non-breaking change
  • Backward compatible (default behavior unchanged)
  • Python-specific feature (output formatting enhancement)
  • Allowed per SPEC §13 (implementation-specific options)

Testing

✅ All 11 new tests pass
✅ All existing tests pass (47 passing in test_api.py)
✅ Code quality: ruff check passes, mypy passes
✅ No breaking changes

Code Quality

  • Type hints present (mypy: 0 errors)
  • Ruff linting passes (0 errors)
  • Code formatted
  • Python 3.8+ compatible

Example Usage

fromtoon_formatimportdecode, DecodeOptions# Default - returns Python objectresult=decode('name: Alice\nage: 30')
# {'name': 'Alice', 'age': 30}# With indentation - returns JSON stringresult=decode('name: Alice\nage: 30', DecodeOptions(json_indent=2))
# {# "name": "Alice",# "age": 30# }

Checklist

  • My code follows the project's coding standards
  • I have added type hints to new code
  • I have added tests that prove my fix/feature works
  • New and existing tests pass locally
  • I have updated documentation
  • My changes do not introduce new dependencies
  • I have maintained Python 3.8+ compatibility
  • I have reviewed the TOON specification

Closes#10

## Description
Implements Issue toon-format#10 by adding optional JSON indentation support to the
decode() function. Users can now request JSON-formatted output with
configurable indentation by passing a json_indent parameter to DecodeOptions.
## Changes Made
- Added json_indent parameter to DecodeOptions class
- Updated decode() to return JSON string when json_indent is specified
- Enhanced docstrings with usage examples
- Added 11 comprehensive tests covering all use cases
## Type of Change
- [x] New feature (non-breaking change that adds functionality)
## SPEC Compliance
- [x] Non-breaking change (default behavior unchanged)
- [x] Backward compatible (json_indent=None by default)
- [x] Python-specific feature (output formatting enhancement)
## Testing
- [x] All existing tests pass
- [x] Added new tests (11 total, 100% pass rate)
- [x] Comprehensive coverage (basic, nested, arrays, unicode, edge cases)
- [x] No breaking changes
## Code Quality
- [x] All type hints present (mypy passes)
- [x] Ruff linting passes (0 errors)
- [x] Code formatted (ruff format)
- [x] Python 3.8+ compatible
## Example Usage
```python
from toon_format import decode, DecodeOptions
# Default behavior - returns Python object
result = decode("name: Alice\nage: 30")
# {'name': 'Alice', 'age': 30}
# With JSON indentation - returns formatted JSON string
result = decode("name: Alice\nage: 30", DecodeOptions(json_indent=2))
# {\n "name": "Alice",\n "age": 30\n}
```
@jreakin
jreakin requested a review from a team as a code ownerNovember 16, 2025 08:21
CopilotAI review requested due to automatic review settings November 16, 2025 08:21

CopilotAI 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.

Pull Request Overview

This PR adds optional JSON indentation support to the decode() function, allowing users to request JSON-formatted output with configurable indentation instead of returning Python objects. This is a Python-specific enhancement that maintains backward compatibility.

Key changes:

  • Added json_indent parameter to DecodeOptions class for output formatting control
  • Modified decode() to conditionally return JSON strings when json_indent is specified
  • Added comprehensive test coverage with 11 new tests covering various edge cases

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

FileDescription
src/toon_format/types.pyAdded json_indent parameter to DecodeOptions with detailed documentation
src/toon_format/decoder.pyImplemented JSON formatting logic and updated return type from JsonValue to Any
tests/test_api.pyAdded 11 comprehensive tests for the new JSON indentation feature

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Per Copilot review, import statements should be at the module level for
consistency and readability. Moved all 'import json' statements from inside
test functions to the top of test_api.py.
- Added 'import json' to module imports (line 13)
- Removed 3 inline imports from test functions
- All tests still passing (11/11)
- Code quality checks pass (ruff, mypy)

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Removed 11 redundant hardcoded test cases and added 19 parametrized tests
that validate json_indent against [TOON spec fixtures](https://github.com/toon-format/spec/tree/main/tests/fixtures/decode).
The feature is now validated across 160+ spec test cases covering primitives,
arrays, objects, nested structures, Unicode, emoji, escape sequences,
different indent sizes and edge cases.
Single source of truth: official TOON specification fixtures. Addresses
maintainer concern about test coverage while maintaining code quality and
reducing test duplication.
Refactored to import and reuse get_all_decode_fixtures() from test_spec_fixtures.py
instead of duplicating fixture loading logic. This reduces code duplication and
ensures both test modules use the same official TOON spec fixtures.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py
Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 5 commits November 16, 2025 16:25
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Removed empty TestDecodeJSONIndentation class (was adding unnecessary code)
- Fixed flawed assertion logic in test_json_indent_with_different_indent_sizes
- Changed return type from Any to Union[JsonValue, str] for better type safety
(makes it clear the function returns either a Python value or a JSON string)
- Added Union to typing imports in decoder.py
All 19 spec-based json_indent tests passing ✓
Kept remote version of TestDecodeJSONIndentation with @pytest.mark.skip decorator
as suggested by Copilot review. This is a better approach than removing the class entirely.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadsrc/toon_format/decoder.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 3 commits November 16, 2025 16:40
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Fix bug where ToonDecodeError from decode_object was incorrectly caught
when checking if input is a key-value pair, causing unterminated strings
to be treated as root primitives instead of raising an error
- Refactor to use try/except/else pattern consistent with codebase style
- Fix linting issues in test_api.py:
- Fix import sorting
- Fix line length violations
- Remove trailing whitespace from blank lines
- Apply code formatting

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
pass


def _get_sample_decode_fixtures() -> List[tuple]:

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The return type hint for _get_sample_decode_fixtures() is List[tuple] which is imprecise. Based on how the return value is used (lines 337-338 and 365-366), it should be List[Tuple[str, Dict[str, Any]]] to match the structure of the tuples being appended at line 325.

Consider updating the import and return type:

fromtypingimportAny, Dict, List, Tuple# Add Tuple to importsdef_get_sample_decode_fixtures() ->List[Tuple[str, Dict[str, Any]]]:

Copilot uses AI. Check for mistakes.
# If json_indent is specified, return JSON-formatted string
if options.json_indent is not None:
if options.json_indent < 0:
raise ToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The error raised for negative json_indent values uses ToonDecodeError, but the docstring documents ValueError. Since json_indent is a formatting option unrelated to TOON format parsing, raising ValueError would be more semantically appropriate and consistent with the documented behavior.

Consider changing to:

raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")
Suggested change
raiseToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")
raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")

Copilot uses AI. Check for mistakes.
Comment on lines 54 to +57
indent: Number of spaces per indentation level (default: 2)
Used for parsing TOON format.
strict: Enable strict validation (default: True)
Enforces spec conformance checks.

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The comment on lines 55-57 uses inconsistent terminology compared to the rest of the codebase. Line 55 says "Used for parsing TOON format" while line 57 says "Enforces spec conformance checks." The wording for indent could be more precise to match the style of the strict parameter.

Consider revising for consistency:

indent: Numberofspacesperindentationlevel (default: 2).
UsedforparsingTOONformatinput.
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.
Suggested change
indent: Numberofspacesperindentationlevel (default: 2)
UsedforparsingTOONformat.
strict: Enablestrictvalidation (default: True)
Enforcesspecconformancechecks.
indent: Numberofspacesperindentationlevel (default: 2).
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +298 to +311
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
class TestDecodeJSONIndentation:
"""Test decode() JSON indentation feature (Issue #10).

Comprehensive tests for the json_indent feature are in
TestDecodeJSONIndentationWithSpecFixtures, which validates against official
TOON specification fixtures.
"""

pass


CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The class TestDecodeJSONIndentation is marked with @pytest.mark.skip but contains only a pass statement. This creates dead code that provides no value.

Since the TODO comment indicates that "comprehensive tests are in TestDecodeJSONIndentationWithSpecFixtures," and that class already exists with actual tests, this entire empty skipped class should be removed to keep the codebase clean. If targeted unit tests are needed in the future, they can be added at that time.

Suggested change
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
classTestDecodeJSONIndentation:
"""Testdecode() JSONindentationfeature (Issue#10).
Comprehensivetestsforthejson_indentfeaturearein
TestDecodeJSONIndentationWithSpecFixtures, whichvalidatesagainstofficial
TOONspecificationfixtures.
"""
pass

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +323 to +325
for test_id, test_data, fixture_name in all_fixtures:
if f"{fixture_name}.json" in selected_files and len(test_cases) < 9:
test_cases.append((test_id, test_data))

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The unpacking in this loop is incorrect. get_all_decode_fixtures() returns tuples of (test_id, test_data, fixture_name) (3 elements), but the code unpacks only 2 elements into (test_id, test_data). This will cause a ValueError: too many values to unpack at runtime.

Fix the unpacking to include all three elements:

fortest_id, test_data, fixture_nameinall_fixtures:
iff"{fixture_name}.json"inselected_filesandlen(test_cases) <9:
test_cases.append((test_id, test_data))

Copilot uses AI. Check for mistakes.
- Add Tuple to typing imports for better type precision
- Update _get_sample_decode_fixtures() return type from List[tuple] to List[Tuple[str, Dict[str, Any]]]
- Improves code clarity and type safety for static analysis tools
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSON indentation option in decode method

2 participants

@jreakin
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: Add JSON indentation option to decode() method - #37

Open
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation
Open

feat: Add JSON indentation option to decode() method#37
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation

Conversation

@jreakin

Copy link
Copy Markdown

Description

Implements Issue #10 by adding optional JSON indentation support to the decode() function. Users can now request JSON-formatted output with configurable indentation.

Changes Made

1. src/toon_format/types.py

  • Added json_indent: Union[int, None] = None parameter to DecodeOptions class
  • Enhanced docstring to document the new Python-specific feature

2. src/toon_format/decoder.py

  • Added import json for JSON serialization
  • Updated decode() return type from JsonValue to Any
  • Implemented conditional JSON formatting when json_indent is specified
  • Enhanced docstring with usage examples

3. tests/test_api.py

  • Added TestDecodeJSONIndentation class with 11 comprehensive tests
  • Coverage: basic usage, indent levels, nested structures, arrays, unicode, edge cases
  • All tests passing (11/11)

Type of Change

  • New feature (non-breaking change that adds functionality)

SPEC Compliance

  • Non-breaking change
  • Backward compatible (default behavior unchanged)
  • Python-specific feature (output formatting enhancement)
  • Allowed per SPEC §13 (implementation-specific options)

Testing

✅ All 11 new tests pass
✅ All existing tests pass (47 passing in test_api.py)
✅ Code quality: ruff check passes, mypy passes
✅ No breaking changes

Code Quality

  • Type hints present (mypy: 0 errors)
  • Ruff linting passes (0 errors)
  • Code formatted
  • Python 3.8+ compatible

Example Usage

fromtoon_formatimportdecode, DecodeOptions# Default - returns Python objectresult=decode('name: Alice\nage: 30')
# {'name': 'Alice', 'age': 30}# With indentation - returns JSON stringresult=decode('name: Alice\nage: 30', DecodeOptions(json_indent=2))
# {# "name": "Alice",# "age": 30# }

Checklist

  • My code follows the project's coding standards
  • I have added type hints to new code
  • I have added tests that prove my fix/feature works
  • New and existing tests pass locally
  • I have updated documentation
  • My changes do not introduce new dependencies
  • I have maintained Python 3.8+ compatibility
  • I have reviewed the TOON specification

Closes#10

## Description
Implements Issue toon-format#10 by adding optional JSON indentation support to the
decode() function. Users can now request JSON-formatted output with
configurable indentation by passing a json_indent parameter to DecodeOptions.
## Changes Made
- Added json_indent parameter to DecodeOptions class
- Updated decode() to return JSON string when json_indent is specified
- Enhanced docstrings with usage examples
- Added 11 comprehensive tests covering all use cases
## Type of Change
- [x] New feature (non-breaking change that adds functionality)
## SPEC Compliance
- [x] Non-breaking change (default behavior unchanged)
- [x] Backward compatible (json_indent=None by default)
- [x] Python-specific feature (output formatting enhancement)
## Testing
- [x] All existing tests pass
- [x] Added new tests (11 total, 100% pass rate)
- [x] Comprehensive coverage (basic, nested, arrays, unicode, edge cases)
- [x] No breaking changes
## Code Quality
- [x] All type hints present (mypy passes)
- [x] Ruff linting passes (0 errors)
- [x] Code formatted (ruff format)
- [x] Python 3.8+ compatible
## Example Usage
```python
from toon_format import decode, DecodeOptions
# Default behavior - returns Python object
result = decode("name: Alice\nage: 30")
# {'name': 'Alice', 'age': 30}
# With JSON indentation - returns formatted JSON string
result = decode("name: Alice\nage: 30", DecodeOptions(json_indent=2))
# {\n "name": "Alice",\n "age": 30\n}
```
@jreakin
jreakin requested a review from a team as a code ownerNovember 16, 2025 08:21
CopilotAI review requested due to automatic review settings November 16, 2025 08:21

CopilotAI 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.

Pull Request Overview

This PR adds optional JSON indentation support to the decode() function, allowing users to request JSON-formatted output with configurable indentation instead of returning Python objects. This is a Python-specific enhancement that maintains backward compatibility.

Key changes:

  • Added json_indent parameter to DecodeOptions class for output formatting control
  • Modified decode() to conditionally return JSON strings when json_indent is specified
  • Added comprehensive test coverage with 11 new tests covering various edge cases

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

FileDescription
src/toon_format/types.pyAdded json_indent parameter to DecodeOptions with detailed documentation
src/toon_format/decoder.pyImplemented JSON formatting logic and updated return type from JsonValue to Any
tests/test_api.pyAdded 11 comprehensive tests for the new JSON indentation feature

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Per Copilot review, import statements should be at the module level for
consistency and readability. Moved all 'import json' statements from inside
test functions to the top of test_api.py.
- Added 'import json' to module imports (line 13)
- Removed 3 inline imports from test functions
- All tests still passing (11/11)
- Code quality checks pass (ruff, mypy)

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Removed 11 redundant hardcoded test cases and added 19 parametrized tests
that validate json_indent against [TOON spec fixtures](https://github.com/toon-format/spec/tree/main/tests/fixtures/decode).
The feature is now validated across 160+ spec test cases covering primitives,
arrays, objects, nested structures, Unicode, emoji, escape sequences,
different indent sizes and edge cases.
Single source of truth: official TOON specification fixtures. Addresses
maintainer concern about test coverage while maintaining code quality and
reducing test duplication.
Refactored to import and reuse get_all_decode_fixtures() from test_spec_fixtures.py
instead of duplicating fixture loading logic. This reduces code duplication and
ensures both test modules use the same official TOON spec fixtures.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py
Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 5 commits November 16, 2025 16:25
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Removed empty TestDecodeJSONIndentation class (was adding unnecessary code)
- Fixed flawed assertion logic in test_json_indent_with_different_indent_sizes
- Changed return type from Any to Union[JsonValue, str] for better type safety
(makes it clear the function returns either a Python value or a JSON string)
- Added Union to typing imports in decoder.py
All 19 spec-based json_indent tests passing ✓
Kept remote version of TestDecodeJSONIndentation with @pytest.mark.skip decorator
as suggested by Copilot review. This is a better approach than removing the class entirely.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadsrc/toon_format/decoder.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 3 commits November 16, 2025 16:40
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Fix bug where ToonDecodeError from decode_object was incorrectly caught
when checking if input is a key-value pair, causing unterminated strings
to be treated as root primitives instead of raising an error
- Refactor to use try/except/else pattern consistent with codebase style
- Fix linting issues in test_api.py:
- Fix import sorting
- Fix line length violations
- Remove trailing whitespace from blank lines
- Apply code formatting

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
pass


def _get_sample_decode_fixtures() -> List[tuple]:

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The return type hint for _get_sample_decode_fixtures() is List[tuple] which is imprecise. Based on how the return value is used (lines 337-338 and 365-366), it should be List[Tuple[str, Dict[str, Any]]] to match the structure of the tuples being appended at line 325.

Consider updating the import and return type:

fromtypingimportAny, Dict, List, Tuple# Add Tuple to importsdef_get_sample_decode_fixtures() ->List[Tuple[str, Dict[str, Any]]]:

Copilot uses AI. Check for mistakes.
# If json_indent is specified, return JSON-formatted string
if options.json_indent is not None:
if options.json_indent < 0:
raise ToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The error raised for negative json_indent values uses ToonDecodeError, but the docstring documents ValueError. Since json_indent is a formatting option unrelated to TOON format parsing, raising ValueError would be more semantically appropriate and consistent with the documented behavior.

Consider changing to:

raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")
Suggested change
raiseToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")
raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")

Copilot uses AI. Check for mistakes.
Comment on lines 54 to +57
indent: Number of spaces per indentation level (default: 2)
Used for parsing TOON format.
strict: Enable strict validation (default: True)
Enforces spec conformance checks.

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The comment on lines 55-57 uses inconsistent terminology compared to the rest of the codebase. Line 55 says "Used for parsing TOON format" while line 57 says "Enforces spec conformance checks." The wording for indent could be more precise to match the style of the strict parameter.

Consider revising for consistency:

indent: Numberofspacesperindentationlevel (default: 2).
UsedforparsingTOONformatinput.
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.
Suggested change
indent: Numberofspacesperindentationlevel (default: 2)
UsedforparsingTOONformat.
strict: Enablestrictvalidation (default: True)
Enforcesspecconformancechecks.
indent: Numberofspacesperindentationlevel (default: 2).
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +298 to +311
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
class TestDecodeJSONIndentation:
"""Test decode() JSON indentation feature (Issue #10).

Comprehensive tests for the json_indent feature are in
TestDecodeJSONIndentationWithSpecFixtures, which validates against official
TOON specification fixtures.
"""

pass


CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The class TestDecodeJSONIndentation is marked with @pytest.mark.skip but contains only a pass statement. This creates dead code that provides no value.

Since the TODO comment indicates that "comprehensive tests are in TestDecodeJSONIndentationWithSpecFixtures," and that class already exists with actual tests, this entire empty skipped class should be removed to keep the codebase clean. If targeted unit tests are needed in the future, they can be added at that time.

Suggested change
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
classTestDecodeJSONIndentation:
"""Testdecode() JSONindentationfeature (Issue#10).
Comprehensivetestsforthejson_indentfeaturearein
TestDecodeJSONIndentationWithSpecFixtures, whichvalidatesagainstofficial
TOONspecificationfixtures.
"""
pass

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +323 to +325
for test_id, test_data, fixture_name in all_fixtures:
if f"{fixture_name}.json" in selected_files and len(test_cases) < 9:
test_cases.append((test_id, test_data))

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The unpacking in this loop is incorrect. get_all_decode_fixtures() returns tuples of (test_id, test_data, fixture_name) (3 elements), but the code unpacks only 2 elements into (test_id, test_data). This will cause a ValueError: too many values to unpack at runtime.

Fix the unpacking to include all three elements:

fortest_id, test_data, fixture_nameinall_fixtures:
iff"{fixture_name}.json"inselected_filesandlen(test_cases) <9:
test_cases.append((test_id, test_data))

Copilot uses AI. Check for mistakes.
- Add Tuple to typing imports for better type precision
- Update _get_sample_decode_fixtures() return type from List[tuple] to List[Tuple[str, Dict[str, Any]]]
- Improves code clarity and type safety for static analysis tools
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSON indentation option in decode method

2 participants

@jreakin
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: Add JSON indentation option to decode() method - #37

Open
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation
Open

feat: Add JSON indentation option to decode() method#37
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation

Conversation

@jreakin

Copy link
Copy Markdown

Description

Implements Issue #10 by adding optional JSON indentation support to the decode() function. Users can now request JSON-formatted output with configurable indentation.

Changes Made

1. src/toon_format/types.py

  • Added json_indent: Union[int, None] = None parameter to DecodeOptions class
  • Enhanced docstring to document the new Python-specific feature

2. src/toon_format/decoder.py

  • Added import json for JSON serialization
  • Updated decode() return type from JsonValue to Any
  • Implemented conditional JSON formatting when json_indent is specified
  • Enhanced docstring with usage examples

3. tests/test_api.py

  • Added TestDecodeJSONIndentation class with 11 comprehensive tests
  • Coverage: basic usage, indent levels, nested structures, arrays, unicode, edge cases
  • All tests passing (11/11)

Type of Change

  • New feature (non-breaking change that adds functionality)

SPEC Compliance

  • Non-breaking change
  • Backward compatible (default behavior unchanged)
  • Python-specific feature (output formatting enhancement)
  • Allowed per SPEC §13 (implementation-specific options)

Testing

✅ All 11 new tests pass
✅ All existing tests pass (47 passing in test_api.py)
✅ Code quality: ruff check passes, mypy passes
✅ No breaking changes

Code Quality

  • Type hints present (mypy: 0 errors)
  • Ruff linting passes (0 errors)
  • Code formatted
  • Python 3.8+ compatible

Example Usage

fromtoon_formatimportdecode, DecodeOptions# Default - returns Python objectresult=decode('name: Alice\nage: 30')
# {'name': 'Alice', 'age': 30}# With indentation - returns JSON stringresult=decode('name: Alice\nage: 30', DecodeOptions(json_indent=2))
# {# "name": "Alice",# "age": 30# }

Checklist

  • My code follows the project's coding standards
  • I have added type hints to new code
  • I have added tests that prove my fix/feature works
  • New and existing tests pass locally
  • I have updated documentation
  • My changes do not introduce new dependencies
  • I have maintained Python 3.8+ compatibility
  • I have reviewed the TOON specification

Closes#10

## Description
Implements Issue toon-format#10 by adding optional JSON indentation support to the
decode() function. Users can now request JSON-formatted output with
configurable indentation by passing a json_indent parameter to DecodeOptions.
## Changes Made
- Added json_indent parameter to DecodeOptions class
- Updated decode() to return JSON string when json_indent is specified
- Enhanced docstrings with usage examples
- Added 11 comprehensive tests covering all use cases
## Type of Change
- [x] New feature (non-breaking change that adds functionality)
## SPEC Compliance
- [x] Non-breaking change (default behavior unchanged)
- [x] Backward compatible (json_indent=None by default)
- [x] Python-specific feature (output formatting enhancement)
## Testing
- [x] All existing tests pass
- [x] Added new tests (11 total, 100% pass rate)
- [x] Comprehensive coverage (basic, nested, arrays, unicode, edge cases)
- [x] No breaking changes
## Code Quality
- [x] All type hints present (mypy passes)
- [x] Ruff linting passes (0 errors)
- [x] Code formatted (ruff format)
- [x] Python 3.8+ compatible
## Example Usage
```python
from toon_format import decode, DecodeOptions
# Default behavior - returns Python object
result = decode("name: Alice\nage: 30")
# {'name': 'Alice', 'age': 30}
# With JSON indentation - returns formatted JSON string
result = decode("name: Alice\nage: 30", DecodeOptions(json_indent=2))
# {\n "name": "Alice",\n "age": 30\n}
```
@jreakin
jreakin requested a review from a team as a code ownerNovember 16, 2025 08:21
CopilotAI review requested due to automatic review settings November 16, 2025 08:21

CopilotAI 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.

Pull Request Overview

This PR adds optional JSON indentation support to the decode() function, allowing users to request JSON-formatted output with configurable indentation instead of returning Python objects. This is a Python-specific enhancement that maintains backward compatibility.

Key changes:

  • Added json_indent parameter to DecodeOptions class for output formatting control
  • Modified decode() to conditionally return JSON strings when json_indent is specified
  • Added comprehensive test coverage with 11 new tests covering various edge cases

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

FileDescription
src/toon_format/types.pyAdded json_indent parameter to DecodeOptions with detailed documentation
src/toon_format/decoder.pyImplemented JSON formatting logic and updated return type from JsonValue to Any
tests/test_api.pyAdded 11 comprehensive tests for the new JSON indentation feature

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Per Copilot review, import statements should be at the module level for
consistency and readability. Moved all 'import json' statements from inside
test functions to the top of test_api.py.
- Added 'import json' to module imports (line 13)
- Removed 3 inline imports from test functions
- All tests still passing (11/11)
- Code quality checks pass (ruff, mypy)

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Removed 11 redundant hardcoded test cases and added 19 parametrized tests
that validate json_indent against [TOON spec fixtures](https://github.com/toon-format/spec/tree/main/tests/fixtures/decode).
The feature is now validated across 160+ spec test cases covering primitives,
arrays, objects, nested structures, Unicode, emoji, escape sequences,
different indent sizes and edge cases.
Single source of truth: official TOON specification fixtures. Addresses
maintainer concern about test coverage while maintaining code quality and
reducing test duplication.
Refactored to import and reuse get_all_decode_fixtures() from test_spec_fixtures.py
instead of duplicating fixture loading logic. This reduces code duplication and
ensures both test modules use the same official TOON spec fixtures.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py
Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 5 commits November 16, 2025 16:25
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Removed empty TestDecodeJSONIndentation class (was adding unnecessary code)
- Fixed flawed assertion logic in test_json_indent_with_different_indent_sizes
- Changed return type from Any to Union[JsonValue, str] for better type safety
(makes it clear the function returns either a Python value or a JSON string)
- Added Union to typing imports in decoder.py
All 19 spec-based json_indent tests passing ✓
Kept remote version of TestDecodeJSONIndentation with @pytest.mark.skip decorator
as suggested by Copilot review. This is a better approach than removing the class entirely.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadsrc/toon_format/decoder.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 3 commits November 16, 2025 16:40
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Fix bug where ToonDecodeError from decode_object was incorrectly caught
when checking if input is a key-value pair, causing unterminated strings
to be treated as root primitives instead of raising an error
- Refactor to use try/except/else pattern consistent with codebase style
- Fix linting issues in test_api.py:
- Fix import sorting
- Fix line length violations
- Remove trailing whitespace from blank lines
- Apply code formatting

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
pass


def _get_sample_decode_fixtures() -> List[tuple]:

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The return type hint for _get_sample_decode_fixtures() is List[tuple] which is imprecise. Based on how the return value is used (lines 337-338 and 365-366), it should be List[Tuple[str, Dict[str, Any]]] to match the structure of the tuples being appended at line 325.

Consider updating the import and return type:

fromtypingimportAny, Dict, List, Tuple# Add Tuple to importsdef_get_sample_decode_fixtures() ->List[Tuple[str, Dict[str, Any]]]:

Copilot uses AI. Check for mistakes.
# If json_indent is specified, return JSON-formatted string
if options.json_indent is not None:
if options.json_indent < 0:
raise ToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The error raised for negative json_indent values uses ToonDecodeError, but the docstring documents ValueError. Since json_indent is a formatting option unrelated to TOON format parsing, raising ValueError would be more semantically appropriate and consistent with the documented behavior.

Consider changing to:

raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")
Suggested change
raiseToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")
raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")

Copilot uses AI. Check for mistakes.
Comment on lines 54 to +57
indent: Number of spaces per indentation level (default: 2)
Used for parsing TOON format.
strict: Enable strict validation (default: True)
Enforces spec conformance checks.

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The comment on lines 55-57 uses inconsistent terminology compared to the rest of the codebase. Line 55 says "Used for parsing TOON format" while line 57 says "Enforces spec conformance checks." The wording for indent could be more precise to match the style of the strict parameter.

Consider revising for consistency:

indent: Numberofspacesperindentationlevel (default: 2).
UsedforparsingTOONformatinput.
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.
Suggested change
indent: Numberofspacesperindentationlevel (default: 2)
UsedforparsingTOONformat.
strict: Enablestrictvalidation (default: True)
Enforcesspecconformancechecks.
indent: Numberofspacesperindentationlevel (default: 2).
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +298 to +311
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
class TestDecodeJSONIndentation:
"""Test decode() JSON indentation feature (Issue #10).

Comprehensive tests for the json_indent feature are in
TestDecodeJSONIndentationWithSpecFixtures, which validates against official
TOON specification fixtures.
"""

pass


CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The class TestDecodeJSONIndentation is marked with @pytest.mark.skip but contains only a pass statement. This creates dead code that provides no value.

Since the TODO comment indicates that "comprehensive tests are in TestDecodeJSONIndentationWithSpecFixtures," and that class already exists with actual tests, this entire empty skipped class should be removed to keep the codebase clean. If targeted unit tests are needed in the future, they can be added at that time.

Suggested change
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
classTestDecodeJSONIndentation:
"""Testdecode() JSONindentationfeature (Issue#10).
Comprehensivetestsforthejson_indentfeaturearein
TestDecodeJSONIndentationWithSpecFixtures, whichvalidatesagainstofficial
TOONspecificationfixtures.
"""
pass

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +323 to +325
for test_id, test_data, fixture_name in all_fixtures:
if f"{fixture_name}.json" in selected_files and len(test_cases) < 9:
test_cases.append((test_id, test_data))

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The unpacking in this loop is incorrect. get_all_decode_fixtures() returns tuples of (test_id, test_data, fixture_name) (3 elements), but the code unpacks only 2 elements into (test_id, test_data). This will cause a ValueError: too many values to unpack at runtime.

Fix the unpacking to include all three elements:

fortest_id, test_data, fixture_nameinall_fixtures:
iff"{fixture_name}.json"inselected_filesandlen(test_cases) <9:
test_cases.append((test_id, test_data))

Copilot uses AI. Check for mistakes.
- Add Tuple to typing imports for better type precision
- Update _get_sample_decode_fixtures() return type from List[tuple] to List[Tuple[str, Dict[str, Any]]]
- Improves code clarity and type safety for static analysis tools
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSON indentation option in decode method

2 participants

@jreakin
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat: Add JSON indentation option to decode() method - #37

Open
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation
Open

feat: Add JSON indentation option to decode() method#37
jreakin wants to merge 15 commits into
toon-format:mainfrom
jreakin:feature/issue-10-json-indentation

Conversation

@jreakin

Copy link
Copy Markdown

Description

Implements Issue #10 by adding optional JSON indentation support to the decode() function. Users can now request JSON-formatted output with configurable indentation.

Changes Made

1. src/toon_format/types.py

  • Added json_indent: Union[int, None] = None parameter to DecodeOptions class
  • Enhanced docstring to document the new Python-specific feature

2. src/toon_format/decoder.py

  • Added import json for JSON serialization
  • Updated decode() return type from JsonValue to Any
  • Implemented conditional JSON formatting when json_indent is specified
  • Enhanced docstring with usage examples

3. tests/test_api.py

  • Added TestDecodeJSONIndentation class with 11 comprehensive tests
  • Coverage: basic usage, indent levels, nested structures, arrays, unicode, edge cases
  • All tests passing (11/11)

Type of Change

  • New feature (non-breaking change that adds functionality)

SPEC Compliance

  • Non-breaking change
  • Backward compatible (default behavior unchanged)
  • Python-specific feature (output formatting enhancement)
  • Allowed per SPEC §13 (implementation-specific options)

Testing

✅ All 11 new tests pass
✅ All existing tests pass (47 passing in test_api.py)
✅ Code quality: ruff check passes, mypy passes
✅ No breaking changes

Code Quality

  • Type hints present (mypy: 0 errors)
  • Ruff linting passes (0 errors)
  • Code formatted
  • Python 3.8+ compatible

Example Usage

fromtoon_formatimportdecode, DecodeOptions# Default - returns Python objectresult=decode('name: Alice\nage: 30')
# {'name': 'Alice', 'age': 30}# With indentation - returns JSON stringresult=decode('name: Alice\nage: 30', DecodeOptions(json_indent=2))
# {# "name": "Alice",# "age": 30# }

Checklist

  • My code follows the project's coding standards
  • I have added type hints to new code
  • I have added tests that prove my fix/feature works
  • New and existing tests pass locally
  • I have updated documentation
  • My changes do not introduce new dependencies
  • I have maintained Python 3.8+ compatibility
  • I have reviewed the TOON specification

Closes#10

## Description
Implements Issue toon-format#10 by adding optional JSON indentation support to the
decode() function. Users can now request JSON-formatted output with
configurable indentation by passing a json_indent parameter to DecodeOptions.
## Changes Made
- Added json_indent parameter to DecodeOptions class
- Updated decode() to return JSON string when json_indent is specified
- Enhanced docstrings with usage examples
- Added 11 comprehensive tests covering all use cases
## Type of Change
- [x] New feature (non-breaking change that adds functionality)
## SPEC Compliance
- [x] Non-breaking change (default behavior unchanged)
- [x] Backward compatible (json_indent=None by default)
- [x] Python-specific feature (output formatting enhancement)
## Testing
- [x] All existing tests pass
- [x] Added new tests (11 total, 100% pass rate)
- [x] Comprehensive coverage (basic, nested, arrays, unicode, edge cases)
- [x] No breaking changes
## Code Quality
- [x] All type hints present (mypy passes)
- [x] Ruff linting passes (0 errors)
- [x] Code formatted (ruff format)
- [x] Python 3.8+ compatible
## Example Usage
```python
from toon_format import decode, DecodeOptions
# Default behavior - returns Python object
result = decode("name: Alice\nage: 30")
# {'name': 'Alice', 'age': 30}
# With JSON indentation - returns formatted JSON string
result = decode("name: Alice\nage: 30", DecodeOptions(json_indent=2))
# {\n "name": "Alice",\n "age": 30\n}
```
@jreakin
jreakin requested a review from a team as a code ownerNovember 16, 2025 08:21
CopilotAI review requested due to automatic review settings November 16, 2025 08:21

CopilotAI 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.

Pull Request Overview

This PR adds optional JSON indentation support to the decode() function, allowing users to request JSON-formatted output with configurable indentation instead of returning Python objects. This is a Python-specific enhancement that maintains backward compatibility.

Key changes:

  • Added json_indent parameter to DecodeOptions class for output formatting control
  • Modified decode() to conditionally return JSON strings when json_indent is specified
  • Added comprehensive test coverage with 11 new tests covering various edge cases

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

FileDescription
src/toon_format/types.pyAdded json_indent parameter to DecodeOptions with detailed documentation
src/toon_format/decoder.pyImplemented JSON formatting logic and updated return type from JsonValue to Any
tests/test_api.pyAdded 11 comprehensive tests for the new JSON indentation feature

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Comment threadtests/test_api.py Outdated
Per Copilot review, import statements should be at the module level for
consistency and readability. Moved all 'import json' statements from inside
test functions to the top of test_api.py.
- Added 'import json' to module imports (line 13)
- Removed 3 inline imports from test functions
- All tests still passing (11/11)
- Code quality checks pass (ruff, mypy)

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Removed 11 redundant hardcoded test cases and added 19 parametrized tests
that validate json_indent against [TOON spec fixtures](https://github.com/toon-format/spec/tree/main/tests/fixtures/decode).
The feature is now validated across 160+ spec test cases covering primitives,
arrays, objects, nested structures, Unicode, emoji, escape sequences,
different indent sizes and edge cases.
Single source of truth: official TOON specification fixtures. Addresses
maintainer concern about test coverage while maintaining code quality and
reducing test duplication.
Refactored to import and reuse get_all_decode_fixtures() from test_spec_fixtures.py
instead of duplicating fixture loading logic. This reduces code duplication and
ensures both test modules use the same official TOON spec fixtures.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py
Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 5 commits November 16, 2025 16:25
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Removed empty TestDecodeJSONIndentation class (was adding unnecessary code)
- Fixed flawed assertion logic in test_json_indent_with_different_indent_sizes
- Changed return type from Any to Union[JsonValue, str] for better type safety
(makes it clear the function returns either a Python value or a JSON string)
- Added Union to typing imports in decoder.py
All 19 spec-based json_indent tests passing ✓
Kept remote version of TestDecodeJSONIndentation with @pytest.mark.skip decorator
as suggested by Copilot review. This is a better approach than removing the class entirely.

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadsrc/toon_format/decoder.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
Comment threadsrc/toon_format/decoder.py
Comment threadsrc/toon_format/decoder.py Outdated
jreakinand others added 3 commits November 16, 2025 16:40
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Fix bug where ToonDecodeError from decode_object was incorrectly caught
when checking if input is a key-value pair, causing unterminated strings
to be treated as root primitives instead of raising an error
- Refactor to use try/except/else pattern consistent with codebase style
- Fix linting issues in test_api.py:
- Fix import sorting
- Fix line length violations
- Remove trailing whitespace from blank lines
- Apply code formatting

CopilotAI 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.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/test_api.py Outdated
pass


def _get_sample_decode_fixtures() -> List[tuple]:

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The return type hint for _get_sample_decode_fixtures() is List[tuple] which is imprecise. Based on how the return value is used (lines 337-338 and 365-366), it should be List[Tuple[str, Dict[str, Any]]] to match the structure of the tuples being appended at line 325.

Consider updating the import and return type:

fromtypingimportAny, Dict, List, Tuple# Add Tuple to importsdef_get_sample_decode_fixtures() ->List[Tuple[str, Dict[str, Any]]]:

Copilot uses AI. Check for mistakes.
# If json_indent is specified, return JSON-formatted string
if options.json_indent is not None:
if options.json_indent < 0:
raise ToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The error raised for negative json_indent values uses ToonDecodeError, but the docstring documents ValueError. Since json_indent is a formatting option unrelated to TOON format parsing, raising ValueError would be more semantically appropriate and consistent with the documented behavior.

Consider changing to:

raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")
Suggested change
raiseToonDecodeError(f"json_indent must be non-negative, got {options.json_indent}")
raiseValueError(f"json_indent must be non-negative, got {options.json_indent}")

Copilot uses AI. Check for mistakes.
Comment on lines 54 to +57
indent: Number of spaces per indentation level (default: 2)
Used for parsing TOON format.
strict: Enable strict validation (default: True)
Enforces spec conformance checks.

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The comment on lines 55-57 uses inconsistent terminology compared to the rest of the codebase. Line 55 says "Used for parsing TOON format" while line 57 says "Enforces spec conformance checks." The wording for indent could be more precise to match the style of the strict parameter.

Consider revising for consistency:

indent: Numberofspacesperindentationlevel (default: 2).
UsedforparsingTOONformatinput.
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.
Suggested change
indent: Numberofspacesperindentationlevel (default: 2)
UsedforparsingTOONformat.
strict: Enablestrictvalidation (default: True)
Enforcesspecconformancechecks.
indent: Numberofspacesperindentationlevel (default: 2).
strict: Enablestrictvalidation (default: True).
EnforcesTOONspecificationconformancechecks.

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +298 to +311
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
class TestDecodeJSONIndentation:
"""Test decode() JSON indentation feature (Issue #10).

Comprehensive tests for the json_indent feature are in
TestDecodeJSONIndentationWithSpecFixtures, which validates against official
TOON specification fixtures.
"""

pass


CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The class TestDecodeJSONIndentation is marked with @pytest.mark.skip but contains only a pass statement. This creates dead code that provides no value.

Since the TODO comment indicates that "comprehensive tests are in TestDecodeJSONIndentationWithSpecFixtures," and that class already exists with actual tests, this entire empty skipped class should be removed to keep the codebase clean. If targeted unit tests are needed in the future, they can be added at that time.

Suggested change
@pytest.mark.skip(
reason="Placeholder for targeted decode() JSON indentation tests. See TODO above."
)
classTestDecodeJSONIndentation:
"""Testdecode() JSONindentationfeature (Issue#10).
Comprehensivetestsforthejson_indentfeaturearein
TestDecodeJSONIndentationWithSpecFixtures, whichvalidatesagainstofficial
TOONspecificationfixtures.
"""
pass

Copilot uses AI. Check for mistakes.
Comment threadtests/test_api.py
Comment on lines +323 to +325
for test_id, test_data, fixture_name in all_fixtures:
if f"{fixture_name}.json" in selected_files and len(test_cases) < 9:
test_cases.append((test_id, test_data))

CopilotAINov 18, 2025

Copy link

Choose a reason for hiding this comment

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

The unpacking in this loop is incorrect. get_all_decode_fixtures() returns tuples of (test_id, test_data, fixture_name) (3 elements), but the code unpacks only 2 elements into (test_id, test_data). This will cause a ValueError: too many values to unpack at runtime.

Fix the unpacking to include all three elements:

fortest_id, test_data, fixture_nameinall_fixtures:
iff"{fixture_name}.json"inselected_filesandlen(test_cases) <9:
test_cases.append((test_id, test_data))

Copilot uses AI. Check for mistakes.
- Add Tuple to typing imports for better type precision
- Update _get_sample_decode_fixtures() return type from List[tuple] to List[Tuple[str, Dict[str, Any]]]
- Improves code clarity and type safety for static analysis tools
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSON indentation option in decode method

2 participants

@jreakin