Skip to content

Extend sprintf to support thousand separator#13284

Merged
lukasmasuch merged 21 commits intodevelopfrom
extend-sprintf-with-thousend-sep-support
Feb 2, 2026
Merged

Extend sprintf to support thousand separator#13284
lukasmasuch merged 21 commits intodevelopfrom
extend-sprintf-with-thousend-sep-support

Conversation

@lukasmasuch
Copy link
Copy Markdown
Collaborator

@lukasmasuch lukasmasuch commented Dec 10, 2025

Describe your changes

Vendors in the sprintf-js library (only a single file) and modifies it slightly to support setting the thousand separator in the python printf format:

1234.56 -> %,.2f -> 1,234.56
1234.56 -> %_.2f -> 1_234.56

GitHub Issue Link (if applicable)

Testing Plan

  • Added unit tests.

Contribution License Agreement

By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.

@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io bot commented Dec 10, 2025

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Dec 10, 2025

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-13284/streamlit-1.53.0-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-13284.streamlit.app (☁️ Deploy here if not accessible)

@lukasmasuch lukasmasuch added security-assessment-completed change:feature PR contains new feature or enhancement implementation impact:users PR changes affect end users labels Dec 10, 2025
@lukasmasuch
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 vendors the sprintf-js library (converting it to TypeScript) and extends it with thousand separator support using , and _ flags, mirroring Python's format mini-language (e.g., f"{x:,}" and f"{x:_}").

Key changes:

  • Vendored sprintf-js as TypeScript with thousand separator support (%,d for comma, %_d for underscore)
  • Removed npm dependency on sprintf-js package
  • Updated all import statements to use vendored version
  • Added comprehensive test coverage for new formatting features

Reviewed changes

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

Show a summary per file
File Description
frontend/yarn.lock Removed sprintf-js and @types/sprintf-js dependencies from lockfile
frontend/lib/src/vendor/sprintf.js/sprintfjs.ts New vendored sprintf implementation with thousand separator support
frontend/lib/src/vendor/sprintf.js/sprintfjs.test.ts Comprehensive tests for sprintf functionality including new thousand separator formats
frontend/lib/src/vendor/sprintf.js/sprintfjs-LICENSE.txt BSD-3-Clause license file for vendored sprintf library
frontend/lib/src/vendor/fzy.js/fuzzySearch.ts Removed Streamlit copyright header and reordered exports (cosmetic changes to vendored file)
frontend/lib/src/vendor/fzy.js/fuzzySearch.test.ts Removed Streamlit copyright header (cosmetic change to vendored file)
frontend/lib/src/util/formatNumber.ts Updated import to use vendored sprintf; added documentation for thousand separator flags
frontend/lib/src/util/formatNumber.test.ts Added test cases for thousand separator formatting, removed now-valid test cases from invalid list
frontend/lib/src/components/widgets/Slider/Slider.tsx Updated import to use vendored sprintf
frontend/lib/src/components/widgets/NumberInput/utils.ts Updated import to use vendored sprintf
frontend/lib/src/components/widgets/DataFrame/columns/ProgressColumn.test.ts Added test cases for thousand separator formatting, removed now-valid test cases from invalid list
frontend/lib/src/components/widgets/DataFrame/columns/NumberColumn.test.ts Added test cases for thousand separator formatting, removed now-valid test cases from invalid list
frontend/lib/package.json Removed sprintf-js and @types/sprintf-js from dependencies
NOTICES Updated license attribution for sprintf library (issue: duplicate entry remains)
Makefile Added sprintfjs-LICENSE.txt to license append script

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no bugs!


@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Dec 13, 2025

📉 Frontend coverage change detected

The frontend unit test (vitest) coverage has decreased by 0.0000%

  • Current PR: 86.4500% (13339 lines, 1807 missed)
  • Latest develop: 86.4500% (13339 lines, 1807 missed)

✅ Coverage change is within normal range.

📊 View detailed coverage comparison

@lukasmasuch lukasmasuch changed the title [WIP] Extend sprintf to support thousand separator Extend sprintf to support thousand separator Jan 17, 2026
@lukasmasuch lukasmasuch marked this pull request as ready for review January 17, 2026 21:15
@lukasmasuch lukasmasuch added the ai-review If applied to PR or issue will run AI review workflow label Jan 17, 2026
@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Jan 17, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Summary

This PR vendors the sprintf-js library (converting it to TypeScript) and extends it to support thousand separator formatting via , and _ flags in printf-style format strings. This mirrors Python's format mini-language: f"{x:,}" produces "1,234,567" and f"{x:_}" produces "1_234_567". The PR closes issue #1301, enabling users to format numbers with thousand separators in DataFrames and number inputs.

Key changes:

  • Vendors sprintf-js as a TypeScript file with extended thousand separator support
  • Removes the npm dependency on sprintf-js and @types/sprintf-js
  • Updates all consumers (NumberInput, Slider, formatNumber) to use the vendored implementation
  • Adds comprehensive unit tests for the new sprintf implementation
  • Updates existing tests to reflect new supported formats
  • Properly handles licensing (BSD-3-Clause) and updates NOTICES file

Code Quality

The implementation is well-structured and follows good practices:

Positives:

  • Clean TypeScript conversion with appropriate typing (interface Placeholder, type ParseTree)
  • Uses Object.create(null) for the cache at line 48 of sprintfjs.ts, avoiding prototype pollution issues
  • Clear JSDoc documentation with examples in the exported sprintf function
  • The addThousandSeparators helper function (lines 57-84) is well-documented and handles edge cases (signs, decimal parts)
  • Uses the shared isNullOrUndefined utility from @streamlit/utils for consistency

Minor observations:

  • The regex pattern at line 40-41 in sprintfjs.ts is complex but well-commented, explaining the capture groups
  • The removal of Streamlit copyright headers from fuzzySearch.ts and fuzzySearch.test.ts is appropriate since these are vendored MIT-licensed files

Test Coverage

Test coverage is excellent:

sprintf unit tests (sprintfjs.test.ts):

  • 409 lines of comprehensive tests covering:
    • All standard printf specifiers (binary, character, decimal, integer, JSON, exponential, unsigned, float, general, octal, string, hex, boolean, type, valueOf)
    • Sign handling with positive/negative numbers
    • Padding and width formatting
    • Precision formatting
    • Thorough thousand separator tests for both comma (,) and underscore (_) flags
    • Positional and named arguments
    • Edge cases and error handling
    • Cache consistency tests

Updated integration tests:

  • formatNumber.test.ts: Added 14 new test cases for thousand separator formats
  • NumberColumn.test.ts: Added 6 positive test cases for thousand separator formats
  • ProgressColumn.test.ts: Added 6 positive test cases for thousand separator formats
  • Tests correctly moved from "cannot format" (negative tests) to positive tests as the formats are now supported

Test patterns follow best practices:

  • Uses it.each for parameterized tests
  • Tests both positive and negative cases (error throwing)
  • Tests edge cases (zero, small numbers, very large numbers, negative numbers)

Backwards Compatibility

Fully backwards compatible:

  • The vendored sprintf implementation supports all standard printf specifiers that the original npm package supported
  • Existing format strings will continue to work identically
  • The new , and _ flags are additive features that don't affect existing behavior
  • Tests explicitly verify formats like %d, %f, %s, etc. continue to work as expected

API compatibility:

  • The exported sprintf function has the same signature: (fmt: string, ...args: unknown[]) => string
  • The vsprintf function was removed, but grep of the codebase shows it wasn't being used anywhere

Security & Risk

No security concerns identified:

  • The cache uses Object.create(null) to prevent prototype pollution attacks (line 48)
  • Input validation throws appropriate errors for invalid formats (SyntaxError) and type mismatches (TypeError)
  • The regex patterns are bounded and don't appear vulnerable to ReDoS attacks
  • The vendored code is based on the well-established sprintf-js library

Low regression risk:

  • The change is well-tested with 400+ lines of unit tests
  • Integration tests verify the functionality works end-to-end with formatNumber
  • The npm dependency removal reduces supply chain risk

Recommendations

No blocking issues found. A few optional suggestions:

  1. Optional - Consider adding E2E test: While unit tests are comprehensive, an E2E test verifying thousand separator formatting in a DataFrame column could provide additional confidence. However, this is optional given the thorough unit test coverage.

  2. Optional - Minor documentation: Consider adding a brief mention of the thousand separator support in the formatNumber function's JSDoc (already partially done at line 83-84 of formatNumber.ts).

  3. Verified - License handling: The BSD-3-Clause license is properly preserved in sprintfjs-LICENSE.txt and the NOTICES file is correctly updated.

Verdict

APPROVED: This PR is well-implemented with excellent test coverage, maintains backwards compatibility, and properly handles the vendored library's licensing. The thousand separator feature is a valuable addition that aligns with Python's format specification, making number formatting more intuitive for Streamlit users.


This is an automated AI review. Please verify the feedback and use your judgment.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

"source.organizeImports": "explicit" interferes with the eslint import sorting.

"typescript.enablePromptUseWorkspaceTsdk": true,
"git.branchProtection": ["develop"]
"git.branchProtection": ["develop"],
"prettier.configPath": "frontend/.prettierrc"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: In the future, please put these kinds of changes in a separate PR to keep the diffs focused.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yep, agree 👍

@@ -1,3 +1,2 @@
vendor
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: Same comment as above, these changes would be helpful as a separate PR to keep the noise down.

lukasmasuch added a commit that referenced this pull request Jan 27, 2026
Rendered spec: https://issues.streamlit.app/spec_renderer?pr=13293

## GitHub Issue Link (if applicable)

- Related feature requests: 
   - #1301
   - #7702
- Prototype:
   - #13284

## Open Questions

---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this
project are made under the Apache 2.0 license.
@lukasmasuch lukasmasuch merged commit a9923f2 into develop Feb 2, 2026
45 checks passed
@lukasmasuch lukasmasuch deleted the extend-sprintf-with-thousend-sep-support branch February 2, 2026 11:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:feature PR contains new feature or enhancement implementation impact:users PR changes affect end users

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve our format parameter to support thousand separator

4 participants