From 5ec44a8df4dd469d76b5ccfb4ab4e3dcb29efc19 Mon Sep 17 00:00:00 2001 From: Muhammad Kamran Date: Tue, 15 Aug 2023 18:38:14 +0000 Subject: [PATCH] first commit --- .cruft.json | 20 ++++ .github/workflows/publish_PYPI_each_tag.yml | 33 ++++++ .gitignore | 109 ++++++++++++++++++ LICENSE | 22 ++++ MANIFEST.in | 3 + README.md | 20 ++++ requirements.txt | 1 + setup.py | 22 ++++ .../__init__.py | 38 ++++++ .../frontend/index.html | 16 +++ .../frontend/main.js | 50 ++++++++ .../frontend/streamlit-component-lib.js | 34 ++++++ .../frontend/style.css | 0 13 files changed, 368 insertions(+) create mode 100644 .cruft.json create mode 100644 .github/workflows/publish_PYPI_each_tag.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 README.md create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 src/streamlit_component_socket_client/__init__.py create mode 100644 src/streamlit_component_socket_client/frontend/index.html create mode 100644 src/streamlit_component_socket_client/frontend/main.js create mode 100644 src/streamlit_component_socket_client/frontend/streamlit-component-lib.js create mode 100644 src/streamlit_component_socket_client/frontend/style.css diff --git a/.cruft.json b/.cruft.json new file mode 100644 index 0000000..d7c1c79 --- /dev/null +++ b/.cruft.json @@ -0,0 +1,20 @@ +{ + "template": "https://github.com/blackary/cookiecutter-streamlit-component/", + "commit": "c972054b7b0890a1aa1c02e792bedc71c525ea87", + "checkout": null, + "context": { + "cookiecutter": { + "author_name": "Muhammad Kamran", + "author_email": "mkrnaqeebi@gmail.com", + "project_name": "Streamlit Component Socket Client", + "package_name": "streamlit-component-socket-client", + "import_name": "streamlit_component_socket_client", + "description": "socket client which allow you to use and render session tate with out to wait for socket.", + "deployment_via_github_actions": "y", + "working_with_dataframes": "n", + "open_source_license": "MIT license", + "_template": "https://github.com/blackary/cookiecutter-streamlit-component/" + } + }, + "directory": null +} diff --git a/.github/workflows/publish_PYPI_each_tag.yml b/.github/workflows/publish_PYPI_each_tag.yml new file mode 100644 index 0000000..5b775fd --- /dev/null +++ b/.github/workflows/publish_PYPI_each_tag.yml @@ -0,0 +1,33 @@ + +# This workflows will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries + +name: Upload Python Package + +on: + release: + types: [created] + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: "3.x" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build and publish + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + pwd + python setup.py sdist bdist_wheel + twine upload dist/* + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a2ff27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,109 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# OSX useful to ignore +*.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +venv/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# IntelliJ Idea family of suites +.idea +*.iml +## File-based project format: +*.ipr +*.iws +## mpeltonen/sbt-idea plugin +.idea_modules/ + +# PyBuilder +target/ + +# Cookiecutter +output/ +python_boilerplate/ +cookiecutter-pypackage-env/ + +# IDE settings +.vscode/ + +# direnv +.envrc +.direnv/ + +# streamlit +.streamlit/secrets.toml diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d78e54d --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2023, Muhammad Kamran + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..818990f --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +recursive-include src/streamlit_component_socket_client/frontend * +include README.md +include LICENSE diff --git a/README.md b/README.md new file mode 100644 index 0000000..75fd74a --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# streamlit-component-socket-client + +socket client which allow you to use and render session tate with out to wait for socket. + +## Installation instructions + +```sh +pip install streamlit-component-socket-client +``` + +## Usage instructions + +```python +import streamlit as st + +from streamlit_component_socket_client import streamlit_component_socket_client + +value = streamlit_component_socket_client() + +st.write(value) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..12a4706 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +streamlit diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..bf44b68 --- /dev/null +++ b/setup.py @@ -0,0 +1,22 @@ +from pathlib import Path + +import setuptools + +this_directory = Path(__file__).parent +long_description = (this_directory / "README.md").read_text() + +setuptools.setup( + name="streamlit-component-socket-client", + version="0.1.0", + author="Muhammad Kamran", + author_email="mkrnaqeebi@gmail.com", + description="socket client which allow you to use and render session tate with out to wait for socket.", + long_description=long_description, + long_description_content_type="text/markdown", + packages=setuptools.find_packages(where="src"), + package_dir={"": "src"}, + include_package_data=True, + classifiers=[], + python_requires=">=3.7", + install_requires=["streamlit>=1.2", "jinja2"], +) diff --git a/src/streamlit_component_socket_client/__init__.py b/src/streamlit_component_socket_client/__init__.py new file mode 100644 index 0000000..76b44fb --- /dev/null +++ b/src/streamlit_component_socket_client/__init__.py @@ -0,0 +1,38 @@ +from pathlib import Path +from typing import Optional + +import streamlit as st +import streamlit.components.v1 as components + +# Tell streamlit that there is a component called streamlit_component_socket_client, +# and that the code to display that component is in the "frontend" folder +frontend_dir = (Path(__file__).parent / "frontend").absolute() +_component_func = components.declare_component( + "streamlit_component_socket_client", path=str(frontend_dir) +) + +# Create the python function that will be called +def streamlit_component_socket_client( + key: Optional[str] = None, + url: str = "http://localhost:5000", + message: str = "Hello World!", +): + """ + Add a descriptive docstring + """ + component_value = _component_func( + key=key, url=url, message=message + ) + return component_value + + +def main(): + st.write("## Example") + value = streamlit_component_socket_client( + url="http://139.177.201.45:5000", message="phone in water" + ) + st.write(value) + + +if __name__ == "__main__": + main() diff --git a/src/streamlit_component_socket_client/frontend/index.html b/src/streamlit_component_socket_client/frontend/index.html new file mode 100644 index 0000000..3a254af --- /dev/null +++ b/src/streamlit_component_socket_client/frontend/index.html @@ -0,0 +1,16 @@ + + + + + + + streamlit-component-socket-client + + + + + + +
+ + diff --git a/src/streamlit_component_socket_client/frontend/main.js b/src/streamlit_component_socket_client/frontend/main.js new file mode 100644 index 0000000..8aa6664 --- /dev/null +++ b/src/streamlit_component_socket_client/frontend/main.js @@ -0,0 +1,50 @@ +// The `Streamlit` object exists because our html file includes +// `streamlit-component-lib.js`. +// If you get an error about "Streamlit" not being defined, that +// means you're missing that file. + +function sendValue(value) { + Streamlit.setComponentValue(value) +} + +/** + * The component's render function. This will be called immediately after + * the component is initially loaded, and then again every time the + * component gets new data from Python. + */ +function onRender(event) { + // Only run the render code the first time the component is loaded. + if (!window.rendered) { + const {url, message} = event.detail.args + console.log(url, message) + // connect to the websocket + var socket = io(url, + { + withCredentials: true, + extraHeaders: { + "my-custom-header": "abcd" + } + }); + socket.on('connect', function() { + console.log('connected'); + socket.emit('message', message); + }); + socket.on('response', function(response) { + console.log('response', response); + sendValue({'response': response}); + }); + // You most likely want to get the data passed in like this + // const {input1, input2, input3} = event.detail.args + + // You'll most likely want to pass some data back to Python like this + // sendValue({output1: "foo", output2: "bar"}) + window.rendered = true + } +} + +// Render the component whenever python send a "render event" +Streamlit.events.addEventListener(Streamlit.RENDER_EVENT, onRender) +// Tell Streamlit that the component is ready to receive events +Streamlit.setComponentReady() +// Render with the correct height, if this is a fixed-height component +Streamlit.setFrameHeight(0) diff --git a/src/streamlit_component_socket_client/frontend/streamlit-component-lib.js b/src/streamlit_component_socket_client/frontend/streamlit-component-lib.js new file mode 100644 index 0000000..7ddce03 --- /dev/null +++ b/src/streamlit_component_socket_client/frontend/streamlit-component-lib.js @@ -0,0 +1,34 @@ + +// Borrowed minimalistic Streamlit API from Thiago +// https://discuss.streamlit.io/t/code-snippet-create-components-without-any-frontend-tooling-no-react-babel-webpack-etc/13064 +function sendMessageToStreamlitClient(type, data) { + console.log(type, data) + const outData = Object.assign({ + isStreamlitMessage: true, + type: type, + }, data); + window.parent.postMessage(outData, "*"); +} + +const Streamlit = { + setComponentReady: function() { + sendMessageToStreamlitClient("streamlit:componentReady", {apiVersion: 1}); + }, + setFrameHeight: function(height) { + sendMessageToStreamlitClient("streamlit:setFrameHeight", {height: height}); + }, + setComponentValue: function(value) { + sendMessageToStreamlitClient("streamlit:setComponentValue", {value: value}); + }, + RENDER_EVENT: "streamlit:render", + events: { + addEventListener: function(type, callback) { + window.addEventListener("message", function(event) { + if (event.data.type === type) { + event.detail = event.data + callback(event); + } + }); + } + } +} diff --git a/src/streamlit_component_socket_client/frontend/style.css b/src/streamlit_component_socket_client/frontend/style.css new file mode 100644 index 0000000..e69de29