Skip to content

Commit

Permalink
Merge pull request #18 from fredrik-corneliusson/17-add-simple-raw-ap…
Browse files Browse the repository at this point in the history
…i-to-make-commands-easier-to-call-from-scripts

17 add simple raw api to make commands easier to call from scripts
  • Loading branch information
fredrik-corneliusson authored Jun 12, 2024
2 parents a54244f + 7b9ddfb commit 855ac42
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 14 deletions.
2 changes: 2 additions & 0 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Running tests and code linting::
Creating a release
==================

**NOTE: this release instruction is deprecated, click-web now uses github to make releases.**

* Checkout the ``master`` branch.
* Pull the latest changes from ``origin``.
* Increment the version number (in setup.py).
Expand Down
66 changes: 60 additions & 6 deletions click_web/resources/cmd_exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,51 @@


class Executor:
RAW_CMD_PATH = "_rawcmd"

def __init__(self):
self.returncode = None
self._command_line = None

def exec(self, command_path):
global logger
logger = click_web.logger
if command_path == self.RAW_CMD_PATH:
command = request.data.decode('utf-8')
return self._exec_raw(command)
else:
return self._exec(command_path)

def _exec_raw(self, command):
"""
This is for providing an API that is easy to call from scripts etc.
Execute the command as provided in the post data and stream the text output from it as response
Note: This does not support posting of files and or generating output links to files.
Also, it does not obfuscate secrets in the logs at the moment.
:param command: the command line after the root command.
For example:
print-lines 5 --delay 1 --message Red
"""
self._command_line = CommandLineRaw(click_web.script_file, command)

def generate():
try:
yield from self._run_script_and_generate_stream()
except Exception as e:
# exited prematurely, show the error to user
yield f"\nERROR: Got exception when reading output from script: {type(e)}\n"
yield traceback.format_exc()
raise

return Response(generate(), content_type='text/plain; charset=utf-8')

def _exec(self, command_path):
"""
Execute the command and stream the output from it as response
:param command_path:
"""
global logger
logger = click_web.logger

root_command, *commands = command_path.split('/')
self._command_line = CommandLine(click_web.script_file, commands)
self._command_line = CommandLineForm(click_web.script_file, commands)

def _generate_output():
yield self._create_cmd_header(commands)
Expand Down Expand Up @@ -140,7 +171,30 @@ def _get_download_link(field_info):
return f'<a href="{uri}">{field_info.link_name}</a>'


class CommandLine:
class CommandLineRaw:
def __init__(self, script_file_path: str, command):
self._parts = []
self.append(_get_python_interpreter())
self.append(script_file_path)
self.append(command)

def append(self, part: str, secret: bool = False):
self._parts.append(part)

def get_commandline(self, obfuscate: bool = False) -> str:
"""
Return command line as string.
"""
return " ".join(self._parts)

def get_download_field_infos(self):
return []

def after_script_executed(self):
pass


class CommandLineForm:
def __init__(self, script_file_path: str, commands: List[str]):
self._parts: List[CmdPart] = list()
self.append(_get_python_interpreter())
Expand Down Expand Up @@ -196,7 +250,7 @@ def __str__(self):

class FormToCommandLineBuilder:

def __init__(self, command_line: CommandLine):
def __init__(self, command_line: CommandLineForm):
self.command_line = command_line
field_infos = [FieldInfo.factory(key) for key in list(request.form.keys()) + list(request.files.keys())]
# important to sort them so they will be in expected order on command line
Expand Down
14 changes: 8 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

requirements = [
'click>=8.0',
'Flask>=1.1',
'Jinja2>=2.11',
'Flask>=2.3.2',
'Jinja2>=3.1.3',
'flask_httpauth>=3.2.4'
]

Expand All @@ -25,7 +25,7 @@

setup(
name='click-web',
version='0.8.3',
version='0.8.4',
url='https://github.com/fredrik-corneliusson/click-web',
author='Fredrik Corneliusson',
author_email='[email protected]',
Expand All @@ -35,7 +35,7 @@
include_package_data=True,
packages=find_packages(),
zip_safe=False,
python_requires='>=3.6',
python_requires='>=3.8',
install_requires=requirements,
dependency_links=[],
extras_require={
Expand All @@ -52,7 +52,9 @@
'Topic :: Utilities',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
]
)
24 changes: 24 additions & 0 deletions tests/test_flask_post.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import pytest
from werkzeug.datastructures import MultiDict

from click_web.resources.cmd_exec import Executor


def test_exec_command(app, client):
resp = client.post('/cli/simple-no-params-command')
Expand Down Expand Up @@ -114,3 +116,25 @@ def test_exec_with_variadic_args(form_data, expected_msg, app, client):
assert resp.status_code == 200
print(resp.data)
assert expected_msg in resp.data


@pytest.mark.parametrize(
'data, expected_msg',
[
('command-with-option-and-argument',
b'Ran command with option: option_value argument 10'),
("command-with-option-and-argument 11",
b'Ran command with option: option_value argument 11'),
("--debug command-with-option-and-argument --an-option ABC 12",
b'Ran command with option: ABC argument 12')
])
def test_rawcmd_exec_with_arg_and_default_opt(data, expected_msg, app, client):
"""
Test of the giving raw command line.
"""
resp = client.post('/' + Executor.RAW_CMD_PATH, data=data)
assert resp.status_code == 200
assert resp.content_type == 'text/plain; charset=utf-8'
assert expected_msg in resp.data
4 changes: 2 additions & 2 deletions tests/test_request_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import flask
import pytest

from click_web.resources.cmd_exec import CommandLine
from click_web.resources.cmd_exec import CommandLineForm

app = flask.Flask(__name__)

Expand All @@ -30,5 +30,5 @@
])
def test_form_post_to_commandline_arguments(data, expected):
with app.test_request_context('/command', data=data):
command_line = CommandLine('/some/script.py', commands=["command2"])
command_line = CommandLineForm('/some/script.py', commands=["command2"])
assert command_line.get_commandline()[2:] == expected

0 comments on commit 855ac42

Please sign in to comment.