Coder Social home page Coder Social logo

texteainc / funix Goto Github PK

View Code? Open in Web Editor NEW
99.0 4.0 11.0 41.51 MB

Building web apps without manually creating widgets

Home Page: http://funix.io

License: MIT License

Python 46.81% JavaScript 12.38% HTML 0.33% CSS 0.07% TypeScript 40.41%
frontend app-builder data-science machine-learning

funix's Introduction

Funix.io
The laziest way to build AI/Data apps in Python

What is Funix?

  • Funix automatically turns an ordinary Python function or class definition into a web app, which is accessible in a browser via a shareable link.
  • Funix is the frontend/backend/fullstack engineer and the infra/ops engineer for AI/Data/Science solo stars like you.
  • Funix is the no-/low-code solution for those who write the most core and innovative code.
  • Funix is open-source and can be deployed on-premises.

Use cases

  • Startups: quickly build demos, iterate on MVPs, get user feedback and statistics, and pitch to investors or your mom.
  • Generative AI: instantly allow people to interact your GenAI model or idea.
  • STEM research: let people run your model and/or algorithm effortlessly.
  • Data browser: an interface to page through your data.
  • A/B test and data labeling: collect human labeling or preference.

Key advantages

  • Minimalist and out-of-box: Make no or little addition to your existing code.
  • CSS for Python: Widgets are generated automatically based on variable types according to a theme, rather than per-variable manually, for centralized, reusable, and consistent UI across apps.
  • Non-intrusive and declarative: As JSON strings, UI configurations are separated from the core logic. Your code can still be run, imported, and called as usual.
  • Seamlessly Pythonic: Map Python's native keywords to web app features, such as global for sessions, yield for streaming, and print for anything you wanna display in your app.
  • Apps, not demos: Out of the box support for access control, user tracking and Do-Not-Track options, multipage apps with data passing, session management, reactiveness, backend telemetry, etc.

Love Funix? Give us a star

Borrowed from AppFlowy

Hello, world!

  1. Install Funix (advanced installation options):

    pip install funix
  2. Save the code below to a file called hello.py:

    def hello(your_name: str) -> str:
        return f"Hello, {your_name}."
  3. Running this command

    $ funix hello.py
  4. The app will pop in a browser on http://localhost:3000:

    screenshots/hello.png

Getting started with Funix

The Zen of Funix is to generate widgets for function I/Os based on their types, instead of manually picking and customizing the widget for each I/O. Funix to Python is like CSS to HTML or style class to LaTeX. UI stuff is not intermingled with the core logic but defined separately. The type-to-widget mapping is controlled by a theme and new types can be defined on top of existing types.

The example below shows how common UI compoents are generated from four Python-native data types: str, bool, Literal and range, as well as types in popular packages such as ipywidgets.Password. The example below further maps default values to placeholders in the UI widgets. From this example, we can see that developers need to learn nothing about Funix or do nothing to their existing code before they can get an app.

import typing # Python native 

import ipywidgets  # popular UI library 

def input_widgets_basic(
    prompt: str = "Who is Oppenheimer?",
    advanced_features: bool = True,
    model: typing.Literal['GPT-3.5', 'GPT-4.0', 'Falcon-7B'] = 'GPT-4.0',
    max_token: range(100, 200, 20) = 140,
    openai_key: ipywidgets.Password = "1234556",
    )  -> str:
    pass

four input types

The example below shows how pandas.DataFrame and matplotlib.figure.Figure that AI/data developers cannot be more familiar with are mapped to tables and charts in an app.

import pandas, matplotlib.pyplot
from numpy import arange, log
from numpy.random import random

def table_and_plot(
    df: pandas.DataFrame = pandas.DataFrame({
        "a": arange(500) + random(500)/5,
        "b": random(500)-0.5 + log(arange(500)+1),
        "c": log(arange(500)+1) })
    ) -> matplotlib.figure.Figure:

    fig = matplotlib.pyplot.figure()
    matplotlib.pyplot.plot(df["a"], df["b"], 'b')
    matplotlib.pyplot.plot(df["a"], df["c"], 'r')

    return fig

table and plot screenshot

You can even continuously update a plot based on user input.

import funix 
import matplotlib.pyplot, matplotlib.figure
import numpy 

@funix.funix(
        autorun=True, 
)
def sine(omega: funix.hint.FloatSlider(0, 4, 0.1)) -> matplotlib.figure.Figure:
    fig = matplotlib.pyplot.figure()
    x = numpy.linspace(0, 20, 200)
    y = numpy.sin(x*omega)
    matplotlib.pyplot.plot(x, y, linewidth=5)
    return fig

continuous sine function

Out-of-box support for classes and OOP

Funix can turn each member method of a class into a page of an app. In two additional lines, the example below turn a class definition into a multi-page app with the OOP experience where the instantiation of an object of the class is done in a page corresponding to the constructor of the class and the member values of the object can be viewed and updated in other pages. No need to manually expose the member methods of the class.

from funix import funix_class

@funix_class()  
class A:
    def __init__(self, a: int):
        self.a = a
        return f"`self.a` has been initialized to {self.a}"

    def set(self, b: int) -> str:
        self.a = b
        return f"`self.a` has been updated to {self.a}"

    def get(self) -> str:
        return f"The value of `self.a` is {self.a}"

class demo app gif

A more advanced example of class is the wordle game. You can find the source code in examples/wordle.py. In less than 100 lines, you can build Wordle! The screenshot is as follows:

Wordle demo

The quickest way to build GenAI apps

You can wrap any Python function into a web app in Funix. For example, you can take the ChatGPT demo code from OpenAI's APi reference and wrap it into a str-to-str function:

from openai import OpenAI

def ChatGPT(prompt: str) -> str:    
    client = OpenAI()

    completion = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "user", "content": prompt}
        ]
    )
    return completion.choices[0].message.content

which is turned into an app by Funix like below:

screenshots/ChatGPT_lazy.png

Themes: the CSS for Python

Funix to Python is like CSS to HTML or macros to LaTeX. It separates the core logic and the UI. All UI stuff is centrally defined in a JSON-based theme to avoid the repetitiveness of individually configuring widgets and keep a consistent look across apps. Consequently, a data scientist or a machine learning engineer does not need to think about anything UI. Just leave it to the UI team and Funix.

Below is an example of a theme file. It defines the widget choices based on variable types and tweaks the props of UI components (currently only MUI ones are supported). Funix exposes frontend components and their props to developers, requiring them to know nothing about frontend. To know more about how to define and apply a theme, please refer to the Themes section in the reference manual.

{
  "name": "test_theme",
  "widgets": {    // dict, map types to widgets
    "str": "inputbox", // using Funix' widget shorthand
    "int": "slider[0,100,2]", // using Funix' widget shorthand
    "float": {
        "widget": "@mui/material/Slider", // using MUI's widget
        // https://mui.com/material-ui/api/slider
        "props": {
            "min": 0,
            "max": 100,
            "step": 0.1
        }
    }, 
    "Literal": "radio"
  },
}

To introduce a new data type, just declare a new Python class, and use a decorator to let Funix know. You can associate the type with a widget either on-the-fly (below) or via a theme file.

from funix import funix, new_funix_type

@new_funix_type(
    widget = {
        "widget": "@mui/material/TextField",
        "props": {
            "type": "password",
            "placeholder": "Enter a secret here."
        }
    }
)
class blackout(str):
    def print(self):
        return self + " is the message."

@funix()
def hoho(x: blackout = "Funix Rocks!") -> str:
    return x.print()

if __name__ == "__main__":
    print (hoho(blackout('Fun'))) 

Python-native to web-native

Funix repurposes some Python-native features to web features.

First, Funix won't let your docstring or print() function calls go to waste. They will appear in the input and output panels of the web app.

from funix import funix
@funix(
    print_to_web=True
)
def foo() -> None:
    """
    ## What a great app in Funix! 
    
    Funix won't let your docstring go to waste.
    """
    print("It supports **Markdown**.")
    print ("And <b>HTML</b>.")
    return None

Docstring and print to web

To stream text to the web, simply use yield:

import time

def stream() -> str:
    """
    This function is used to test the stream feature of Funix.
    """
    message = "We the People of the United States, in Order to form a more perfect Union, establish Justice, insure domestic Tranquility, provide for the common defence, promote the general Welfare, and secure the Blessings of Liberty to ourselves and our Posterity, do ordain and establish this Constitution for the United States of America."

    for i in range(len(message)):
        time.sleep(0.01)
        yield message[0:i]

Streamt text to web

Advanced features

History: Keep your call logs

Click to expand

A feature request that we hear from many users is that they do not use a Funix-converted app as a demo that they will use only a couple of time but as a real app that they will use again and again. In this case, they want to keep the call history of the app. Funix supports this feature by default. You can access the history of your app by clicking the history button on the top right corner of the app.

history sidebar

Multipage data passing and session/state management

Click to expand

A real app usually comes with multiple page, e.g., setting OpenAI token key in one page and then use the token in other GenAI pages. Functions in the same .py script will become different pages of one app. Any global variable can be used to pass data between functions. When you start Funix with the flag -t, it will further sessionize the global variables so that different users can have their own sessions. Below is a simple example and the corresponding GIF animation. In the GIF animation you can see that the value y differs in two browser sessions.

import funix

y = "The default value of y."

@funix.funix()
def set_y(x: str="123") -> str:
    global y
    y = x
    return "Y has been changed. Now check it in the get_y() page."


@funix.funix()
def get_y() -> str:
    return y

session

Prefill

Click to expand

A special case of passing data between functions is to use the return value of one function as the value in the widget of an argument of another function. This is called prefill. Funix support prefill by using a decorator attribute pre_fill. Below is an example and the corresponding GIF animation.

import funix

@funix.funix()
def first_action(x: int) -> int:
  return x - 1

@funix.funix()
def second_action(message: str) -> list[str]:
  return message.split(" ")

@funix.funix()
def third_action(x: int, y: int) -> dict:
  return {"x": x, "y": y}

@funix.funix(
  pre_fill={
    "a": first_action,
    "b": (second_action, -1),
    "c": (third_action, "x")
  }
)
def final_action(a: int, b: str, c: int) -> str:
    return f"{a} {b} {c}"

Secret: Access control

Click to expand

Note: This is not a strong way to protect your app.

To protect your code (e.g., OpenAI-related functions, which may result in some financial loss), you can use the secret option.

funix  my_app.py --secret  my_secret_token # use a token provided by you
# or 
funix  my_app.py --secret  True # randomly generate a token

The token will be printed on the Terminal. For example,

$ funix hello.py --secret True
Secrets:
---------------
Name: hello
Secret: 8c9f55d0eb74adbb3c87a445ea0ae92f
Link: http://127.0.0.1:3000/hello?secret=8c9f55d0eb74adbb3c87a445ea0ae92f

The token needs to be included in the URL or manually entered in order to execute the app.

secret

Gallery

More examples in Reference Manual or the ./examples folder.

  • ChatPaper (It's like the popular ChatPDF. But in Funix, only 70 lines of code needed.)
  • mFlux (synthetic biology)

ChatGPT, multi-turn

examples/AI/chatGPT_multi_turn.py 👈 Toggle me to show source code
  import os
  import IPython     
  from openai import OpenAI
  import funix

  client = OpenAI(api_key=os.environ.get("OPENAI_KEY"))

  messages  = []  # list of dicts, dict keys: role, content, system

  @funix.funix(
      disable=True,
  )
  def print_messages_html(messages):
      printout = ""
      for message in messages:
          if message["role"] == "user":
              align, name = "left", "You"
          elif message["role"] == "assistant":
              align, name = "right", "ChatGPT"
          printout += f'<div style="width: 100%; text-align: {align}"><b>{name}</b>: {message["content"]}</div>'
      return printout


  @funix.funix(
      direction="column-reverse",
  )
  def ChatGPT_multi_turn(current_message: str)  -> IPython.display.HTML:
      current_message = current_message.strip()
      messages.append({"role": "user", "content": current_message})
      completion = client.chat.completions.create(messages=messages,
      model='gpt-3.5-turbo',
      max_tokens=100)
      chatgpt_response = completion.choices[0].message.content
      messages.append({"role": "assistant", "content": chatgpt_response})

      # return print_messages_markdown(messages)
      return print_messages_html(messages)

Multiturn chat

Shortest Dall-E web app in Python

from funix import funix                      # add line one
from IPython.display import Image
from openai import OpenAI                    # pip install openai

import os
client = OpenAI(api_key=os.environ.get("OPENAI_KEY"))  


@funix()                                     # add line two
def dalle(prompt: str = "a cat") -> Image:
    response = client.images.generate(prompt=prompt)
    return response.data[0].url

Dalle demo

Bioinformatics: vector stripping

examples/bioinformatics/vector_strip.py 👈 Click to see source code

bioinfo vector strip

Multimedia inputs and outputs

examples/multimedia/rgb2gray.py 👈 Toggle me to show source code
import  io # Python's native 

import PIL # the Python Image Library
import IPython 
import funix 

@funix.funix(
    title="Convert color images to grayscale images",
)
def gray_it(image: funix.hint.BytesImage) -> IPython.display.Image:
    img = PIL.Image.open(io.BytesIO(image))
    gray = PIL.ImageOps.grayscale(img) 
    output = io.BytesIO()
    gray.save(output, format="PNG")
    return output.getvalue()

shipping example

Layout and EasyPost shipping

examples/layout_easypost_shipping.py 👈 Click me to see source code

shipping example

Usage

Installation

  • From PyPI (stable, lacking the latest feature)

    pip install funix
  • From GitHub (latest)

    pip install "git+https://github.com/TexteaInc/funix.git@develop" # Develop version
    pip install "git+https://github.com/TexteaInc/funix.git" # Stable version
  • Local development

    git clone -b develop https://github.com/TexteaInc/funix
    cd funix
    pip install -e .

    Add --prefix=~/.local if pip insists to install to system paths. See #24 and #23

Additional type support

  1. If you want to use git related features (use project from GitHub), install funix by:

    pip install funix[git]
  2. If you want to use ipython features, install funix by:

    pip install funix[ipython]
  3. If you need to have more than one feature, you can try installing them together:

    pip install funix[all]

Building the frontend

In the last two cases above, you will need to compile the frontend by yourself. Suppose you are in the funix folder. Then run the following commands:

  1. cd frontend
  2. yarn install (you need install Node.JS and Yarn)
  3. yarn funix:build

For debugging:

  1. yarn funix:test (Start development mode)
  2. funix xxx -p 8080 -F (Use 8080 port and no frontend mode)

Building the frontend with MUI Pro

Our table widget uses advanced features in MUI Pro. If you have a MUI Pro license, you can build the frontend with MUI Pro by following the steps below:

  1. Install Node.js and Yarn;
  2. Run export MUI_PRO_LICENSE_KEY=[MUI_KEY];
  3. Run yarn install && yarn funix:build to build the frontend;
  4. Done!

Command line options

Run the command below to see the command line options:

funix -h 

Call funix in Python

Besides starting Funix from the command line, you can also use a self-contained .py script:

import funix

@funix.funix()
def hello(your_name: str) -> str:
    return f"Hello, {your_name}."

if __name__ == "__main__":
    funix.run(__file__)

Exposing a Funix-converted app to the public

funix [module] --host [your_server_ip]

How to contribute

Funix is open-sourced under the MIT License. Community contribution is not only welcomed but desired. Feel free to fork and make a pull request when you are ready. You can also report bugs, suggest new features via the issue tracker or our Discord server.

Acknowledgment

Funix draws inspiration from FastAPI and Python-Fire: building software interfaces by inferring from function signatures containing type hints. We port this idea from the backend (FastAPI) or the terminal (Python-Fire) to the frontend. We also thank Streamlit, Gradio, PyWebIO, and Pynecone/Reflex. They inspired us. We are just too lazy to manually define widgets imperatively. Funix’s backend is implemented in Flask and the frontend in Material UI.

Contact

Forrest dot Bao @ Gmail

funix's People

Contributors

colerar avatar forrestbao avatar github-actions[bot] avatar himself65 avatar labelray avatar luochen1990 avatar nkwbtb avatar turx avatar yazawazi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

funix's Issues

Error with calc_default_add

Branch: dev

When I try to use PyDataFront to convert the function calc_default_add in examples/examples.py , I got the following error:

  File "/home/forrest/repos/PyDataFront/examples/examples.py", line 118, in <module>
    def calc_default_add(a: List[int], b: List[int], op: Optional[str] = "add") -> calc_return:
  File "/home/forrest/repos/PyDataFront/backend/pydatafront/decorator/__init__.py", line 168, in decorator
    json_schema_props[function_arg_name] = get_type_prop(function_arg_type_name)
  File "/home/forrest/repos/PyDataFront/backend/pydatafront/decorator/__init__.py", line 68, in get_type_prop
    typing_list_search_result = re.search(
  File "/usr/lib/python3.10/re.py", line 200, in search
    return _compile(pattern, flags).search(string)
TypeError: expected string or bytes-like object

`return` must follow with an expression

How to repeat:

Create a simple function decorated with Funix and save the code as test.py:

@funix.funix()
def test() -> int:
    return #line break
    1234 

Then execute funix test at the terminal. Then run the function in the browser.

Finally, this is the screenshot where 1234 (the expected result) is not displayed but a null.

image

The issue will be gone, if the last line is return 1234.

Two errors with new dev branch

I am at commit d68bc01 (HEAD -> dev, origin/dev)

Author: Ruixuan Tu <[email protected]>
Date:   Thu Aug 18 00:22:06 2022 -0500

    feat: "treat_as" default to be "config", "path" default to be function name
    example: remove Optional, empty decorator

But when I tried to convert examples/examples.py, I ran into two errors:

  File "/home/forrest/Textea/PyDataFront/examples/examples.py", line 116, in <module>
    def calc_literal(a: List[int], b: List[int], op: Literal["add", "minus"]) -> calc_return:
  File "/home/forrest/Textea/PyDataFront/backend/pydatafront/decorator/__init__.py", line 173, in decorator
    decorated_params[function_arg_name].update(function_arg_type_dict)
TypeError: 'NoneType' object is not iterable
    def calc_add(a: List[int], b: List[int]) -> calc_return:
  File "/home/forrest/Textea/PyDataFront/backend/pydatafront/decorator/__init__.py", line 222, in decorator
    get_wrapper(decorated_function_param_getter)
  File "/home/forrest/.local/lib/python3.10/site-packages/flask/scaffold.py", line 440, in decorator
    self.add_url_rule(rule, endpoint, f, **options)
  File "/home/forrest/.local/lib/python3.10/site-packages/flask/scaffold.py", line 56, in wrapper_func
    return f(self, *args, **kwargs)
  File "/home/forrest/.local/lib/python3.10/site-packages/flask/app.py", line 1090, in add_url_rule
    raise AssertionError(
AssertionError: View function mapping is overwriting an existing endpoint function: calc_add_param_getter

I am not sure whether this is due to that the example functions have not be refactored to be compatible with the latest PyDataFront decorator in the dev branch.

HTTPS URL passed to browser

When starting the frontend, the URL passed to the browser is httpS://127.0.0.1 instead of the HTTP one.

I have turned off the HTTPS-only mode on Firefox. My system's default browser is Firefox. Not sure whether this problem only happens on Firefox.

Now the workaround is to enter the HTTP URL manually in the browser's address bar.

Backend form check

Now we return 500 for most failed calls even if it is a form issue (e.g., missing fields, impossible values). We should check if the form is acceptable before we process it on the backend.

Function error response

Currently, if we have an error raised, the user will get a 500 without any meaningful error message or stack trace.

We should catch error raised and pass to frontend.

Cannot use TypedDict

Maybe I did something wrong, but I ran into the error to convert this Python code using PyDataFront:

$ python3 -m pydatafront bio.py 
Traceback (most recent call last):
  File "/usr/lib/python3.10/runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/home/forrest/Textea/PyDataFront/backend/pydatafront/__main__.py", line 11, in <module>
    importlib.import_module(parsed_sys_args.main_class)
  File "/usr/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 992, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/forrest/Textea/funcpool/bio.py", line 25, in <module>
    class telomere_check_return(typing.TypedDict):
  File "/home/forrest/Textea/PyDataFront/backend/pydatafront/decorator/__init__.py", line 33, in decorator
    function_signature = inspect.signature(function)
  File "/usr/lib/python3.10/inspect.py", line 3247, in signature
    return Signature.from_callable(obj, follow_wrapped=follow_wrapped,
  File "/usr/lib/python3.10/inspect.py", line 2995, in from_callable
    return _signature_from_callable(obj, sigcls=cls,
  File "/usr/lib/python3.10/inspect.py", line 2476, in _signature_from_callable
    sig = _get_signature_of(call)
  File "/usr/lib/python3.10/inspect.py", line 2529, in _signature_from_callable
    raise ValueError(
ValueError: no signature found for builtin type <class 'dict'>

I don't know why. Running bio.py has no error:

$ python3 bio.py
{'is_telomere': [False, True, False, True, True]}

I am using the latest version of PyDataFront:

commit d49a934250f95d88550994778fe4e6ec9494ed11 (HEAD -> main, origin/main, origin/HEAD)
Author: Ruixuan Tu <[email protected]>
Date:   Thu Jul 7 06:50:54 2022 -0500

    fix: use backend path for setuptools package search to avoid conflict

Editor with Basic Types

Now the editor only supports type as helperText and everything is in string. The frontend should support basic types supported by the backend.

How to acess `calc` function?

backend/functions.py has two functions, calc and test. But somehow I can only access test on the web front. What endpoint should I use to access the calc function's web front?

pip install writes to system path with `-e`

It is really weird that pip detects to switch to user installation (first line printed out below) because I did not sudo. But then the error says that it wants to write to /usr/lib.

I could do python3 -m pip install --user . but not $ python3 -m pip install --user -e . either.

I executed the command below from the root directory a cloned copy of PyDataFront.

$ python3 -m pip install -e .                                                                         
Defaulting to user installation because normal site-packages is not writeable
Looking in indexes: https://pypi.org/simple, https://pypi.ngc.nvidia.com
Obtaining file:///home/forrest/Textea/PyDataFront
  Installing build dependencies ... done
  Checking if build backend supports build_editable ... done
  Getting requirements to build wheel ... done
  Preparing metadata (pyproject.toml) ... done
Installing collected packages: pydatafront
  Attempting uninstall: pydatafront
    Found existing installation: pydatafront 0.0.0
    Uninstalling pydatafront-0.0.0:
      Successfully uninstalled pydatafront-0.0.0
  Running setup.py develop for pydatafront
    error: subprocess-exited-with-error
    
    × python setup.py develop did not run successfully.
    │ exit code: 1
    ╰─> [32 lines of output]
        running develop
        /usr/lib/python3/dist-packages/setuptools/command/easy_install.py:158: EasyInstallDeprecationWarning: easy_install command is deprecated. Use build and pip and other standards-based tools.
          warnings.warn(
        WARNING: The user site-packages directory is disabled.
        /usr/lib/python3/dist-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.
          warnings.warn(
        error: can't create or remove files in install directory
        
        The following error occurred while trying to add or remove files in the
        installation directory:
        
            [Errno 13] Permission denied: '/usr/local/lib/python3.10/dist-packages/test-easy-install-769272.write-test'
        
        The installation directory you specified (via --install-dir, --prefix, or
        the distutils default setting) was:
        
            /usr/local/lib/python3.10/dist-packages/
        
        Perhaps your account does not have write access to this directory?  If the
        installation directory is a system-owned directory, you may need to sign in
        as the administrator or "root" account.  If you do not have administrative
        access to this machine, you may wish to choose a different installation
        directory, preferably one that is listed in your PYTHONPATH environment
        variable.
        
        For information on other options, you may wish to consult the
        documentation at:
        
          https://setuptools.pypa.io/en/latest/deprecated/easy_install.html
        
        Please make the appropriate changes for your system and try again.
        
        [end of output]
    
    note: This error originates from a subprocess, and is likely not a problem with pip.
  WARNING: No metadata found in /home/forrest/.local/lib/python3.10/site-packages
  Rolling back uninstall of pydatafront                                                                                                           
  Moving to /home/forrest/.local/lib/python3.10/site-packages/pydatafront-0.0.0.dist-info/
   from /home/forrest/.local/lib/python3.10/site-packages/~ydatafront-0.0.0.dist-info
error: subprocess-exited-with-error

× python setup.py develop did not run successfully.
│ exit code: 1
╰─> [32 lines of output]
    running develop
    /usr/lib/python3/dist-packages/setuptools/command/easy_install.py:158: EasyInstallDeprecationWarning: easy_install command is deprecated. Use build and pip and other standards-based tools.
      warnings.warn(
    WARNING: The user site-packages directory is disabled.
    /usr/lib/python3/dist-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.
      warnings.warn(
    error: can't create or remove files in install directory
    
    The following error occurred while trying to add or remove files in the
    installation directory:
    
        [Errno 13] Permission denied: '/usr/local/lib/python3.10/dist-packages/test-easy-install-769272.write-test'
    
    The installation directory you specified (via --install-dir, --prefix, or
    the distutils default setting) was:
    
        /usr/local/lib/python3.10/dist-packages/
    
    Perhaps your account does not have write access to this directory?  If the
    installation directory is a system-owned directory, you may need to sign in
    as the administrator or "root" account.  If you do not have administrative
    access to this machine, you may wish to choose a different installation
    directory, preferably one that is listed in your PYTHONPATH environment
    variable.
    
    For information on other options, you may wish to consult the
    documentation at:
    
      https://setuptools.pypa.io/en/latest/deprecated/easy_install.html
    
    Please make the appropriate changes for your system and try again.
    
    [end of output]

note: This error originates from a subprocess, and is likely not a problem with pip.

Dependency issue at frontend

I am just testing the latest commit of PyDataFront on my local computer.
But I ran into the following error at the frontend:

$ npm install react-scripts
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: @textea/py-data-front@undefined
npm ERR! Found: [email protected]
npm ERR! node_modules/react
npm ERR!   react@"^18.2.0" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^17.0.0 || ^16.3.0 || ^15.5.4" from [email protected]
npm ERR! node_modules/react-json-view
npm ERR!   react-json-view@"^1.21.3" from the root project
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR! 
npm ERR! See /home/forrest/.npm/eresolve-report.txt for a full report.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/forrest/.npm/_logs/2022-07-07T02_43_18_172Z-debug-0.log

Please fix this ASAP.

unable to resolve dependency tree

Update: When I switch back to main branch, the problem is gone.

Branch: dev

I am setting up the frontend on a new computer and got the error below upon executing npm install react-scripts:

$ npm install react-scripts
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: @textea/py-data-front@undefined
npm ERR! Found: @types/[email protected]
npm ERR! node_modules/@types/react
npm ERR!   @types/react@"^18.0.15" from the root project
npm ERR!   peerOptional @types/react@"^17.0.0 || ^18.0.0" from @mui/[email protected]
npm ERR!   node_modules/@mui/icons-material
npm ERR!     @mui/icons-material@"^5.8.4" from the root project
npm ERR!     peerOptional @mui/icons-material@"^5.2.0" from @rjsf/[email protected]
npm ERR!     node_modules/@rjsf/material-ui
npm ERR!       @rjsf/material-ui@"^4.2.2" from the root project
npm ERR!   1 more (@mui/material)
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer @types/react@"^16.8.6 || ^17.0.0" from @rjsf/[email protected]
npm ERR! node_modules/@rjsf/material-ui
npm ERR!   @rjsf/material-ui@"^4.2.2" from the root project
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR! 
npm ERR! See /home/forrest/.npm/eresolve-report.txt for a full report.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/forrest/.npm/_logs/2022-08-05T16_54_32_177Z-debug-0.log

pip install `--user` fails

I just tried to install PyDataFront using pip from a local git clone of PyDataFront. and with the option --user. But after the installation, I cannot find pydatafront under ~/.local/lib/python3.10/site-packages. Importing pydatafront also gets the error No module named 'pydatafront'

My Python version is 3.10.

$ python3 -m pip install  --user . 
Looking in indexes: https://pypi.org/simple, https://pypi.ngc.nvidia.com
Processing /home/forrest/Textea/PyDataFront
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
  Preparing metadata (pyproject.toml) ... done
Building wheels for collected packages: pydatafront
  Building wheel for pydatafront (pyproject.toml) ... done
  Created wheel for pydatafront: filename=pydatafront-0.0.0-py3-none-any.whl size=1012 sha256=96499be8e950bea404d576d6a5c020bcc4626961b8f2dbc0aa66f94c3d1025ee
  Stored in directory: /tmp/pip-ephem-wheel-cache-skjduxoi/wheels/5e/1b/ed/7a32aaf1f6dcc2c36b88d2ba1d01ce11d4f196fe75ccf93a3f
Successfully built pydatafront
Installing collected packages: pydatafront
  Attempting uninstall: pydatafront
    Found existing installation: pydatafront 0.0.0
    Uninstalling pydatafront-0.0.0:
      Successfully uninstalled pydatafront-0.0.0
Successfully installed pydatafront-0.0.0

follow RESTful API

make GET /list to GET /
make GET /param/{id} to GET /{id}
make POST /call/{id} to POST /{id}

Spaces in Pasted Text Interpreted as Tabs

I am writing to bring to your attention an issue that I am currently experiencing while attempting to paste text into Funix. It appears that any spaces within the text are being interpreted as tabs, causing the cursor to jump to the next column. I would greatly appreciate any guidance or assistance you could provide in resolving this matter.

image

image

the `range()` type was improperly associated with sliders

To associate an int or float to a slider widget, we use range type like this:

max_token: range(100, 200, 20)=140

But this is misleading. In the notation above, max_token's type is hinted as range while actually we want it to be an int, as indicated by the default value 140 and the nature of the slider widget.

Hence, we should do something like this:

@dataclass
class IntRange:
    min: int
    max: int
    step: int

# Then
def foo(max_token: typing.Annotated[int, IntRange(-10, 5, 1)] = 6):
    pass

Multiple returns in the normal way

When a function has multiple returns, the normal way is to return them as tuples, implicitly or explicitly. For example,

def hello_world(your_name: str) -> (str, int):
	return f"Welcome to Funix, {your_name}.", len(your_name) 

Previously, Funix requires the multiple returns to be packed into a typedDict. This is very inconvenient as Funix users will have to define the typedDict first, increasing the amount of code to write. This is against the philosophy of Funix which is to reduce the amount of manual work.

Hence, please automatically handle multiple returns as tuples. Find the proper widgets based on their types.

App authentication

Existing solutions in Streamlit and Gradio

Without SSO

With SSO 

Future: widgets with parameters as tuples

Currently we are dealing with only one widget with parameters, which are sliders. In the future, it's every likely many other widgets will have parameters, such as textareas which have attributes like number of lines or width.

Hence, we need a solution to parametrize widgets.

Below is the brainstorming result of me and Yazawazi tonight.

@funix(
    widgets = {
         "x":("slider", 
               {
                "start" : 10,
                "end"   : 100,
                "step"   : 4
               }
            )
    }
)
def bar(x:int) -> int:
    return x=1

@funix(
    widgets = {"x": ["sheet", ("slider", {"start":10, "end":100, "step":4})]}
)
def f(x: List[int]) -> List[int]:
    return x

Easy association from a new type to a parametric or non-parametric widget

To-do: support usage like this below where the user can different types of strings, inherited from the str type and associated with different UI components.

import funix

@funix.new_funix_type(
    widget=[
        # "MUI-TextField", 
        "textarea",
        {"min":10, "max":20 ,"multiline":True} # props of TextField in MUI
    ]
) 
class long_str(str):
    pass

type="password"@funix.new_funix_type(
    widget=[
        # "MUI-TextField", 
        "input",
        {"type":"password"} # props of TextField in MUI
    ]
) # Coming soon! 
class password(str): # For this type of strings,  the content will be hidden unless revealed
    pass

@funix.funix()
def foo(x: long_str, y: password) -> str: 
    return f"{x} {y}"

Unknown type error with 3.10

I am using the latest commit (7a99068) from the main branch.

I am trying to host this simple function:

from funix import funix_yaml

@funix_yaml("""
widgets: 
        your_name: textarea
        your_age: slider
""")
def hello(your_name: str, your_age: int) -> (str, str):
    return f"Hello, {your_name}.", f"You are {your_age} now!"

But I got the error below in the terminal when trying to execute the hosted function from my browser:

127.0.0.1 - - [05/Feb/2023 22:01:57] "GET / HTTP/1.1" 304 -
127.0.0.1 - - [05/Feb/2023 22:01:57] "GET /static/css/main.e6c13ad2.css HTTP/1.1" 200 -
127.0.0.1 - - [05/Feb/2023 22:01:57] "GET /static/js/main.a97b7a7c.js HTTP/1.1" 304 -
127.0.0.1 - - [05/Feb/2023 22:01:58] "GET /config/backend HTTP/1.1" 200 -
127.0.0.1 - - [05/Feb/2023 22:01:58] "GET /list HTTP/1.1" 200 -
127.0.0.1 - - [05/Feb/2023 22:01:58] "GET /param/hello HTTP/1.1" 200 -
127.0.0.1 - - [05/Feb/2023 22:02:06] "OPTIONS /call/eab84f46-171b-4e06-8747-b5b6fcff2b39 HTTP/1.1" 200 -
[2023-02-05 22:02:06,437] ERROR in app: Exception on /call/eab84f46-171b-4e06-8747-b5b6fcff2b39 [POST]
Traceback (most recent call last):
  File "/home/forrest/.local/lib/python3.10/site-packages/flask/app.py", line 2073, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/forrest/.local/lib/python3.10/site-packages/flask/app.py", line 1520, in full_dispatch_request
    return self.finalize_request(rv)
  File "/home/forrest/.local/lib/python3.10/site-packages/flask/app.py", line 1539, in finalize_request
    response = self.make_response(rv)
  File "/home/forrest/.local/lib/python3.10/site-packages/flask/app.py", line 1730, in make_response
    raise TypeError(

Type incorrect implementation

For now, the response of types to the frontend is based on string. However, this will cause serious problems.

  1. We need to implement N-to-N parsers for both sides. For now, we only have the javascript and python sides. Supporting basic type has caused many bugs on both sides. WHAT IF we have more than two languages support?

  2. type checks based on string matches will become shit codes. For example:

https://github.com/TexteaInc/PyDataFront/blob/abaf7ee6929bee265391748fd63ee7577e10c028/backend/pydatafront/decorator/__init__.py#L41-L67

The regex is human unreadble code. For now, we have only str, int, and list, but the code already become unreadable.

  1. Making a wheel will cost extra time on defining the proposals

Our type checking should be based on JSON schema, one of the famous and stable formats for describing data structure.

https://json-schema.org/

And you can just write one code to build your own data form, https://react-jsonschema-form.readthedocs.io/en/latest/

Editor with `possible` and `example`

In the backend, we have defined the possible and example fields. The possible field overrides the example field, and it provides a list of acceptable values for this argument, while the example field only provides some sample values, and users can customize their own values for function calls.

design document incompatible

please review destination, output_type return_type schema callee in the document.

The document should follow the implementation whenever the time

Publish package to Pip

As the PyDataFront will not only be used in this package, but also in our NLP_backend, Roger's package...

syntax check in decorator

I was struggling with an error for about 30 minutes, until I found out that I made a small syntactical error. Specifically, for example, I used one integer rather than a list of integer:

         minimal_match_length={'treat_as':'confi',g 'example':6}

instead of

                minimal_match_length={'treat_as':'confi',g 'example':[6]}

I'd suggest we enforce some syntax check to throw an error in such cases. Of course, this is a not a priority. Just keep in mind.

Table input

Can we allow a user to enter a list of dictionaries or tuples in a table format? Like the one for entering many pairs of summaries and references in Huggingface/metrics/BlEURT .

image

Separate PyDataFront and the function(s) it converts

I wish the user experience can be like this:

python3 PydataFront{.py} my_project/app.py {--port_front=4000 --port_back=4001}

Then both the frontend and backend servers will be fired up. And visiting localhost:4000 will show the app.

Can we do this?

adding ploomber cloud as a deployment option

hi funix team!

I've been discussing with @forrestbao adding ploomber cloud as a deployment option to funix's docs. I went ahead and added it to our documentation: https://docs.cloud.ploomber.io/en/latest/apps/funix.html

also deployed a demo: https://gentle-heart-9135.ploomberapp.io (I tried to deploy the whole examples directory but got some errors)

the last step is to open a PR to the readme.md to link from this repo to ploomber cloud's docs, but I'm unsure where is the best place to put this. the only place that mentions deployment is this:

  • Apps, not demos: Funix comes with the following features so what you build is not just a demo in your company's Intranet but an app accessible to the entire world: history, access control, multipage apps, session management, and cloud deployment (via Funix-Deploy).

Looks like funix-deploy is still work in progress. so I'm unsure if it's best to keep it and add ploomber cloud there, or maybe add a "third party deployment services" section. Let me know what you think it's best!

Support `bool`

For a bool argument, there should be only True and False as acceptable values (or which is specified by possible which only accepts 1 or 2 values).

In the frontend, we can implement by checkbox, but readonly when there is only one possible value.

Update frontend

Currently, the frontend does not seem to be working. Before moving to the dev branch, we should update the frontend of stable (main) branch first.

Disable function selector if the only function is hosted

Currently, Funix's frontend creates a function selector for a project hosted by it, regardless of how many functions the project has.

If only one function is being hosted, please disable the selector and directly directs the user to the only function's web interface.

IP Issue on cloud deployment

I just tried to set up the demo on Google Compute Engine. But I ran into an issue related to the internal and external IP of an cloud VM.

Observations

I started my Funix server using

python3 -m funix examples_better -H 10.128.0.2

where 10.128.0.2 is the internal IP of the instance.

However, the Funix frontend in my browser (on my local computer) cannot access this IP.

I managed to get it to work by changing the backend IP in the settings in the Funix app in the browser to the external IP of the server. But this is very not out-of-the-box. We can expect common Joes to change this setting.

Proposed change

Add another option to Funix to configure the external IP and pass this to the browser.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.