Merge branch 'test'

This commit is contained in:
Ian Adam Naval 2015-03-02 12:29:11 -05:00
commit 26529fe5a1
14 changed files with 146 additions and 25 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
__pycache__ __pycache__
*.pyc *.pyc
*.egg-info

44
README.md Normal file
View 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
View 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()

View File

@ -38,9 +38,11 @@ class BaseCommand(object):
return cmd return cmd
registered_cmds = [] registered_cmds = {}
def register_cmd(cls): def register_cmd(name):
def decorator(cls):
"""Decorator for putting all of the commands in one nice place.""" """Decorator for putting all of the commands in one nice place."""
registered_cmds.append(cls.__name__) registered_cmds[name] = cls
return cls return cls
return decorator

View File

@ -2,10 +2,8 @@ import atexit
import code import code
import os import os
import readline import readline
import shlex
from commands import registered_cmds from psh.commands import registered_cmds
import example_cmd
DEFAULT_HISTORY_FILE = "~/.psh_history" DEFAULT_HISTORY_FILE = "~/.psh_history"
@ -24,9 +22,10 @@ def parse_cmd(potential_cmd):
if args: if args:
args = args[1:] args = args[1:]
if cmd_name not in registered_cmds: if cmd_name not in registered_cmds:
return "RawCommand({})".format(shlex.split(potential_cmd)) return "RawCommand('{}')".format(potential_cmd)
else: 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): def parse_cmds(raw_input_line):

View File

@ -1,8 +1,8 @@
from commands import BaseCommand, register_cmd from psh.commands import BaseCommand, register_cmd
@register_cmd @register_cmd("example")
class example_cmd(BaseCommand): class Example(BaseCommand):
"""Simple command that just returns 'example' and 'command'. Does """Simple command that just returns 'example' and 'command'. Does
nothing at all with the input.""" nothing at all with the input."""
@ -13,13 +13,13 @@ class example_cmd(BaseCommand):
return output_generator return output_generator
@register_cmd @register_cmd("echo")
class echo(BaseCommand): class Echo(BaseCommand):
"""Echoes anything from the command line arguments as well as input """Echoes anything from the command line arguments as well as input
from the previous command.""" from the previous command."""
def __init__(self, args): def __init__(self, args=[]):
super(echo, self).__init__() super(Echo, self).__init__()
self.args = args self.args = args
def call(self,*args,**kwargs): def call(self,*args,**kwargs):
@ -30,4 +30,3 @@ class echo(BaseCommand):
for line in input_generator: for line in input_generator:
yield line yield line
return output_generator return output_generator

View File

@ -1,4 +1,4 @@
from commands import BaseCommand from psh.commands import BaseCommand
class Printer(BaseCommand): class Printer(BaseCommand):

View File

@ -1,5 +1,7 @@
from formatters import Printer import shlex
from commands import BaseCommand
from psh.formatters import Printer
from psh.commands import BaseCommand
class RawCommand(BaseCommand): class RawCommand(BaseCommand):
@ -15,7 +17,7 @@ class RawCommand(BaseCommand):
input_generator = self.get_input_generator() input_generator = self.get_input_generator()
import subprocess import subprocess
try: 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(): def make_output_generator():
input_str = b"" input_str = b""
for line in input_generator: for line in input_generator:

View File

@ -1,8 +1,10 @@
import os import os
import os.path import os.path
from formatters import * from psh.formatters import *
from raw_commands import RawCommand
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 # Load all of the commands in the path into the global namespace as raw
# commands. # commands.
@ -15,7 +17,7 @@ for path in os.environ['PATH'].split(':'):
def main(): def main():
from console import HistoryConsole from psh.console import HistoryConsole
console = HistoryConsole(globals()) console = HistoryConsole(globals())
console.interact("Augmented Unix Userland") console.interact("Augmented Unix Userland")

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
pytest

20
setup.py Normal file
View 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
View 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
View 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()