streamlit2stlite 0.1.0__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.
@@ -0,0 +1,109 @@
1
+ Metadata-Version: 2.4
2
+ Name: streamlit2stlite
3
+ Version: 0.1.0
4
+ Summary: Convert Streamlit Python apps to stlite HTML apps
5
+ Project-URL: Homepage, https://github.com/caggionim/streamlit2stlite
6
+ Project-URL: Bug Tracker, https://github.com/caggionim/streamlit2stlite/issues
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.7
11
+ Description-Content-Type: text/markdown
12
+ Provides-Extra: dev
13
+ Requires-Dist: pytest; extra == "dev"
14
+
15
+ # 🔦 streamlit2stlite
16
+
17
+ **Turn your Streamlit apps into standalone HTML files in seconds.**
18
+
19
+ `streamlit2stlite` is a simple tool that bundles your Streamlit application and all its dependencies into a single HTML file. This file can be opened in any modern browser, running entirely purely on the client side using WebAssembly (via [stlite](https://github.com/whitphx/stlite)). **No server, no hosting costs, no deployment headaches.**
20
+
21
+ > 💡 Perfect for sharing data dashboards, prototypes, and tools with colleagues or clients who don't have Python installed.
22
+
23
+ ## 🚀 Quick Start
24
+
25
+ 1. **Install** the tool:
26
+ ```bash
27
+ pip install streamlit2stlite
28
+ ```
29
+
30
+ 2. **Convert** your app:
31
+ ```bash
32
+ streamlit2stlite my_app.py
33
+ ```
34
+
35
+ 3. **Open** `my_app.html` in your browser. That's it!
36
+
37
+ ---
38
+
39
+ ## ✨ Features
40
+
41
+ * **📦 Auto-Magic Dependency Detection**: Automatically scans your imports to determine which packages to install in the browser (e.g., proper handling of `pandas`, `numpy`, `scipy` for `lmfit`, etc.).
42
+ * **� LaTeX Support**: Correctly handles backslashes in your math equations so they render perfectly.
43
+ * **�️ Smart Titles**: Automatically detects your app's title from `st.set_page_config()` or `st.title()`.
44
+ * **🛠️ Full Control**: Override requirements, titles, or the stlite version via CLI flags if you need to.
45
+
46
+ ## � Usage Guide
47
+
48
+ ### Basic Conversion
49
+ The simplest way to use it. Defaults to creating an HTML file with the same name as your script.
50
+
51
+ ```bash
52
+ streamlit2stlite dashboard.py
53
+ # -> Creates dashboard.html
54
+ ```
55
+
56
+ ### Custom Output Name
57
+ Specify exactly where you want the file to go.
58
+
59
+ ```bash
60
+ streamlit2stlite script.py -o ./dist/awesome_dashboard.html
61
+ ```
62
+
63
+ ### Managing Dependencies
64
+ We try to guess your dependencies, but sometimes you need to be specific.
65
+
66
+ **Add extra packages:**
67
+ ```bash
68
+ streamlit2stlite app.py --add-requirements "scikit-learn,purple-air"
69
+ ```
70
+
71
+ **Override completely:**
72
+ ```bash
73
+ streamlit2stlite app.py --requirements "streamlit,pandas,numpy"
74
+ ```
75
+
76
+ ### Full CLI Options
77
+
78
+ ```text
79
+ usage: streamlit2stlite [-h] [-o OUTPUT] [-r REQUIREMENTS] [-t TITLE]
80
+ [--stlite-version STLITE_VERSION]
81
+ [--add-requirements ADD_REQUIREMENTS] [-v]
82
+ input
83
+
84
+ positional arguments:
85
+ input Path to the input Streamlit Python file
86
+
87
+ options:
88
+ -h, --help show this help message
89
+ -o, --output Path to the output HTML file
90
+ -r, --requirements Comma-separated list of packages to install
91
+ -t, --title Title for the HTML page
92
+ --add-requirements Additional packages to add to auto-detected ones
93
+ -v, --verbose Print verbose output
94
+ ```
95
+
96
+ ## ❓ FAQ
97
+
98
+ **How does this work?**
99
+ It embeds your Python code into a template that loads `stlite` (a port of Streamlit to WebAssembly). When you open the HTML file, your browser downloads a mini Python environment (Pyodide) and runs your code locally.
100
+
101
+ **Can I read local files?**
102
+ Because this runs in the browser, it cannot read files from your hard drive directly (sandbox security). You should use `st.file_uploader` to let users provide files, or embed data directly into your script.
103
+
104
+ **Does it support all Python packages?**
105
+ It supports packages available in [Pyodide](https://pyodide.org/en/stable/usage/packages-in-pyodide.html) (including numpy, pandas, scipy, matplotlib, scikit-learn) and pure Python packages from PyPI (micropip).
106
+
107
+ ## 📄 License
108
+
109
+ MIT License. Feel free to use this for whatever you want!
@@ -0,0 +1,95 @@
1
+ # 🔦 streamlit2stlite
2
+
3
+ **Turn your Streamlit apps into standalone HTML files in seconds.**
4
+
5
+ `streamlit2stlite` is a simple tool that bundles your Streamlit application and all its dependencies into a single HTML file. This file can be opened in any modern browser, running entirely purely on the client side using WebAssembly (via [stlite](https://github.com/whitphx/stlite)). **No server, no hosting costs, no deployment headaches.**
6
+
7
+ > 💡 Perfect for sharing data dashboards, prototypes, and tools with colleagues or clients who don't have Python installed.
8
+
9
+ ## 🚀 Quick Start
10
+
11
+ 1. **Install** the tool:
12
+ ```bash
13
+ pip install streamlit2stlite
14
+ ```
15
+
16
+ 2. **Convert** your app:
17
+ ```bash
18
+ streamlit2stlite my_app.py
19
+ ```
20
+
21
+ 3. **Open** `my_app.html` in your browser. That's it!
22
+
23
+ ---
24
+
25
+ ## ✨ Features
26
+
27
+ * **📦 Auto-Magic Dependency Detection**: Automatically scans your imports to determine which packages to install in the browser (e.g., proper handling of `pandas`, `numpy`, `scipy` for `lmfit`, etc.).
28
+ * **� LaTeX Support**: Correctly handles backslashes in your math equations so they render perfectly.
29
+ * **�️ Smart Titles**: Automatically detects your app's title from `st.set_page_config()` or `st.title()`.
30
+ * **🛠️ Full Control**: Override requirements, titles, or the stlite version via CLI flags if you need to.
31
+
32
+ ## � Usage Guide
33
+
34
+ ### Basic Conversion
35
+ The simplest way to use it. Defaults to creating an HTML file with the same name as your script.
36
+
37
+ ```bash
38
+ streamlit2stlite dashboard.py
39
+ # -> Creates dashboard.html
40
+ ```
41
+
42
+ ### Custom Output Name
43
+ Specify exactly where you want the file to go.
44
+
45
+ ```bash
46
+ streamlit2stlite script.py -o ./dist/awesome_dashboard.html
47
+ ```
48
+
49
+ ### Managing Dependencies
50
+ We try to guess your dependencies, but sometimes you need to be specific.
51
+
52
+ **Add extra packages:**
53
+ ```bash
54
+ streamlit2stlite app.py --add-requirements "scikit-learn,purple-air"
55
+ ```
56
+
57
+ **Override completely:**
58
+ ```bash
59
+ streamlit2stlite app.py --requirements "streamlit,pandas,numpy"
60
+ ```
61
+
62
+ ### Full CLI Options
63
+
64
+ ```text
65
+ usage: streamlit2stlite [-h] [-o OUTPUT] [-r REQUIREMENTS] [-t TITLE]
66
+ [--stlite-version STLITE_VERSION]
67
+ [--add-requirements ADD_REQUIREMENTS] [-v]
68
+ input
69
+
70
+ positional arguments:
71
+ input Path to the input Streamlit Python file
72
+
73
+ options:
74
+ -h, --help show this help message
75
+ -o, --output Path to the output HTML file
76
+ -r, --requirements Comma-separated list of packages to install
77
+ -t, --title Title for the HTML page
78
+ --add-requirements Additional packages to add to auto-detected ones
79
+ -v, --verbose Print verbose output
80
+ ```
81
+
82
+ ## ❓ FAQ
83
+
84
+ **How does this work?**
85
+ It embeds your Python code into a template that loads `stlite` (a port of Streamlit to WebAssembly). When you open the HTML file, your browser downloads a mini Python environment (Pyodide) and runs your code locally.
86
+
87
+ **Can I read local files?**
88
+ Because this runs in the browser, it cannot read files from your hard drive directly (sandbox security). You should use `st.file_uploader` to let users provide files, or embed data directly into your script.
89
+
90
+ **Does it support all Python packages?**
91
+ It supports packages available in [Pyodide](https://pyodide.org/en/stable/usage/packages-in-pyodide.html) (including numpy, pandas, scipy, matplotlib, scikit-learn) and pure Python packages from PyPI (micropip).
92
+
93
+ ## 📄 License
94
+
95
+ MIT License. Feel free to use this for whatever you want!
@@ -0,0 +1,29 @@
1
+ [project]
2
+ name = "streamlit2stlite"
3
+ version = "0.1.0"
4
+ description = "Convert Streamlit Python apps to stlite HTML apps"
5
+ readme = "README.md"
6
+ requires-python = ">=3.7"
7
+ classifiers = [
8
+ "Programming Language :: Python :: 3",
9
+ "License :: OSI Approved :: MIT License",
10
+ "Operating System :: OS Independent",
11
+ ]
12
+ dependencies = []
13
+
14
+ [project.optional-dependencies]
15
+ dev = ["pytest"]
16
+
17
+ [project.scripts]
18
+ streamlit2stlite = "streamlit2stlite.cli:main"
19
+
20
+ [project.urls]
21
+ "Homepage" = "https://github.com/caggionim/streamlit2stlite"
22
+ "Bug Tracker" = "https://github.com/caggionim/streamlit2stlite/issues"
23
+
24
+ [tool.setuptools]
25
+ packages = ["streamlit2stlite"]
26
+
27
+ [build-system]
28
+ requires = ["setuptools>=61.0"]
29
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ from .core import convert_streamlit_to_stlite
2
+ from .cli import main
3
+
4
+ __version__ = "0.1.0"
@@ -0,0 +1,152 @@
1
+ import argparse
2
+ import sys
3
+ from pathlib import Path
4
+ from .core import convert_streamlit_to_stlite, extract_imports, STLITE_VERSION
5
+
6
+ def main():
7
+ """Main CLI entry point."""
8
+ parser = argparse.ArgumentParser(
9
+ description='Convert Streamlit Python apps to stlite HTML apps',
10
+ formatter_class=argparse.RawDescriptionHelpFormatter,
11
+ epilog='''
12
+ Examples:
13
+ streamlit2stlite my_app.py
14
+ Converts my_app.py to my_app.html with auto-detected requirements
15
+
16
+ streamlit2stlite my_app.py -o output.html
17
+ Converts my_app.py to output.html
18
+
19
+ streamlit2stlite my_app.py --requirements pandas,numpy,plotly
20
+ Specifies exact requirements to install
21
+
22
+ streamlit2stlite my_app.py --title "My Dashboard"
23
+ Sets a custom page title
24
+
25
+ streamlit2stlite my_app.py --stlite-version 0.80.0
26
+ Uses a specific stlite version
27
+ '''
28
+ )
29
+
30
+ parser.add_argument(
31
+ 'input',
32
+ type=str,
33
+ help='Path to the input Streamlit Python file'
34
+ )
35
+
36
+ parser.add_argument(
37
+ '-o', '--output',
38
+ type=str,
39
+ default=None,
40
+ help='Path to the output HTML file (default: same name as input with .html extension)'
41
+ )
42
+
43
+ parser.add_argument(
44
+ '-r', '--requirements',
45
+ type=str,
46
+ default=None,
47
+ help='Comma-separated list of pip packages to install (default: auto-detect from imports)'
48
+ )
49
+
50
+ parser.add_argument(
51
+ '-t', '--title',
52
+ type=str,
53
+ default=None,
54
+ help='Title for the HTML page (default: auto-detect from code)'
55
+ )
56
+
57
+ parser.add_argument(
58
+ '--stlite-version',
59
+ type=str,
60
+ default=STLITE_VERSION,
61
+ help=f'Version of stlite to use (default: {STLITE_VERSION})'
62
+ )
63
+
64
+ parser.add_argument(
65
+ '--add-requirements',
66
+ type=str,
67
+ default=None,
68
+ help='Additional requirements to add to auto-detected ones (comma-separated)'
69
+ )
70
+
71
+ parser.add_argument(
72
+ '-v', '--verbose',
73
+ action='store_true',
74
+ help='Print verbose output'
75
+ )
76
+
77
+ args = parser.parse_args()
78
+
79
+ # Read input file
80
+ input_path = Path(args.input)
81
+ if not input_path.exists():
82
+ print(f"Error: Input file '{args.input}' not found", file=sys.stderr)
83
+ sys.exit(1)
84
+
85
+ # Check extension merely as a warning
86
+ if not input_path.suffix.lower() == '.py':
87
+ # Just a warning, proceed anyway
88
+ pass
89
+
90
+ try:
91
+ python_code = input_path.read_text(encoding='utf-8')
92
+ except Exception as e:
93
+ print(f"Error reading input file: {e}", file=sys.stderr)
94
+ sys.exit(1)
95
+
96
+ # Determine output path
97
+ if args.output:
98
+ output_path = Path(args.output)
99
+ else:
100
+ output_path = input_path.with_suffix('.html')
101
+
102
+ # Parse requirements
103
+ requirements = None
104
+ if args.requirements:
105
+ requirements = [r.strip() for r in args.requirements.split(',') if r.strip()]
106
+
107
+ # Add additional requirements if specified
108
+ if args.add_requirements:
109
+ additional = [r.strip() for r in args.add_requirements.split(',') if r.strip()]
110
+ if requirements is None:
111
+ requirements = extract_imports(python_code)
112
+ requirements = list(set(requirements + additional))
113
+
114
+ # Verbose output
115
+ if args.verbose:
116
+ print(f"Input: {input_path}")
117
+ print(f"Output: {output_path}")
118
+ if requirements is None:
119
+ detected_reqs = extract_imports(python_code)
120
+ print(f"Auto-detected requirements: {detected_reqs}")
121
+ else:
122
+ print(f"Using requirements: {requirements}")
123
+ print(f"stlite version: {args.stlite_version}")
124
+
125
+ # Convert
126
+ try:
127
+ html_content = convert_streamlit_to_stlite(
128
+ python_code=python_code,
129
+ title=args.title,
130
+ requirements=requirements,
131
+ stlite_version=args.stlite_version
132
+ )
133
+ except Exception as e:
134
+ print(f"Error during conversion: {e}", file=sys.stderr)
135
+ sys.exit(1)
136
+
137
+ # Write output
138
+ try:
139
+ output_path.write_text(html_content, encoding='utf-8')
140
+ print(f"Successfully converted '{input_path}' to '{output_path}'")
141
+
142
+ if args.verbose:
143
+ # Show final requirements used if we haven't already
144
+ if requirements is None:
145
+ print(f"Final requirements used: {extract_imports(python_code)}")
146
+
147
+ except Exception as e:
148
+ print(f"Error writing output file: {e}", file=sys.stderr)
149
+ sys.exit(1)
150
+
151
+ if __name__ == '__main__':
152
+ main()
@@ -0,0 +1,231 @@
1
+ import re
2
+ from typing import Optional, List, Set, Union
3
+
4
+ # Default stlite version
5
+ STLITE_VERSION = "1.0.0"
6
+
7
+ # HTML template header
8
+ HTML_HEADER = '''<!doctype html>
9
+ <html>
10
+ <head>
11
+ <meta charset="UTF-8" />
12
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
13
+ <meta
14
+ name="viewport"
15
+ content="width=device-width, initial-scale=1, shrink-to-fit=no"
16
+ />
17
+ <title>{title}</title>
18
+ <link
19
+ rel="stylesheet"
20
+ href="https://cdn.jsdelivr.net/npm/@stlite/browser@{stlite_version}/build/stlite.css"
21
+ />
22
+ </head>
23
+ <body>
24
+ <div id="root"></div>
25
+ <script type="module">
26
+ import {{ mount }} from "https://cdn.jsdelivr.net/npm/@stlite/browser@{stlite_version}/build/stlite.js";
27
+
28
+ // The Streamlit application code is defined here
29
+ const streamlit_app_code = `'''
30
+
31
+ # HTML template footer
32
+ HTML_FOOTER = '''`;
33
+ // Mount the stlite app with the specified requirements and files
34
+ mount(
35
+ {{
36
+ requirements: [{requirements}], // Packages to install
37
+ entrypoint: "streamlit_app.py", // This field is required
38
+ files: {{
39
+ "streamlit_app.py": streamlit_app_code,
40
+ }},
41
+ }},
42
+ document.getElementById("root"),
43
+ );
44
+ </script>
45
+ </body>
46
+ </html>
47
+ '''
48
+
49
+
50
+ def escape_for_js_template_literal(python_code: str) -> str:
51
+ r"""
52
+ Escape Python code for embedding in a JavaScript template literal.
53
+
54
+ Key transformations:
55
+ 1. Escape backticks (`) as they delimit template literals
56
+ 2. Escape ${} sequences as they are template literal interpolation
57
+ 3. Escape backslashes properly for JavaScript template literals
58
+
59
+ In Python source code:
60
+ - `\sigma` in source = literal `\sigma` when read
61
+ - For JS template literal, we need `\\sigma` to represent `\sigma`
62
+
63
+ So when reading a Python file:
64
+ - Single backslash in the read content needs to become double backslash in JS
65
+
66
+ Args:
67
+ python_code: The raw Python code to escape
68
+
69
+ Returns:
70
+ Escaped Python code safe for JS template literal embedding
71
+ """
72
+ # First, escape all backslashes for JavaScript template literal
73
+ # A single backslash in the content needs to be \\ in JS template literal
74
+ escaped = python_code.replace('\\', '\\\\')
75
+
76
+ # Escape backticks with backslash (they delimit template literals)
77
+ escaped = escaped.replace('`', '\\`')
78
+
79
+ # Escape template literal interpolation sequences
80
+ escaped = escaped.replace('${', '\\${')
81
+
82
+ return escaped
83
+
84
+
85
+ def extract_imports(python_code: str) -> List[str]:
86
+ """
87
+ Extract imported packages from Python code to suggest requirements.
88
+
89
+ Args:
90
+ python_code: The Python source code
91
+
92
+ Returns:
93
+ List of package names that should be installed
94
+ """
95
+ packages: Set[str] = set()
96
+
97
+ # Common import patterns - extract the top-level package name
98
+ # Handles: import pkg, import pkg.submodule, from pkg import ..., from pkg.sub import ...
99
+ import_patterns = [
100
+ r'^import\s+([\w]+)', # import package or import package.submodule
101
+ r'^from\s+([\w]+)(?:\.\w+)*\s+import', # from package.submodule import ...
102
+ ]
103
+
104
+ for line in python_code.split('\n'):
105
+ line = line.strip()
106
+ for pattern in import_patterns:
107
+ match = re.match(pattern, line)
108
+ if match:
109
+ pkg = match.group(1)
110
+ # Map common module names to pip package names
111
+ pkg_mapping = {
112
+ 'cv2': 'opencv-python',
113
+ 'sklearn': 'scikit-learn',
114
+ 'PIL': 'Pillow',
115
+ 'yaml': 'pyyaml',
116
+ 'bs4': 'beautifulsoup4',
117
+ }
118
+ packages.add(pkg_mapping.get(pkg, pkg))
119
+
120
+ # Remove standard library modules that don't need installation
121
+ stdlib = {
122
+ 'os', 'sys', 're', 'json', 'datetime', 'time', 'math', 'random',
123
+ 'collections', 'itertools', 'functools', 'operator', 'copy',
124
+ 'io', 'base64', 'hashlib', 'pickle', 'pathlib', 'typing',
125
+ 'abc', 'contextlib', 'dataclasses', 'enum', 'warnings',
126
+ 'threading', 'multiprocessing', 'subprocess', 'socket',
127
+ 'urllib', 'http', 'email', 'html', 'xml', 'csv', 'sqlite3',
128
+ 'logging', 'unittest', 'doctest', 'pdb', 'traceback',
129
+ 'gc', 'inspect', 'importlib', 'pkgutil', 'platform',
130
+ 'struct', 'array', 'decimal', 'fractions', 'statistics',
131
+ 'tempfile', 'shutil', 'glob', 'fnmatch', 'linecache',
132
+ 'textwrap', 'difflib', 'string', 'secrets', 'uuid',
133
+ 'argparse', 'getopt', 'configparser', 'fileinput',
134
+ 'stat', 'filecmp', 'zipfile', 'tarfile', 'gzip', 'bz2', 'lzma',
135
+ 'zlib', 'binascii', 'quopri', 'uu', 'codecs',
136
+ }
137
+
138
+ # Also remove streamlit since it's built into stlite
139
+ stdlib.add('streamlit')
140
+ stdlib.add('st')
141
+
142
+ packages = packages - stdlib
143
+
144
+ # Add common dependencies that aren't directly imported but are needed
145
+ # These are dependencies of commonly used packages
146
+ dependency_additions = {
147
+ 'lmfit': ['scipy'], # lmfit needs scipy
148
+ 'tadatakit': ['pydantic'], # tadatakit needs pydantic
149
+ 'plotly': [],
150
+ 'pandas': ['xlsxwriter'],
151
+ 'numpy': [],
152
+ 'matplotlib': [],
153
+ }
154
+
155
+ for pkg in list(packages):
156
+ if pkg in dependency_additions:
157
+ packages.update(dependency_additions[pkg])
158
+
159
+ return sorted(list(packages))
160
+
161
+
162
+ def detect_title_from_code(python_code: str) -> str:
163
+ """
164
+ Try to detect the app title from st.set_page_config or st.title calls.
165
+
166
+ Args:
167
+ python_code: The Python source code
168
+
169
+ Returns:
170
+ Detected title or default
171
+ """
172
+ # Look for page_title in set_page_config
173
+ match = re.search(r'page_title\s*=\s*["\']([^"\']+)["\']', python_code)
174
+ if match:
175
+ return match.group(1)
176
+
177
+ # Look for st.title
178
+ match = re.search(r'st\.title\s*\(\s*["\']([^"\']+)["\']', python_code)
179
+ if match:
180
+ # Remove emoji prefixes if present
181
+ title = match.group(1)
182
+ # Clean up emoji at the start
183
+ title = re.sub(r'^[^\w\s]+\s*', '', title)
184
+ return title if title else "Streamlit App"
185
+
186
+ return "Streamlit App"
187
+
188
+
189
+ def convert_streamlit_to_stlite(
190
+ python_code: str,
191
+ title: Optional[str] = None,
192
+ requirements: Optional[List[str]] = None,
193
+ stlite_version: str = STLITE_VERSION
194
+ ) -> str:
195
+ """
196
+ Convert a Streamlit Python app to an stlite HTML app.
197
+
198
+ Args:
199
+ python_code: The Streamlit Python source code
200
+ title: Optional title for the HTML page
201
+ requirements: Optional list of pip packages to install
202
+ stlite_version: Version of stlite to use
203
+
204
+ Returns:
205
+ Complete HTML document with embedded stlite app
206
+ """
207
+ # Detect title if not provided
208
+ if title is None:
209
+ title = detect_title_from_code(python_code)
210
+
211
+ # Detect requirements if not provided
212
+ if requirements is None:
213
+ requirements = extract_imports(python_code)
214
+
215
+ # Escape the Python code for JavaScript template literal
216
+ escaped_code = escape_for_js_template_literal(python_code)
217
+
218
+ # Format requirements as JavaScript array elements
219
+ req_str = ', '.join(f'"{pkg}"' for pkg in requirements)
220
+
221
+ # Build the HTML document
222
+ header = HTML_HEADER.format(
223
+ title=title,
224
+ stlite_version=stlite_version
225
+ )
226
+
227
+ footer = HTML_FOOTER.format(
228
+ requirements=req_str
229
+ )
230
+
231
+ return header + escaped_code + footer
@@ -0,0 +1,109 @@
1
+ Metadata-Version: 2.4
2
+ Name: streamlit2stlite
3
+ Version: 0.1.0
4
+ Summary: Convert Streamlit Python apps to stlite HTML apps
5
+ Project-URL: Homepage, https://github.com/caggionim/streamlit2stlite
6
+ Project-URL: Bug Tracker, https://github.com/caggionim/streamlit2stlite/issues
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.7
11
+ Description-Content-Type: text/markdown
12
+ Provides-Extra: dev
13
+ Requires-Dist: pytest; extra == "dev"
14
+
15
+ # 🔦 streamlit2stlite
16
+
17
+ **Turn your Streamlit apps into standalone HTML files in seconds.**
18
+
19
+ `streamlit2stlite` is a simple tool that bundles your Streamlit application and all its dependencies into a single HTML file. This file can be opened in any modern browser, running entirely purely on the client side using WebAssembly (via [stlite](https://github.com/whitphx/stlite)). **No server, no hosting costs, no deployment headaches.**
20
+
21
+ > 💡 Perfect for sharing data dashboards, prototypes, and tools with colleagues or clients who don't have Python installed.
22
+
23
+ ## 🚀 Quick Start
24
+
25
+ 1. **Install** the tool:
26
+ ```bash
27
+ pip install streamlit2stlite
28
+ ```
29
+
30
+ 2. **Convert** your app:
31
+ ```bash
32
+ streamlit2stlite my_app.py
33
+ ```
34
+
35
+ 3. **Open** `my_app.html` in your browser. That's it!
36
+
37
+ ---
38
+
39
+ ## ✨ Features
40
+
41
+ * **📦 Auto-Magic Dependency Detection**: Automatically scans your imports to determine which packages to install in the browser (e.g., proper handling of `pandas`, `numpy`, `scipy` for `lmfit`, etc.).
42
+ * **� LaTeX Support**: Correctly handles backslashes in your math equations so they render perfectly.
43
+ * **�️ Smart Titles**: Automatically detects your app's title from `st.set_page_config()` or `st.title()`.
44
+ * **🛠️ Full Control**: Override requirements, titles, or the stlite version via CLI flags if you need to.
45
+
46
+ ## � Usage Guide
47
+
48
+ ### Basic Conversion
49
+ The simplest way to use it. Defaults to creating an HTML file with the same name as your script.
50
+
51
+ ```bash
52
+ streamlit2stlite dashboard.py
53
+ # -> Creates dashboard.html
54
+ ```
55
+
56
+ ### Custom Output Name
57
+ Specify exactly where you want the file to go.
58
+
59
+ ```bash
60
+ streamlit2stlite script.py -o ./dist/awesome_dashboard.html
61
+ ```
62
+
63
+ ### Managing Dependencies
64
+ We try to guess your dependencies, but sometimes you need to be specific.
65
+
66
+ **Add extra packages:**
67
+ ```bash
68
+ streamlit2stlite app.py --add-requirements "scikit-learn,purple-air"
69
+ ```
70
+
71
+ **Override completely:**
72
+ ```bash
73
+ streamlit2stlite app.py --requirements "streamlit,pandas,numpy"
74
+ ```
75
+
76
+ ### Full CLI Options
77
+
78
+ ```text
79
+ usage: streamlit2stlite [-h] [-o OUTPUT] [-r REQUIREMENTS] [-t TITLE]
80
+ [--stlite-version STLITE_VERSION]
81
+ [--add-requirements ADD_REQUIREMENTS] [-v]
82
+ input
83
+
84
+ positional arguments:
85
+ input Path to the input Streamlit Python file
86
+
87
+ options:
88
+ -h, --help show this help message
89
+ -o, --output Path to the output HTML file
90
+ -r, --requirements Comma-separated list of packages to install
91
+ -t, --title Title for the HTML page
92
+ --add-requirements Additional packages to add to auto-detected ones
93
+ -v, --verbose Print verbose output
94
+ ```
95
+
96
+ ## ❓ FAQ
97
+
98
+ **How does this work?**
99
+ It embeds your Python code into a template that loads `stlite` (a port of Streamlit to WebAssembly). When you open the HTML file, your browser downloads a mini Python environment (Pyodide) and runs your code locally.
100
+
101
+ **Can I read local files?**
102
+ Because this runs in the browser, it cannot read files from your hard drive directly (sandbox security). You should use `st.file_uploader` to let users provide files, or embed data directly into your script.
103
+
104
+ **Does it support all Python packages?**
105
+ It supports packages available in [Pyodide](https://pyodide.org/en/stable/usage/packages-in-pyodide.html) (including numpy, pandas, scipy, matplotlib, scikit-learn) and pure Python packages from PyPI (micropip).
106
+
107
+ ## 📄 License
108
+
109
+ MIT License. Feel free to use this for whatever you want!
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ streamlit2stlite/__init__.py
4
+ streamlit2stlite/cli.py
5
+ streamlit2stlite/core.py
6
+ streamlit2stlite.egg-info/PKG-INFO
7
+ streamlit2stlite.egg-info/SOURCES.txt
8
+ streamlit2stlite.egg-info/dependency_links.txt
9
+ streamlit2stlite.egg-info/entry_points.txt
10
+ streamlit2stlite.egg-info/requires.txt
11
+ streamlit2stlite.egg-info/top_level.txt
12
+ tests/test_core.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ streamlit2stlite = streamlit2stlite.cli:main
@@ -0,0 +1,3 @@
1
+
2
+ [dev]
3
+ pytest
@@ -0,0 +1 @@
1
+ streamlit2stlite
@@ -0,0 +1,109 @@
1
+ import pytest
2
+ from streamlit2stlite.core import (
3
+ escape_for_js_template_literal,
4
+ extract_imports,
5
+ detect_title_from_code,
6
+ convert_streamlit_to_stlite
7
+ )
8
+
9
+ def test_escape_for_js_template_literal():
10
+ # Test backslash escaping
11
+ code = r"print('Hello \n World')"
12
+ escaped = escape_for_js_template_literal(code)
13
+ # The result should have double backslashes for the newline char representation in Python string
14
+ # But effectively passing \\ to JS.
15
+ # In python string literal r"\\" is actually \
16
+ assert r"\\" in escaped
17
+
18
+ # Test backtick escaping
19
+ code = "print(f`Hello`)"
20
+ escaped = escape_for_js_template_literal(code)
21
+ assert r"\`" in escaped
22
+
23
+ # Test template interpolation escaping
24
+ code = "const x = `${variable}`"
25
+ escaped = escape_for_js_template_literal(code)
26
+ assert r"\${" in escaped
27
+
28
+ # Combined test
29
+ code = r"path = 'c:\users\name'"
30
+ # Expected: c:\\users\\name in the JS string
31
+ escaped = escape_for_js_template_literal(code)
32
+ assert r"\\" in escaped
33
+
34
+ def test_extract_imports():
35
+ code = """
36
+ import pandas as pd
37
+ import numpy
38
+ from plotly import express
39
+ import cv2
40
+ import os
41
+ import sys
42
+ """
43
+ reqs = extract_imports(code)
44
+
45
+ # Check that standard lib is ignored
46
+ assert "os" not in reqs
47
+ assert "sys" not in reqs
48
+
49
+ # Check simple imports
50
+ assert "pandas" in reqs
51
+ assert "numpy" in reqs
52
+
53
+ # Check from imports
54
+ assert "plotly" in reqs
55
+
56
+ # Check mapping
57
+ assert "opencv-python" in reqs
58
+ assert "cv2" not in reqs
59
+
60
+ def test_extract_imports_dependencies():
61
+ code = "import lmfit"
62
+ reqs = extract_imports(code)
63
+ assert "lmfit" in reqs
64
+ assert "scipy" in reqs # lmfit dependency
65
+
66
+ # Test pandas -> xlsxwriter
67
+ code_pandas = "import pandas"
68
+ reqs_pandas = extract_imports(code_pandas)
69
+ assert "pandas" in reqs_pandas
70
+ assert "xlsxwriter" in reqs_pandas
71
+
72
+ def test_detect_title_from_code():
73
+ # Test set_page_config
74
+ code1 = """
75
+ import streamlit as st
76
+ st.set_page_config(page_title="My Cool App")
77
+ """
78
+ assert detect_title_from_code(code1) == "My Cool App"
79
+
80
+ # Test st.title
81
+ code2 = """
82
+ import streamlit as st
83
+ st.title("My Title")
84
+ """
85
+ assert detect_title_from_code(code2) == "My Title"
86
+
87
+ # Test emoji cleanup
88
+ code3 = """
89
+ st.title("🚀 Blast Off")
90
+ """
91
+ assert detect_title_from_code(code3) == "Blast Off"
92
+
93
+ # Test default
94
+ code4 = "print('hello')"
95
+ assert detect_title_from_code(code4) == "Streamlit App"
96
+
97
+ def test_convert_streamlit_to_stlite():
98
+ code = "import streamlit as st\nst.write('Hello')"
99
+ html = convert_streamlit_to_stlite(code, title="Test App")
100
+
101
+ assert "<!doctype html>" in html
102
+ assert "<title>Test App</title>" in html
103
+ assert "stlite.js" in html
104
+ assert "stlite.css" in html
105
+ assert "streamlit_app_code = `" in html
106
+ assert "mount(" in html
107
+
108
+ # Check that code is present
109
+ assert "st.write('Hello')" in html