Skip to content

Commit

Permalink
Initial
Browse files Browse the repository at this point in the history
  • Loading branch information
hzlmn committed Mar 19, 2018
0 parents commit 75d1883
Show file tree
Hide file tree
Showing 17 changed files with 352 additions and 0 deletions.
18 changes: 18 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.py]
indent_size = 4

[Makefile]
indent_style = tab

[*.md]
trim_trailing_whitespace = false
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto
108 changes: 108 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
.static_storage/
.media/
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

sample/
.vscode/
.pytest_cache/
Empty file added .pyup.yml
Empty file.
Empty file added .travis.yml
Empty file.
Empty file added LICENSE.txt
Empty file.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[WIP] Tool for writing modular shell scripts
3 changes: 3 additions & 0 deletions msh/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__version__ = '0.0.3'

__description__ = 'Tool for writing modular shell scripts'
29 changes: 29 additions & 0 deletions msh/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import sys
from argparse import ArgumentParser
from pathlib import Path

from . import __description__ as desc


def entrypoint():
parser = ArgumentParser(
prog='msh',
description=desc,
)

parser.add_argument(
'--entry',
required=True,
type=Path,
)

parser.add_argument(
'--output',
type=Path,
default='build.sh'
)

options = parser.parse_args(sys.argv[1:])

if not options.entry.exists():
sys.stdout.write('Please provide existing path\n')
28 changes: 28 additions & 0 deletions msh/graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from .node import Node


class Graph:
def __init__(self):
self._graph = {}

def add_node(self, key, params=None):
if not isinstance(key, str):
raise TypeError('key should a str')

if key in self._graph:
return self._graph[key]

self._graph[key] = Node(key, params)
return self._graph[key]

def get_node(self, key):
return self._graph.get(key)

def add_edge(self, start, end):
self.add_node(start).add_edge(self.add_node(end))

def get_connections(self, key):
if key not in self._graph:
return []

return self._graph[key].get_connections()
22 changes: 22 additions & 0 deletions msh/node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Node:
def __init__(self, key, params=None):
if not isinstance(key, str):
raise TypeError('key should be str')

self.key = key
if params is None:
params = {}
self.params = params
self.edges = []

@property
def id(self):
return self.key

def add_edge(self, node):
assert isinstance(node, Node)
if node not in self.edges:
self.edges.append(node)

def get_connections(self):
return self.edges
24 changes: 24 additions & 0 deletions msh/resolve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from .node import Node


def resolve(node):
if not isinstance(node, Node):
raise ValueError('value should be instance of Node')

unresolved, resolved = [], []

def traverse(node, resolved, unresolved):
unresolved.append(node)
connections = node.get_connections()
for edge in connections:
if edge not in resolved:
if edge in unresolved:
return
traverse(edge, resolved, unresolved)

resolved.append(node)
unresolved.remove(node)

traverse(node, resolved, unresolved)

return resolved
62 changes: 62 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import io
import os
import re
import sys

from setuptools import setup


def get_variable(var_name):
regex = "__{var_name}__\s=\s\'(?P<{var_name}>.*)\'".format(
var_name=var_name)

path = ('msh', '__init__.py',)

return re.search(regex, read(*path)).group(var_name)


def read(*parts):
filename = os.path.join(os.path.abspath(os.path.dirname(__file__)), *parts)

sys.stdout.write(filename)

with io.open(filename, encoding='utf-8', mode='rt') as fp:
return fp.read()


packages = ['msh']


install_requires = []


classifiers = ['Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python',
'Programming Language :: Python :: 3.6',
]

setup(
name='msh',
version=get_variable('version'),
description=get_variable('description'),
long_description=read('README.md'),
url='https://github.com/msh-contrib/msh',
author='Oleh Kuchuk',
author_email='[email protected]',
license=read('LICENSE.txt'),
packages=packages,
install_requires=install_requires,
zip_safe=False,
classifiers=classifiers,
entry_points={
'console_scripts': [
'msh=msh.cli:entrypoint'
],
},
keywords=[
'shell',
'posix',
'bash',
],
)
Empty file added tests/conftest.py
Empty file.
5 changes: 5 additions & 0 deletions tests/test_graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from msh.graph import Graph


def test_graph():
pass
30 changes: 30 additions & 0 deletions tests/test_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import pytest

from msh.node import Node


def test_key():
node = Node('a')
assert node.key == 'a'


def test_fail_on_wrong_type():
with pytest.raises(TypeError):
Node([])


def test_add_edge():
a, b = Node('a'), Node('b')
a.add_edge(b)
assert b in a.edges


def test_add_edge_wrong_param():
with pytest.raises(AssertionError):
Node('a').add_edge([])


def test_get_connections():
a, b = Node('a'), Node('b')
a.add_edge(b)
assert a.get_connections() == [b]
21 changes: 21 additions & 0 deletions tests/test_resolve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from msh.resolve import resolve
from msh.graph import Graph
from msh.node import Node


def test_resolve():
graph = Graph()
graph.add_node('a')
graph.add_node('b')
graph.add_node('c')
graph.add_node('d')

graph.add_edge('a', 'b')
graph.add_edge('b', 'c')
graph.add_edge('c', 'a')
# graph.add_edge('a', 'c')
# graph.add_edge('a', 'd')


assert list(map(lambda node: node.id, resolve(
graph.get_node('a')))) == ['b', 'a']

0 comments on commit 75d1883

Please sign in to comment.