Skip to content

Commit

Permalink
optimizing code
Browse files Browse the repository at this point in the history
  • Loading branch information
theonlyamos committed Jul 13, 2023
1 parent 455a083 commit 7cedc97
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 53 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ The Runit Command Line Interface (CLI) Tools can be used to test, manage, and de
You can install the Runit CLI using pip (Python package manager). Note that you will need to install [Python](https://python.org).
To download and install the runit CLI run the following command:
```shell
pip install runit
pip install python-runit
```
This will provide you with the globally accessible ```runit``` command.

### Install from source
```shell
git clone https://github.com/theonlyamos/runit.git
cd runit
pip install -e .
pip install .
```

## Usage
Expand Down
59 changes: 48 additions & 11 deletions runit/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
from threading import Thread

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from dotenv import load_dotenv, set_key, find_dotenv, dotenv_values
import json

from .languages import LanguageParser
from .modules import Account
Expand All @@ -22,16 +24,46 @@ def StartWebserver(project: Type[RunIt], host: str = '127.0.0.1', port: int = 50
app = FastAPI()
app.secret_key = "fdakfjlfdsaflfkjbasdoiefanckdareafasdfkowadbfakidfadfkj"

@app.api_route('/')
@app.api_route('/{func}')
@app.api_route('/{func}/')
async def serve(func: Optional[str], request: Request):
if request.method.lower() in ['get', 'post']:
func = func if func else 'index'
params = list(request.query_params.values())
return project.serve(func, params) \
if len(params) else project.serve(func)
return 'MethodNodAllowed'
@app.api_route('/', methods=["GET", "POST"])
@app.api_route('/{func}', methods=["GET", "POST"])
@app.api_route('/{func}/', methods=["GET", "POST"])
@app.api_route('/{func}/{output_format}', methods=["GET", "POST"])
@app.api_route('/{func}/{output_format}/', methods=["GET", "POST"])
async def serve(func: str = 'index', output_format: str = 'json', request: Request = None):
try:
response = {'status': True, 'data': {}}
result = ''

if request.method.lower() in ['get', 'post']:
parameters = request.query_params._dict
if 'content-type' in request.headers.keys() and request.headers['content-type'] == "application/json":
data = await request.json()
parameters = {**parameters, **data}

if 'output_format' in parameters.keys():
del parameters['output_format']

params = list(parameters.values()) if request else request
result = project.serve(func, params) \
if len(params) else project.serve(func)

if output_format == 'html':
return HTMLResponse(result)
else:
response['data'] = json.loads(result.replace("'", '"'))
return response

response['status': False]
response['message'] = 'Method Not Allowed'
return 'MethodNodAllowed' if output_format == 'html' \
else response
except json.decoder.JSONDecodeError:
return result
except:
response['status': False]
response['message'] = 'Not Found'
return HTMLResponse(RunIt.notfound()) if \
output_format == 'html' else response

try:
import uvicorn
Expand Down Expand Up @@ -86,6 +118,9 @@ def run_project(args):
global CONFIG_FILE
CONFIG_FILE = args.config

RunIt.DOCKER = args.docker
RunIt.KUBERNETES = args.kubernetes

if not CONFIG_FILE and not args.file:
raise FileNotFoundError
else:
Expand Down Expand Up @@ -249,7 +284,7 @@ def get_arguments():
new_parser = subparsers.add_parser('new', help='Create new project or function')
new_parser.add_argument("name", type=str, nargs="?",
help="Name of the new project")
new_parser.add_argument('-l', '--language', type=str, choices=['multi' 'python', 'php', 'javascript'],
new_parser.add_argument('-l', '--language', type=str, choices=['multi', 'python', 'php', 'javascript'],
help="Language of the new project", default="multi")
new_parser.add_argument('-r','--runtime', type=str,
help="Runtime of the project language. E.g: python3.11, node, php8")
Expand Down Expand Up @@ -316,6 +351,8 @@ def get_arguments():
clone_parser.add_argument('project_name', type=str, help='Name of project to clone')
clone_parser.set_defaults(func=clone)

parser.add_argument('--docker', action='store_true', help="Run program in docker container")
parser.add_argument('--kubernetes', action='store_true', help="Run program using kubernetes")
parser.add_argument('-f', '--function', default='index', type=str, nargs='?', help='Name of function to run')
parser.add_argument('--file', type=str, nargs='?', help='Name of file to run')
parser.add_argument('--shell', action='store_true', help='Run function only in shell')
Expand Down
2 changes: 1 addition & 1 deletion runit/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

load_dotenv()

VERSION = "0.2.2"
VERSION = "0.2.3"
CURRENT_PROJECT = ""
NOT_FOUND_FILE = '404.html'
DOT_RUNIT_IGNORE = '.runitignore'
Expand Down
13 changes: 9 additions & 4 deletions runit/languages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@ def __init__(self):
pass

@staticmethod
def detect_language(filename: str, runtime: str)-> Union[Python, PHP, Javascript]:
def detect_language(filename: str, runtime: str, is_file: bool = False, is_docker: bool = False, project_id: str = '')-> Union[Python, PHP, Javascript, Multi]:
if runtime == 'multi':
return Multi(filename, runtime)
return LanguageParser.EXT_TO_LANG[os.path.splitext(filename)[1].lower()](filename, runtime)
return Multi(filename, runtime, is_docker, is_file, project_id)
return LanguageParser.EXT_TO_LANG[os.path.splitext(filename)[1].lower()](
filename,
runtime,
is_docker=is_docker,
project_id=project_id
)

@staticmethod
def run_file(filename, runtime):
lang_parser = LanguageParser.detect_language(filename, runtime)
lang_parser = LanguageParser.detect_language(filename, runtime, True)
lang_parser.is_file = True
lang_parser.anon_function()
4 changes: 2 additions & 2 deletions runit/languages/javascript.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ class Javascript(Runtime):
LOADER = os.path.realpath(os.path.join(JS_TOOLS_DIR, 'loader.js'))
RUNNER = os.path.realpath(os.path.join(JS_TOOLS_DIR, 'runner.js'))

def __init__(self, filename, runtime):
super().__init__(filename, runtime)
def __init__(self, filename, runtime, is_file, is_docker, project_id):
super().__init__(filename, runtime, is_file, is_docker, project_id)
4 changes: 2 additions & 2 deletions runit/languages/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ class Multi(Runtime):
LOADER = os.path.realpath(os.path.join(MULTI_TOOLS_DIR, 'loader.py'))
RUNNER = os.path.realpath(os.path.join(MULTI_TOOLS_DIR, 'runner.py'))

def __init__(self, filename, runtime):
super().__init__(filename, runtime)
def __init__(self, filename, runtime, is_file, is_docker, project_id):
super().__init__(filename, runtime, is_file, is_docker, project_id)

def load_files(self)-> list[str]:
'''
Expand Down
4 changes: 2 additions & 2 deletions runit/languages/php.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ class PHP(Runtime):
LOADER = os.path.realpath(os.path.join(PHP_TOOLS_DIR, 'loader.php'))
RUNNER = os.path.realpath(os.path.join(PHP_TOOLS_DIR, 'runner.php'))

def __init__(self, filename, runtime):
super().__init__(filename, runtime)
def __init__(self, filename, runtime, is_file, is_docker, project_id):
super().__init__(filename, runtime, is_file, is_docker, project_id)
4 changes: 2 additions & 2 deletions runit/languages/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ class Python(Runtime):
LOADER = os.path.realpath(os.path.join(PY_TOOLS_DIR, 'loader.py'))
RUNNER = os.path.realpath(os.path.join(PY_TOOLS_DIR, 'runner.py'))

def __init__(self, filename, runtime):
super().__init__(filename, runtime)
def __init__(self, filename, runtime, is_file=False, is_docker=False, project_id=''):
super().__init__(filename, runtime, is_file, is_docker, project_id)
14 changes: 12 additions & 2 deletions runit/languages/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ class Runtime(object):
LOADER = ""
RUNNER = ""

def __init__(self, filename="", runtime="", is_file = False):
def __init__(self, filename="", runtime="", is_file = False, is_docker=False, project_id=''):
extension = os.path.splitext(filename)[1].lower()
self.filename = filename
self.runtime = runtime
self.is_file = is_file
self.is_docker = is_docker
self.project_id = project_id
self.module = os.path.realpath(os.path.join(os.curdir, self.filename))
self.functions = []
self.load_functions_from_supported_files()
Expand All @@ -34,7 +36,15 @@ def load_functions_from_supported_files(self):
'''

try:
result = check_output(f'{self.runtime} {self.LOADER} {self.module}', shell=True, encoding='utf-8')
if self.is_docker:
import docker
client = docker.from_env()

print(f"{self.project_id} {self.LOADER} {self.module}")
result = client.containers.run(self.project_id, f'{self.LOADER} {self.module}', auto_remove=True)
print(result)
else:
result = check_output(f'{self.runtime} {self.LOADER} {self.module}', shell=True, encoding='utf-8')
result = result.strip()
if self.runtime == 'php':
self.functions = result.split(',')
Expand Down
13 changes: 13 additions & 0 deletions runit/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,31 @@ charset-normalizer==3.1.0
click==8.1.3
colorama==0.4.6
dnspython==2.3.0
docker==6.1.3
exceptiongroup==1.1.1
fastapi==0.95.2
h11==0.14.0
idna==3.4
iniconfig==2.0.0
mysql==0.0.3
mysql-connector==2.2.9
mysql-connector-python==8.0.33
mysqlclient==2.1.1
packaging==23.1
passlib==1.7.4
pluggy==1.2.0
protobuf==3.20.3
pydantic==1.10.8
pymongo==4.3.3
pytest==7.4.0
python-dotenv==1.0.0
python-multipart==0.0.6
pywin32==306
requests==2.31.0
sniffio==1.3.0
starlette==0.27.0
tomli==2.0.1
typing_extensions==4.6.2
urllib3==2.0.2
uvicorn==0.22.0
websocket-client==1.6.1
93 changes: 70 additions & 23 deletions runit/runit.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import json
from zipfile import ZipFile
from io import TextIOWrapper
from typing import Optional, Union
from typing import Optional, Union, Callable
from threading import Thread

from flask import request
Expand All @@ -18,6 +18,9 @@

load_dotenv()
class RunIt:
DOCKER = False
KUBERNETES = False

def __init__(self, name, _id="", version="0.0.1", description="", homepage="",
language="", runtime="", start_file="", private=False, author={}, is_file: bool = False):
global STARTER_FILES
Expand Down Expand Up @@ -97,6 +100,69 @@ def set_project_name(name: str =None)-> str:
return name
except KeyboardInterrupt:
sys.exit(1)

@staticmethod
def notfound():
global TEMPLATES_FOLDER
global NOT_FOUND_FILE

with open(os.path.join(os.curdir, TEMPLATES_FOLDER, NOT_FOUND_FILE),'rt') as file:
return file.read()

@staticmethod
def extract_project(filepath):
directory, filename = os.path.split(filepath)

with ZipFile(filepath, 'r') as file:
file.extractall(directory)
os.unlink(filepath)

def get_functions(self)->list:
lang_parser = LanguageParser.detect_language(self.start_file, self.runtime, RunIt.DOCKER, self._id)
return lang_parser.list_functions()

@staticmethod
def dockerize(project_path: str):
'''
Create docker image for project
@params project_path
@return None
'''
try:
import docker

client = docker.from_env()

project_id = os.path.split(project_path)[-1]
print(f"[-] Building image for {project_id}")

image = client.images.build(
path=project_path,
tag=project_id,
)
print(image)

except ImportError:
print('[!] Docker package not installed.')
print('[-] Use `pip install docker` to install the package.')
sys.exit(1)
except Exception as e:
print(str(e))
sys.exit(1)

@staticmethod
def run_background_task(target: Callable, *args: list):
'''
Runction for running threads
@param target Function to run
@param args Arguments for the function
@return None
'''
bg_thread = Thread(target=target, args=(args))
bg_thread.start()
bg_thread.join()

@classmethod
def is_private(cls, project_id: str, projects_folder: str = PROJECTS_DIR)-> bool:
Expand Down Expand Up @@ -129,7 +195,7 @@ def start(cls, project_id: str, func='index', projects_folder: str = PROJECTS_DI

start_file = project.start_file

lang_parser = LanguageParser.detect_language(start_file, os.getenv('RUNTIME_'+project.language.upper(), project.runtime))
lang_parser = LanguageParser.detect_language(start_file, os.getenv('RUNTIME_'+project.language.upper(), project.runtime), RunIt.DOCKER, project_id)
lang_parser.current_func = func
try:
return getattr(lang_parser, func)(*args_list)
Expand All @@ -142,26 +208,6 @@ def start(cls, project_id: str, func='index', projects_folder: str = PROJECTS_DI
except TypeError as e:
return str(e)

@staticmethod
def notfound():
global TEMPLATES_FOLDER
global NOT_FOUND_FILE

with open(os.path.join(os.curdir, TEMPLATES_FOLDER, NOT_FOUND_FILE),'rt') as file:
return file.read()

@staticmethod
def extract_project(filepath):
directory, filename = os.path.split(filepath)

with ZipFile(filepath, 'r') as file:
file.extractall(directory)
os.unlink(filepath)

def get_functions(self)->list:
lang_parser = LanguageParser.detect_language(self.start_file, self.runtime)
return lang_parser.list_functions()

def serve(self, func: str = 'index', args: Optional[Union[dict,list]]=None, filename: str = ''):
global NOT_FOUND_FILE

Expand Down Expand Up @@ -227,7 +273,7 @@ def compress(self):
# os.chdir(CURRENT_PROJECT_DIR)
zipname = f'{self.name}.zip'

exclude_list = [zipname, 'account.db', '.git', '.venv', 'venv']
exclude_list = [zipname, 'account.db', '.git', '.venv', 'venv', 'Dockerfile']

if '.runitignore' in os.listdir():
with open('.runitignore', 'rt') as file:
Expand Down Expand Up @@ -362,4 +408,5 @@ def install_python_packages(self):
print(str(e))
print("[!] Couldn't install packages")
print("[~] Try again manually later.")


Loading

0 comments on commit 7cedc97

Please sign in to comment.