Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Highlightsfor the most attractive new features.
- BREAKINGfor breaking changes.
- Addedfor new features.
- Changedfor changes in existing functionality.
- Deprecatedfor soon-to-be removed features.
- Removedfor now removed features.
- Fixedfor any bug fixes.
- Securityin case of vulnerabilities.
- Docsfor documentation changes.
- YANKEDfor deprecated releases.
- Internalfor internal changes. Only for maintainers.
Tip
This homepage is used to provide a blog-like changelog and BREAKING CHANGE migration guide.
You can expand sub-projects to view detailed changelogs.
Unreleased¶
0.8.0¶
Highlights¶
Official Discord Server¶
- #266 - docs: add discord server link.
PyTauri now has an official Discord server! Everyone is welcome to join and chat with PyTauri users and developers 😇!
WebviewWindowBuilder bindings¶
- #265 - feat(pytauri): add
WebviewWindowBuilderbindings.
Now we can create new WebviewWindow from python
import json
from pytauri import AppHandle, WebviewUrl
from pytauri.webview import WebviewWindow, WebviewWindowBuilder
def create_new_webview_window(manager: AppHandle) -> WebviewWindow:
    webview_window = WebviewWindow(
        manager,
        "new_window",
        WebviewUrl.External("https://example.com"),
        transparent=True,
    )
    # or use `WebviewWindowBuilder`
    webview_window = WebviewWindowBuilder.from_config(
        manager,
        {"label": "new_window_1", "url": "https://example.com", "transparent": True},
    )
    # or use json str/bytes config
    webview_window = WebviewWindowBuilder.from_config(
        manager,
        json.dumps(
            {"label": "new_window_2", "url": "https://example.com", "transparent": True}
        ),
    )
    return webview_window
Added APIs
- Added tauri-macos-private-apifeature
- mod tauri::- struct {WebviewUrl, WebviewUrlType}
- mod webview::- struct {WebviewWindowBuilder, WebviewWindowBuilderArgs}
- fn WebviewWindow::{__new__}
 
- struct 
 
- struct 
More WebviewWindow and AppHandle bindings¶
- #259 - feat(pytauri): more
WebviewWindowandAppHandlebindings.
Added APIs
- Added tauri-devtoolsfeature
- mod tauri::- fn App::{run_on_main_thread}
- fn AppHandle::{remove_plugin, restart, request_restart, set_dock_visibility, config, primary_monitor, monitor_from_point, available_monitors, cursor_position, cleanup_before_exit}
- struct {PhysicalRect, LogicalRect, UserAttentionType, CursorIcon}
- mod webview::- fn WebviewWindow::{scale_factor, inner_position, outer_position, inner_size, outer_size, is_always_on_top, current_monitor, primary_monitor, monitor_from_point, available_monitors, cursor_position, request_user_attention, set_effects, set_size, set_min_size, set_max_size, set_position, set_background_color, set_cursor_icon, set_cursor_position, set_overlay_icon, set_badge_label, set_progress_bar, set_title_bar_style, reload, open_devtools, close_devtools, is_devtools_open, cookies_for_url, cookies, set_cookie, delete_cookie}
- struct {Color, SameSite, Cookie}
 
- fn 
- mod window::- struct {Monitor, Effect, EffectState, Effects, ProgressBarStatus, ProgressBarState, TitleBarStyle}
 
- struct 
 
- fn 
Registering Plugin From Python¶
- #220 - feat: support registering plugin from python.
See https://pytauri.github.io/pytauri/0.8/usage/tutorial/using-plugins/#install-tauri-plugin, now we support registering plugins from Python:
from pytauri import (
    builder_factory,
    context_factory,
)
from pytauri_plugins import notification
app = builder_factory().build(
    context=context_factory(),
    invoke_handler=None,
    plugins=[notification.init()],  # 👈
)
More Plugin Bindings¶
- #220 - feat: support registering plugin from python.
See https://pytauri.github.io/pytauri/0.8/usage/tutorial/using-plugins/#all-plugins-we-support, now we add more plugin bindings for Python.
Added APIs
- mod tauri::- mod plugin
- field BuilderArgs::plugins
- fn AppHandle::plugin
 
- mod 
- mod tauri_plugin_dialog::- fn init
 
- fn 
- mod tauri_plugin_notification::- fn init
 
- fn 
- mod tauri_plugin_clipboard_manager
- mod tauri_plugin_fs
- mod tauri_plugin_opener
- mod tauri_plugin_autostart
- mod tauri_plugin_deep_link
- mod tauri_plugin_deep_link
- mod tauri_plugin_http
- mod tauri_plugin_os
- mod tauri_plugin_persisted_scope
- mod tauri_plugin_positioner
- mod tauri_plugin_process
- mod tauri_plugin_shell
- mod tauri_plugin_single_instance
- mod tauri_plugin_updater
- mod tauri_plugin_upload
- mod tauri_plugin_websocket
- mod tauri_plugin_window_state
- mod tauri_plugin_global_shortcut
BREAKING¶
- 
#220 - feat: support registering plugin from python. See: https://pytauri.github.io/pytauri/0.8/usage/pytauri-wheel/#pytauri-plugins. The parameters pytauri_wheel.builder_factory(opener, clipboard_manager, dialog, fs, notification)have been removed. Please usepytauri.BuilderArgs.pluginsorpytauri.Apphandle.pluginto manually register plugins.Migrationfrom pytauri_plugins import clipboard_manager, dialog, fs, notification, opener from pytauri_wheel.lib import builder_factory builder = builder_factory() app = builder.build( context=..., invoke_handler=..., # 👇 plugins=[ opener.init(), clipboard_manager.init(), dialog.init(), fs.init(), notification.init(), ], )
- 
#220 - feat: support registering plugin from python. See: https://pytauri.github.io/pytauri/0.8/usage/pytauri-wheel/#best-practices. It is no longer recommended to use pytauri-wheelin script mode (i.e.,python main.py). Instead, it is recommended to use a standardpyproject.tomlproject and generate venv standalone executables via[project.scripts]/[project.gui-scripts].
Added¶
- 
#220 - #259 - chore: bump tauridependencies:Bumptauri = { version = "2.8" } tauri-build = { version = "2.4" } tauri-plugin = { version = "2.4" } tauri-utils = { version = "~2.7" } tauri-plugin-opener = { version = "2.5" } tauri-plugin-clipboard-manager = { version = "2.3" } tauri-plugin-dialog = { version = "2.3" } tauri-plugin-fs = { version = "2.4" } tauri-plugin-notification = { version = "2.3" } tauri-plugin-autostart = { version = "2.5" } tauri-plugin-deep-link = { version = "2.4" } tauri-plugin-upload = { version = "2.3" } tauri-plugin-websocket = { version = "2.4" } tauri-plugin-http = { version = "2.5" } tauri-plugin-os = { version = "2.3" } tauri-plugin-persisted-scope = { version = "2.3" } tauri-plugin-positioner = { version = "2.3" } tauri-plugin-process = { version = "2.3" } tauri-plugin-shell = { version = "2.3" } tauri-plugin-single-instance = { version = "2.3" } tauri-plugin-updater = { version = "2.9" } tauri-plugin-window-state = { version = "2.4" } tauri-plugin-global-shortcut = { version = "2.3" }
Changed¶
- 
#263 - build: remove upper version constraint for build-system.requiresdeps.Updated pyproject.tomland docs and to use the following build version constraints:
Docs¶
- 
#262 - feat: support json str | bytesordictas input fortauri::Config.Use a dictinstead ofjson.dumps({...})as the input forcontext_factory(tauri_config)in thepytauri-wheeldocumentation.
Internal¶
- 
#220 - feat: support registering plugin from python. - build(docs): downgrade git-revision-date-localizedto1.3.*to speed up the docs build.
- build(docs): enable git-revision-date-localizedandgit-committersmkdocs-material plugins only ifCIenvironment variable is set.
- chore: add yaml.customTagsforvscode-yamlto resolvemkdocs.ymlschema error.
- build(docs): add ENABLE_MKDOCSTRINGSenvironment variable to reduce dev build time
- docs(CONTRIBUTING): use mkdocs serve --dirtyto speed up the dev hot-reload time.
 
- build(docs): downgrade 
0.7.2¶
Fixed¶
- 
#253 - fix(pytauri): support deferred evaluation type hint in @command.
Docs¶
- #254 - docs(fix): add note on "patching install_name of libpython" for macOS standalone
0.7.1¶
Fixed¶
- #240 - fix(pytauri): include pytauri_utilsin package. Yankpy/pytauri/v0.7.0release.
0.7.0¶
Highlights¶
App State Management and AsyncTools utils¶
- #208 - feat(pytauri)!: app state management and
AsyncToolsutils.
See:
- State Management: https://pytauri.github.io/pytauri/0.7/usage/tutorial/state-management
- AsyncTools: https://pytauri.github.io/pytauri/0.7/usage/concepts/async
Debugging Tutorial¶
- #182 - feat!: debugging tutorial.
See: https://pytauri.github.io/pytauri/0.7/usage/tutorial/debugging
Also, we added tauri::{IS_DEV, RESTART_EXIT_CODE, VERSION, webview_version} for obtaining pytauri runtime configuration.
Generate TypeScript Client for IPC¶
- #179 - feat(pytauri): generate typescript client for IPC.
See: https://pytauri.github.io/pytauri/0.7/usage/tutorial/gen-ts
automatically generates the following TypeScript client code:
export async function greetToPerson(
    body: Commands["greet_to_person"]["input"],
    options?: InvokeOptions
): Promise<Commands["greet_to_person"]["output"]> {
    return await pyInvoke("greet_to_person", body, options);
}
Support using arbitrary types in command¶
- #171 - feat(pytauri): support using arbitrary types in
command.
from pydantic import RootModel
from pytauri import Commands
commands = Commands()
RootModelStr = RootModel[str]
# 😫 ❌
@commands.command()
async def old(body: RootModelStr) -> bytes:
    return b"null"
# 😇 ✔
@commands.command()
async def new(body: str) -> None:
    return
BREAKING¶
- 
#208 - feat(pytauri)!: app state management and AsyncToolsutils.The following parameters have been removed: - pytauri.Commands- set_command(check_signature)
- parse_parameters(check_signature)
 
 
- 
#178 - feat(plugin-api)!: remove rawPyInvokeandChannel.Migration Before v0.7, IPC between Python and the frontend always usedbytes/ArrayBufferfor data transfer.pyInvokeandChannel.addJsonListenerwould force serialization and deserialization of input and output, whilerawPyInvokeandChannel.onmessagewould directly send and receive rawArrayBufferdata. This mechanism has been improved inv0.7:- 
Now, pyInvokeautomatically chooses whether to serialize based on your input type:- ArrayBuffer/- Uint8Arraywill be sent directly to the backend.
- Other types (any) will be converted toArrayBufferusingJSON.stringifyandTextEncoderbefore being sent to the backend.
 Only the backend can decide whether to deserialize or accept the raw bytes data. 
- 
Only the backend can decide whether to return deserialized JSON data or raw ArrayBufferdata:- If Commandreturns abytestype or Channel.send sends abytestype, the frontend will receive anArrayBuffer.
- If Commandreturns other types (BaseModel/Any) and Channel.send sends astrtype, the frontend will receive automatically deserialized JSON data.
 
- If 
 Therefore: - pyInvokereplaces- rawPyInvoke
- import { Channel } from "@tauri-apps/api/core"replaces- import { Channel } from "tauri-plugin-pytauri-api".
 
- 
0.6.1¶
Highlights¶
Rebranding¶
#173 - docs: rebranding.
A branding overhaul with an updated logo and colors:
The shape is similar to shapes found in the Tauri logo.
We have verified with the Tauri board and they are OK with this logo.
Comparison
The updated brand color is a mix of the Python and Tauri blue.
Q: Changing the logo and branding color might confuse users that they have entered the wrong site
While this might be true, we think PyTauri is not large enough yet to have this be a big issue. Either way I would suggest leaving this issue #170 up for a week and adding the banner above to the website to prepare users for the transition. An argument could also be made that a more professional looking icon attracts more users. Anyway, we will not change the logo (at least the color) again in the future.
Fixed¶
- 
#175 - fix(pytauri): bump tauri-plugin-*to fix rust docs build failures ondocs.rs.See tauri-apps/tauri#13597 for details. - tauri-plugin-opener = { version = "2.3.0" }
- tauri-plugin-clipboard-manager = { version = "2.2.3" }
- tauri-plugin-dialog = { version = "2.2.2" }
- tauri-plugin-fs = { version = "2.3.0" }
- tauri-plugin-notification = { version = "2.2.3" }
 
0.6.0¶
Highlights¶
create-pytauri-app template generator¶
- #169 - docs: update docs for v0.6.0 .
Since version 0.6, create-pytauri-app is the recommended way to start a new PyTauri project, even if it is still in development.
Usage
Refer to uv and copier, run the following command:
This will initialize the project in the form of an interactive questionnaire:
However, we still recommend reading the entire "Tutorial" section, as it will help you understand all the details of pytauri.
tauri-plugin-dialog bingings¶
- #163 - feat(plugin): implement
tauri-plugin-dialogbindings.
tauri-plugin-dialog has now been integrated into pytauri as the plugin-dialog gated feature.
Usage
from pytauri_plugins.dialog import DialogExt, MessageDialogButtons, MessageDialogKind
@commands.command()
async def greet(
    app_handle: AppHandle, webview_window: WebviewWindow
) -> bytes:
    file_dialog_builder = DialogExt.file(app_handle)
    file_dialog_builder.pick_files(
        lambda files: print(f"Files selected: {files}"),
        add_filter=("markdown", ["md"]),
        set_parent=webview_window,
        set_title="Select a Markdown file",
    )
    message_dialog_builder = DialogExt.message(app_handle, "Hello!")
    message_dialog_builder.show(
        lambda is_ok: print(f"Dialog closed with: {is_ok}"),
        parent=webview_window,
        buttons=MessageDialogButtons.OkCancelCustom("ok", "cancel"),
        kind=MessageDialogKind.Info,
    )
    return b"null"
Integrate plugins as features¶
- #160 - feat(pytauri)!: integrate
plugin-notificationas a gated-feature ofpytauri.
The rs/pytauri-plugin-notification crate and the py/pytauri-plugin-notification package have been removed. Instead, use the plugin-notification feature of the rs/pytauri crate.
For details, compare the v0.5 and v0.6 "tutorial/using-plugins" documentation.
Migration
# src-tauri/Cargo.toml
[dependencies]
-pytauri-plugin-notification = ...
+pytauri = { version = "...", features = ["plugin-notification"] }
BREAKING¶
- 
#166 - fix(standalone)!: standalone binary not working on MacOS. Patchinstall_nameforlibpython3.*.dylibofpython-build-standaloneSee: https://github.com/pytauri/pytauri/issues/99#issuecomment-2704556726. The install_nameoflibpython3.*.dylibbuilt bypython-build-standalonedoes not include@rpath, which makes therpathset for the executable ineffective.Migration Until this is fixed upstream in python-build-standalone, you need to manually patch theinstall_name:Do not create more than oneAppinstanceSee: https://github.com/tauri-apps/tauri/issues/12934 tauridoes not allow creating more than oneAppinstance per process. Previously, we were unaware of this limitation and suggested creating asample_appto obtain theresource_dir, which subsequently caused a panic inAppin__init__.py.Migration 
- 
#161 - refactor(pytauri)!: refactor BuilderArgstoTypedDict.
- 
#157 - feat(pytauri)!: Position.Physical(x, y)->Position.Physical((x, y)).These APIs have changed: - Position.Physical
- Position.Logical
- Size.Physical
- Size.Logical
 Migrationfrom pytauri import Position, PositionType, Size, SizeType def foo(pos: PositionType, size: SizeType) -> None: match pos: - case Position.Physical(x, y): + case Position.Physical((x, y)): print(f"Physical position: {x}, {y}") - case Position.Logical(x, y): + case Position.Logical((x, y)): print(f"Logical position: {x}, {y}") match size: - case Size.Physical(w, h): + case Size.Physical((w, h)): print(f"Physical size: {w}, {h}") - case Size.Logical(w, h): + case Size.Logical((w, h)): print(f"Logical size: {w}, {h}") -foo(pos=Position.Physical(1, 2), size=Size.Physical(3, 4)) +foo(pos=Position.Physical((1, 2)), size=Size.Physical((3, 4)))
Added¶
- 
#157 - feat(pytauri): fully implement tauri::RunEventbindings.- 
mod tauri::- Theme
- CloseRequestApi
- ExitRequestApi
- DragDropEvent(- DragDropEventType)
- WebviewEvent(- WebviewEventType)
- WindowEvent(- WindowEventType)
- RunEvent::{ExitRequested::api, WebviewEvent::event, WindowEvent::event}fields
- webview::WebviewWindow::{on_webview_event, on_window_event}methods
- AppHandle::set_themeand- webview::WebviewWindow::{set_theme, theme}methods
 
- 
add _NonExhaustivefield to all#[non_exhaustive]enums
 Usagefrom pytauri import AppHandle, Manager, WindowEvent, WindowEventType def register_window_event_handler(app_handle: AppHandle): webview_window = Manager.get_webview_window(app_handle, "main") assert webview_window is not None close_requested = False def window_event_handler(event: WindowEventType) -> None: nonlocal close_requested match event: case WindowEvent.CloseRequested(api=api): if not close_requested: print("Preventing window closing") api.prevent_close() close_requested = True case WindowEvent.Focused(focused): print(f"Window focused: {focused}") case WindowEvent.Moved((x, y)): print(f"Moved to ({x}, {y})") case _: pass webview_window.on_window_event(window_event_handler)
- 
- 
#158 - chore: bump pyo3to0.25.
0.5.0¶
Highlights¶
Wheel on Windows Arm64¶
- #139 - feat: add support for the
windows-11-armplatform wheel.
You can now install pytauri-wheel from PyPI on Windows 11 Arm64.
Using Unreleased Commits¶
- #147 - feat: support installation from git repository.
See: https://pytauri.github.io/pytauri/0.5/usage/using-unreleased-commits/
Accessing the request headers¶
- #136 - feat(pytauri): accessing the request headers in
Commands.
See: https://pytauri.github.io/pytauri/0.5/usage/concepts/ipc/#accessing-request-headers
Protect source code¶
- #134 - docs: add tutorial on using
Cythonto protect source code.
See: https://pytauri.github.io/pytauri/0.5/usage/tutorial/build-standalone-cython/
BREAKING¶
- #136 - tauri v2.5requires upgrading@tauri-apps/api: ^2.5andtauri-plugin-pytauri-api: ^0.5.
- #141 - feat(pytauri)!: pytauri.path.PathResolvernow returns apathlib.Pathobject instead of astr.
- 
#133 - fix(pytauri)!: make BuilderArgs.invoke_handleras required parameter for #110.If you do not specify invoke_handler,pytauriwill not register thetauri-plugin-pytauriplugin, which means you cannot usepyInvokein the frontend to callCommands(you will receive an error like "plugin pytauri not found"). If this is indeed the behavior you expect, explicitly passNone.
Added¶
- #141 - chore: bump pyo3to0.24.1.
- #124 - #136 - chore: bump tauridependencies:- taurito- 2.5.1
- tauri-utilsto- ~2.4
- tauri-buildto- 2.2
- tauri-pluginto- 2.2
- tauri-plugin-notificationto- 2.2
- tauri-plugin-openerto- 2.2
- @tauri-apps/apito- ^2.5
- @tauri-apps/plugin-openerto- ^2.2
- @tauri-apps/plugin-dialogto- ^2.2
 
Docs¶
- #124 - docs: update example to use App.run_returninstead ofApp.run, allowing cleanup tasks to be performed after the app exits (e.g., shutting down theniceguiserver) and retrieving the exit code.
0.4.0¶
Highlights¶
Precompiled python wheel (goodbye Rust compiler)¶
In v0.4.0, we have introduced an exciting new feature: precompiled Python wheel support! 🎉
This means you can use PyTauri without writing any Rust code or needing a Rust compiler.
This allows you to perform full-stack development in pure Python (like pywebview but battery-included 🤓).
Please refer to the PyTauri Wheel documentation for more information.
New logo for PyTauri¶
Thanks to @ISOR3X in #111! PyTauri now has its own logo 🎉:

It is indeed a snake, but not eating a bean, or perhaps it was not intended to be. It is more so a combination of the Tauri logo (two dots with rings around them) and the Python logo (the snake, specifically the head). The left part is more intended to visualize the snake curling around. Perhaps it is a bit too abstract.
Changed¶
- #63 - chore: bump tauritov2.3.
Internal¶
- #119 - ci(rs/release): add --no-verifytocargo publishso that we can release parallelly.
- #113 - ci: add macos-latestos inlint-testCI.
- #111 - docs: added PyTauri logo and updated documentation colors
- #103 - chore: transfer repo to pytauriorg.
0.3.0¶
Highlights¶
Changed¶
- #79 - bump rust-version = "1.82"
Docs¶
- #88 - docs: add rust api reference section.
- #85 - docs: add concepts IPCandusing multiprocessingsections.
- #80 - example/nicegui-app:- Use BuilderArgs.setupfor initialization instead of listening to theRunEvent.Readyevent.
- Rewrite the FrontServerstartup/shutdownevent hook logic.
- Modularize the code.
 
- Use 
- #79 - example/nicegui-app:- use trayandmenufeature
- use python3.10matchstatement instead ofif-elsestatement
- bump requires-python = ">=3.10"
 
- use 
Internal¶
- #81 - ci: add clear-cache.ymlworkflow.
0.2.0¶
BREAKING¶
- #70 - feat(notification): removed NotificationBuilderArgs. SeeCHANGELOG.mdofpy/pytauri-plugin-notificationfor how to migrate.
- #57 - refactor(py/pytauri): remove RunEventEnum, use matchedRunEventdirectly. SeeCHANGELOG.mdofpy/pytaurifor how to migrate.
- #56 - perf(pytauri): all IPC methods that previously accepted bytearrayas a parameter now only acceptbytesas a parameter.
- #52 - refactor(standalone)!: new API for preparing python interpreter.
    The pytauri::standalonemodule has been completely rewritten. Previously, you usedprepare_freethreaded_python_with_executableandappend_ext_mod. Now, you need to usePythonInterpreterBuilder. See thepytauricrate rust API docs and tutorial (examples/tauri-app)main.rscode for more information on how to migrate.
Docs¶
- #60 - update examplesmain.rsto removeresource_dir()UNC path prefix\\?\forPythonInterpreterEnv::Standalone. Fix pallets/jinja#1675 fornicegui-appstandalone example.
- #55 - Add integrate with niceguiexamplenicegui-app. Seeexamples/nicegui-app.
- #52 - update examples/tauri-appmain.rsfor new API to prepare python interpreter.
- #52 - add the usage of multiprocessing.freeze_supportinexamples/tauri-app__main__.py.
Changed¶
- #46 - bump tauritov2.2
Internal¶
- #83 - chore: bump pyo3tov0.23.4inCargo.lockto fix PyO3/pyo3#4828.
- #64 - test: add integration tests for commandandchannelipc