├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── LICENSE.MD
├── README.md
├── lumi
├── __init__.py
├── api.py
├── enums.py
├── helpers.py
└── server.py
├── requirements-dev.txt
├── requirements.txt
└── setup.py
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - main
5 | merge_group:
6 | branches:
7 | - main
8 |
9 | jobs:
10 | contrib-readme-job:
11 | runs-on: ubuntu-latest
12 | name: A job to automate contrib in readme
13 | steps:
14 | - name: Contribute List
15 | uses: akhilmhdh/contributors-readme-action@v2.3.6
16 | env:
17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 | test.py
54 |
55 | # Translations
56 | *.mo
57 | *.pot
58 |
59 | # Django stuff:
60 | *.log
61 | local_settings.py
62 | db.sqlite3
63 | db.sqlite3-journal
64 |
65 | # Flask stuff:
66 | instance/
67 | .webassets-cache
68 |
69 | # Scrapy stuff:
70 | .scrapy
71 |
72 | # Sphinx documentation
73 | docs/_build/
74 |
75 | # PyBuilder
76 | .pybuilder/
77 | target/
78 |
79 | # Jupyter Notebook
80 | .ipynb_checkpoints
81 |
82 | # IPython
83 | profile_default/
84 | ipython_config.py
85 |
86 | # pyenv
87 | # For a library or package, you might want to ignore these files since the code is
88 | # intended to run in multiple environments; otherwise, check them in:
89 | # .python-version
90 |
91 | # pipenv
92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
95 | # install all needed dependencies.
96 | #Pipfile.lock
97 |
98 | # poetry
99 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
100 | # This is especially recommended for binary packages to ensure reproducibility, and is more
101 | # commonly ignored for libraries.
102 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
103 | #poetry.lock
104 |
105 | # pdm
106 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
107 | #pdm.lock
108 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
109 | # in version control.
110 | # https://pdm.fming.dev/#use-with-ide
111 | .pdm.toml
112 |
113 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
114 | __pypackages__/
115 |
116 | # Celery stuff
117 | celerybeat-schedule
118 | celerybeat.pid
119 |
120 | # SageMath parsed files
121 | *.sage.py
122 |
123 | # Environments
124 | .env
125 | .venv
126 | env/
127 | venv/
128 | ENV/
129 | env.bak/
130 | venv.bak/
131 |
132 | # Spyder project settings
133 | .spyderproject
134 | .spyproject
135 |
136 | # Rope project settings
137 | .ropeproject
138 |
139 | # mkdocs documentation
140 | /site
141 |
142 | # mypy
143 | .mypy_cache/
144 | .dmypy.json
145 | dmypy.json
146 |
147 | # Pyre type checker
148 | .pyre/
149 |
150 | # pytype static type analyzer
151 | .pytype/
152 |
153 | # Cython debug symbols
154 | cython_debug/
155 |
156 | # PyCharm
157 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
158 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
159 | # and can be added to the global gitignore or merged into this file. For a more nuclear
160 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
161 | .idea/
162 |
163 | test.py
--------------------------------------------------------------------------------
/LICENSE.MD:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2022, Tanmoy Sarkar
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Lumi 💧
2 |
3 |
4 |
5 | Lumi is a nano framework to convert your python functions into a REST API without any extra headache.
6 |
7 | * This library is created by taking the concept of **RPC** and blended with **REST API** specs.
8 | * We need to just register the function and it will be available as a REST API.
9 | * Web-server written with **Gunicorn**
10 | * Local development server provided for rapid development and prototyping.
11 |
12 | ## Installation
13 |
14 | ```bash
15 | pip install lumi
16 | ```
17 |
18 | ## Function <--> API mapping
19 | 
20 |
21 |
22 | ## How to use 🤔
23 |
24 | Let's create a simple function to add two numbers.
25 |
26 | ```python
27 | def add(a, b):
28 | return a + b
29 |
30 | def subtract(a, b):
31 | return a - b
32 | ```
33 |
34 | Now, we want to expose this function as a REST API. We can do this by registering the function with Lumi.
35 |
36 | ```python
37 | # app.py
38 |
39 | from lumi import Lumi
40 |
41 | app = Lumi()
42 |
43 | app.register(add) # Registering the function
44 | app.register(subtract)
45 |
46 | app.runServer(host="127.0.0.1", port=8080)
47 | ```
48 |
49 | Noice 🎉🎉 API has been generated
50 |
51 | Run the sever by
52 | ```
53 | python app.py
54 | ```
55 | You are going to see this in your terminal
56 | ```
57 | [2022-11-24 17:32:08 +0530] [10490] [INFO] Starting gunicorn 20.1.0
58 | [2022-11-24 17:32:08 +0530] [10490] [INFO] Listening at: http://127.0.0.1:8080 (10490)
59 | [2022-11-24 17:32:08 +0530] [10490] [INFO] Using worker: sync
60 | [2022-11-24 17:32:08 +0530] [10492] [INFO] Booting worker with pid: 10492
61 | ...
62 | ...
63 | [2022-11-24 17:32:08 +0530] [10500] [INFO] Booting worker with pid: 10500
64 | ```
65 |
66 | Congratulations 👏. Our Server is online.
67 |
68 |
69 | The above code will generate a REST API with the following details.
70 |
71 | - Endpoint : `127.0.0.1:8080`
72 | - Route : `/add`
73 | - Method : `POST`
74 | - Sample Request Body : `{"a": 1, "b": 2}`
75 |
76 | Let's run the API and test it.
77 |
78 | ```curl
79 | curl -X POST -H "Content-Type: application/json" -d '{"a": 1, "b": 2}' http://127.0.0.1:8080/add
80 | ```
81 |
82 | Output
83 |
84 | ```json
85 | {
86 | "exit_code": 0,
87 | "status_code": 200,
88 | "result": 3,
89 | "error": ""
90 | }
91 | ```
92 |
93 | ## Custom Routing
94 | Now you may think, the function name will be always same as the route. But, you can change the route by passing the route parameter.
95 |
96 | ```python
97 | app.register(add, route="/addition")
98 | ```
99 | ## Custom Request Method
100 | By default, the request method is `POST`. But, you can change it by passing the method parameter. Currently, it supports `GET`, `POST`, `PUT` and `PATCH` methods.
101 |
102 | ```python
103 | from lumi import Lumi, RequestMethod
104 |
105 | app = Lumi()
106 |
107 | def add(a, b):
108 | return a+b
109 |
110 | # Default : Register function for POST method
111 | app.register(add)
112 | # Register function for GET method
113 | app.register(add, request_method=RequestMethod.GET)
114 | # Register function for POST method
115 | app.register(add, request_method=RequestMethod.POST)
116 | # Register function for PUT method
117 | app.register(add, request_method=RequestMethod.PUT)
118 | # Register function for PATCH method
119 | app.register(add, request_method=RequestMethod.PATCH)
120 |
121 | app.runServer()
122 | ```
123 |
124 | 🟡 **Pay attention before using GET request :** If you are using `GET` method
125 | - You need to pass the parameters in the query string, as `GET` dont support request body.
126 | - All those arguments, that will be passed to function will be in **String** format. So take care to convert them to the desired type in your function.
127 |
128 |
129 | ## Send File
130 | Send file to user by returning the file object.
131 |
132 | ```python
133 | from lumi import Lumi, RequestMethod
134 | app = Lumi()
135 |
136 | def download_file():
137 | return open("file.txt", "rb") # Return file object
138 |
139 | app.register(download_file)
140 | ```
141 |
142 | ## Debug Mode
143 | By default, the debug mode is `True`. But, you can change it by passing the debug parameter.
144 |
145 | ```python
146 | # app.py
147 |
148 | from lumi import Lumi
149 |
150 | app = Lumi(debug=False)
151 | ...
152 | ```
153 |
154 | ## Status Codes
155 |
156 | | Status Code | Description |
157 | | --- | --- |
158 | | 200 | Request successfully executed and No Error happened during function execution |
159 | | 500 | Request was received but there was an error during function execution |
160 | | 400 | Bad Request (Possible Reason - The required parameters for the function has not provided) |
161 | | 405 | Method Not Allowed (Lumi only supports **POST** request) |
162 | | 404 | The route has no function associated with that |
163 |
164 |
165 | ## Exit Codes
166 | | Exit Code | Description |
167 | | --- | --- |
168 | | 0 | No Error |
169 | | 1 | Error |
170 |
171 | > Note : If the function has some error , you can expect the exit code to be 1 and the error message in the response.
172 |
173 | ## Task Lists
174 | - [x] Base System
175 | - [x] Add support for default parameters that is provided in the function
176 | - [x] Debug mode and logging support
177 | - [x] Make available GET request for the function
178 | - [x] Provide option to override POST with PUT if the user wants
179 | - [x] Add support to send file directly to user
180 | - [ ] Add support to serve files through a public folder [Customizable]
181 | - [ ] Add suport for middleware integration
182 | - [ ] Support nested routing of urls
183 | - [ ] For local development, create an file observer that can automatically reload the server when the file is changed.
184 | - [ ] Add support for object serialization and deserialization based on argument types of function
185 |
186 | ## Contributing
187 |
188 | Contributions are always welcome!
189 | ## Our community
190 |
191 |
192 |
195 |
196 | 198 | Tanmoy Sarkar 199 | 200 | |
201 |
202 |
203 | 205 | Amir M. Ghanem 206 | 207 | |
208 |
209 |
210 | 212 | Matheus Felipe 213 | 214 | |
215 |
216 |
217 | 219 | 0xflotus 220 | 221 | |