Skip to content

Commit

Permalink
Alpha Release
Browse files Browse the repository at this point in the history
Today we are going to release v1.0.0.a1 of pyshell.

Some big changes have come in this release. We no longer are using defined functions but instead we parse the caller for commands. More information on this is in the official docs listed in the readme.
volitank committed Oct 31, 2021

Verified

This commit was signed with the committer’s verified signature.
1 parent 6db563c commit eac03b2
Showing 13 changed files with 1,056 additions and 264 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
# pyshell
A Linux subprocess module, An easier way to interact with the Linux shell
>pyshell should be cross platform but has only been tested with linux
# Installation
`$ pip install pyshell`

# Usage

from pyshell import pyshell
shell = pyshell()
shell.echo('Hello', "GitHub!")
# Docs

Check out the Official [Documentation](https://volitank.com/pyshell/index.html) for help with syntax and different arguments
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
35 changes: 35 additions & 0 deletions docs/make.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@ECHO OFF

pushd %~dp0

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build

if "%1" == "" goto help

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end

:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%

:end
popd
45 changes: 45 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Configuration file for the Sphinx documentation builder.
import sphinx_rtd_theme
# pyshell must be installed for this import to work
import pyshell
# -- Project information

project = pyshell.__title__
copyright = pyshell.__copyright__
author = pyshell.__author__
version = pyshell.__version__

# -- General configuration

extensions = [
'sphinx.ext.duration',
'sphinx.ext.doctest',
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
'sphinx.ext.intersphinx',
]

intersphinx_mapping = {
'python': ('https://docs.python.org/3/', None),
'sphinx': ('https://www.sphinx-doc.org/en/master/', None),
}
intersphinx_disabled_domains = ['std']

templates_path = ['_templates']

# -- Options for HTML output
# import sphinx_bootstrap_theme

html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
pygments_style = 'sphinx'
html_theme_options = {
'collapse_navigation': False,
'display_version': False,
}
# html_theme = 'bootstrap'
# html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()

# -- Options for EPUB output
epub_show_urls = 'footnote'
intersphinx_mapping = {"python": ("https://docs.python.org/3.9", None)}
19 changes: 19 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.. toctree::
:hidden:

usage

Welcome to pyshell's documentation!
===================================

**pyshell** is a Linux subprocess module,
an easier way to interact with the Linux shell.
Check out the *source* code at pyshell's `Official Github <https://github.com/volitank/pyshell>`_

Check out the :doc:`usage` section for further information, including :ref:`installation` of the project.

.. note::

This project is under active development.

Thank you for checking us out.
26 changes: 26 additions & 0 deletions docs/source/sections/initial_setup.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.. _installation:

Installation
============

To use pyshell, first install it using pip:

.. code-block:: console
$ pip install pyshell
.. _initialization:

Initialization
--------------

Now that it's installed you can import and initialize:

.. code-block:: python
from pyshell import pyshell
shell = pyshell(logfile='/tmp/shell.log', shell='/bin/bash')
shell.echo('Hello', 'World')
.. seealso:: :ref:`pyshell_arguments`
90 changes: 90 additions & 0 deletions docs/source/sections/passing_arguments.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
.. _passing_arguments:

Passing Arguments
=================

When passing multiple arguments to a command, each argument *must* be a separate
string:

.. code-block:: python
shell = pyshell()
shell.tar("cvf", "/tmp/test.tar", "/my/home/directory/")
This *will not work*:

.. code-block:: python
shell.tar("cvf /tmp/test.tar /my/home/directory")
If you're using the ``shell='/bin/bash'`` Both of these will work. This is because pyshell does some special magic behind the scenes so the shell gets exactly what you told it. This feature is almost certainly to be buggy, example with output in comments:

.. code-block:: python
dash = pyshell(shell='/bin/dash')
dash.echo('shell is $0')
# shell is /bin/dash
dash.echo('shell', 'is' ,'$0')
# shell is /bin/dash
dash.run(["echo", "shell", "is", "$0"], shell='/bin/bash')
# shell is /bin/bash ## Notice we can change the shell one time on the fly and not effect the instance
dash.run(["echo shell is $0"])
# shell is /bin/dash
dash("echo", "shell", "is", "$0")
# shell is /bin/dash
dash("echo shell is $0")
# shell is /bin/dash
dash("echo", "shell", "is", "$0", shell=DEFAULT)
# shell is $0 ## We can override back to no shell using the DEFAULT switch.
dash("echo shell is $0", shell=DEFAULT)
# FileNotFoundError: [Errno 2] No such file or directory: 'echo shell is $0'
# This one doesn't work because with out a shell it tries to use that string as a command
The only difference between using our run function and calling the class directly is with run you must use a list, as seen above. If something isn't working by calling the class then you can try to call run directly, it *should* work with run.

.. seealso:: Pyshell Arguments section on the :ref:`pyshell_arguments_shell`

Dashes
------

For commands with dashes we use underscores instead. All underscores in the caller will be converted to dashes. Anything in the args section will not. Below is an example of different ways to run commands and their outputs in comments below so you can get an idea as how it works.

.. code-block:: python
shell = pyshell()
shell.echo.test_echo()
# test-echo
shell.echo("test_echo")
# test_echo
shell.run(["echo_test"])
# FileNotFoundError: [Errno 2] No such file or directory: 'echo_test'
shell("echo_test")
# pyshell.pyshell.CommandNotFound: command echo_test does not exist
shell.echo_test()
# pyshell.pyshell.CommandNotFound: command echo-test does not exist
Equivalents
-----------

There are many ways to go about running commands in pyshell. For example all commands below are equal.

.. code-block:: python
shell = pyshell()
shell.mkfs.ext4('/dev/sdb1')
shell.run(['mkfs.ext4', '/dev/sdb1'])
shell('mkfs.ext4', '/dev/sdb1')
When initialized we build a tuple of program names that you can use. If you do something like ``shell.mkfs.ext4.hello`` we take ``mkfs.ext4.hello`` and test it against that tuple. If nothing is found we try ``mkfs.ext4``, and then ``mkfs``. if something matches we stop, use that as the command and then the rest as arguments. This is how we are able to accommodate some commands.
97 changes: 97 additions & 0 deletions docs/source/sections/pyshell_arguments.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
.. _pyshell_arguments:

Pyshell Arguments
=================

Defaults
--------

.. code-block:: python
input=None, capture_output=False, check=False,
logfile=None, timeout=None, alias: dict=None, **kwargs):
Most of pyshell's arguments are much like subprocess.run.
We actually use a slightly modified version of subprocess.run

input
-----

With pyshell input can be either a string or bytes, and there is no need to specify ``text=true`` as with subprocess

.. code-block:: python
shell = pyshell(input='this will go to stdin')
capture_output
--------------

This option works exactly like the one in subprocess. It essentially sets stdout and stderr to PIPE

check
-----

Another option we have that works just like subprocess. If set to true it will throw an exception on a bad exit code

.. code-block:: python
shell = pyshell(capture_output=False, check=True)
logfile
-------

This is our first totally new option. Setting this will change stdout and stderr to a file. This cannot be used with **capture_output**. This should be a file in string or PathLike format.


.. code-block:: python
shell = pyshell(logfile='/tmp/pyshell.log')
timeout
-------

Sets the timeout, in seconds, that we will wait on a child process.

.. code-block:: python
shell = pyshell(timeout=10)
alias
-----

This one is probably one of my favorite additions. To set this it is simple a dict with Key=command Value=alias.

.. code-block:: python
alias = {
'ls': ['ls', '-lah', '--color'],
'echo': ['printf']
}
shell = pyshell(alias=alias)
You can also change them on the fly with ``shell.setAlias(ls, ['-lah'])``

.. _pyshell_arguments_shell:

shell
-----

While this is technically a kwarg we do run with a patched Popen that allows you to set the shell for ``shell=`` rather than True and setting ``executable=``. Another change that I've made is that you can pass a list for ``shell=`` if you want your executable to have more options than just ``-c``

.. code-block:: python
shell = pyshell(shell='/bin/dash') # Sets shell=True and executable='/bin/dash'
shell = pyshell(shell=['/bin/bash', '-O', 'extglob']) # Same as above yet we will have extglob for bash
All other ``**kwargs`` are passed directly to Popen. If you're interested check out their documentation for more options.

override
--------

Any settings whether initialized or not can be overrode while running your commands. Any option other than the defaults will override, to override to the default we have a special constant.

.. code-block:: python
shell = pyshell(shell='/bin/dash', logfile='/tmp/pyshell.log')
shell.echo('Hello', 'World', logfile=DEFAULT) # This will run this command with the logfile set to the default of None
shell.echo('Hello', 'World', shell='/bin/bash') # This will change our shell to bash for this command only
8 changes: 8 additions & 0 deletions docs/source/usage.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Usage
=====

.. toctree::

sections/initial_setup
sections/pyshell_arguments
sections/passing_arguments
264 changes: 0 additions & 264 deletions pyshell.py

This file was deleted.

8 changes: 8 additions & 0 deletions pyshell/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

__title__ = 'pyshell'
__author__ = 'volitank'
__license__ = 'GPLv3'
__copyright__ = '2021 volitank'
__version__ = '1.0.0.a1'

from .pyshell import pyshell, DEFAULT, DEVNULL, PIPE, STDOUT
644 changes: 644 additions & 0 deletions pyshell/pyshell.py

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Always prefer setuptools over distutils
from setuptools import setup, find_packages
from pathlib import Path
import pyshell
# Define the directory that setup.py is in
here = Path(__file__).parent.resolve()

# Get the long description from the README file
long_description = (here / 'README.md').read_text(encoding='utf-8')

# Arguments marked as "Required" below must be included for upload to PyPI.
# Fields marked as "Optional" may be commented out.

setup(
name=pyshell.__title__, # Required
version=pyshell.__version__, # Required
description='A Linux subprocess module.', # Optional
long_description=long_description, # Optional
long_description_content_type='text/markdown', # Optional (see note above)
url='https://github.com/volitank/pyshell', # Optional
author=pyshell.__author__, # Optional
author_email='blake@volitank.com', # Optional
classifiers=[ # Optional
# List of classifiers https://gist.github.com/nazrulworld/3800c84e28dc464b2b30cec8bc1287fc
'Development Status :: 1 - Planning',
'Environment :: Console',
'Intended Audience :: System Administrators',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
'Natural Language :: English',
'Operating System :: POSIX :: Linux',
'Topic :: System :: Operating System Kernels :: Linux',
'Topic :: System :: Systems Administration',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3 :: Only',
],

keywords='python, shell, subprocess', # Optional
packages=['pyshell'], # Required
python_requires='>=3.6, <4',

project_urls={ # Optional
'Documentation': 'https://volitank.com/pyshell',
'Source': 'https://github.com/volitank/pyshell',
},
)

0 comments on commit eac03b2

Please sign in to comment.