pywire 0.1.4__tar.gz → 0.1.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pywire-0.1.4 → pywire-0.1.5}/.github/workflows/publish.yml +0 -6
- {pywire-0.1.4 → pywire-0.1.5}/PKG-INFO +1 -1
- pywire-0.1.5/hatch_build.py +140 -0
- {pywire-0.1.4 → pywire-0.1.5}/pyproject.toml +10 -2
- pywire-0.1.5/src/pywire/__init__.py +12 -0
- pywire-0.1.5/src/pywire/_version.py +34 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/cli/main.py +13 -10
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/build.mjs +4 -1
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/package.json +1 -1
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/core/app.ts +5 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/core/transports/base.ts +7 -1
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/core/transports/http.ts +5 -1
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/tsconfig.json +2 -1
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/app.py +2 -1
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/http_transport.py +3 -1
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/websocket.py +6 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/static/pywire.core.min.js +3 -3
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/static/pywire.dev.min.js +4 -4
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_app_runtime.py +2 -1
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_pjax_error.py +3 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_relocation_error.py +3 -0
- pywire-0.1.4/src/pywire/__init__.py +0 -2
- {pywire-0.1.4 → pywire-0.1.5}/.github/CODEOWNERS +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/.github/workflows/ci.yml +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/.github/workflows/deploy-docs.yml +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/.gitignore +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/LICENSE +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/README.md +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/.gitignore +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/.prettierignore +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/.prettierrc +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/.vscode/extensions.json +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/.vscode/launch.json +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/README.md +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/astro.config.mjs +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/eslint.config.js +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/package.json +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/pnpm-lock.yaml +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/public/favicon.svg +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/scripts/check +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/scripts/lint +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/src/assets/houston.webp +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/src/content/docs/guides/example.md +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/src/content/docs/guides/getting-started.md +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/src/content/docs/guides/walkthrough.md +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/src/content/docs/index.mdx +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/src/content/docs/reference/api.md +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/src/content/docs/reference/example.md +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/src/content.config.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/src/styles/custom.css +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/docs/tsconfig.json +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/scripts/README.md +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/scripts/build +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/scripts/check +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/scripts/coverage +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/scripts/docs +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/scripts/install +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/scripts/lint +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/scripts/test +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/cli/__init__.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/cli/generators.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/cli/tui.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/cli/validate.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/.prettierignore +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/.prettierrc +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/eslint.config.js +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/pnpm-lock.yaml +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/core/dom-updater.test.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/core/dom-updater.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/core/index.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/core/transport-manager.test.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/core/transport-manager.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/core/transports/index.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/core/transports/websocket.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/core/transports/webtransport.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/dev/dev-app.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/dev/error-trace.test.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/dev/error-trace.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/dev/index.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/dev/status-overlay.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/events/handler.test.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/events/handler.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/pywire.core.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/src/pywire.dev.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/client/vitest.config.ts +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/__init__.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/ast_nodes.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/attributes/__init__.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/attributes/base.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/attributes/conditional.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/attributes/events.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/attributes/form.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/attributes/loop.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/attributes/reactive.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/build.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/build_artifacts.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/codegen/__init__.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/codegen/attributes/__init__.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/codegen/attributes/base.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/codegen/attributes/events.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/codegen/directives/__init__.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/codegen/directives/base.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/codegen/directives/path.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/codegen/generator.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/codegen/template.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/directives/__init__.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/directives/base.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/directives/component.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/directives/context.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/directives/layout.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/directives/no_spa.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/directives/path.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/directives/props.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/exceptions.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/interpolation/__init__.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/interpolation/base.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/interpolation/jinja.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/parser.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/paths.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/compiler/preprocessor.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/core/wire.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/py.typed +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/__init__.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/aioquic_server.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/compile_error_page.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/debug.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/dev_server.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/dev_server.py.broken +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/error_page.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/error_renderer.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/escape.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/files.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/helpers.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/loader.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/logging.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/page.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/pydantic_integration.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/router.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/server.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/style_collector.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/upload_manager.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/validation.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/runtime/webtransport_handler.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/templates/error/404.html +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/templates/error/500.html +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/templates/error/base.html +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/src/pywire/templates/error/compile_error.html +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/debug_router.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_app_advanced.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_app_exhaustive.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_app_mode.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_async_features.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_codegen_generator.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_codegen_template.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_codegen_template_exhaustive.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_compile_error.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_compiler_sourcemap.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_comprehensive_python_syntax.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_config.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_custom_errors.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_debug_middleware.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_debug_mock.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_error_handling_debug.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_file_routing.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_fixes.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_form_validation.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_generator_advanced.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_generator_exhaustive.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_hooks_removal.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_http_transport.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_inline_compilation.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_interactivity_codegen_complex.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_interactivity_compiler.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_jinja_interpolation.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_layouts.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_lifecycle_hooks.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_logging_stream.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_method_binding.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_middleware_error.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_mode_gating.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_naming_conventions.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_parser_advanced.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_parser_compiler.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_reactive_attributes.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_rendering.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_router_exhaustive.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_router_expansion.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_router_runtime.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_runtime_helpers.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_script_injection.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_server_validation.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_static_assets.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_strict_routing.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_style_collector.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_syntax_enforcement.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_templating_features.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_transport_advanced.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_transport_exhaustive.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_validation_exhaustive.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_websocket_advanced.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_websocket_exhaustive.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_websocket_handler.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tests/test_wire_primitive.py +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/tox.ini +0 -0
- {pywire-0.1.4 → pywire-0.1.5}/uv.lock +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from contextlib import suppress
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Dict
|
|
8
|
+
|
|
9
|
+
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
|
10
|
+
|
|
11
|
+
log = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
class CustomBuildHook(BuildHookInterface):
|
|
14
|
+
def initialize(self, version: str, build_data: Dict[str, Any]) -> None:
|
|
15
|
+
real_version = self.metadata.version
|
|
16
|
+
|
|
17
|
+
# We only want to do this if we actually have a version
|
|
18
|
+
if not real_version:
|
|
19
|
+
return
|
|
20
|
+
|
|
21
|
+
# Attempt to find the client package.json
|
|
22
|
+
# When building sdist, we might be in the source tree.
|
|
23
|
+
# When building wheel, we might be in a copy.
|
|
24
|
+
|
|
25
|
+
# Try explicit relative path first
|
|
26
|
+
pkg_path = Path(self.root) / "src" / "pywire" / "client" / "package.json"
|
|
27
|
+
|
|
28
|
+
if not pkg_path.exists():
|
|
29
|
+
log.warning(f"Client package.json not found at {pkg_path}")
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
content = pkg_path.read_text("utf-8")
|
|
34
|
+
data = json.loads(content)
|
|
35
|
+
|
|
36
|
+
current_version = data.get("version")
|
|
37
|
+
|
|
38
|
+
# Normalize PEP 440 to SemVer
|
|
39
|
+
# 0.1.5.dev0+... -> 0.1.5-dev0+...
|
|
40
|
+
semver = real_version.replace(".dev", "-dev")
|
|
41
|
+
semver = semver.replace(".a", "-alpha")
|
|
42
|
+
semver = semver.replace(".b", "-beta")
|
|
43
|
+
semver = semver.replace(".rc", "-rc")
|
|
44
|
+
|
|
45
|
+
version_changed = False
|
|
46
|
+
if current_version != semver:
|
|
47
|
+
log.info(f"Syncing client version: {current_version} -> {semver}")
|
|
48
|
+
data["version"] = semver
|
|
49
|
+
|
|
50
|
+
# formatting with 2 spaces to match typical usage
|
|
51
|
+
pkg_path.write_text(json.dumps(data, indent=2) + "\n", "utf-8")
|
|
52
|
+
version_changed = True
|
|
53
|
+
|
|
54
|
+
# Now run the build
|
|
55
|
+
# We need to run pnpm install and pnpm run build
|
|
56
|
+
import subprocess
|
|
57
|
+
import shutil
|
|
58
|
+
|
|
59
|
+
client_dir = pkg_path.parent
|
|
60
|
+
static_dir = client_dir.parent / "static"
|
|
61
|
+
|
|
62
|
+
if not shutil.which("pnpm"):
|
|
63
|
+
log.warning("pnpm not found, skipping client build. Assets may be stale.")
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
# Check if we need to install
|
|
67
|
+
if self._should_install(client_dir):
|
|
68
|
+
log.info("Installing client dependencies...")
|
|
69
|
+
subprocess.run(["pnpm", "install"], cwd=client_dir, check=True, shell=False)
|
|
70
|
+
else:
|
|
71
|
+
log.debug("Client dependencies up to date.")
|
|
72
|
+
|
|
73
|
+
# Check if we need to build
|
|
74
|
+
if self._should_build(client_dir, static_dir) or version_changed:
|
|
75
|
+
log.info("Building client assets with pnpm...")
|
|
76
|
+
subprocess.run(["pnpm", "run", "build"], cwd=client_dir, check=True, shell=False)
|
|
77
|
+
log.info("Client build complete.")
|
|
78
|
+
else:
|
|
79
|
+
log.info("Client assets up to date, skipping build.")
|
|
80
|
+
|
|
81
|
+
except Exception as e:
|
|
82
|
+
log.error(f"Failed to sync client version: {e}")
|
|
83
|
+
|
|
84
|
+
def _should_install(self, client_dir: Path) -> bool:
|
|
85
|
+
"""Check if node_modules is missing or package.json changed."""
|
|
86
|
+
node_modules = client_dir / "node_modules"
|
|
87
|
+
package_json = client_dir / "package.json"
|
|
88
|
+
lock_file = client_dir / "pnpm-lock.yaml"
|
|
89
|
+
|
|
90
|
+
if not node_modules.exists():
|
|
91
|
+
return True
|
|
92
|
+
|
|
93
|
+
# If no package.json, can't install (shouldn't happen here)
|
|
94
|
+
if not package_json.exists():
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
# Check timestamps
|
|
98
|
+
# Use node_modules directory mtime as proxy for install time
|
|
99
|
+
nm_mtime = node_modules.stat().st_mtime
|
|
100
|
+
pkg_mtime = package_json.stat().st_mtime
|
|
101
|
+
|
|
102
|
+
if pkg_mtime > nm_mtime:
|
|
103
|
+
return True
|
|
104
|
+
|
|
105
|
+
if lock_file.exists():
|
|
106
|
+
if lock_file.stat().st_mtime > nm_mtime:
|
|
107
|
+
return True
|
|
108
|
+
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
def _should_build(self, client_dir: Path, static_dir: Path) -> bool:
|
|
112
|
+
"""Check if source files are newer than built assets."""
|
|
113
|
+
core_bundle = static_dir / "pywire.core.min.js"
|
|
114
|
+
|
|
115
|
+
if not core_bundle.exists():
|
|
116
|
+
return True
|
|
117
|
+
|
|
118
|
+
build_mtime = core_bundle.stat().st_mtime
|
|
119
|
+
|
|
120
|
+
# Check source files
|
|
121
|
+
src_dir = client_dir / "src"
|
|
122
|
+
if not src_dir.exists():
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
# Recursive check for any file in src
|
|
126
|
+
for root, _, files in os.walk(src_dir):
|
|
127
|
+
for file in files:
|
|
128
|
+
file_path = Path(root) / file
|
|
129
|
+
if file_path.stat().st_mtime > build_mtime:
|
|
130
|
+
return True
|
|
131
|
+
|
|
132
|
+
# Check build script and package.json
|
|
133
|
+
build_script = client_dir / "build.mjs"
|
|
134
|
+
if build_script.exists() and build_script.stat().st_mtime > build_mtime:
|
|
135
|
+
return True
|
|
136
|
+
|
|
137
|
+
if (client_dir / "package.json").stat().st_mtime > build_mtime:
|
|
138
|
+
return True
|
|
139
|
+
|
|
140
|
+
return False
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["hatchling"]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
3
|
build-backend = "hatchling.build"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
@@ -7,7 +7,7 @@ name = "pywire"
|
|
|
7
7
|
authors = [
|
|
8
8
|
{ name="Reece Holmdahl", email="reece@pywire.dev" },
|
|
9
9
|
]
|
|
10
|
-
|
|
10
|
+
dynamic = ["version"]
|
|
11
11
|
description = "HTML-over-the-wire Python web framework"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
requires-python = ">=3.11"
|
|
@@ -58,6 +58,14 @@ include = [
|
|
|
58
58
|
[tool.hatch.build.targets.sdist.force-include]
|
|
59
59
|
"src/pywire/static" = "src/pywire/static"
|
|
60
60
|
|
|
61
|
+
[tool.hatch.version]
|
|
62
|
+
source = "vcs"
|
|
63
|
+
|
|
64
|
+
[tool.hatch.build.hooks.vcs]
|
|
65
|
+
version-file = "src/pywire/_version.py"
|
|
66
|
+
|
|
67
|
+
[tool.hatch.build.hooks.custom]
|
|
68
|
+
|
|
61
69
|
[tool.mypy]
|
|
62
70
|
python_version = "3.11"
|
|
63
71
|
check_untyped_defs = true
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
try:
|
|
2
|
+
from ._version import __version__
|
|
3
|
+
except ImportError:
|
|
4
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
__version__ = version("pywire")
|
|
8
|
+
except PackageNotFoundError:
|
|
9
|
+
__version__ = "unknown"
|
|
10
|
+
|
|
11
|
+
from .runtime.app import PyWire as PyWire
|
|
12
|
+
from .core.wire import wire as wire
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"__version__",
|
|
6
|
+
"__version_tuple__",
|
|
7
|
+
"version",
|
|
8
|
+
"version_tuple",
|
|
9
|
+
"__commit_id__",
|
|
10
|
+
"commit_id",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
TYPE_CHECKING = False
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from typing import Tuple
|
|
16
|
+
from typing import Union
|
|
17
|
+
|
|
18
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
+
COMMIT_ID = Union[str, None]
|
|
20
|
+
else:
|
|
21
|
+
VERSION_TUPLE = object
|
|
22
|
+
COMMIT_ID = object
|
|
23
|
+
|
|
24
|
+
version: str
|
|
25
|
+
__version__: str
|
|
26
|
+
__version_tuple__: VERSION_TUPLE
|
|
27
|
+
version_tuple: VERSION_TUPLE
|
|
28
|
+
commit_id: COMMIT_ID
|
|
29
|
+
__commit_id__: COMMIT_ID
|
|
30
|
+
|
|
31
|
+
__version__ = version = '0.1.5'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 1, 5)
|
|
33
|
+
|
|
34
|
+
__commit_id__ = commit_id = None
|
|
@@ -8,6 +8,8 @@ from typing import Any, Optional
|
|
|
8
8
|
import rich.panel
|
|
9
9
|
import rich_click as click
|
|
10
10
|
|
|
11
|
+
from pywire import __version__
|
|
12
|
+
|
|
11
13
|
# Astro-like styling configuration (Cyan Theme)
|
|
12
14
|
click.rich_click.USE_RICH_MARKUP = True
|
|
13
15
|
click.rich_click.STYLE_HELPTEXT_FIRST = True
|
|
@@ -141,18 +143,19 @@ def panel_init(self, *args, **kwargs):
|
|
|
141
143
|
rich.panel.Panel.__init__ = panel_init # type: ignore[method-assign]
|
|
142
144
|
|
|
143
145
|
|
|
144
|
-
@click.group(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
"""
|
|
148
|
-
[bold white on cyan] pywire [/] [bold cyan]v0.1.3[/] Build faster python web apps.
|
|
146
|
+
@click.group(
|
|
147
|
+
help=f"""
|
|
148
|
+
[bold white on cyan] pywire [/] [bold cyan]v{__version__}[/] Build faster python web apps.
|
|
149
149
|
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
Run [bold cyan]pywire dev APP[/] to start development server.
|
|
151
|
+
Run [bold cyan]pywire run APP[/] to start production server.
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
[dim]APP should be a string in format 'module:instance', e.g. 'src.main:app' or 'main:app'
|
|
154
|
+
If not provided, pywire tries to discover it in main.py, app.py, etc.[/dim]
|
|
155
|
+
"""
|
|
156
|
+
)
|
|
157
|
+
@click.version_option(__version__)
|
|
158
|
+
def cli() -> None:
|
|
156
159
|
pass
|
|
157
160
|
|
|
158
161
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as esbuild from 'esbuild'
|
|
2
2
|
import { resolve, dirname } from 'path'
|
|
3
3
|
import { fileURLToPath } from 'url'
|
|
4
|
+
import { createRequire } from 'module'
|
|
5
|
+
const require = createRequire(import.meta.url)
|
|
6
|
+
const { version } = require('./package.json')
|
|
4
7
|
|
|
5
8
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
6
9
|
|
|
@@ -38,7 +41,7 @@ function getBuildOptions(bundle) {
|
|
|
38
41
|
'process.env.NODE_ENV': isDev ? '"development"' : '"production"',
|
|
39
42
|
},
|
|
40
43
|
banner: {
|
|
41
|
-
js: `/* PyWire Client ${bundle.name}
|
|
44
|
+
js: `/* PyWire Client ${bundle.name} v${version} - https://github.com/pywire/pywire */`,
|
|
42
45
|
},
|
|
43
46
|
}
|
|
44
47
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TransportManager, TransportConfig } from './transport-manager'
|
|
2
|
+
import { version as clientVersion } from '../../package.json'
|
|
2
3
|
import { DOMUpdater } from './dom-updater'
|
|
3
4
|
import { ServerMessage, ClientMessage, EventData, RelocateMessage } from './transports'
|
|
4
5
|
import { UnifiedEventHandler } from '../events/handler'
|
|
@@ -242,6 +243,10 @@ export class PyWireApp {
|
|
|
242
243
|
}
|
|
243
244
|
break
|
|
244
245
|
|
|
246
|
+
case 'init':
|
|
247
|
+
console.log(`PyWire Client v${clientVersion} • Server v${msg.version}`)
|
|
248
|
+
break
|
|
249
|
+
|
|
245
250
|
default:
|
|
246
251
|
console.warn('PyWire: Unknown message type', msg)
|
|
247
252
|
}
|
|
@@ -36,13 +36,19 @@ export interface StackFrame {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export interface ServerMessage {
|
|
39
|
-
type: 'update' | 'reload' | 'error' | 'console' | 'error_trace'
|
|
39
|
+
type: 'update' | 'reload' | 'error' | 'console' | 'error_trace' | 'init'
|
|
40
40
|
html?: string
|
|
41
41
|
regions?: Array<{ region: string; html: string }>
|
|
42
42
|
error?: string
|
|
43
43
|
level?: 'info' | 'warn' | 'error'
|
|
44
44
|
lines?: string[]
|
|
45
45
|
trace?: StackFrame[]
|
|
46
|
+
version?: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface InitMessage {
|
|
50
|
+
type: 'init'
|
|
51
|
+
version: string
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
export interface ConsoleMessage {
|
|
@@ -35,9 +35,13 @@ export class HTTPTransport extends BaseTransport {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const buffer = await response.arrayBuffer()
|
|
38
|
-
const data = decode(buffer) as { sessionId: string }
|
|
38
|
+
const data = decode(buffer) as { sessionId: string; version?: string }
|
|
39
39
|
this.sessionId = data.sessionId
|
|
40
40
|
|
|
41
|
+
if (data.version) {
|
|
42
|
+
this.notifyHandlers({ type: 'init', version: data.version })
|
|
43
|
+
}
|
|
44
|
+
|
|
41
45
|
console.log('PyWire: HTTP transport connected')
|
|
42
46
|
this.notifyStatus(true)
|
|
43
47
|
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"declaration": false,
|
|
11
11
|
"outDir": "./dist",
|
|
12
12
|
"rootDir": "./src",
|
|
13
|
-
"lib": ["ES2020", "DOM", "DOM.Iterable"]
|
|
13
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
14
|
+
"resolveJsonModule": true
|
|
14
15
|
},
|
|
15
16
|
"include": ["src/**/*"],
|
|
16
17
|
"exclude": ["node_modules", "dist"]
|
|
@@ -12,6 +12,7 @@ from starlette.responses import JSONResponse, PlainTextResponse, Response
|
|
|
12
12
|
from starlette.routing import Mount, Route, WebSocketRoute
|
|
13
13
|
from starlette.staticfiles import StaticFiles
|
|
14
14
|
|
|
15
|
+
from pywire import __version__
|
|
15
16
|
from pywire.runtime.error_page import ErrorPage
|
|
16
17
|
from pywire.runtime.http_transport import HTTPTransportHandler
|
|
17
18
|
from pywire.runtime.router import Router
|
|
@@ -208,7 +209,7 @@ class PyWire:
|
|
|
208
209
|
"transports": ["websocket", "http"],
|
|
209
210
|
# WebTransport requires HTTP/3 - only available when running with Hypercorn
|
|
210
211
|
"webtransport": False,
|
|
211
|
-
"version":
|
|
212
|
+
"version": __version__,
|
|
212
213
|
}
|
|
213
214
|
)
|
|
214
215
|
|
|
@@ -12,6 +12,7 @@ from starlette.requests import Request
|
|
|
12
12
|
from starlette.responses import Response
|
|
13
13
|
|
|
14
14
|
from pywire.runtime.page import BasePage
|
|
15
|
+
from pywire import __version__
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
@dataclass
|
|
@@ -118,7 +119,8 @@ class HTTPTransportHandler:
|
|
|
118
119
|
self.start_cleanup_task()
|
|
119
120
|
|
|
120
121
|
return Response(
|
|
121
|
-
msgpack.packb({"sessionId": session_id
|
|
122
|
+
msgpack.packb({"sessionId": session_id, "version": __version__}),
|
|
123
|
+
media_type="application/x-msgpack",
|
|
122
124
|
)
|
|
123
125
|
|
|
124
126
|
async def poll(self, request: Request) -> Response:
|
|
@@ -12,6 +12,7 @@ from starlette.websockets import WebSocket, WebSocketDisconnect
|
|
|
12
12
|
|
|
13
13
|
from pywire.runtime.logging import log_callback_ctx
|
|
14
14
|
from pywire.runtime.page import BasePage
|
|
15
|
+
from pywire import __version__
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
class WebSocketHandler:
|
|
@@ -34,6 +35,11 @@ class WebSocketHandler:
|
|
|
34
35
|
await websocket.accept()
|
|
35
36
|
self.active_connections.add(websocket)
|
|
36
37
|
|
|
38
|
+
# Send init message
|
|
39
|
+
await websocket.send_bytes(
|
|
40
|
+
msgpack.packb({"type": "init", "version": __version__})
|
|
41
|
+
)
|
|
42
|
+
|
|
37
43
|
try:
|
|
38
44
|
# Create isolated page instance for this connection
|
|
39
45
|
# We need to reconstruct the page based on current URL
|