improvements all around

This commit is contained in:
2019-09-15 10:20:38 -04:00
parent 4e14c69dfc
commit 23d5eab472
5 changed files with 105 additions and 115 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ echo:
format:
black .
full-install: system-deps python-deps
full-install: system-deps python-deps @confirm
install: node-deps python-deps
python-deps:
+8 -10
View File
@@ -36,9 +36,7 @@ class BashProcess:
args = " ".join(args)
self.start_time = time.time()
self.sub = delegator.run(
f"{self.parent.path} {args}",
env=self.parent.environ,
block=blocking,
f"{self.parent.path} {args}", env=self.parent.environ, block=blocking
)
if blocking:
self.elapsed_time = time.time() - self.start_time
@@ -75,7 +73,9 @@ class BashProcess:
def __repr__(self) -> str:
"""string representation of the bash process"""
return f"<BashProcess pid={self.sub.pid!r} return_code={self.sub.return_code!r}>"
return (
f"<BashProcess pid={self.sub.pid!r} return_code={self.sub.return_code!r}>"
)
class Bash:
@@ -106,23 +106,21 @@ class Bash:
def command(self, script: str, debug=False, **kwargs) -> BashProcess:
"""form up the command with shlex and execute"""
tf = mkstemp(suffix='.sh', prefix='bashf-')[1]
tf = mkstemp(suffix=".sh", prefix="bashf-")[1]
with open(tf, 'w') as f:
with open(tf, "w") as f:
f.write(script)
# Mark the temporary file as executable.
st = os.stat(tf)
os.chmod(tf, st.st_mode | stat.S_IEXEC)
stdlib_path = os.path.join(
os.path.dirname(__file__), 'scripts', 'stdlib.sh'
)
stdlib_path = os.path.join(os.path.dirname(__file__), "scripts", "stdlib.sh")
# print(stdlib_path)
# cmd = f"bash -c {(script)}"
script = shlex_quote(f"unbuffer {tf} 2>&1 | bashf-indent")
cmd = f'bash --init-file {shlex_quote(stdlib_path)} -i -c {script} '
cmd = f"bash --init-file {shlex_quote(stdlib_path)} -i -c {script} "
if debug:
print(cmd)
+21 -19
View File
@@ -3,7 +3,7 @@ import json
from .bash import Bash
INDENT_STYLES = ('\t', ' ' * 4)
INDENT_STYLES = ("\t", " " * 4)
class NoBashfileFound(RuntimeError):
@@ -14,6 +14,15 @@ class TaskNotInBashfile(ValueError):
pass
class FlagNotAvailable(ValueError):
pass
class Flag:
def __init__(self, name):
self.name = name
class TaskScript:
def __init__(self, bashfile, chunk_index=None):
self.bashfile = bashfile
@@ -33,10 +42,8 @@ class TaskScript:
def depends_on(self, *, reverse=False, recursive=False):
def gen_tasks():
task_names = self.declaration_line.split(':')[1].split()
task_indexes = [
self.bashfile.find_chunk(task_name=n) for n in task_names
]
task_names = self.declaration_line.split(":")[1].split()
task_indexes = [self.bashfile.find_chunk(task_name=n) for n in task_names]
for i in task_indexes:
yield TaskScript(bashfile=self.bashfile, chunk_index=i)
@@ -67,7 +74,7 @@ class TaskScript:
@property
def name(self):
return self.chunk[0].split(':')[0].strip()
return self.chunk[0].split(":")[0].strip()
@property
def chunk(self):
@@ -81,7 +88,7 @@ class TaskScript:
@property
def source(self):
return '\n'.join([s for s in self._iter_source()])
return "\n".join([s for s in self._iter_source()])
@property
def source_lines(self):
@@ -132,7 +139,7 @@ class Bashfile:
def find_chunk(self, task_name):
for i, chunk in enumerate(self.chunks):
if chunk[0].split(':')[0].strip() == task_name:
if chunk[0].split(":")[0].strip() == task_name:
return i
def __iter__(self):
@@ -147,7 +154,7 @@ class Bashfile:
except json.JSONDecodeError:
assert os.path.exists(s)
# Assume a path was passed, instead.
with open(s, 'r') as f:
with open(s, "r") as f:
j = json.load(f)
self.environ.update(j)
@@ -158,34 +165,29 @@ class Bashfile:
@classmethod
def find(
Class,
*,
filename='Bashfile',
root=os.getcwd(),
max_depth=4,
topdown=True,
Class, *, filename="Bashfile", root=os.getcwd(), max_depth=4, topdown=True
):
"""Returns the path of a Pipfile in parent directories."""
i = 0
for c, d, f in os.walk(root, topdown=topdown):
if i > max_depth:
raise NoBashfileFound('No {filename} found!')
raise NoBashfileFound("No {filename} found!")
elif filename in f:
return Class(path=os.path.join(c, filename))
i += 1
@property
def source(self):
with open(self.path, 'r') as f:
with open(self.path, "r") as f:
return f.read()
@property
def source_lines(self):
return self.source.split('\n')
return self.source.split("\n")
@staticmethod
def _is_declaration_line(line):
return not (line.startswith(' ') or line.startswith('\t'))
return not (line.startswith(" ") or line.startswith("\t"))
@property
def tasks(self):
+40 -46
View File
@@ -10,78 +10,66 @@ def indent(line):
@click.command()
@click.argument(
'task',
"task",
type=click.STRING,
default='__LIST_ALL__',
default="__LIST_ALL__",
envvar="BASHFILE_TASK",
# required=False,
)
@click.option(
'--bashfile',
'-b',
default='__BASHFILE__',
envvar='BASHFILE_PATH',
"--bashfile",
"-b",
default="__BASHFILE__",
envvar="BASHFILE_PATH",
nargs=1,
type=click.Path(),
)
@click.option('--list', '-l', '_list', default=False, is_flag=True)
@click.option('--debug', default=False, is_flag=True, hidden=True)
@click.option('--shellcheck', default=False, is_flag=True, hidden=True)
@click.option("--list", "-l", "_list", default=False, is_flag=True)
@click.option("--debug", default=False, is_flag=True, hidden=True)
@click.option("--shellcheck", default=False, is_flag=True, hidden=True)
@click.option(
'--environ',
'-e',
"--environ",
"-e",
nargs=2,
type=click.STRING,
multiple=True,
help='task environment variable (can be passed multiple times).',
help="task environment variable (can be passed multiple times).",
)
@click.option(
'--fail',
'-x',
"--fail",
"-x",
is_flag=True,
type=click.BOOL,
help='Fail immediately, if any task fails.',
help="Fail immediately, if any task fails.",
)
@click.option(
'--arg',
'-a',
"--arg",
"-a",
nargs=1,
type=click.STRING,
multiple=True,
help='task ARGV arguments (can be passed multiple times).',
help="task ARGV arguments (can be passed multiple times).",
)
@click.option("--quiet", "-q", is_flag=True, type=click.BOOL, help="Reduce output.")
@click.option(
'--quiet', '-q', is_flag=True, type=click.BOOL, help='Reduce output.'
)
@click.option(
'--environ-json',
'-j',
"--environ-json",
"-j",
nargs=1,
type=click.STRING,
help='environment variables, in JSON format.',
help="environment variables, in JSON format.",
)
def task(
*,
task,
bashfile,
arg,
_list,
environ,
fail,
environ_json,
shellcheck,
debug,
quiet,
*, task, bashfile, arg, _list, environ, fail, environ_json, shellcheck, debug, quiet
):
"""bashf — Bashfile runner (the familiar Bash/Make hybrid)."""
# Default to list behavior, when no task is provided.
if task == '__LIST_ALL__':
if task == "__LIST_ALL__":
_list = True
task = None
if bashfile == '__BASHFILE__':
bashfile = Bashfile.find(root='.')
if bashfile == "__BASHFILE__":
bashfile = Bashfile.find(root=".")
if environ_json:
bashfile.add_environ_json(environ_json)
@@ -104,21 +92,27 @@ def task(
try:
task = bashfile[task]
except KeyError:
click.echo(crayons.red(f'Task {task!r} does not exist!'))
click.echo(crayons.red(f"Task {task!r} does not exist!"))
sys.exit(1)
# print(task)
for task in task.depends_on(recursive=True):
def execute_task(task):
if not quiet:
click.echo(crayons.yellow(f'Executing task {task.name!r}'))
click.echo(
crayons.white(" · ")
+ crayons.yellow(f"Executing task {task.name!r}")
+ crayons.white("")
)
return_code = task.execute()
if fail:
if not return_code == 0:
click.echo(f'Task {task.name!r} failed!')
click.echo(f"Task {task.name!r} failed!")
sys.exit(return_code)
click.echo('Done!')
for task in task.depends_on(recursive=True) + [task]:
execute_task(task)
click.echo(crayons.white(" · ") + crayons.green("Done") + crayons.white("!"))
sys.exit(0)
@@ -126,12 +120,12 @@ def entrypoint():
try:
main()
except KeyboardInterrupt:
print('ool beans.')
print("ool beans.")
def main():
task()
if __name__ == '__main__':
if __name__ == "__main__":
entrypoint()
+35 -39
View File
@@ -12,16 +12,16 @@ from shutil import rmtree
from setuptools import find_packages, setup, Command
# Package meta-data.
NAME = 'bashf'
DESCRIPTION = 'The familar Make / Bash hybrid.'
URL = 'https://github.com/kennethreitz/bashfile'
EMAIL = 'me@kennethreitz.org'
AUTHOR = 'Kenneth Reitz'
REQUIRES_PYTHON = '>=3.6.0'
VERSION = '0.1.0'
NAME = "bashf"
DESCRIPTION = "The familar Make / Bash hybrid."
URL = "https://github.com/kennethreitz/bashfile"
EMAIL = "me@kennethreitz.org"
AUTHOR = "Kenneth Reitz"
REQUIRES_PYTHON = ">=3.6.0"
VERSION = "0.1.0"
# What packages are required for this module to be executed?
REQUIRED = ['click', 'delegator.py', 'crayons']
REQUIRED = ["click", "delegator.py", "crayons"]
# What packages are optional?
EXTRAS = {
@@ -38,8 +38,8 @@ here = os.path.abspath(os.path.dirname(__file__))
# Import the README and use it as the long-description.
# Note: this will only work if 'README.md' is present in your MANIFEST.in file!
try:
with io.open(os.path.join(here, 'README.md'), encoding='utf-8') as f:
long_description = '\n' + f.read()
with io.open(os.path.join(here, "README.md"), encoding="utf-8") as f:
long_description = "\n" + f.read()
except FileNotFoundError:
long_description = DESCRIPTION
@@ -47,22 +47,22 @@ except FileNotFoundError:
about = {}
if not VERSION:
project_slug = NAME.lower().replace("-", "_").replace(" ", "_")
with open(os.path.join(here, project_slug, '__version__.py')) as f:
with open(os.path.join(here, project_slug, "__version__.py")) as f:
exec(f.read(), about)
else:
about['__version__'] = VERSION
about["__version__"] = VERSION
class UploadCommand(Command):
"""Support setup.py upload."""
description = 'Build and publish the package.'
description = "Build and publish the package."
user_options = []
@staticmethod
def status(s):
"""Prints things in bold."""
print('\033[1m{0}\033[0m'.format(s))
print("\033[1m{0}\033[0m".format(s))
def initialize_options(self):
pass
@@ -72,22 +72,20 @@ class UploadCommand(Command):
def run(self):
try:
self.status('Removing previous builds…')
rmtree(os.path.join(here, 'dist'))
self.status("Removing previous builds…")
rmtree(os.path.join(here, "dist"))
except OSError:
pass
self.status('Building Source and Wheel (universal) distribution…')
os.system(
'{0} setup.py sdist bdist_wheel --universal'.format(sys.executable)
)
self.status("Building Source and Wheel (universal) distribution…")
os.system("{0} setup.py sdist bdist_wheel --universal".format(sys.executable))
self.status('Uploading the package to PyPI via Twine…')
os.system('twine upload dist/*')
self.status("Uploading the package to PyPI via Twine…")
os.system("twine upload dist/*")
self.status('Pushing git tags…')
os.system('git tag v{0}'.format(about['__version__']))
os.system('git push --tags')
self.status("Pushing git tags…")
os.system("git tag v{0}".format(about["__version__"]))
os.system("git push --tags")
sys.exit()
@@ -95,34 +93,32 @@ class UploadCommand(Command):
# Where the magic happens:
setup(
name=NAME,
version=about['__version__'],
version=about["__version__"],
description=DESCRIPTION,
long_description=long_description,
long_description_content_type='text/markdown',
long_description_content_type="text/markdown",
author=AUTHOR,
author_email=EMAIL,
python_requires=REQUIRES_PYTHON,
url=URL,
packages=find_packages(
exclude=["tests", "*.tests", "*.tests.*", "tests.*"]
),
packages=find_packages(exclude=["tests", "*.tests", "*.tests.*", "tests.*"]),
# If your package is a single module, use this instead of 'packages':
# py_modules=['mypackage'],
entry_points={'console_scripts': ['bashf=bashf.cli:entrypoint']},
entry_points={"console_scripts": ["bashf=bashf.cli:entrypoint"]},
install_requires=REQUIRED,
extras_require=EXTRAS,
include_package_data=True,
license='MIT',
license="MIT",
classifiers=[
# Trove classifiers
# Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
'License :: OSI Approved :: MIT License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
],
# $ setup.py publish support.
cmdclass={'upload': UploadCommand},
cmdclass={"upload": UploadCommand},
)