Skip to content

Conversation

@riebecj
Copy link
Contributor

@riebecj riebecj commented Nov 18, 2025

Provides better support for converting Var data to JS Blobs. Adds BytesVar to back Python bytes with JS Uint8Array. To keep things consistent and pythonic, .encode(...) and .decode(...) methods have been added to StringVar. Also, both StringVar and BytesVar have a to_blob(...) method that assists in creating a new Blob(...) from the Python data. The BlobVar has a .create_object_url(...) method that returns the URL string for downloading with rx.download(...). It also seems to handle Unicode quite nicely.

All Submissions:

  • Have you followed the guidelines stated in CONTRIBUTING.md file?
  • Have you checked to ensure there aren't any other open Pull Requests for the desired changed?

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

New Feature Submission:

  • Does your submission pass the tests?
  • Have you linted your code locally prior to submission?

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

@codspeed-hq
Copy link

codspeed-hq bot commented Nov 18, 2025

CodSpeed Performance Report

Merging #5985 will not alter performance

Comparing riebecj:main (08097dd) with main (4ee713f)

Summary

✅ 8 untouched

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Nov 18, 2025

Greptile Summary

  • Introduces BlobVar and BytesVar classes to bridge Python bytes/strings with JavaScript Blob API and Uint8Array for better file handling and download support
  • Adds .encode(), .decode(), and .to_blob() methods to StringVar and BytesVar to maintain Python-like consistency for data conversion operations
  • Extends the var system with proper type annotations, overloads, and imports following established patterns for seamless integration with existing reactive state management

Important Files Changed

Filename Overview
reflex/vars/blob.py New file implementing BlobVar class with MIME type detection and object URL creation for JavaScript Blob support
reflex/vars/sequence.py Added BytesVar class and enhanced StringVar with encode/decode/to_blob methods for Python-JavaScript data conversion
reflex/vars/base.py Extended var type system with new overloads and imports to support bytes and Blob types consistently

Confidence score: 4/5

  • This PR introduces solid new functionality with well-structured code that follows established patterns in the Reflex framework
  • Score reflects thorough implementation of both runtime and literal variable variants, proper type handling, and consistent API design
  • Pay close attention to reflex/vars/blob.py for MIME type detection logic and JavaScript code generation accuracy

Sequence Diagram

sequenceDiagram
    participant User
    participant StringVar
    participant BytesVar
    participant BlobVar
    participant LiteralBytesVar
    participant LiteralBlobVar
    
    User->>StringVar: "encode('utf-8')"
    StringVar->>StringVar: "string_encode_operation()"
    StringVar-->>BytesVar: "return BytesVar"
    
    User->>BytesVar: "create(b'data')"
    BytesVar->>LiteralBytesVar: "create(bytes_value)"
    LiteralBytesVar->>LiteralBytesVar: "new Uint8Array([...])"
    LiteralBytesVar-->>BytesVar: "return BytesVar"
    
    User->>BytesVar: "decode('utf-8')"
    BytesVar->>BytesVar: "bytes_decode_operation()"
    BytesVar-->>StringVar: "return StringVar"
    
    User->>StringVar: "to_blob('text/plain')"
    StringVar->>BlobVar: "blob_object_create_operation()"
    BlobVar->>BlobVar: "new Blob([data], {type: mime_type})"
    BlobVar-->>BlobVar: "return BlobVar"
    
    User->>BlobVar: "create_object_url()"
    BlobVar->>BlobVar: "create_url_from_blob_operation()"
    BlobVar->>BlobVar: "window.URL.createObjectURL()"
    BlobVar-->>StringVar: "return URL string"
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

4 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile
React with 👍 or 👎 to share your feedback on this new summary format

Copy link
Collaborator

@masenf masenf left a comment

Choose a reason for hiding this comment

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

try to reduce the redundant paths in the new code. there should never be two separate paths to format what is essentially the same value; the Var system is cumulative, so if you have already correctly dealt with some subtype in a different Var, you can just include that directly in other expressions.

pay attention to the diference between the base type Var which corresponds to some JS-native type (StringVar, BytesVar, ObjectVar, etc) and the LiteralVar which corresponds to some Python-native type. The LiteralVar is responsible for representing the python value in the JS world, and the base type Var is responsible for defining what operations on that type can be performed in JS on the frontend.

it would be helpful to add some test case, at the very least a few more examples in tests/integration/test_var_operations.py where these various conversions are used end-to-end in the browser to validate their functionality, but more importantly to notice when some future change in reflex causes their behavior to regress.

A StringVar containing the decoded string.
"""
return var_operation_return(
f"(new TextDecoder({encoding})).decode(new Uint8Array({value}))",
Copy link
Collaborator

Choose a reason for hiding this comment

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

rx.Var.create(b"foobar").decode()

Produces code that duplicates the Uint8Array (it still works, but it seems suboptimal)

(new TextDecoder("utf-8")).decode(new Uint8Array(new Uint8Array([102, 111, 111, 98, 97, 114])))

Comment on lines +1333 to +1348
@classmethod
def create(
cls, value: str | bytes, _var_data: VarData | None = None
) -> LiteralBytesVar:
"""Create a BytesVar from a string or bytes value.
Args:
value: The string or bytes value to create the var from.
_var_data: Additional hooks and imports associated with the Var.
Returns:
A LiteralBytesVar representing the bytes value.
"""
if isinstance(value, str):
value = value.encode()
return LiteralVar.create(value, _var_data=_var_data)
Copy link
Collaborator

Choose a reason for hiding this comment

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

The non-literal BytesVar should not define create like this -- that's what the LiteralBytesVar is for.

The non-literal Var just holds the valid operations for that type, and generally speaking the _js_expr will be some variable name (imagine a State variable of type bytes, the _js_expr would be the state+var name).

Comment on lines +56 to +72
@classmethod
def create(
cls,
value: str | bytes | Blob | Var,
mime_type: str | Var | None = None,
_var_data: VarData | None = None,
):
"""Create a BlobVar from the given value and MIME type.
Args:
value: The data to create the Blob from (string, bytes, or Var).
mime_type: The MIME type of the Blob (string or Var).
_var_data: Optional variable data.
Returns:
A BlobVar instance representing the JavaScript Blob object.
"""
Copy link
Collaborator

Choose a reason for hiding this comment

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

Combine this with LiteralBlobVar.create and remove the definition in the non-literal BlobVar

Comment on lines +132 to +133
value: bytes | str | Blob,
mime_type: str | None = None,
Copy link
Collaborator

Choose a reason for hiding this comment

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

i think this method should just take a Blob object to simplify the API.

it also needs to assign that Blob object to _var_value so it can be pulled out on the python side later.

Comment on lines +32 to +54
@classmethod
def _determine_mime_type(cls, value: str | bytes | Blob | Var) -> str:
mime_type = ""
if isinstance(value, str | bytes | Blob):
match value:
case str():
mime_type = "text/plain"
case bytes():
mime_type = "application/octet-stream"
case Blob():
mime_type = value.mime_type

elif isinstance(value, Var):
if isinstance(value._var_type, str):
mime_type = "text/plain"
if isinstance(value._var_type, bytes):
mime_type = "application/octet-stream"

if not mime_type:
msg = "Unable to determine mime type for blob creation."
raise ValueError(msg)

return mime_type
Copy link
Collaborator

Choose a reason for hiding this comment

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

suggest moving this logic into a create function for the Blob dataclass

Comment on lines +85 to +86
if isinstance(value._var_type, bytes):
value = f"new Uint8Array({value})"
Copy link
Collaborator

Choose a reason for hiding this comment

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

if the value is a BytesVar, then you don't need this additional conversion, because the BytesVar already includes the array cast in its _js_expr, so when you format it into the expression, it will already appear like this

str(rx.Var(f"{rx.Var.create(b"foo")}")) -> 'new Uint8Array([102, 111, 111])'

Comment on lines +1433 to +1437
if isinstance(value, BytesVar):
return var_operation_return(
js_expression=f"new Blob([new Uint8Array({value})], {{ type: {mime_type} }})",
var_type=Blob,
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

this case is unnecessary; if the value is a BytesVar, then simply formatting it into the js_expression below will use it's JS-form which already includes the Uint8Array cast

Sign up for free to 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.

2 participants