Merge branch 'test'
This commit is contained in:
commit
26529fe5a1
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
__pycache__
|
||||
*.pyc
|
||||
*.egg-info
|
||||
|
44
README.md
Normal file
44
README.md
Normal file
@ -0,0 +1,44 @@
|
||||
psh
|
||||
===
|
||||
|
||||
Augmented Unix Userland shell inspired by Windows PowerShell, written in Python.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
* Python 3+
|
||||
* pip
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
Preferably, you would use a separate virtual env
|
||||
|
||||
```
|
||||
pip install -r requirements.txt
|
||||
pip install -e . # installs the 'psh' package in editable mode
|
||||
```
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
From Python shell:
|
||||
|
||||
```
|
||||
from psh.run import main
|
||||
main()
|
||||
```
|
||||
|
||||
From Unix shell:
|
||||
```
|
||||
python -m psh.run
|
||||
```
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
From Unix shell:
|
||||
|
||||
```
|
||||
py.test
|
||||
```
|
11
psh/__init__.py
Normal file
11
psh/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
from psh.commands import registered_cmds
|
||||
|
||||
# Import the exported commands
|
||||
from psh.example_cmd import *
|
||||
|
||||
# Instantiate the registered commands
|
||||
for name, cls in registered_cmds.items():
|
||||
globals()[name] = cls()
|
||||
|
||||
# Only export the names of registered commands
|
||||
__all__ = registered_cmds.keys()
|
@ -38,9 +38,11 @@ class BaseCommand(object):
|
||||
return cmd
|
||||
|
||||
|
||||
registered_cmds = []
|
||||
registered_cmds = {}
|
||||
|
||||
def register_cmd(cls):
|
||||
"""Decorator for putting all of the commands in one nice place."""
|
||||
registered_cmds.append(cls.__name__)
|
||||
return cls
|
||||
def register_cmd(name):
|
||||
def decorator(cls):
|
||||
"""Decorator for putting all of the commands in one nice place."""
|
||||
registered_cmds[name] = cls
|
||||
return cls
|
||||
return decorator
|
@ -2,10 +2,8 @@ import atexit
|
||||
import code
|
||||
import os
|
||||
import readline
|
||||
import shlex
|
||||
|
||||
from commands import registered_cmds
|
||||
import example_cmd
|
||||
from psh.commands import registered_cmds
|
||||
|
||||
DEFAULT_HISTORY_FILE = "~/.psh_history"
|
||||
|
||||
@ -24,9 +22,10 @@ def parse_cmd(potential_cmd):
|
||||
if args:
|
||||
args = args[1:]
|
||||
if cmd_name not in registered_cmds:
|
||||
return "RawCommand({})".format(shlex.split(potential_cmd))
|
||||
return "RawCommand('{}')".format(potential_cmd)
|
||||
else:
|
||||
return "{0}({1})".format(cmd_name,str(args))
|
||||
cls = registered_cmds[cmd_name].__name__
|
||||
return "{0}({1})".format(cls, str(args))
|
||||
|
||||
|
||||
def parse_cmds(raw_input_line):
|
@ -1,8 +1,8 @@
|
||||
from commands import BaseCommand, register_cmd
|
||||
from psh.commands import BaseCommand, register_cmd
|
||||
|
||||
|
||||
@register_cmd
|
||||
class example_cmd(BaseCommand):
|
||||
@register_cmd("example")
|
||||
class Example(BaseCommand):
|
||||
"""Simple command that just returns 'example' and 'command'. Does
|
||||
nothing at all with the input."""
|
||||
|
||||
@ -13,13 +13,13 @@ class example_cmd(BaseCommand):
|
||||
return output_generator
|
||||
|
||||
|
||||
@register_cmd
|
||||
class echo(BaseCommand):
|
||||
@register_cmd("echo")
|
||||
class Echo(BaseCommand):
|
||||
"""Echoes anything from the command line arguments as well as input
|
||||
from the previous command."""
|
||||
|
||||
def __init__(self, args):
|
||||
super(echo, self).__init__()
|
||||
def __init__(self, args=[]):
|
||||
super(Echo, self).__init__()
|
||||
self.args = args
|
||||
|
||||
def call(self,*args,**kwargs):
|
||||
@ -30,4 +30,3 @@ class echo(BaseCommand):
|
||||
for line in input_generator:
|
||||
yield line
|
||||
return output_generator
|
||||
|
@ -1,4 +1,4 @@
|
||||
from commands import BaseCommand
|
||||
from psh.commands import BaseCommand
|
||||
|
||||
|
||||
class Printer(BaseCommand):
|
@ -1,5 +1,7 @@
|
||||
from formatters import Printer
|
||||
from commands import BaseCommand
|
||||
import shlex
|
||||
|
||||
from psh.formatters import Printer
|
||||
from psh.commands import BaseCommand
|
||||
|
||||
|
||||
class RawCommand(BaseCommand):
|
||||
@ -15,7 +17,7 @@ class RawCommand(BaseCommand):
|
||||
input_generator = self.get_input_generator()
|
||||
import subprocess
|
||||
try:
|
||||
p = subprocess.Popen(self.cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
p = subprocess.Popen(shlex.split(self.cmd), stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
def make_output_generator():
|
||||
input_str = b""
|
||||
for line in input_generator:
|
@ -1,8 +1,10 @@
|
||||
import os
|
||||
import os.path
|
||||
|
||||
from formatters import *
|
||||
from raw_commands import RawCommand
|
||||
from psh.formatters import *
|
||||
|
||||
from psh.example_cmd import Echo, Example
|
||||
from psh.raw_commands import RawCommand
|
||||
|
||||
# Load all of the commands in the path into the global namespace as raw
|
||||
# commands.
|
||||
@ -15,7 +17,7 @@ for path in os.environ['PATH'].split(':'):
|
||||
|
||||
|
||||
def main():
|
||||
from console import HistoryConsole
|
||||
from psh.console import HistoryConsole
|
||||
console = HistoryConsole(globals())
|
||||
console.interact("Augmented Unix Userland")
|
||||
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
pytest
|
20
setup.py
Normal file
20
setup.py
Normal file
@ -0,0 +1,20 @@
|
||||
import os
|
||||
from setuptools import setup
|
||||
|
||||
# Utility function to read the README file.
|
||||
# Used for the long_description. It's nice, because now 1) we have a top level
|
||||
# README file and 2) it's easier to type in the README file than to put a raw
|
||||
# string in below ...
|
||||
def read(fname):
|
||||
return open(os.path.join(os.path.dirname(__file__), fname)).read()
|
||||
|
||||
setup(
|
||||
name = "psh",
|
||||
version = "0.0.1",
|
||||
author = "WPI Augmented Unix Userland MQP",
|
||||
author_email = "jsh@wpi.edu",
|
||||
description = ("Simple Unix shell inspired by PowerShell"),
|
||||
license = "MIT",
|
||||
packages=['psh'],
|
||||
long_description=read('README.md'),
|
||||
)
|
19
test/test_example_cmd.py
Normal file
19
test/test_example_cmd.py
Normal file
@ -0,0 +1,19 @@
|
||||
import pytest
|
||||
|
||||
from psh import echo, example
|
||||
from utils import TestFormatter
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_formatter():
|
||||
return TestFormatter()
|
||||
|
||||
|
||||
def test_example_cmd_should_return_two_things(test_formatter):
|
||||
example.chain(test_formatter).call()
|
||||
assert "examplecommand" == test_formatter.get_data()
|
||||
|
||||
|
||||
def test_echo_should_echo(test_formatter):
|
||||
example.chain(echo).chain(test_formatter).call()
|
||||
assert "examplecommand" == test_formatter.get_data()
|
21
test/utils.py
Normal file
21
test/utils.py
Normal file
@ -0,0 +1,21 @@
|
||||
from psh.commands import BaseCommand
|
||||
|
||||
from io import StringIO
|
||||
|
||||
class TestFormatter(BaseCommand):
|
||||
"""Formatter useful for tests. Instead of printing to stdout, it
|
||||
stores any output inside a stringio buffer. This can be retrieved
|
||||
with the get_data method."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestFormatter, self).__init__(*args, **kwargs)
|
||||
self.buffer = StringIO()
|
||||
|
||||
def call(self):
|
||||
input_generator = self.get_input_generator()
|
||||
for line in input_generator:
|
||||
self.buffer.write(line.decode('utf-8'))
|
||||
return None
|
||||
|
||||
def get_data(self):
|
||||
return self.buffer.getvalue()
|
Reference in New Issue
Block a user