Compare commits
10 commits
960dc76302
...
da271dcd52
Author | SHA1 | Date | |
---|---|---|---|
da271dcd52 | |||
4bc9d7bd9b | |||
0c69977e61 | |||
63e8059e94 | |||
b589dd70f3 | |||
c0d0e11591 | |||
de5549e43c | |||
f7fcde3100 | |||
cef9c04dc3 | |||
4d11bc4917 |
|
@ -1,2 +1,2 @@
|
|||
recursive-include warp/templates *
|
||||
recursive-include warp/static *
|
||||
recursive-include warped/templates *
|
||||
recursive-include warped/static *
|
||||
|
|
22
README.md
|
@ -1,6 +1,6 @@
|
|||
# WARP - a Webbased frontend for ARgparser in Python
|
||||
# WARPED - a Webbased frontend for ARgparser in Python
|
||||
|
||||
`warp` can be used to execute single Python file and Python modules. It captures
|
||||
`warped` can be used to execute single Python file and Python modules. It captures
|
||||
calls to the `argparse` module of Python and renders a web GUI based on the
|
||||
options and arguments defined. It also displays the output of the program inside
|
||||
the web GUI and allows you to stop, pause and resume the program, as well as
|
||||
|
@ -11,14 +11,14 @@ downloading the output.
|
|||
Either get a stable version from PyPI, or install the current version from git
|
||||
|
||||
# Installation from PyPI
|
||||
pip install warp
|
||||
pip install warped
|
||||
|
||||
# Installation from git
|
||||
pip install git+https://git.k-fortytwo.de/christofsteel/warp/
|
||||
pip install git+https://git.k-fortytwo.de/christofsteel/warped/
|
||||
|
||||
## Usage
|
||||
|
||||
warp [-h] [--port PORT] [--host HOST] [--module] file
|
||||
warped [-h] [--port PORT] [--host HOST] [--module] file
|
||||
|
||||
a Webbased frontend for ARgparse in Python
|
||||
|
||||
|
@ -33,25 +33,25 @@ Either get a stable version from PyPI, or install the current version from git
|
|||
|
||||
## Sample
|
||||
|
||||
To test the capabilities of `warp` an example module was included. You can run
|
||||
To test the capabilities of `warped` an example module was included. You can run
|
||||
it like this:
|
||||
|
||||
warp -m warp.samples.hooked
|
||||
warped -m warped.samples.hooked
|
||||
|
||||
Since `warp` also makes use of the argparse module, `warp` itself can be //warped//.
|
||||
Since `warped` also makes use of the argparse module, `warped` itself can be //warped//.
|
||||
|
||||
warp -m warp.hook
|
||||
warped -m warped.hook
|
||||
|
||||
## How does it work?
|
||||
|
||||
When `warp` is executed, it starts a flask webserver. The javascript of the
|
||||
When `warped` is executed, it starts a flask webserver. The javascript of the
|
||||
website reads the `/arguments` resource of the server, where the configuration
|
||||
of the argparser returned. In a seperate process the given program is executed
|
||||
using the `runpy` library, redirecting `sys.stdin` and `sys.stdout` to a
|
||||
`multiprocessing.Queue`, which can be read by the warp process to display it
|
||||
via the web GUI.
|
||||
|
||||
Additionally, `warp` adds an entry for `argparse` in the `sys.modules` list. Python
|
||||
Additionally, `warped` adds an entry for `argparse` in the `sys.modules` list. Python
|
||||
looks first looks at this list, everytime a module is imported, to avoid
|
||||
importing a module multiple times. This custom `argparse` module behaves similar
|
||||
to the original `argparse` module. In fact with the exception of the
|
||||
|
|
77
README.rst
Normal file
|
@ -0,0 +1,77 @@
|
|||
WARPED - a Webbased frontend for ARgparser in Python
|
||||
====================================================
|
||||
|
||||
``warped`` can be used to execute single Python file and Python modules.
|
||||
It captures calls to the ``argparse`` module of Python and renders a web
|
||||
GUI based on the options and arguments defined. It also displays the
|
||||
output of the program inside the web GUI and allows you to stop, pause
|
||||
and resume the program, as well as downloading the output.
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
Either get a stable version from PyPI, or install the current version
|
||||
from git
|
||||
|
||||
::
|
||||
|
||||
# Installation from PyPI
|
||||
pip install warped
|
||||
|
||||
# Installation from git
|
||||
pip install git+https://git.k-fortytwo.de/christofsteel/warped/
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
::
|
||||
|
||||
warped [-h] [--port PORT] [--host HOST] [--module] file
|
||||
|
||||
a Webbased frontend for ARgparse in Python
|
||||
|
||||
positional arguments:
|
||||
file File to run
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--port PORT, -p PORT The port to listen on (default 5000)
|
||||
--host HOST The host to bind to (default 0.0.0.0)
|
||||
--module, -m If set, loads a module instead of a file
|
||||
|
||||
Sample
|
||||
------
|
||||
|
||||
To test the capabilities of ``warped`` an example module was included.
|
||||
You can run it like this:
|
||||
|
||||
::
|
||||
|
||||
warped -m warped.samples.hooked
|
||||
|
||||
Since ``warped`` also makes use of the argparse module, ``warped``
|
||||
itself can be //warped//.
|
||||
|
||||
::
|
||||
|
||||
warped -m warped.hook
|
||||
|
||||
How does it work?
|
||||
-----------------
|
||||
|
||||
When ``warped`` is executed, it starts a flask webserver. The javascript
|
||||
of the website reads the ``/arguments`` resource of the server, where
|
||||
the configuration of the argparser returned. In a seperate process the
|
||||
given program is executed using the ``runpy`` library, redirecting
|
||||
``sys.stdin`` and ``sys.stdout`` to a ``multiprocessing.Queue``, which
|
||||
can be read by the warp process to display it via the web GUI.
|
||||
|
||||
Additionally, ``warped`` adds an entry for ``argparse`` in the
|
||||
``sys.modules`` list. Python looks first looks at this list, everytime a
|
||||
module is imported, to avoid importing a module multiple times. This
|
||||
custom ``argparse`` module behaves similar to the original ``argparse``
|
||||
module. In fact with the exception of the
|
||||
``ArgumentParser.parse_args()`` method, it works exactly like the
|
||||
original. Once the program calls the ``parse_args()`` method, it blocks
|
||||
and waits for user interaction via the web GUI. Once the user submits
|
||||
the data, the process continues.
|
2
setup.cfg
Normal file
|
@ -0,0 +1,2 @@
|
|||
[metadata]
|
||||
description-file = README.md
|
9
setup.py
|
@ -1,17 +1,20 @@
|
|||
from setuptools import setup, find_packages
|
||||
|
||||
version = "0.1.2"
|
||||
|
||||
setup(
|
||||
name='warp',
|
||||
version='0.0.1',
|
||||
name='warped',
|
||||
version=version,
|
||||
packages=find_packages(),
|
||||
url='https://git.k-fortytwo.de/christofsteel/warp',
|
||||
download_url = 'https://git.k-fortytwo.de/christofsteel/warped/archive/%s.tar.gz' % version,
|
||||
license='MIT',
|
||||
author='Christoph Stahl',
|
||||
author_email='christoph.stahl@uni-dortmund.de',
|
||||
description='A webbased wrapper for the argument parser in Python',
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
"warp = warp.hook:main"
|
||||
"warped = warped.hook:main"
|
||||
]
|
||||
},
|
||||
include_package_data = True,
|
||||
|
|
|
@ -43,6 +43,7 @@ class Action():
|
|||
internal_dict['dest'] = self.dest
|
||||
internal_dict['desc'] = self.desc
|
||||
internal_dict['checked'] = self.on_none if self.on_none is True or self.on_none is False else None
|
||||
internal_dict['default'] = self.on_none if type(self.on_none) == str else None
|
||||
internal_dict['optional'] = self.optional
|
||||
internal_dict['is_const'] = self.const is not None
|
||||
internal_dict['type'] = self.type_function.__name__
|
||||
|
@ -89,7 +90,7 @@ class ActionContainer():
|
|||
internal_dict = {}
|
||||
internal_dict['uuid'] = str(self.uuid)
|
||||
internal_dict['actions'] = [action.as_dict() for action in self.actions]
|
||||
internal_dict['groups'] = [[action.as_dict() for action in group] for group in self.groups]
|
||||
internal_dict['groups'] = [group.as_dict() for group in self.groups]
|
||||
return internal_dict
|
||||
|
||||
class StoreAction(Action):
|
||||
|
@ -97,6 +98,8 @@ class StoreAction(Action):
|
|||
|
||||
class StoreConstAction(Action):
|
||||
def store_type_function(self, x):
|
||||
if x == 'false':
|
||||
x = None
|
||||
return self.const if x is not None else self.on_none
|
||||
|
||||
def __init__(self, action, **kwargs):
|
||||
|
@ -109,4 +112,4 @@ class MutuallyExclusiveGroup(ActionContainer):
|
|||
super().__init__()
|
||||
|
||||
def __repr__(self):
|
||||
return "Group Object: ( Actions: {}, Groups: {} )".format(self.actions, self.mutex_groups)
|
||||
return "Group Object: ( Actions: {}, Groups: {} )".format(self.actions, self.groups)
|
|
@ -73,8 +73,6 @@ def argParserGenerator(actionQueue, namespaceQueue):
|
|||
|
||||
actions, mutex_groups = self.get_actions()
|
||||
|
||||
print(actions, file=sys.__stdout__)
|
||||
|
||||
self.actionQueue.put((mutex_groups, actions, name, desc))
|
||||
namespace = self.namespaceQueue.get()
|
||||
return namespace
|
|
@ -4,11 +4,10 @@ import runpy
|
|||
from traceback import print_exc
|
||||
from contextlib import redirect_stdout, redirect_stderr
|
||||
|
||||
|
||||
from multiprocessing import Process
|
||||
|
||||
class Context(Process):
|
||||
def __init__(self, name, path, stdout, stderr, arguments=[], is_module = False, overwritten_modules={}):
|
||||
def __init__(self, name, path, stdout, stderr, arguments=[], is_module = False, overwritten_modules={}, original_modules=None):
|
||||
super().__init__()
|
||||
self.name = name
|
||||
self.stdout = stdout
|
||||
|
@ -17,12 +16,17 @@ class Context(Process):
|
|||
self.is_module = is_module
|
||||
self.arguments = arguments
|
||||
self.overwritten_modules = overwritten_modules
|
||||
self.original_modules = original_modules
|
||||
|
||||
def run(self):
|
||||
if self.arguments is not None:
|
||||
sys.argv = [''] + self.arguments
|
||||
else:
|
||||
sys.argv = ['']
|
||||
# if not self.original_modules is None:
|
||||
# sys.modules.clear()
|
||||
# sys.modules.update(self.original_modules)
|
||||
|
||||
sys.modules.update(self.overwritten_modules)
|
||||
with redirect_stdout(self.stdout):
|
||||
with redirect_stderr(self.stderr):
|
||||
|
@ -41,10 +45,5 @@ if __name__ == "__main__":
|
|||
parser.add_argument("--is-module", "-m", default=False, action="store_true", dest="is_module")
|
||||
args = parser.parse_args()
|
||||
|
||||
#newModule = ModuleType('argparse', 'Argument Parser')
|
||||
#newModule.__dict__.update(argparse.__dict__)
|
||||
#newModule.__dict__['ArgumentParser'].parse_args = f
|
||||
|
||||
print(args)
|
||||
context = Context("test", args.path, sys.stdout, sys.stderr, None if args.arguments is None else [args.arguments], args.is_module)
|
||||
context.start()
|
|
@ -1,3 +1,4 @@
|
|||
from . import savemodules
|
||||
import sys
|
||||
|
||||
import io
|
||||
|
@ -24,9 +25,15 @@ class QueuedOut(io.StringIO):
|
|||
lines = b.split('\n')
|
||||
for line in lines[:-1]:
|
||||
super().write(line)
|
||||
self.flush()
|
||||
self.explflush()
|
||||
super().write(lines[-1])
|
||||
|
||||
def explflush(self):
|
||||
value = self.getvalue()
|
||||
self.queue.put((self.name, value))
|
||||
self.seek(0)
|
||||
self.truncate(0)
|
||||
|
||||
def flush(self):
|
||||
value = self.getvalue()
|
||||
if len(value) > 1:
|
||||
|
@ -99,7 +106,8 @@ def start_module(name, is_module):
|
|||
ioout,
|
||||
ioerr,
|
||||
overwritten_modules={'argparse': argparser},
|
||||
is_module = is_module
|
||||
is_module = is_module,
|
||||
original_modules = savemodules.savedmodules
|
||||
)
|
||||
views.app.module_process.start()
|
||||
views.app.output.start()
|
||||
|
@ -111,6 +119,7 @@ def start_module(name, is_module):
|
|||
views.app.output.stop()
|
||||
|
||||
ioerr.write("Process stopped ({})\n".format(views.app.module_process.exitcode))
|
||||
views.app.output.queue.put(("sig", "stop"))
|
||||
views.app.restart.wait()
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ def main():
|
|||
group3 = group2.add_mutually_exclusive_group()
|
||||
group3.add_argument('--three')
|
||||
group3.add_argument('--four')
|
||||
bla.add_argument('--test', '-t', help="Blubb")
|
||||
bla.add_argument('--test', '-t', help="Blubb", default="Blabla")
|
||||
bla.add_argument('--bla', action="store_true", default=True)
|
||||
bla.add_argument('--blubb', action="store_true")
|
||||
bla.add_argument('-f', action="append")
|
|
@ -11,3 +11,4 @@ if __name__ == "__main__":
|
|||
args = parser.parse_args()
|
||||
|
||||
print("Subparser %s was selected" % args.command)
|
||||
print(args)
|
2
warped/savemodules.py
Normal file
|
@ -0,0 +1,2 @@
|
|||
import sys
|
||||
savedmodules = dict(sys.modules)
|
|
@ -7,6 +7,15 @@
|
|||
display: block;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
height:100%;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
#output {
|
||||
flex:1;
|
||||
|
@ -15,7 +24,7 @@
|
|||
font-weight: bold;
|
||||
font-family: "Lucida Console", Monaco, monospace;
|
||||
white-space:pre;
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
@ -62,7 +71,7 @@ li.subparser {
|
|||
|
||||
#arguments {
|
||||
padding: 0px;
|
||||
overflow: scroll;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tabs-panel {
|
Before Width: | Height: | Size: 147 KiB After Width: | Height: | Size: 147 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 921 B After Width: | Height: | Size: 921 B |
Before Width: | Height: | Size: 919 B After Width: | Height: | Size: 919 B |
Before Width: | Height: | Size: 922 B After Width: | Height: | Size: 922 B |
Before Width: | Height: | Size: 921 B After Width: | Height: | Size: 921 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 984 B After Width: | Height: | Size: 984 B |
Before Width: | Height: | Size: 876 B After Width: | Height: | Size: 876 B |
Before Width: | Height: | Size: 937 B After Width: | Height: | Size: 937 B |
Before Width: | Height: | Size: 956 B After Width: | Height: | Size: 956 B |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 976 B After Width: | Height: | Size: 976 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 896 B After Width: | Height: | Size: 896 B |
Before Width: | Height: | Size: 760 B After Width: | Height: | Size: 760 B |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 941 B After Width: | Height: | Size: 941 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 869 B After Width: | Height: | Size: 869 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 822 B After Width: | Height: | Size: 822 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 756 B After Width: | Height: | Size: 756 B |
Before Width: | Height: | Size: 986 B After Width: | Height: | Size: 986 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 929 B After Width: | Height: | Size: 929 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 881 B After Width: | Height: | Size: 881 B |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 984 B After Width: | Height: | Size: 984 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |